I want to create a function that messes around with the classes passed to it. What would be the most idiomatic way to reopen those classes to add functionality? Here's what I mean:
def class_messer(target_object)
#would like to reopen class here with something like:
class target_object.class
#add methods
end
end
Obviously that syntax doesn't work. I could get the target_object's class and eval some strings, but that feels gross. Is there a more idiomatic way to do this?
I think you're looking for class_eval. If you want to reopen a class and you do not have the constant as is, but a reference, you can call class_eval on it and pass a block (or even a string) of code to be evaluated in that classes context.
def class_messer(target_object)
# assuming that target_object is an instance of desired class
target_object.class.class_eval do
#add methods
end
end
target_object.class.class_exec do
# add methods
end
Maybe it's not correct to change class, for example, if you had an instance of Array class and changed its class, then this change could impact on other instances of Array class. So instead use singleton class of instance and the definition of method will be:
target_object.send(:define_method, :new_method) do
#...
end
or
class << target_object
def new_method
#...
end
end
You can also do this:
class << target_object.class
end
Related
I am a beginner in ruby.
I've tried to run this code and it shows run time error.
What's wrong with this code?
class Calc
attr_accessor :val1, :val2
def initialize (val1,val2)
#val1=val1
#val2=val2
end
end
a=Calc.new(2,3)
a.add_two_numbers(3)
def add_two_numbers(v3)
return #val1+#val2+v3
end
The method add_two_numbers is not defined on the class Calc, however you are using it as if it is. This is the problem.
I would presume you got a NoMethodError.
Update: As pointed out in the comments, in actuallity, the method is defined on the Object class by default, which then gets auto inherited into all classes, but as private. This actually means that you will be getting the error saying that a private method is being called. The fix remains the same, since the overarching problem is a confusion in how to define classes and their methods.
The fix would be to define the method on the class, by putting it in the class body.
class Calc
attr_accessor :val1, :val2
def initialize (val1,val2)
#val1=val1
#val2=val2
end
def add_two_numbers(v3)
return #val1+#val2+v3
end
end
So you are defining a method outside of a class (which is want we don't want)
def add_two_numbers(v3)
return #val1+#val2+v3
end
You always want to make sure that you keep your classes and you logic as two separate entities in terms of organization. By that I mean:
Your classes in one file (calc.rb):
**class Calc
attr_accessor :val1, :val2
def initialize (val1,val2)
#val1=val1
#val2=val2
end
def add_two_numbers(v3)
return #val1+#val2+v3
end
end**
And your logic to access calc.rb in another file. Use require relative to access the logic inside the class file:
require_relative"/calc.rb"
a=Calc.new(2,3)
a.add_two_numbers(3)
Tip: When I was learning ruby the best way to keep them in two separate files for better organization.That way you know you don't have a method somewhere outside of the class. This would avoid your "no method error"
I have a class that has a "factory" method which returns new instances given a file_name. Depending on the type of file given it needs to construct the object differently. It also happens to be a swig generated class wrapping a c++ class, I am not sure that matters, but I am including that detail just in case it does. So I have this class defined somewhere, which has among other things this new_from_file method
class Wv::WvWaveList
def self.new_from_file file_name
...
Wv::WaveList.new
end
....
end
I wanted to add a method copy_wave, so my first thought was to subclass and add it there so something like this.
class MyWaveList < Wv::WvWaveList
def copy_wave
...
end
end
The problem is that new_from_file still returns a Wv::WaveList not a MyWaveList so I can't call copy_wave on instances returned by new_from_file
One simple solution is to just open the class up here and add the method
class Wv::WvWave
def copy_wave
...
end
end
Another solution would be to have MyWaveList have an instance of a Wv::WaveList and delegate all the appropriate calls to that instance.
So I am just wondering what the inheritance solution might be? I just don't see it right now.
I believe this should work
class Wv::WvWaveList
def self.new_from_file file_name
...
self.new
end
....
end
Because self.new_from_file was always calling Wv::WaveList.new, it was always instantiating objects with that class, even from subclasses. But by using self, you'll be able to call new_from_file on any subclass, and the objects will be of the correct class:
>> a = MyWaveList.new_from_file "some_file"
=> #<MyWaveList:0x007fd473004318 #file_name="some_file">
>> a.class
=> MyWaveList
I have two classes and I want to copy all of the methods from one class to another. Some methods will have no arguments, some will have arguments, and some will have hashes as arguments. And I never know in advance which ones will. So I created this code, until I figured out that it didn't take into account arguments. Is there any way to get a list of methods from a Class, and then clone them exactly to another class?
def partial(cls)
cls.instance_methods(false).each do |method_name|
define_method(method_name) do
cls.new.method(method_name.to_sym).call
end
end
end
The methods are created on the fly using define_method in the first class, so I can't just use an include. The code above has cls being passed in, then it finds all of the instance methods that are actually written in that Class, not ones it inherits, and then creates a new method with the same name. When that method is called, it actually calls the other Class with its method of the same name. This works wonderfully, unless I have args. I had condition check to see if it had arguments, and then had it call a method with arguments, but it did not handle hashes very well. It made the hash as an array for an argument, which is not what I wanted.
I was wondering if there was a simple way to literally say "Hey you know this method, whatever it is, literally make the same thing for this other Class."
you could also try DelegateClass:
class NamedArray < DelegateClass(Array)
def initialize n
#name = n
super(Array.new)
end
def sayName
"My name is #{#name}"
end
end
You could try SimpleDelegator: http://www.ruby-doc.org/stdlib-1.9.3/libdoc/delegate/rdoc/SimpleDelegator.html
If all the methods are identical, why not just define them in a common module which you include in both classes? You mention not using include because the methods are dynamically defined, but that doesn't mean they won't be found when you mixin the module:
module Foo
def self.make_an_example_method(name)
define_method(name) do |*args|
puts "I am #{name} called with (#{args.inspect})"
end
end
end
class A
include Foo
end
class B
include Foo
end
Foo.make_an_example_method(:example)
Foo.make_an_example_method(:dynamic)
A.new.example # => I am example called with ([])
B.new.dynamic(1,2,3) # => I am dynamic called with ([1, 2, 3])
I have two classes
class ClassOne
def do_something
[...]
end
end
class ClassTwo
def do_something
[...]
end
end
I am getting a class name (either ClassOne or ClassTwo) from the database and I want to call do_something in that class
so I have
class_name = "ClassOne"
and I want to call ClassOne.do_something or ClassTwo.do_something if class_name is equals to "ClassTwo".
I can't do it using a simple if condition, I have many classes and am checking if the class exists before calling..
Is there a way to do it?
For vanilla ruby:
Kernel.const_get('ClassOne').do_something
For Rails:
'ClassOne'.constantize.do_something
Although you can convert any arbitrary string to a class using constantize from ActiveSupport if available, this could cause exceptions if users can submit the string in question. It might be safer to use a case:
case (with_class)
when 'ClassOne', 'ClassTwo'
with_class.constantize.do_something
else
raise "Um, what are you doing?"
end
The same thing could be achieved with a Hash or Array defining valid classes and testing with either [] or include? accordingly.
eval("#{classname}.do_something")
Note: you have to change your code to def self.do_something, otherwise these are instance methods. It looks like this is your intention.
I personally don't have anything against this, apart from the fact that's is long, but what really bothers me is the word eval.
I do a lot of stuff in JavaScript and I run from anything resembling eval like it's the devil, I also don't fancy the fact that the parameter is a string (again, probably because it's eval).
I know I could write my own method to fix the method-name-length problem, my 'method name issue' and the parameter-being-a-string thingy, but what I really want to know is: Is there a better, shorter, fancier, yet native, way of doing class_eval to extract class variables?
Side note: I know about the existence of class_variable_get() and class_variables(), but they don't really look appealing to me; horribly long, aren't they?
EDIT: Updated the question to be more specific.
Thanks!
Use class_variable_get, but only if you must
class_variable_get is the better way, other than the fact that it is not "appealing" to you. If you are reaching inside a class and breaking encapsulation, perhaps it is appropriate to have this extra barrier to indicate that you're doing something wrong.
Create accessor methods for the variables you want to access
If these are your classes, and accessing the variables doesn't break encapsulation, then you should create class accessor methods for them to make it easier and prettier:
class Foo
def self.bar
##bar
end
end
p Foo.bar
If this is your class, however, are you sure that you need class variables? If you don't understand the implications (see below), you may actually be wanting instance variables of the class itself:
class Foo
class << self
attr_accessor :bar
end
end
Foo.bar = 42
p Foo.bar
The behavior of class variables
Class variables appear to newcomers like the right way to store information at a class level, mostly because of the name. They are also convenient because you can use the same syntax to read and write them whether you are in a method of the class or an instance method. However, class variables are shared between a class and all its subclasses.
For example, consider the following code:
class Rectangle
def self.instances
##instances ||= []
end
def initialize
(##instances ||= []) << self
end
end
class Square < Rectangle
def initialize
super
end
end
2.times{ Rectangle.new }
p Rectangle.instances
#=> [#<Rectangle:0x25c7808>, #<Rectangle:0x25c77d8>]
Square.new
p Square.instances
#=> [#<Rectangle:0x25c7808>, #<Rectangle:0x25c77d8>, #<Square:0x25c76d0>]
Ack! Rectangles are not squares! Here's a better way to do the same thing:
class Rectangle
def self.instances
#instances ||= []
end
def initialize
self.class.instances << self
end
end
class Square < Rectangle
def initialize
super
end
end
2.times{ Rectangle.new }
p Rectangle.instances
#=> [#<Rectangle:0x25c7808>, #<Rectangle:0x25c77d8>]
2.times{ Square.new }
p Square.instances
#=> [#<Square:0x25c76d0>, #<Square:0x25c76b8>]
By creating an instance variable and accesor methods on the class itself—which happens to be an instance of the Class class, similar to MyClass = Class.new—all instances of the class (and outsiders) have a common, clean location to read/write information that is not shared between other classes.
Note that explicitly tracking every instance created will prevent garbage collection on 'unused' instances. Use code like the above carefully.
Using class_eval in a cleaner manner
Finally, if you're going to use class_eval, note that it also has a block form that doesn't have to parse and lex the string to evaluate it:
Foo.class_eval('##bar') # ugh
Foo.class_eval{ ##bar } # yum