HI
I try to build some dynamic defined methods and chain some scope methods something like:
define_method "#{instance_name_method}" do
Kernel.const_get(model_name).___some_chaining methods basd on condition
end
One idea for that is something like:
method_action = model_name #ex Post
['latest', 'old', 'deleted','latest_deleted','archived'].each do |prefix|
method_action << ".deleted" if prefix.match('deleted')
method_action << ".latest" if prefix.match('latest')
method_action << ".old" if prefix.match('old')
define_method "#{prefix}_#{instance_name_method}" do
eval( method_action)
end
end
in post we have defiend scopes latest,old ...
Now we can call methods like:
Post.latest or Post.old_archived etc...
My questions are:
Is there a better approach for doing this? (similar to active record find but without method_missing) this is kind ugly...
How can I chain methods dynamically ?
I already know for send('method',var) but i don't know how to join those methods from strings based on condition...
Thanks
I'm sorry but it's difficult for me to understand exactly what you're asking. And I'm not sure you're using some terms correctly for instance what do you mean by "scope methods?" Do you mean a class method vs. an instance method? That would pertain to scope.
And when you say chain do you mean to call one method after the other? Like so?
f = Foo.new
puts f.method1(some_value).method2(some_other_value)
I will just comment that your not so dynamic part above could be written:
method_action << ".#{prefix}"
I don't see any actual chaining in your question so I'm not sure if you just mean to concatenate stings to build names dynamically. If you do in fact mean to chain methods you need to remember that you need to always return self at the end of a method you want to make chainable back to that class.
For instance:
class Foo
def method1(value)
puts "method1 called with #{value}"
self
end
def method2(value)
puts "method2 called with #{value}"
self
end
end
f = Foo.new
puts f.method1("Hello").method2("World").method1("I can").method2("do this").method2("all").method1("day!")
Would output:
method1 called with Hello
method2 called with World
method1 called with I can
method2 called with do this
method2 called with all
method1 called with day!
#<Foo:0x0000010084de50>
Related
I want to know, what method calls another method (I'm just trying to create simple expect("string").to eq("string") model (just like in RSpect, but more easier).
But i get "main", what is that? (I see that "main" for first time)
public
def expect(message)
message.to_s
end
def to
caller_method = caller_locations.first.label
puts caller_method
end
expect("test").to #=> <main>
#what output i expected:
expect("test").to #=> expect
My goal:
#first i need to do something like that:
expect("test").to eq("test") #=> true
#final must look like this:
expect(expect("test").to eq("test")).to eq(true) #=> true
I would recommend against using caller_method in this case. Rather, make a class whose methods return self - that way they will be chainable:
module Expectation
attr_accessor :caller_method
def expect(arg)
self.caller_method = "expect"
self
end
def to
caller_method
end
end
include Expectation
expect("foo").to
# => "expect"
Obviously this is only a starting point, and this doesn't actually do any comparisons / validations yet. But hopefully you can understand this pattern. The key thing is returning self to make a chainable API, and storing internal state using something like attr_accessor
Define a class as follows. I want to call one_method dynamically. By default, wow.one_method calls the first one_method. If I want to change the behavior of the method, just call redefine.
I can implement the method as a function type property, but that is not what I want.
If I use the following code directly, it would report errors. Could you modify it slightly.
class Wow
def one_method
puts "hello Ruby"
end
def redefine(what="none")
define_method :one_method do
puts what
end
end
end
wow = Wow.new
wow.redefine("Whatever I want.")
You can achieve that via class_eval or instance_eval:
class Wow
def one_method
puts "hello Ruby"
end
def redefine(what="none")
self.class.class_eval do
define_method :one_method do
puts what
end
end
end
end
wow = Wow.new
wow.one_method #=> hello Ruby
wow.redefine("Whatever I want.")
wow.one_method #=> Whatever I want.
Reason is that define_method defines instance method on the receiver and is a class's instance method so you'll have to call it on the eigen class of the object that you want to redefine a method on.
I would recommend achieving your goal in a more canonical way, just redefine the method on the instance itself:
class Wow
def one
:one
end
end
w = Wow.new
w.one
#=> :one
def w.one
:two
end
w.one
#=> :two
Drawbacks are:
your methods lookup table caches will be dropped
the code is becoming more obscure and hard to debug
Alternatives:
I don't know your real problem, but for your particular question it is better to parameterize your one_method method just to receive an argument for puts. Also, you can pass a block, so you will receive more grained control over the behavior.
I would like to make something like this:
class Result<<ActiveRecord::Base
def condensation
#some code here that calculates #winner and #looser and #condresalut
def winner
#winner
end
def looser
#looser
end
def showresault
#condresalut
end
end
end
so that I can call res.condensation.winner and res.condensation.looser and res.condensation.showresault.
What is the best way to do it? Apparently this way it does not work, I got nils.
It is indeed possible to do so. Not sure what the intent is, as that has been asked, but not sure if that question was clarified.
However Jay Fields has a well visited blog entry that shows how to define a method inside a method.
class Class
def def_each(*method_names, &block)
method_names.each do |method_name|
define_method method_name do
instance_exec method_name, &block
end
end
end
end
Your methods themselves inside your definition though are likely better served using the attr_reader technique.
As far as calling nested defined methods:
def testing
def testing2
'it worked'
end
end
puts testing::testing2
Thogh as Alex D reminds me in the comments, the scope operator is a deception.
I don't think you can get there from here.
Ruby allows us to define methods inside methods, but the inner methods are not exposed, or available directly.
The inner methods are only available from within the outer method, so, in your example, winner, looser and showresault are only accessible from inside condensation.
You could create lambdas or procs and return them, en masse, as closures, which would give you access to the internal values inside condensation, but, really, it seems as if you're confusing the use of a class vs. a method and trying to make a method behave like a class with its accessors. Instead, I'd probably create a class within a class, and go from there.
def condensation
#condensation ||= Struct.new(:winner, :looser, :showresult).new
end
def winner
#winner ||= condensation.winner
end
def winner=(winner)
#winner = winner
end
... and so on
I changed resault by result, and I wanted to change showresult with show_result
You can calculate winner like this:
def calculate_winner
# something using winner= method
end
EDIT: To be clear. This is a question about how to do something with meta programming. It's not about memoizing. Clearly there are better ways to memoize. The relevant methods have "memoize" in them just to illustrate their purpose.
I'm just toying around with meta programming, so please don't answer use a
#foo instance variable.
I have the following that tries to memoize both an instance and a class method
by overwriting the method definition from the running method..
class Obj
class << self
def meta_me; self; end
def class_memoize
puts "hard core calculating ..."
abc = "huge calculation result"
raise "broken here with infinite loop"
define_class_method "class_memoize" do
puts abc
abc
end
class_memoize
end
def define_class_method name, &blk
meta_me.instance_eval do
define_method name, &blk
end
end
end
def instance_memoize
puts "hard core calculating ..."
abc = "huge calculation result"
self.class.meta_me.send :define_method, :instance_memoize do
puts abc
abc
end
instance_memoize
end
end
o = Obj.new
o.instance_memoize
# hard core calculating ...
# huge calculation result
o.instance_memoize
# huge calculation result
The instance version works, but the class version does not.
I've left in an attempt at the class version for reference.
Ethics aside, It's much easier than you think. Your main issue is you're using the wrong thing for your meta_me method. Try this:
class Object
def metaclass
class<<self;self;end
end
end
This is a fairly common monkeypatch when doing metaprogramming in Ruby. Now it's easy to reimplement a method, dynamically:
class Obj
def self.class_memoize
puts "calculating..."
abc = "result"
metaclass.send(:define_method, :class_memoize) do
puts abc
abc
end
class_memoize
end
def instance_memoize
puts "calculating..."
abc = "result"
metaclass.send(:define_method, :instance_memoize) do
puts abc
abc
end
instance_memoize
end
end
As you can see, once you have the metaclass object, redefining the methods done the same way whether it's a class method or an instance method. Note that if you don't want to sully the whole namespace with a metaclass method, it's probably better just to use the class<<self;self;end idiom whenever you want to refer to it. You can call methods directly on it, like this:
(class<<self;self;end).send(:define_method, :foo){|bar| bar*23}
Note that the parens aren't truly needed, it just helps to contain the messiness that is the bare metaclass reference :) Hope this helps.
Say I have class B derived from class A
Is it possible to invoke overrided method of A like this?
class A
def method1
end
def method2
end
end
class B < A
def method1
### invoke method2 of class A is what I want to do here
end
def method2
end
end
# not exactly duplicate to How do I call an overridden parent class method from a child class? , but we seem want to do the same thing.
I'm assuming here that B is supposed to inherit from A and you simply made a typo in your example code. If this is not the case, there is no way to do what you want.
Otherwise you can do what you want using reflection by binding A's method2 instance method to your current B object and calling it like this:
class A
def method1
end
def method2
end
end
class B < A
def method1
A.instance_method(:method2).bind(self).call
end
def method2
end
end
Note though that you shouldn't pull out the big black-magic-guns like this unless you really need to. In most cases redesigning your class hierarchy so that you don't need to do this is the better alternative.
You can create a synonym for parent method using alias statement and call it from the overriden method:
class A
def method1
puts '1'
end
def method2
puts '2'
end
end
class B < A
alias parent_method1 method1
alias parent_method2 method2
def method1
parent_method2
end
def method2
end
end
b = B.new
b.method1 # => 2
The answer of #sepp2k is technically correct, however I would like to explain why this technique is not appropriate in my opinion (so the question is technically interesting, but leads to the wrong goal):
Ruby does not allow to call super.method2 in the context of method1called in an instance of B, because it is just wrong to do it. Class inheritance should be used when your instances are specializations of the superclass. That includes that you normally only expand behavior, by calling super and doing something additionally before or after that call.
There are languages like Java and others, that allow to call super for another method, and that leads to something similar to spaghetti code, but the object-oriented way. No one understands when which methods are called, so try to avoid it.
So try to find the reason why you want to change the call, and fix that. If your method1 in A is wrong implemented in B, then you should not subclass is.