Ruby beginner needs a hand - ruby

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"

Related

Subclassing a class that has a "factory" method

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

How to Convert an ActiveRecord::Relation by default

I am working on a project that requires very specific methods to be called on an ActiveRecord::Relation object. These methods cannot extend ActiveRecord::Relation because the Class has it's own initialize method to determine if the object should be collected. I have tried a dozen ways to handle this but because of method chaining in AR I have been unable to accomplish this. Currently I have monkey patched ActiveRecord::Relation with a method that converts it like so:
module ActiveRecord
class Relation
def to_claim_set
exec_queries unless loaded?
ClaimSet.new(#records)
end
end
end
Firstly I am sure this is an improper way to handle it. Secondly this causes me to have to call #to_claim_set constantly throughout the application.
I am hoping someone can assist on making this the default return after all method chaining is complete.
What I am hoping for is something like
Claim.policy_number('913006')
#=> ClaimSetObjectHere
But I need it to support chaining like AR does so that things like
Claim.policy_number('913006').by_program('Base')
#=> ClaimSetObjectHere
I also tried to patch the #where method inside Claim which works great unless I use a scope or I chain methods in which case it complains that ClaimSet does not define default_scoped?.
Any insight would be greatly appreciated. As for "Why would you want to do this" like I said I am constantly calling this method throughout the application and I need the methods defined in ClaimSet for this to function properly.
Note: This is being used outside of rails
Okay so what I ended up doing was imposing a wrapper for ActiveRecord::Relation like so:(removed specific business logic for brevity)
class ClaimSet
def initialize(object)
process_target(object)
# ...
end
# ...
def respond_to_missing?(method_name,include_private=false)
#target.respond_to?(method_name)
end
def method_missing(method_name, *args, &block)
if #target.respond_to?(method_name)
ClaimSet.new(#target.send(method_name,*args,&block))
else
super
end
end
private
def process_target(object)
#target = object if object.is_a?(ActiveRecord::Relation)
#target = object.target if object.is_a?(ClaimSet)
end
end
Then in the Claim class.
class Claim < ActiveRecord::Base
class << self
def where(*args)
ClaimSet.new(super(*args))
end
def localized_scope(name,proc)
scope_proc = lambda do |*args|
ClaimSet.new(proc.call(*args))
end
singleton_class.send(:define_method,name,scope_proc)
end
end
end
Then I define all my scopes as localized_scope e.g.
localized_scope :policy_number, ->(policy_number){where(policy_number: policy_number)}
Now it always returns a ClaimSet in place of an ActiveRecord::Relation for #where and #localized_scope and supports method chaining through #method_missing. It also removes the monkey patch on ActiveRecord::Relation.
If you have any other suggestions please let me know as I would be glad to entertain other ideas but this works for the time being.

Allow a block to reference classes/modules not currently in scope, but will be in scope when invoked?

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

Help a ruby noob understand class inheritance

I'm trying to learn ruby by building a basic Campfire bot to screw around with at work. I've gotten pretty far (it works!) and learned a lot (it works!), but now I'm trying to make it a bit more complex by separating the actions to be performed out into their own classes, so that they can be easier to write / fix when broken. If you're interested in seeing all the (probably crappy) code, it's all up on GitHub. But for the sake of this question, I'll narrow the scope a bit.
Ideally, I would like to be able to create plugins easily, name them the same as the class name, and drop them into an "actions" directory in the root of the project, where they will be instantiated at runtime. I want the plugins themselves to be as simple as possible to write, so I want them all to inherit some basic methods and properties from an action class.
Here is action.rb as it currently exists:
module CampfireBot
class Action
#handlers = {}
def initialize(room)
#room = room
end
class << self
attr_reader :handlers
attr_reader :room
def hear(pattern, &action)
Action.handlers[pattern] = action
end
end
end
end
Where #room is the room object, and #handlers is a hash of patterns and blocks. I kind of don't understand why I have to do that class << self call, but that's the only way I could get the child plugin classes to see that hear method.
I then attempt to create a simple plugin like so (named Foo.rb):
class Foo < CampfireBot::Action
hear /foo/i do
#room.speak "bar"
end
end
I then have my plugins instantiated inside bot.rb like so:
def load_handlers(room)
actions = Dir.entries("#{BOT_ROOT}/actions").delete_if {|action| /^\./.match(action)}
action_classes = []
# load the source
actions.each do |action|
load "#{BOT_ROOT}/actions/#{action}"
action_classes.push(action.chomp(".rb"))
end
# and instantiate
action_classes.each do |action_class|
Kernel.const_get(action_class).new(room)
end
#handlers = Action.handlers
end
The blocks are then called inside room.rb when the pattern is matched by the following:
handlers.each do |pattern, action|
if pattern.match(msg)
action.call($~)
end
end
If I do puts #room inside the initialization of Action, I see the room object printed out in the console. And if I do puts "foo" inside Foo.rb's hear method, I see foo printed out on the console (so, the pattern match is working). But, I can't read that #room object from the parent class (it comes out as a nil object). So obviously I'm missing something about how this is supposed to be working.
Furthermore, if I do something to make the plugin a bit cleaner (for larger functions) and rewrite it like so:
class Foo < CampfireBot::Action
hear /foo/i do
say_bar
end
def say_bar
#room.speak "bar"
end
end
I get NoMethodError: undefined method 'say_bar' for Foo:Class.
The definition of hear can be pulled out of the class << self block and changed to:
def self.hear(pattern, &action)
Action.handlers[pattern] = action
end
to yield the exact same result. That also immediately explains the problem. hear Is a class method. say_bar is an instance method. You can't call an instance method from a class method, because there simply isn't an instance of the class available.
To understand the class << self bit, you'll have to do your own reading and experiments: I won't try to improve on what has already been said. I'll only say that within the class << self .. end block, self refers to the eigenclass or metaclass of the CampfireBot::Action class. This is the instance of the Class class that holds the definition of the CampfireBot::Action class.

Mocking the initialize method on a ruby class?

How can I mock the initialize method on a ruby class?
I'm doing some testing and want to mock out the object that is created from a new call.
I tried to write a few things and none of them seemed to get the mock class to return from the new call. It just keeps returning the normal, expected object.
EDIT:
one attempt -
class MockReminderTimingInfoParser < ReminderTimingInfoParser
def new(blank)
ReminderTimingInfoParserForTest.new
end
end
describe ReminderParser do
parser = ReminderParser.new(MockReminderTimingInfoParser)
it "should parse line into a Reminder" do
parser.parse(" doesnt matter \"message\"").should == Reminder.new('message', ReminderTimingInfo.new([DaysOfWeek.new([:sundays])], [1]))
end
end
class ReminderTimingInfoParserForTest
include TimingInfoParser
def parse_section(section); [DaysOfWeek.new([:sundays]), 1] end
def reminder_times_converter(times); times end
end
class MockReminderTimingInfoParser < ReminderTimingInfoParser
def new(blank)
ReminderTimingInfoParserForTest.new
end
end
Here, you are defining a method called new for all instances of the class MockReminderTimingInfoParser. In your question, you mention that you want to hook into instance creation. However, in Ruby, instance creation is not done by instance methods. Obviously, this cannot work, since in order to call an instance method, you'd need to have an instance first!
Instead, instances are created by calling a factory method (commonly called new) on the class.
In other words, in order to create an instance of MockReminderTimingInfoParser, you would call MockReminderTimingInfoParser.new, but you have defined a method MockReminderTimingInfoParser#new. In order to call the method you have defined, you would have to call MockReminderTimingInfoParser.new.new.
You need to define a method on MockReminderTimingInfoParser's singleton class. There's several ways to do that. One way would be just mimicking the way you would call the method:
def MockReminderTimingInfoParser.new(blank)
ReminderTimingInfoParserForTest.new
end
Another would be opening up MockReminderTimingInfoParser's singleton class:
class << MockReminderTimingInfoParser
def new(blank)
ReminderTimingInfoParserForTest.new
end
end
However, in both of these cases, MockReminderTimingInfoParser obviously has to exist first. Given that you need to define the class anyway, here's the most idiomatic way of defining methods on a class's (or module's) singleton class:
class MockReminderTimingInfoParser < ReminderTimingInfoParser
def self.new(blank)
ReminderTimingInfoParserForTest.new
end
end
Could you inherit the class and then supply your own initialize?

Resources