I would like to count method calls of new methods defined in a class. To do this, I redefine each newly defined method using method_added hook. Inside it, I use define_methodand increment the value of a class variable##test_count`.
Base class:
class Unit
#new_method = true
##test_count = 0
def self.test_count
puts ##test_count
end
def self.method_added(name)
old_method = instance_method(name)
if #new_method
#new_method = false
define_method(name) do |*args, &block|
##test_count += 1
old_method.call(*args, &block)
end
#new_method = true
end
end
end
Subclass with newly defined methods:
class Test < Unit
def test_assertion
puts true
end
end
When I call a new method, ##test_count is still 0:
Test.new.test_assertion
Unit.test_count
true
0
Why ##test_count value isn't changed?
The problem was caused by initializing class instance variable #new_method in the body of class Unit. As I was subclassing this class with Test this variable was not initialized in subclass Test. No ideas why it's like that but I solved the problem by changing if to unless thus using nil from uninitialized variable to pass the check and then assign appropriate values to it:
class Unit
##test_count = 0
def self.method_added(name)
unless #new_method
#new_method = true
old_method = instance_method(name)
define_method(name) do |*args, &block|
##test_count += 1
old_method.bind(self).call(*args, &block)
end
#new_method = false
end
end
end
Any ideas why class instance variable initialization isn't "inherited" in a subclass?
Related
I have the following code:
class A
def self.scope
yield
end
def self.method_added method
self.instance_eval %{
# do something involving the added method
}
end
end
class B < A
scope do
def foo
end
end
end
When the method_added hook is fired, will the code inside instance_eval run within the same scope as the method that was added? Or, will it run outside of it?
What are the caveats and gotchas involved within this?
Your scope method is basically a no-op. When you pass a block to a method that yields, the block is evaluated in the current scope. Observe:
class A
def self.scope
yield
end
end
A.scope { p self }
# main
Since nothing is yielded to the block, and nothing is done with the return value of yield, any code run in the block will have the same effect run outside the scope block.
This isn't the case with instance_eval, however. When instance_eval runs a block, self in the block is set to the receiver (rather than whatever self is in the block's scope). Like this:
class A
end
A.instance_eval { p self }
# A
But note that this means that self.instance_eval { ... } is also a fancy no-op, because you're changing the block's self to the same self outside the block.
So your code is equivalent to this:
class A
def self.method_added method
# do something involving the added method
end
end
class B < A
def foo
end
end
Let's find out!
class A
def self.scope
yield
end
def self.method_added method
puts "In method_added, method = #{method}, self = #{self}"
instance_eval 'puts "In instance_eval, method = #{method}, self = #{self}"'
end
end
class B < A
scope do
puts "In scope's block, self = #{self}"
def foo
end
end
end
# In scope's block, self = B
# In method_added, method = foo, self = B
# In instance_eval, method = foo, self = B
Notice that you don't need self. in self.instance_eval.
I'm trying to create some kind of module or superclass that wraps one method call after each method of the subclass.
There are some constraints though: I wouldn't want the method to be run after initialize() is called nor after a few other methods of my choice is called.
Another constraint is that I would only want that method to be executed IF the flag #check_ec is set to true.
I have classes with more than 60 methods that I have hard-coded the same piece of code that ispasted all over the place.
Is there a way that I could make a wrapper that would automatically execute that method for my class methods?
So the idea is this:
class Abstract
def initialize(check_ec)
#check_ec = check_ec
end
def after(result) # this is the method that I'd like to be added to most methods
puts "ERROR CODE: #{result[EC]}"
end
def methods(method) # below each method it would execute after
result = method() # execute the given method normally
after(result) if #check_ec and method != :initialize and method != :has_valid_params
end
end
class MyClass < Abstract
def initialize(name, some_stuff, check_error_code)
# do some stuff...
#name = name
super(check_error_code)
end
def my_method_a() # execute after() after this method
return {EC: 0}
end
def my_method_b() # execute after() after this method
return {EC: 7}
end
def has_valid_params() # don't execute after() on this method
return true
end
end
This is trivially easy using method_missing, and composition instead of inheritance. You can build a very simple class which forwards method invocations, and then executes an after callback, except for specific method names:
class Abstract
def initialize(object)
#object = object
end
def method_missing(method, *arguments)
result = #object.send(method, *arguments)
after() unless method == "has_valid_params"
result
end
def after
# whatever
end
end
o = Abstract.new(MyClass.new)
A solution using singleton class.
class MyClass
def initialize(name, some_stuff)
# do some stuff...
#name = name
end
def my_method_a # execute after() after this method
return {EC: 0}
end
def my_method_b() # execute after() after this method
return {EC: 7}
end
def has_valid_params() # don't execute after() on this method
return true
end
end
module ErrorCodeChecker
def after(result) # this is the method that I'd like to be added to most methods
puts "ERROR CODE: #{result[:EC]}"
end
def addErrorCodeCheck(exclude = [])
methods = self.class.superclass.public_instance_methods(false) - exclude
class << self
self
end.class_exec {
methods.each {|method|
define_method(method) {|*p|
super(*p).tap {|res| after(res)}
}
}
}
end
end
class MyClassEC < MyClass
include ErrorCodeChecker
def initialize(name, some_stuff, check_error_code, exclude = [])
super name, some_stuff
addErrorCodeCheck(exclude) if check_error_code
end
end
'addErrorCodeCheck' opens up the singleton class of an instance of MyClassEC, and redefines instance methods of MyClass not in the exclude list. The redefined methods hide the original methods but call them via 'super' method inside before calling 'after'.
You can apply 'addErrorCodeCheck' repeatedly later if needed.
Execution example: (tested in Ruby 1.9.3)
my = MyClassEC.new('test', 'abc', true, [:has_valid_params])
my.my_method_a # => ERROR CODE: 0
my.my_method_b # => ERROR CODE: 7
my.has_valid_params # => (nothing)
What about this? It has a major drawback which is that your methods must be already defined before calling check_error_code, but it may suit your needs. You could look for inspiration for a better solution in Rails callbacks, or defer the redefinition of each method until that method is added using the method_added hook.
Include ErrorCodeChecker and call check_error_code in each class you want to check the error code (as in the last line of the snippet).
module ErrorCodeChecker
def self.included(base)
base.send(:extend, ClassMethods)
end
def after(result) # this is the method that I'd like to be added to most methods
puts "ERROR CODE: #{result[:ec]}"
end
module ClassMethods
def check_error_code(options = {})
check_on = instance_methods(false) - Array(options[:except])
check_on &= Array(options[:only]) if options[:only]
class_eval do
check_on.each do |method|
alias_method "#{ method }_without_ec", method
define_method(method) do |*args, &block|
send("#{ method }_without_ec", *args, &block).tap { |result| after(result) if #check_ec }
#if you want to actually return the return value of calling after:
#result = send("#{ method }_without_ec")
##check_ec ? after(result) : result
end
end
end
end
end
end
class Abstract
include ErrorCodeChecker
def initialize(check_ec)
#check_ec = check_ec
end
end
class MyClass < Abstract
def initialize(name, some_stuff, check_error_code)
# do some stuff...
#name = name
super(check_error_code)
end
def my_method_a # execute after() after this method
{ec: 0}
end
def my_method_b # execute after() after this method
{ec: 7}
end
def has_valid_params # don't execute after() on this method
true
end
check_error_code except: :has_valid_params
#or whitelisting:
#check_error_code only: [:my_method_a, :my_method_b]
#or both:
#check_error_code only: :my_method_a, except: [:has_valid_params, dont_check_this_one]
end
I have the following class:
module StatCalculators
class Passing
def initialize(user_id, game_id)
#user_id = user_id
#game_id = game_id
end
def save_completion_percentage
completions = StatType.find_by_name("Completions").stats.where(athlete_id: #user_id).sum(:float_value)
attempts = StatType.find_by_name("Pass Attempts").stats.where(athlete_id: #user_id).sum(:float_value)
value = completions/attempts
stat = Stat.new(value: value, game_id: #game_id, athlete_id: #user_id, float_value: value)
stat.save(validate: false)
end
end
end
The class above has the potential to have a lot more methods that need to be run without having to call each method individually... is there a way to run all instance methods in the initialize method?
It is possible:
module StatCalculators
class Passing
def initialize(user_id, game_id)
#user_id = user_id
#game_id = game_id
klass = self.class
klass.instance_methods(false).each do |method|
klass.instance_method(method).bind(self).call
end
end
...
end
end
To achieve a DSL like attribute assignment,a dual-purpose accessor was utilized. However, I was seeking a way to refactor the obvious code duplication.
class Layer
def size(size=nil)
return #size unless size
#size = size
end
def type(type=nil)
return #type unless type
#type = type
end
def color(color=nil)
return #color unless color
#color = color
end
end
I was thinking define those method in a class method by using define_method along with other methods to get/set the instance variables. However, the dilemma is how can I access the instance from class method?
def self.createAttrMethods
[:size,:type,:color].each do |attr|
define_method(attr) do |arg=nil|
#either use instance.send() or
#instance_variable_get/set
#But those method are instance method !!
end
end
end
Inside of define_method block, self will be pointing to current instance of class. So use instance_variable_get.
class Foo
def self.createAttrMethods
[:size,:type,:color].each do |attr|
define_method(attr) do |arg = nil|
name = "##{attr}"
return instance_variable_get(name) unless arg
instance_variable_set(name, arg)
end
end
end
createAttrMethods
end
f = Foo.new
f.size # => nil
f.size 3
f.size # => 3
This question already has answers here:
When to use `self.foo` instead of `foo` in Ruby methods
(3 answers)
Closed 9 years ago.
When do you use self.property_name in Ruby?
Use self when calling a class's mutator. For example, this won't work:
class Foo
attr_writer :bar
def do_something
bar = 2
end
end
The problem is that 'bar = 2' creates a local variable named 'bar', rather than calling the method 'bar=' which was created by attr_writer. However, a little self will fix it:
class Foo
attr_writer :bar
def do_something
self.bar = 2
end
end
self.bar = 2 calls the method bar=, as desired.
You may also use self to call a reader with the same name as a local variable:
class Foo
attr_reader :bar
def do_something
bar = 123
puts self.bar
end
end
But it's usually better to avoid giving a local variable the same name as an accessor.
self references the current object. This lends itself to many uses:
calling a method on the current object
class A
def initialize val
#val = val
end
def method1
1 + self.method2()
end
def method2
#val*2
end
end
Here running A.new(1).method1() will return 3. The use of self is optional here - the following code is equivalent:
class A
def initialize val
#val = val
end
def method1
1 + method2()
end
def method2
#val*2
end
end
self is not redundant for this purpose though - operator overloading makes it neccessary:
class A
def initialize val
#val = val
end
def [] x
#val + x
end
def method1 y
[y] #returns an array!
end
def method2 y
self.[y] #executes the [] method
end
end
This shows how self must be used if you want to call the current object's [] method.
referencing attributes
You can generate the methods to read and write to instance variables using attr_accessor and co.
class A
attr_accessor :val
def initialize val
#val = val
end
def increment!
self.val += 1
end
end
Using self is redundant here because you can just reference the variable directly, eg. #val.
Using the previous class, A.new(1).increment! would return 2.
method chaining
You can return self to provide a form of syntactical sugar known as chaining:
class A
attr_reader :val
def initialize val
#val = val
end
def increment!
#val += 1
self
end
end
Here, because we are returning the current object, methods can be chained:
A.new(1).increment!.increment!.increment!.val #returns 4
creating class methods
You can define class methods using self:
class A
def self.double x
x*2
end
def self.quadruple x
self.double(self.double(x))
end
end
This will enable you to call A.double(2) #= 4 and A.quadruple(2) #=8. Note that in a class method, self references that class because the class is the current object.
how the value of self is determined
The current value of self in a particular method is set to the object that that method was called upon. Normally this uses the '.' notation. When you run some_object.some_method(), self is bound to some_object for the duration of some_method, meaning that some_method can use self in one of the ways mentioned above.
Using self is used will reference the current object accessible within a program. Therefore, self.property is used when accessing a variable through a attr_accessor of some sort. In must cases, it can be used in place of #property from within an object.