Lets say I have this House class:
class House
def self.building_steps
[
[Worker, :buy_material],
[Worker, :blend_material],
[Truck, :remove_ground],
......
#more Tasks defined by [CLASS,METHOD]
]
end
def self.buy_material
check_process(Worker, __method__)
end
def self.blend_material
check_process(Worker, __method__)
end
def self.remove_ground
check_process(Truck, __method__)
end
............
#More Methods that have same Method names like the building steps
end
As you can see in my code I have a lot of repetitions.
My question is how i can automatically define the class methods from the building_steps list.
So that I dont have to add the methods manually!
I search something like:
House.building_steps.each do |step|
define_house_method_with_name( step[1] )
in this method do
check_process(step[0], step[1])
end
end
Is something like this possible? Thanks!
You can do this using define_singleton_method:
class Worker; end
class Truck; end
class House
def self.building_steps
[
[Worker, :buy_material],
[Worker, :blend_material],
[Truck, :remove_ground]
]
end
def self.check_process(klass, method)
"executing #{method}"
end
building_steps.each do |klass, method|
define_singleton_method(method) do
check_process(klass, method)
end
end
end
puts House.buy_material #=> executing buy_material
Related
Want to achieve the following code using metaprogramming.
#resource = {}
#voters = {}
#is_upvoted = {}
def resource(comment)
#resource[comment.id]
end
def voters(comment)
#voters[comment.id]
end
def is_upvoted(comment)
#is_upvoted[comment.id]
end
How can I create these methods using ruby metaprogramming and access the hash?
Can you tell me what is wrong in my code ?
['resource', 'voters', 'is_upvoted'].each do |attribute|
define_method("#{attribute}") do |comment|
instance_variable_set("##{attribute}", comment.id)
end
end
This bit seems redundant:
#resource = {}
#voters = {}
#is_upvoted = {}
Since you're already looping an array to do your metaprogramming.
You might try something like:
class Foo
%w(
resource
voters
is_upvoted
).each do |attr_sym|
define_method attr_sym do |comment|
instance_variable_set("##{attr_sym}", {}) unless instance_variable_get("##{attr_sym}")
instance_variable_get("##{attr_sym}")[comment.id]
end
end
end
Which I believe will give you methods roughly like:
class Foo
def resource(comment)
#resource ||= {}
#resource[comment.id]
end
end
Personally, it seems not great to me to have comment.id in your method. Because what if someday you want to use a different attribute (or something else altogether) as the key?
So, I think I would do:
class Foo
%w(
resource
voters
is_upvoted
).each do |attr_sym|
define_method attr_sym do |key|
instance_variable_set("##{attr_sym}", {}) unless instance_variable_get("##{attr_sym}")
instance_variable_get("##{attr_sym}")[key]
end
end
end
Now, it seems like you're going to want an easy way to set key-value pairs on your instance variable, so I guess I would try something like:
class Foo
%w(
resource
voters
is_upvoted
).each do |attr_sym|
define_method attr_sym do |key=nil|
instance_variable_set("##{attr_sym}", {}) unless instance_variable_get("##{attr_sym}")
hsh = instance_variable_get("##{attr_sym}")
return hsh[key] if key
hsh
end
end
end
In which case you should be able to do (assuming you have a #comment variable that responds to id):
#comment.id
=> 1
foo = Foo.new
=> #<Foo:0x000056536d7504b0>
foo.resource
=> {}
foo.resource[#comment.id] = :bar
=> :bar
foo.resource
=> {1=>:bar}
foo.resource[#comment.id]
=> :bar
Can you tell me what is wrong in my code ?
It's doing the equivalent of this:
def resource(comment)
#resource = comment.id
end
instance_variable_get would be a better choice.
This is how I used it and it works
['resource', 'voters', 'is_upvoted'].each do |attribute|
define_method("#{attribute}") do |comment|
instance_variable_get("##{attribute}")[comment.id]
end
end
Instead of defining a scope in a class like this:
scope :first_user, -> { first }
And calling it like this: User.first_user
I would like to define a block in another class, that can be called on the user class and works like a Scope:
This code is not working but it should signalize what behaviour I want to achieve:
class Manage
def get_first_user
User.&first_added
end
def first_added
Proc.new { first }
end
end
When I run this code:
a = Manage.new
a.get_first_user
it says me, & undefined method for User. How can I execute the Block defined in first_added on the User model?
How can I in general call a block on a class? Thanks
If I understand your question correctly, you can use class_eval:
>> foo = Proc.new { count }
=> #<Proc:0x007f1aa7cacfd8#(pry):30>
>> Buyer.class_eval(&foo)
(30.6ms) SELECT COUNT(*) FROM "buyers"
=> 1234
Or with your example:
class Manage
def get_first_user
User.class_eval(&first_added)
end
def first_added
Proc.new { first }
end
end
It is not what you want but maybe this will be helpful.
I am not sure if one can call proc on something. I think one can only call proc with something, i.t. in your case passing User as parameter.
def get_first_user
wrap User, &first_added
end
def first_added
Proc.new { |model| model.where(...) }
end
private
def wrap(model, &block)
yield model
end
Here's three ways to call first on User from Manager using:
Object#send:
http://ruby-doc.org/core-2.3.1/Object.html#method-i-send
Module#class_eval:
http://ruby-doc.org/core-2.3.1/Module.html#method-i-class_eval
File manage.rb:
class User
def self.first
puts 'record would probably go here'
end
def self.yielder
print "via yielder => "
self.send(yield) if block_given?
end
def self.blocker(&block)
print "via blocker => "
self.send(block.call) if block_given?
end
def self.evaller(&block)
print "via evaller => "
self.class_eval(block.call) if block_given?
end
end
class Manage
def get_first_user
User.yielder(&first_added)
User.blocker(&first_added)
User.evaller(&first_added)
end
def first_added
Proc.new {"first"}
end
end
a = Manage.new
a.get_first_user
Output:
$ ruby manage.rb
via yielder => record would probably go here
via blocker => record would probably go here
via evaller => record would probably go here
So in Rails, you get an error if you try to call render multiple times within a controller action.
I have another Ruby class that I'm writing, and I'd like to try to do something similar (make sure that my own respond_with method is only called once.
So for example, this would be fine:
def my_method
if (my_value == true)
...
respond_with(:a, :b, :c)
else
...
respond_with(:x, :y, :z)
end
end
But this would raise an error if my_value == 4
def my_method
if (my_value >= 4)
...
respond_with(:a, :b, :c)
end
if (my_value <= 4)
...
respond_with(:d, :e, :f)
else
...
respond_with(:x, :y, :z)
end
end
Any thoughts on how to best accomplish that?
class MyBaseClass
def respond_with(arguments)
if #rendered
raise DoubleRenderError #or whatever
end
#rendered = true
#whatever the respond_with function should do
end
end
Here's one way I could think of - in your Ruby class, define a #responded attribute by doing attr_accessor :responded. In your respond_with method, add the following lines:
def respond_with
raise DoubleRenderError if self.responded
# do stuff
self.responded = true
end
The above code should raise an error if you call respond_with twice on the same object.
I have multiple model methods and I want to loop and execute through each of them. How would I perform this in rails 2.3.11? Preferably in a begin/rescue.
Edit:
Thanks maprihoda, I used your example and was able to apply it with the begin/rescue:
class MyModel
def method_1
puts 'In method_1'
end
def method_2
puts 'In method_2'
end
def method_3
%w( method_1 method_2).each { |m|
begin
self.send(m)
rescue => e
puts "#{e.message}"
end
}
end
end
Something like this?
class MyModel
def method_1
puts 'In method_1'
end
def method_2
puts 'In method_2'
end
def method_3
%w( method_1 method_2).each { |m| self.send(m) }
end
end
my_model = MyModel.new
my_model.method_3
I'm using Ruby 1.9.2
I have a class method called search that takes a block
e.g.
class MyClass
def self.search do
if criteria1
keywords "abcde", fields: :c1 do
minimum_match(1)
end
end
if criteria2
keywords "defghi", fields: :c2 do
minimum_match(1)
end
end
end
end
What I'd like to do is refactor the MyClass.search method and have a simple one-line method for each if/end statement
e.g. it would look something like this:
class MyClass
def self.search do
c1_method
c2_method
end
def self.c1_method
if criteria1
return keywords "abcde", fields: :c1 do
minimum_match(1)
end
end
end
def self.c2_method
if criteria2
return keywords "defghi", fields: :c2 do
minimum_match(1)
end
end
end
end
But the refactoring that I show above doesn't quite work. It looks like the "blocks" that I'm returning in c1_method and c2_method aren't really being returned and evaluated in the search method, but I'm not sure how to do that.
Well, you can use the method(sym) call in order to get at the body of a method.
>> def foo(bar); bar * 2; end
=> nil
>> def baz(bleep); method(:foo).call(bleep); end
=> nil
>> baz(6)
=> 12