How to Use Define_Method Inside Initialize()

How to use define_method inside initialize()

Do as below :

class C
def initialize(n)
self.class.send(:define_method,n) { puts "some method #{n}" }
end
end

ob = C.new("abc")
ob.abc
# >> some method abc

Module#define_method is a private method and also a class method.Your one didn't work,as you tried to call it on the instance of C.You have to call it on C,using #send in your case.

define_method in a class method

When you encounter seeming conundrums such as this one, try salting your code with puts self statements:

module HashInitialized
puts "self when parsed=#{self}"
def hash_initialized(*fields)
puts "self within hash_initialized=#{self}"
define_method(:initialize) do |h|
missing = fields - h.keys
raise ArgumentError, "Not all fields set: #{missing}" if missing.any?
fields.each { |k| instance_variable_set("@#{k}", h[k]) }
end
private :initialize
end
end
#-> self when parsed=HashInitialized

class Cheese
extend HashInitialized
attr_accessor :color, :odor, :taste
hash_initialized :color, :odor, :taste
end
#-> self within hash_initialized=Cheese

As you see, self is the class Cheese, not Cheese's singleton_class. Hence, the receiver for Module#define_method is Cheese, so the method obligingly creates the instance method initialize on Cheese.

Cheese.instance_methods(false)
#=> [:color, :color=, :odor, :odor=, :taste, :taste=]

initialize is not among the instance methods created on Cheese because I modified the code slightly to make it a private method:

Cheese.private_instance_methods(false)
#=> [:initialize]

I also slightly altered the code that assigns values to the instance variables, and made the type of exception more specific.

If appropriate, you could change your argument test to:

raise ArgumentError, "Fields #{fields} and keys #{h.keys} don't match" if
(fields-h.keys).any? || (h.keys-fields).any?

You may wish to have initialize create the assessors:

module HashInitialized
def hash_initialized(*fields)
define_method(:initialize) do |h|
missing = fields - h.keys
raise ArgumentError, "Not all fields set: #{missing}" if missing.any?
fields.each do |k|
instance_variable_set("@#{k}", h[k])
self.class.singleton_class.send(:attr_accessor, k)
end
end
private :initialize
end
end

class Cheese
extend HashInitialized
hash_initialized :color, :odor, :taste
end

Cheese.new :color=>'blue', odor: 'whew!', taste: "wow!"
=> #<Cheese:0x007f97fa07d2a0 @color="blue", @odor="whew!", @taste="wow!">

Can I define “method-private” fields in Scala?

Taken from In Scala, how would you declare static data inside a function?. Don’t use a method but a function object:

val init = { // or lazy val
var inited = false

(config: Config) => {
if (inited)
throw new IllegalStateException

inited = true
}
}

During initialisation of the outer scope (in case of val) or first access (lazy val), the body of the variable is executed. Thus, inited is set to false. The last expression is an anonymous function which is then assigned to init. Every further access to init will then execute this anonymous function.

Note that it does not behave exactly like a method. I.e. it is perfectly valid to call it without arguments. It will then behave like a method with trailing underscore method _, which means that it will just return the anonymous function without complaining.

If for some reason or another, you actually need method behaviour, you could make it a private val _init = ... and call it from public def init(config: Config) = _init(config).

use define_method to create a method with equal on console

Note that if you have an expression

x.y=z

the Method "y=" is invoked on the object x, with the argument z.

However, if you have an expression

y=z

y is interpreted as the name of a variable, and gets assigned the value z.

Since you want the a= method to be invoked, you need a receiver. While we often can leave out the receiver, if it is 'self', Ruby can't recognize here that you want to do a method invocation. Hence you have to explicitly use 'self'.

Understanding Ruby define_method with initialize

Short answer, yes, long answer:

First, let's start explaining in a really (REALLY) simple way, how metaprogramming works on Ruby. In Ruby, the definition of anything is never close, that means that you can add, update, or delete the behavior of anything (really, almost anything) at any moment. So, if you want to add a method to Object class, you are allowed, same for delete or update.

