How can I redefine a method before it exists? - ruby

In homebrew on OS X, there's a DownloadStrategyDetector class, I'm making a tap and need to override a couple methods on this class to add additional download strategies (they're very niche so won't be accepted into core).
I tried simply redefining (see below) the class above my Formula class in an attempt to override the default definition, however the regular version of the class is still used.
class DownloadStrategyDetector
def self.detect_from_url(url)
# ...
end
def self.detect_from_symbol(symbol)
# ...
end
end
Is there a way to force all uses of a class to a particular definition? This is the backtrace from the call that winds up using the default definition of the class.
My code is in tester.rb and that indirectly calls resource.rb which loads download_strategy.rb which overrides my definition of DownloadStrategyDetector.
backtrace:
/usr/local/Library/Homebrew/download_strategy.rb:963:in `detect_from_symbol'
/usr/local/Library/Homebrew/download_strategy.rb:916:in `detect'
/usr/local/Library/Homebrew/resource.rb:144:in `url'
/usr/local/Library/Homebrew/software_spec.rb:62:in `url'
/usr/local/Library/Homebrew/formula.rb:1666:in `url'
/Users/camdennarzt/Developer/Ruby/homebrew-core/formula/tester.rb:68:in `<class:Tester>'
/Users/camdennarzt/Developer/Ruby/homebrew-core/formula/tester.rb:63:in `load_formula'
/usr/local/Library/Homebrew/formulary.rb:25:in `module_eval'
/usr/local/Library/Homebrew/formulary.rb:25:in `load_formula'
/usr/local/Library/Homebrew/formulary.rb:42:in `load_formula_from_path'
/usr/local/Library/Homebrew/formulary.rb:91:in `load_file'
/usr/local/Library/Homebrew/formulary.rb:82:in `klass'
/usr/local/Library/Homebrew/formulary.rb:78:in `get_formula'
/usr/local/Library/Homebrew/formulary.rb:215:in `factory'
/usr/local/Library/Homebrew/extend/ARGV.rb:29:in `block in resolved_formulae'
/usr/local/Library/Homebrew/extend/ARGV.rb:27:in `map'
/usr/local/Library/Homebrew/extend/ARGV.rb:27:in `resolved_formulae'
/usr/local/Library/Homebrew/cmd/reinstall.rb:10:in `reinstall'
/usr/local/Library/brew.rb:87:in `<main>'
I tried another strategy as well, where I added a hook to my DownloadStrategyDetector class like this, and tried to swap the methods using aliases:
class DownloadStrategyDetector
def self.singleton_method_added(name)
puts name
alias_method :detect_from_url, :detect_from_url_2 if name == :detect_from_url
alias_method :detect_from_symbol, :detect_from_symbol_2 if name == :detect_from_symbol
end
def self.detect_from_url_2(url)
# ...
end
def self.detect_from_symbol_2(symbol)
# ...
end
end
but it only printed the following, as if the class was never reopened:
singleton_method_added
detect_from_url_2
detect_from_symbol_2

Related

Parent class's instance variable in Ruby

So, for whatever reason there is no peek method in the ruby core Queue class. I am trying to create a child class that implements the peek method. However, I don't understand why I am getting an error. Is it not possible to use instance variables in this way? Looking at the source code for Queue, there are instance variables in the constructor of the parent class. Is there a way to reference these in the subclass?
class PeekQueue < Queue
def peek
#mutex.synchronize{
while true
if #que.empty?
raise ThreadError, "queue empty" if non_block
#waiting.push Thread.current
#mutex.sleep
else
return #que[0]
end
end
}
end
end
a = PeekQueue.new
a.push(1)
a.peek
NoMethodError: undefined method 'synchronize' for nil:NilClass
Edit: The Queue class is created at compile time, which is why I couldn't find the source on the ruby source code on github. This is what the parent class looks like:
https://gist.github.com/anonymous/574e20fea3a28663bfe2
I do not see that error:
irb(main):025:0> qq = PeekQueue.new
=> #<PeekQueue:0x000006002bf498 #que=[], #num_waiting=0, #mutex=#<Mutex:0x000006002bf420>, #cond=#<ConditionVariable:0x000006002bf3f8 #waiters={}, #waiters_mutex=#<Mutex:0x000006002bf3a8>>>
irb(main):026:0> qq.peek
NameError: undefined local variable or method `non_block' for #<PeekQueue:0x000006002bf498>
from (irb):15:in `block in peek'
from (irb):12:in `synchronize'
from (irb):12:in `peek'
from (irb):26
from /usr/bin/irb:12:in `<main>'
irb(main):027:0> qq.push 1
=> #<ConditionVariable:0x000006002bf3f8 #waiters={}, #waiters_mutex=#<Mutex:0x000006002bf3a8>>
irb(main):028:0> qq.peek
=> 1
Method #non_block seems to be an issue. But access to #mutex works with your code.

