Here is my situation:
XMLRPC::Client has a proxy constructor, new3, that takes a hash of options. It takes out the individual values to then delegate the construction to the default initializer, initialize
I am deriving from XMLRPC::Client. I want a class that is XMLRPC::Client but with some added functionality.
I want to be able to instantiate this derived class using a hash of options as well. This means that in my derived class' initializer, I have to somehow instantiate super using the new3 proxy constructor.
My Question Is if this is possible. If not, then is the only way to solve this is to practically 'copy and paste' the code within the XMLRPC::Client.new3 method into my derived class' constructor?
The reason I'm asking this is simply to see if there is a way of solving this problem, since there is this recurring theme of DRY (Don't Repeat Yourself) within the Ruby community. But of course, if this is the only way, it wont kill me.
I am only posting an answer supplement the other answers by showing you how XMLRPC's code is written
def new3(hash={})
# convert all keys into lowercase strings
h = {}
hash.each { |k,v| h[k.to_s.downcase] = v }
self.new(h['host'], h['path'], h['port'], h['proxy_host'], h['proxy_port'], h['user'], h['password'],
h['use_ssl'], h['timeout'])
end
http://www.ensta.fr/~diam/ruby/online/ruby-doc-stdlib/libdoc/xmlrpc/rdoc/classes/XMLRPC/Client.html
Make a new class method in your derived class (much like they did to make new3 in the first place):
class MyDerived < XMLRPC::Client
def self.new3(hashoptions)
client = XMLRPC::Client.new3(hashoptions)
# Do further initialisation here.
end
end
myone = MyDerived.new3(:abc => 123, ...)
super only works in initialize (and only changes the parameters to the superclass's initialize), so it doesn't apply here.
You should probably be able to call new3 on you subclass
class MyClient < XMLRPC::Client
end
MyClient.new3({})
Or overwrite it if you need to do extra stuff:
class MyClient < XMLRPC::Client
def self.new3(args)
client = super(args)
# do some more stuff
client
end
end
MyClient.new3({})
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 met strange difficulty developing one project. I dont have enough experience with classes so this is why I ask here. I have one class which is initialized with one parameter and I need other classes to call that class functions but I cant until that class is initialized so I asking how I could do that.
Here is some example what I talking about:
class AVR
def initialize(device)
#device = device
#avr_conf = YAML::load(File.open("./devices/#{device}.yaml"))
end
def avr_conf
return #avr_conf
end
end
class IO
def setMode(a,b)
"#{AVR.avr_conf[a]} = #{b}"
end
end
You either: need an instance, or to make avr_conf a class method (and initialize differently).
With an instance:
avr = AVR.new(a_device)
avr.avr_conf[a]
With a config singleton (roughly):
class AVR
def self.class_initialize(device)
##avr_conf ... etc ...
end
def self.avr_conf
return ##avr_conf
end
end
Then class IO would need to use the updated version, however that's appropriate.
If IO isn't going to/can't get an instance, the class/singleton-config might make more sense, although that approach always makes me a little nervous.
You could have the dependent method simply throw an exception if the independent class is not yet initialized:
class IO
def setMode(a, b)
raise StandardError.new("AVR device is not initialized") unless AVR.avr_conf
AVR.avr_conf[a] = b
end
end
Note however that this solution requires the AVR class to be a singleton (or module) since the IO extension methods will need to know which instance should be modified.
There are two kinds of methods in Ruby classes - actually class and instance methods.
Class methods defines with self. prefix and there's no need to initialize class:
class AVR
def self.avr_conf
'something here'
end
end
So, in this case you must call: AVR.avr_conf to get 'something here' result.
But, in your case, #avr_conf variable defines in initialize method and you have to create a class instance with:
avr = AVR.new('something')
avr.avr_conf[a] = b
Is it possible to do something like this in Ruby (1.9.2-p290)?
class SomeClass
include SomeModuleThatProvidesLotOfConstants
def build(&block)
singleton_class.instance_eval(&block)
end
end
obj = SomeClass.new
obj.build do
some_class_method SomeConstant, :an => :option
...
end
Where some_class_method is a method that is available to SomeClass (not to instances of it) and SomeConstant is a class/module that is in scope inside of SomeClass, but would have to be references as SomeClass::SomeConstant from outside.
I can get this working if I always pass fully-qualified class names inside my block, but I'm trying to effectively "re-scope" the block when it is invoked. Is this possible? I'm pretty sure RSpec and other such tools that make heavy use of blocks achieve something like this :)
Note that while I'm calling class methods from inside the block, I only want the changes to affect this individual singleton class, rather than propogate to all instances.
EDIT | Ok, here's the non-pseudo version of what I'm trying to achieve. I'm trying to add some DataMapper properties at runtime, but only to singleton classes... I don't want them to suddenly appear across all instances of the model.
class Post
include DataMapper::Resource
property :id, Serial
property :title, String
property :created_at, DateTime
... etc ...
def virtualize(&block)
singleton_class.instance_eval(&block)
self
end
end
def suspend_post
#post = Post.get!(1).virtualize do
property :delete_comments, Boolean
end
end
I know there are other ways to do virtual attributes (I'm currently using a couple of different approaches, depending on the complexity), but I'm just experimenting with a few ideas to avoid cluttering my model definitions with transient methods that are only used for transporting form data in one specific part of the site and don't mean anything when you're reading the source code of the model by itself. One or two virtual attributes are ok, but as they start to mount up on commonly used models I start to explore things like this ;)
In the above, the resource would have all of the standard properties defined in the concrete class, plus any that are added in the #virtualize method. It's the reference to Boolean without the DataMapper::Property:: prefix that's throwing it off.
You've already got what you want with respect to methods. If you define some_class_method like this:
def Foo.some_class_method(name)
define_method name do
puts("this is the method #{name}")
end
end
and do
f = Foo.new
f.build { some_class_method "new_method" }
f.singleton_methods # => [:new_method]
You've defined behavior on just that one instance.
However I don't think you can get what you're looking for with respect to constants. One option would be to use methods instead of constants for those arguments. Another would be to have the client code mix in whatever module defines the constants.
Do keep in mind this is pretty dense metaprogramming, so the complexity may not be justified.
What's wrong with this:
class SomeClass
SOME_CONSTANT = 42
class << self
def some_class_method
'foo'
end
end
def build &block
self.class.instance_eval(&block)
end
end
SomeClass.new.build do
puts "#{some_class_method} #{SOME_CONSTANT}"
end
#=>foo 42
Using module_eval, my code allows me to dynamically create and add new methods to a class based on input parameters. See this post for an example
Ruby class_eval and yield
Now my question is how do I reset the class back to its original methods? Is there a simple method I can use to reset a class back to it's original state? The reason for this is that once new methods are added to the class, they persists and I need to be get rid of them if I create a new object with a different set of methods.
Also I must apologize if this question doesn't quite make sense, I've been up for 24 hours and probably needed to get some rest to think clearly. If this is not clear, I can provide an example. Thanks for your help in advance.
Edit: Added complete solution
If you keep a list of the methods added in your earlier post, you can use remove_method to remove these methods with something like:
class MyTest
##methods_list = []
def self.show_methods
##methods_list
end
def self.reset_methods
##methods_list.each do |method|
remove_method(method)
end
##methods_list = []
end
def self.add_methods
define_method("method1") { puts "This is method1" }
define_method("method2") { puts "This is method2" }
true
end
def self.method_added(method_name)
##methods_list << method_name.to_s
puts "Added: " + method_name.to_s + ", list: " + ##methods_list.inspect
end
end
Now you can try out the following:
>> require 'mytest.rb'
>> t = MyTest.new # => #<MyTest:0x2b1e293247f0>
>> MyTest.add_methods
Added: method1, list: ["method1"]
Added: method2, list: ["method1", "method2"]
>> t.method1 # Method is available:
This is method1
>> MyTest.reset_methods
>> t.method1 # Method is undefined now, so we'd expect an error
NoMethodError: undefined method `method1' for #<MyTest:0x2b1e293247f0>
from (irb):6
I would take one of two tacks:
either move the original class methods to a module, which you can re-include later
or use module_eval on a subclass of the original class, and just get a new subclass when you want to reset.
The second's a little easier to do:
subklass = Class.new(MyAwesomeClass)
subklass.module_eval #...
# ok, I'm done, I want to reset
subklass = Class.new(MyAwesomeClass)
I don't know of such a facility, and I would probably think that your code could be better structured if you need one, but there are a few things you can try:
Override the new methods with variants that will throw an exception, or do nothing. If you know the names of the new methods, it'd be pretty easy to do.
Instead of adding methods to one class, create an anonymous subclass that inherits from the base class, to which you add your methods. (See the answer to this question for a means of doing this.) Then, your base class is still unmodified, so there'd be no need to reset it.
HTH