I'm using the following snippet to only alias the method if the method exists:
alias_method old_name, func_name if self.respond_to? func_name
How can it Error with alias_method': undefined method 'get' for class 'Sinatra::Base' (NameError) when self.respond_to? func_name returns true?
func_name = :get in this snippet
I found a good answer to my problem here at this link
Instead of monkey patching the original class I instead create a module and then use prepend and just call super to call the original method.
I now do this:
Sinatra::Base.prepend Restman::Patches::Sinatra_Base_Patch
With the module Sinatra_Base_Patch containing the function that overwrites the original.
The example I followed is this:
class Foo
def bar
'Hello'
end
end
module FooExtensions
def bar
super + ' World'
end
end
class Foo
prepend FooExtensions # the only change to above: prepend instead of include
end
Foo.new.bar # => 'Hello World'
I bet you call alias_method inside the scope of Sinatra::Base. You should call it inside the singleton class of Sinatra::Base, because the method get is defined as a class method.
The syntax is:
alias_method :new_name, :func_name
E.g., you have a name attribute in a record:
alias_method :to_s, :name
Related
I am writing a method that will define an instance method inside a class; something similar to attr_accessor:
class Foo
custom_method(:foo)
end
I have implemented that by adding custom_method function to the Module module, and defining the method with define_method, which works fine. But I cannot figure out how to take into account visibility attributes from the class. For example, in the following class
class Foo
custom_method(:foo)
private
custom_method(:bar)
end
the first generated method (foo) must be public, and the second one (bar) must be private. How do I do that? Or, how do I find the context in which my custom_method is called: private, public, or protected?
Thanks!
After experimenting with this for a bit, I'm completely baffled. Initially, I thought that Ruby took the default visibility (public, private, or protected) into account when you call Module#define_method. It turns out though that on Ruby versions <= 2.0, that's not the case:
class Foo
private
define_method :foo do
puts "Foo called!"
end
end
Foo.new.foo # Prints "Foo called!"
On Ruby 2.1+, it's even more confusing. Module#define_method seems to take default method visibility into account:
class Foo
private
define_method :foo do
puts "Foo called!"
end
end
Foo.new.foo # NoMethodError: private method `foo' called for #<Foo:0x8cb75ac>
But it only works when you are calling define_method from directly inside the class. Calling a method which then calls define_method doesn't work:
class Foo
def self.hello_on name
define_method name do
puts "Hello, #{name}!"
end
end
private
hello_on :foo
end
Foo.new.foo # Prints "Hello, foo!"
Dang it Ruby! Why?
Okay, this calls for desperate measures...
module DefaultMethodVisibilityAccessor
attr_reader :current_default_method_visibility
def public(*args)
#current_default_method_visibility = :public if args.empty?
super
end
def protected(*args)
#current_default_method_visibility = :protected if args.empty?
super
end
def private(*args)
#current_default_method_visibility = :private if args.empty?
super
end
end
class Module
prepend DefaultMethodVisibilityAccessor
end
module MethodDefiner
def hello_on name
define_method name do
puts "Hello, #{name}!"
end
case current_default_method_visibility
when :public
public name
when :protected
protected name
when :private
private name
end
end
end
Usage:
class Foo
extend MethodDefiner
hello_on :foo
private
hello_on :bar
end
Foo.new.foo # Prints "Hello, foo!"
Foo.new.bar # NoMethodError: private method `bar' called for #<Foo:0x8ec18fc>
There, fixed!
I think this is impossible, because the scope visibility level set by Module.private is managed at the C virtual machine level and not exposed to Ruby.
EDIT: and it's only available in the same syntactical scope that it is called, so when you call custom_method it loses the visibility level set inside the class declaration.
It's set in set_visibility(), and used in vm_define_method(), but I can't find any reference to the corresponding variable being available from Ruby.
I suggest using some kind of custom parameter to specify the visibility level of your methods.
You can use Module#private_method_defined? to verify if a method is defined as private
Suppose I want to override a method of an existing class Array as below.
class Array
def to_s
self.join(',')
end
end
So my question is - How does this overriding work? Does this add this definition of method to_s to the class Array? I mean if the class contained 'n' method definitions, it would now contain 'n+1' method definitions. Also, is it that there are two definitions of to_s method and the one that is added last is the one that would work?
Thanks.
You aren't overriding the method, you are re-defining it. The one that was there is gone, replaced with what you put in. This is a risky thing to do w/the standard libraries, you don't know what behavior other code is relying upon.
You can try it w/your own class.
class Foo
def bar
puts 'One'
end
end
class Foo
def bar
puts 'Two'
end
end
Foo.new.bar
# Two
class Foo
def bar
puts 'Three'
super
end
end
Foo.new.bar
# Three
# test.rb:18:in `bar': super: no superclass method `bar'
# for #<Foo:0x007fd642029278> (NoMethodError)
I found an interesting problem: http://rubeque.com/problems/fixing-bad-code-the-wrong-way/solutions
Generally we have a simple class (notice that we don't have attr_accessor here):
class Foo
def itnialize(name)
self.foo = name
end
def set_bar
self.bar = 'it will fail..'
end
end
I thought that ruby will raise no method error when I call Foo.new but it passes without any problems. The code will fail when I try Foo.new.bar
How is it possible and how to access Foo.new.foo variable?
You have a typo and have miss-spelt initialize as itnialize so it won't be being called - so no error.
It looks like you're trying to create an instance variable - to do so you need, somewhere, to define it with the # prefix. So you might do:
def initialize(name)
#foo = name
end
which would then mean you are able to access #foo inside the class.
self.foo can only ever refer to a method foo, so you need to define that method if you want to call it, either explicitly or by using one of the attr variants.
However, in this case, you could just do
def set_bar
#bar = 'it will succeed!'
end
I'm trying to override a method located in a Gem in Ruby/Rails, and I'm struggling with some problems.
My goal is to execute custom code when a method from the Gem is called, but also to keep executing the original code.
I tried to abstract the code into the following script:
module Foo
class << self
def foobar
puts "foo"
end
end
end
module Foo
class << self
def foobar
puts "bar"
super
end
end
end
Foo.foobar
Executing this script gives me this error:
in `foobar': super: no superclass method `foobar' for Foo:Module (NoMethodError)
How should I write the overriding method so I can call super with this exception being raised?
PS: The overriding works just fine if I remove the super, but then the original method isn't called and I don't want that.
You can do what you want like this:
module Foo
class << self
alias_method :original_foobar, :foobar
def foobar
puts "bar"
original_foobar
end
end
end
Calling super looks for the next method in the method lookup chain. The error is telling you exactly what you are doing here: there is foobar method in the method lookup chain for Foo, since it is not inheriting from anything. The code you show in your example is just a redefinition of the Foo module, so having the first Foo does nothing.
I have a Ruby module for constants. It has a list of variables and one method which applies formatting.
I can't seem to access the method in this module. Any idea why?
If you include the module the method becomes an instance method but if you extend the module then it becomes a class method.
module Const
def format
puts 'Done!'
end
end
class Car
include Const
end
Car.new.format # Done!
Car.format # NoMethodError: undefined method format for Car:Class
class Bus
extend Const
end
Bus.format # Done!
Bus.new.format # NoMethodError: undefined method format
module Foo
def self.hello # This is a class method
puts "self.hello"
end
def hello # When you include this module, it becomes an instance method
puts "hello"
end
end
Foo.hello #=> self.hello
class Bar
include Foo
end
Bar.new.hello #=> hello
Generally, with modules, these things should be happening :
Autoload path in application.rb, add:
config.autoload_paths += %W(#{config.root}/lib)
Place module in /lib
Include module with include NAMEOFMODULE
If the module name has an underscore like "game_engine", you need to use include GameEngine