Undefined Method - Calling a class in one file from another

I am trying to learn a little about GUI testing with Ruby & Cucumber, partially following a book called "Scripted GUI Testing with Ruby" by Ian Dees. I'm new to Ruby, and I'm facing what seems like a simple problem - undefined method. I have two classes, each in different modules. The first class will handle Win32API calls, the second represents the top level of the application (boot-up, close, find window etc). They will work a little like page objects, separating UI interaction logic from test logic. I'm getting an undefined method on the Windows API class, for user32:
#Win32API class
require 'Win32API'
class WindowsAPI
def user32(name, param_types, return_value)
Win32API.new 'user32', name, param_types, return_value
end
#find_window = user32 'FindWindow', ['P', 'P'], 'L'
end
The second:
#Application class
require_relative 'WindowsAPI'
class VideoLibrarian
#main_win_title = "VidLibMainWin"
attr_accessor :main_win_handle, :win_api
def initialize
#win_api = WindowsAPI.new
end
def Start()
system 'start "" "C:/Users/VideoAnalyser.exe"'
sleep 0.2 while (#main_win_handle = win_api.find_window.call nil, #main_win_title) <= 0
end
end
vl = VideoLibrarian.new
vl.Start
The full stack trace/ error message is:
C:/Users/Ruby Scripts/vidlibtests/WindowsAPI.rb:11:in `<class:WindowsAPI>': undefined method `user32' for WindowsAPI:Class (NoMethodError)
from C:/Users/Ruby Scripts/vidlibtests/WindowsAPI.rb:5:in `<top (required)>'
from C:/Users/Ruby Scripts/vidlibtests/VideoLibrarian.rb:3:in `require_relative'
from C:/Users/Ruby Scripts/vidlibtests/VideoLibrarian.rb:3:in `<main>'
Any help greatly appreciated!
You defined user32 as an instance method while you clearly need it to be class method of WindowsAPI class:
def self.user32(name, param_types, return_value)
Win32API.new 'user32', name, param_types, return_value
end

Rails 4: Undefined method on module

I have a module in app/misc/dsl/builder.rb that has this code
module Dsl
class Builder
def initialize(context, &block)
return if not block_given?
parent_context = block.binding.eval "self"
parent_context.extend Proxy
parent_context.object = context
parent_context.instance_eval &block
end
end
def self.set_context(context, &block)
Dsl::Builder.new(context, &block)
end
end
Note: this directory misc is preloaded in application.rb
config.autoload_paths += Dir[Rails.root.join('app', 'models', '{**/}'),
Rails.root.join('app', 'misc', '{**/}')
]
Then, somewhere in the text (lets say at foo.rb) I have this code:
Dsl.set_context(obj) do
#some code with obj receiving messages
end
The test stack we are using consists on Zeus+Guard+Rspec. Now, lets say I rewrite the code to something not working
Dsl.set_context(obj) do
asdqwe #this message does not exists
end
From times to times, I receive this baffling message
1) SomeOtherClass search_hash receiving keywords params should query for those keywords
Failure/Error: subject.search_hash
NoMethodError:
undefined method `set_context' for Dsl:Module
# ./app/misc/product_query.rb:116:in `base_search_hash'
# ./app/misc/product_query.rb:25:in `search_hash'
# ./spec/misc/product_query_spec.rb:78:in `block (4 levels) in <top (required)>'
# -e:1:in `<main>'
instead of the correct message that should be regarding undefined method asdqwe
Any clue about this?
Look here
it says:
Rails 3 has been updated such that classes/modules (henceforth, C/M)
are lazy loaded from the autoload paths as they are needed
so, you can do require_relative 'app/misc/dsl/builder.rb' in your rspec_helper.rb (can it be better with just require?) The problem must be that the loader doesn't know in advance where to find Dsl.set_context, but he will know once you have referenced Dsl::Builder
Hope it helps

Monkey patch class methods

I'm trying to do some monkey patching in ActiveShipping UPS class .
I need to add a class level method (starting with .self), so here it's what I'm trying to do:
module ActiveMerchant
module Shipping
class UPS < Carrier
def self.process_request(receiver, sender, packages, options = {})
# some code
end
def regular_method
"foobar"
end
end
end
end
Unfortunately when I'm trying to use it:
ActiveMerchant::Shipping::UPS.process_request(receiver etc)
I get an error:
NoMethodError: undefined method `process_request' for ActiveMerchant::Shipping::UPS:Class
from (irb):6
from C:/Ruby19/bin/irb.bat:19:in `<main>'
There is no class method named process_request in original class.
In original UPS class provided in gem there is one static method defined self.retry_safe = true
and I can use it without errors.
I can also use regular_method after creating instance of UPS class.
More details provided:
I'm working with Rails 2.3 ( :-( ) and Ruby 1.9.2. I have no influce on environment.
Monkey patched code is under plugins/my_plugin/lib/active_shipping/ext/carriers/ups.rb
In /active_shipping I have file named extensions.rb in which i have:
require 'active_shipping'
require_relative 'ext/carriers'
require_relative 'ext/carriers/ups'
It deals with loading everything properly (I suppose basing on regular_method beheaviour from first chunk of code in my question).
I try to invoke process_request in one of my Controllers. This part is little tricky, beacuse i'm using sth like this:
MyModel.courier_service.process_request(parameters)
where courier_service, in this case holds the ActiveMerchant::Shipping::UPS class.
I'm still a newbie in Ruby and don't know what sort of details i should provide.
Maybe you want to do it in another way
File patch_classes.rb:
module ActiveMerchantExpand
module Shipping
module ClassMethods
def self.process_request(receiver, sender, packages, options = {})
# some code
end
end
module InstanceMethods
def regular_method
"foobar"
end
end
def self.included(receiver)
receiver.extend ClassMethods
receiver.send :include, InstanceMethods
end
end
end
Then you have to load your class "ActiveMerchant::Shipping::UPS"
and after that you can attach your methods to your class via
Rails.configuration.to_prepare do
require_dependency [[file for ActiveMerchant::Shipping::UPS]]
require 'patch_classes' )
ActiveMerchant::Shipping::UPS.send(:include, ::ActiveMerchantExpand::Shipping)
end
This is from rails plugin writing, i hope this helps.
regards tingel2k
Do you explicitly require file with your monkey patch? If you just put it under your app or lib path without requiring, it wouldn't load because constant ActiveMerchant::Shipping::UPS is defined in gem and it doesn't trigger dependency resolution mechanism.

