How to monkeypatch Ruby properly? - ruby

I'm trying to monkeypatch a line in Net class in the standard library. I created a file called patches.rb into the lib folder of the project and added this
module Net
class HTTP < Protocol
module HTTPHeader
def initialize_http_header(initheader)
#header = {}
return unless initheader
initheader.each do |key, value|
#header[key.downcase] = [value.strip] rescue ""
end
end
end
end
end
But it doesn't work. Am I doing this right? (That parallels the inheritance hierarchy exactly.)
Edit: part of the problem was I had to put the file in the initalizers folder. But still seeing the same error.

Since things in the lib/ directory are only loaded on demand, you may have more success putting patches like this in config/initializers/ where they are automatically loaded after the stack has been initialized.
You can also collapse the definition for extensions to something like this:
module Net::HTTP::HTTPHeader
# ... (redefined methods) ...
end

Related

Unable to override RSpec Sysntax module method

I wanted to override the expect in under Syntax module. So, i have placed the below code into the .config/initializers/syntax.rb file
module RSpec
module Expectations
module Syntax
def enable_expect(syntax_host=::RSpec::Matchers)
return if expect_enabled?(syntax_host)
syntax_host.module_exec do
def expect(value=::RSpec::Expectations::ExpectationTarget::UndefinedValue, &block)
::RSpec::Expectations::ExpectationTarget.for(value, block)
end
end
end
end
end
end
And required this inside the env.rb file.
require_relative '../../.config/initializers/syntax'
This is not overriding the existing method. I'm using RSpec gem 3.2.0
What went wrong with the configuration?
I suggest you to put this override codes in spec/support directory and require it in rails or spec helper instead of putting in initializers.

Extending Modules from a gem instead of monkey patching

Assuming that "clean" Ruby monkey patching isn't an option, and all gem extension methods need to be completely contained within their own namespace, and given a structure like
module ARubyGem
class GemClassOne
def method1
# instance method to be available when calling extended module
end
def self.method2
# singleton method to also be available in extended module
end
end
end
Then which of these is the better way to extend a gem if the original gem code has some singleton methods that begin with "self"?
module ARubyGemExtension
class GemClassOneExtension < ARubyGem::GemClassOne
def method_to_override
# new code here
end
end
end
vs.
module ARubyGemExtension
include ARubyGem
class GemClassOneExtension
def method_to_override
# new code here
end
end
end
Seems like you would need to go with:
module ARubyGemExtension
class GemClassOneExtension < ARubyGem::GemClassOne
def method_to_override
# new code here
end
end
end
(Remember to namespace GemClassOne.)
I don't know anything about the gem you're using, but unless it's specifically set up for providing methods through include, I think you may not get what you're after.

How to parse and dump Ruby config files?

In this blog post he gives this example of a Ruby config file.
config do
allow ['server.com', `hostname`.strip]
vhost 'api.server.com' do
path ‘/usr/local/api’
end
vhost 'www.server.com' do
path '/usr/local/web'
end
%w{wiki blog support}.each do |host|
vhost "#{host}.server.com" do
path "/usr/local/#{host}"
end
end
end
I think of a hash after a config file have been loaded, but maybe that is not how this type of configs are intended for...
Update
If I execute it, I get
$ ruby config.rb
config.rb:2:in `<main>': undefined method `config' for main:Object (NoMethodError)
Question
What Ruby code is needed to parse and dump the content of this config file?
That config example is not directly loadable and, if I understand the blog post author correctly, it's not meant to be either so there's no easy way of loading/parsing that example.
The key part is in the blog post where he states "build simple DSLs to design semantically robust config files without the underlying ruby being conspicuous" (my emphasis). The 'underlying ruby' I take to mean the code that enables the DSL elements you're seeing such as 'config' and 'vhost'.
Your original question was, however, what code is required to load that config - below is a sample of something would work, full implementation is up to you and tbh I'm pretty sure there are cleaner, "better" ways of doing the same.
class AppConfig
attr_accessor :hosts
def allow(hosts)
#hosts = hosts
end
def vhost(hostname)
end
def process_config(&block)
instance_eval(&block)
end
end
def config(&block)
config = AppConfig.new
config.process_config &block
puts "Hosts are: #{config.hosts}"
end
load 'config.rb'

Rspec - Working directory

I have a class that laod config file from config/config.yml.
class Example
def initialize
config = YAML.load_file('config/config.yml')
end
end
I have created proyect/spec/config/config.yml example file but when I run the test it try load file proyect/config/config.yml.
How I change rspec working directory?
Why not point the config to the right path?
class Example
def initialize
config = YAML.load_file('spec/config/config.yml')
end
end
According to this, you can set the load_path like so
$LOAD_PATH.unshift 'spec'
or
$:.unshift 'spec'
Also according to that,
As much as I like removing unnecessary requires in specs I prefer verbosity over magic -- anything that gives the code reader more clarity as to what's necessary for a particular example group.

How do I implement a custom buildr task defined externally?

I have a task which I'm trying to refactor into an external module so that I can later separate it from this project and use it for other projects.
When I try to run my task, I get an error:
Buildr aborted!
NoMethodError : undefined method `path_to' for nil:NilClass
Essentially it seems like the code for the project.task block is never called.
The code is as follows. Some of the code comes from the working code for the compile task, so I know that those bits are probably correct. Other parts come from documented examples for buildr, which from experience earlier today can be taken with a grain (or sometimes an entire lake) of salt. I can't figure out what I've done wrong, though.
module MacAppBundle
include Buildr::Extension
class MacAppBundleTask < Rake::Task
attr_accessor :app_name
def initialize(*args)
super
enhance do |task|
#TODO: #project is always nil here because associate_with is never called
app = #project.path_to("target/#{app_name}.app")
if File.exists?(app)
FileUtils.rm_rf(app)
end
Dir.mkdir(app)
#... omitting copying the rest of the stuff into the bundle ...
end
end
protected
def associate_with(project)
#project = project
end
end
before_define do |project|
mac_app_bundle = MacAppBundleTask.define_task('mac_app_bundle')
project.task 'mac_app_bundle' do |task|
#TODO: This code never executes. Why?
mac_app_bundle.send :associate_with, project
project.local_task('mac_app_bundle')
end
end
after_define do |project|
#TODO: This bit is definitely questionable because I can't find any documentation
# or working examples of similar code.
task('mac_app_bundle' => project.package(:jar))
end
def mac_app_bundle
task('mac_app_bundle')
end
end
class Buildr::Project
include MacAppBundle
end
#TODO: Find a place to move this. Seems weird to have to call it in global scope.
Project.local_task('mac_app_bundle') do |name|
puts "Creating Mac OS X app bundle for #{name}"
end
The correct way appears to be:
before_define do |project|
mac_app_bundle = MacAppBundleTask.define_task('mac_app_bundle')
mac_app_bundle.send :associate_with, project
project.task('mac_app_bundle')
end

Resources