In your example, you are doing nothing more than update or create the initialize method of a given class. Note that initialize is not mandatory, because ruby builds a default "blank" one for you if you didn't create one. You may think, "what happens if the initialize method already exist?" and the answer is "nothing". I mean, ruby is going to rewrite the initialize method again, and new Foo.new calls are going to call the new initialize.

how define method and apply to it with some parameters

if you don't know how to create a class in ruby, you should probably buy yourself a book about it, or read one of the gazillion online resources that help you learning ruby...

class Title
def initialize(title)
@a = title
end
end

Some problems with define_method

What you want to do, is to evaluate Random when you're defining the method. That way, the value is fixed for the defined method:

class Colors
def initialize color
@int = {}
color.each {|c| color c}
end

def color c
intensity = Random.rand
self.class.send(:define_method, "about_#{c}") do
puts "The color #{c} has an intensity of #{intensity}"
end
end
end

a = Colors.new(["orange", "pink", "yellow", "green"])
a.about_pink
a.about_pink
a.about_pink

As you can see, I save the result of Random in a variable, which is fixed in the internal context. What happens in your initial example, is that the string that you output gets evaluated in every call, and that evaluation has the call to Random inside of it, so it runs it every time.

define_method for setter wont work inside class call

module M
def create(name)
define_method("#{name}=") do |value|
"called #{name} with #{value}"
end
end
end

class C
extend M
create(:custom)
def initialize(val)
puts public_send(:custom=, val) # this is the only change needed
end
end

C.new('haha')
# called custom with haha

I only had to change one line in your code.

There were two problems with your code:

  1. custom = val is not a method call, it assigns to a local variable named custom. If you want to call a setter, you need to make it explicit that you are calling a method, by providing an explicit receiver: self.custom = val. See Why do Ruby setters need “self.” qualification within the class?
  2. Assignments evaluate to the right-hand side of the assignment. The return value of a setter method is ignored, unless you don't use assignment syntax, i.e. public_send. See Why does irb echo the right hand side of an assignment instead of the return value in the case of a setter method?

Is it possible to have Methods inside Methods?

UPDATE: Since this answer seems to have gotten some interest lately, I wanted to point out that there is discussion on the Ruby issue tracker to remove the feature discussed here, namely to forbid having method definitions inside a method body.


No, Ruby doesn't have nested methods.

You can do something like this:

class Test1
def meth1
def meth2
puts "Yay"
end
meth2
end
end

Test1.new.meth1

But that is not a nested method. I repeat: Ruby does not have nested methods.

What this is, is a dynamic method definition. When you run meth1, the body of meth1 will be executed. The body just happens to define a method named meth2, which is why after running meth1 once, you can call meth2.

But where is meth2 defined? Well, it's obviously not defined as a nested method, since there are no nested methods in Ruby. It's defined as an instance method of Test1:

Test1.new.meth2
# Yay

Also, it will obviously be redefined every time you run meth1:

Test1.new.meth1
# Yay

Test1.new.meth1
# test1.rb:3: warning: method redefined; discarding old meth2
# test1.rb:3: warning: previous definition of meth2 was here
# Yay

In short: no, Ruby does not support nested methods.

Note also that in Ruby, method bodies cannot be closures, only block bodies can. This pretty much eliminates the major use case for nested methods, since even if Ruby supported nested methods, you couldn't use the outer method's variables in the nested method.


UPDATE CONTINUED: at a later stage, then, this syntax might be re-used for adding nested methods to Ruby, which would behave the way I described: they would be scoped to their containing method, i.e. invisible and inaccessible outside of their containing method body. And possibly, they would have access to their containing method's lexical scope. However, if you read the discussion I linked above, you can observe that matz is heavily against nested methods (but still for removing nested method definitions).



Related Topics



Leave a reply



Submit