How do I correctly include and test an ActiveSupport::Concern in an RSpec mocked_model?

I'm trying to spec a module by including it in a basic mock_model object. However, when I call the instance method defined in the module ActiveRecord tries to establish a connection with the database.
The module:
module Stuff
module SoftDelete
extend ActiveSupport::Concern
def soft_delete
puts "Called here"
end
end
end
The Spec:
describe Stuff::SoftDelete do
class Network < ActiveRecord::Base
include Stuff::SoftDelete
attr_accessor :deleted_at
end
before (:each) do
#network = mock_model(Network)
end
context "When a record is deleted" do
it "is marked as deleted" do
#network.soft_delete
end
end
end
When I run this Spec, the following error occurs:
1) Stuff::SoftDelete When a record is deleted is marked as deleted
Failure/Error: #network.soft_delete
ActiveRecord::ConnectionNotEstablished:
ActiveRecord::ConnectionNotEstablished
# ./spec/apoc/soft_delete_spec.rb:18:in `block (3 levels) in <top (required)>'
Note: If I include the SoftDelete module in a real ActiveRecord class, it will work. It just seems that mock_model isn't able to deal with the module.
Would love some help on this one.
Thanks!
Do you trust ActiveRecord? If so, don't inherit from it; test your module in isolation. If your module includes calls ActiveRecord methods, stub them and test only your code.

Resources