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.
Related
I apologize up front. I'm going to struggle articulating this question.
TL;DR - I have an object I'm using to call a method in the subclass. That method needs access to a method in a parent class (forcing inheritance...read below). I'm thinking this won't work because I'm instantiating the subclass, so the inheritance won't work right, but I'm not sure. I'm still seeking out documentation. Basically, the method in the parent class is not "seen" the way I'm doing this - NoMethodError is the exception.
I prefer DRY code, as most people do. I usually use compositing in lieu of inheritance in my code, but I think I'm at a point where if I want to keep this DRY, I have to use inheritance (though I could be wrong, so I'm open to suggestions), and so I'm trying it out, which leads me to this question.
Given the following Ruby "pseudo" code or example to kind of demonstrate what I'm trying to accomplish:
module SomeModule
class ParentClass
def initialize
end
def method_i_want_to_use(arg1, *args)
# does all the things
end
def self.method_i_want_to_use(arg1, *args)
arg = args.first unless args.empty?
self.class.method_i_want_to_use(arg1, arg)
end
end
end
And then in a different file, same module
module SomeModule
class SubClass < ParentClass
def initialize
end
# this isn't working
def my_other_method
# things get done and then
method_i_want_to_use(arg1, args) ## <<=== fails
end
end
end
Yet in another file
module SomeModule
class Thing
def initialize
#my_obj = SubClass.new
end
def my_method
#my_obj.my_other_method
end
end
end
So one important thing I missed. The method method_i_want_to_use is a method that is used all over the place in my code. It just so happens that in this one class, inheritance was NOT originally used because this class is basically atomic with the exception of this one method. So my problem is either I copy the method into this class and use it (but that kinda breaks the DRY principle sorta) or I find a way to share this method between classes.
This gets into OOP design pretty heavily and I am aware of that. One could ask: well, is the inheritance as it currently sits even relevant to the objects in question? Yes...and no. They can be. In short, principally, it works, but frankly, I don't like it. TBH, I almost prefer to just copy the method into the "subclass" and remove the inheritance and be done with it, but DRY -- unless I'm going a little too wild with DRY in this context and I kinda think I am.
Anyway, just curious what folks with more knowledge than I have for me on this. This really is the first time I've dabbled this deeply into inheritance. :)
I'd love pointers on how I can keep from implementing
There are two different methods here:
an instance method:
def method_i_want_to_use(arg1, *args)
# does all the things
end
and a class method:
def self.method_i_want_to_use(arg1, *args)
arg = args.first unless args.empty?
self.class.method_i_want_to_use(arg1, arg)
end
but what you probably want in this case is
def self.method_i_want_to_use(arg1, *args)
arg = args.first unless args.empty?
self.new.method_i_want_to_use(arg1, arg)
end
There are a few choices and it depends on what method_i_want_to_use is doing. Is it a separate thing? Then you can call it as a class method ParentClass.method_i_want_to_use inside the SubClass without inheritance.
Another way is to define it in a module and include it
include ModuleX
# and then in your code
method_i_want_to_use(...)
I'd use inheritance if you want to have some kind of common abstraction layer and you expect multiple subclasses to behave the same way. If the classes/objects that need to use method_i_want_to_use have different behaviours then inheritance is not the correct choice. Let's say you have a class that send a request to a 3rd party API and you have a class that does saves records to your db. For some reason you need to use the same piece of code (a method) in both cases, maybe to calculate some value. Using inheritance to include the method would be a mistake, because both classes have different behaviours.
Hope that helps.
After fixing some of the syntax errors and changing the call self.class.method_i_want_to_use to self.new.method_i_want_to_use as Adam also mentioned in his answer, this code seems to work fine.
I did not get any undefined methods until I tried to call SomeModule::ParentClass.method_i_want_to_use(3,4) and that was fixed by the change from class to new. Are you sure your undefined method error was not related to that?
module SomeModule
class ParentClass
def initialize
end
def method_i_want_to_use(arg1, *args)
# does all the things
puts "here #{arg1} , #{args}"
end
def self.method_i_want_to_use(arg1, *args)
arg = args.first unless args.empty?
self.new.method_i_want_to_use(arg1, arg)
end
end
end
module SomeModule
class SubClass < ParentClass
def initialize
end
# this isn't working
def my_other_method(arg1, arg2)
# things get done and then
method_i_want_to_use(arg1, arg2) ## <<=== fails
end
end
end
module SomeModule
class Thing
def initialize
#my_obj = SubClass.new
end
def my_method(arg1,arg2)
#my_obj.my_other_method(arg1, arg2)
end
end
end
SomeModule::Thing.new.my_method(1,2)
SomeModule::ParentClass.method_i_want_to_use(3,4)
prints:
here 1 , [2]
here 3 , [4]
2 main techniques for creating class methods (without the obvious "def self.method") are:
Defining them in "class << self" block
Defining ClassMethod module and extending it later
I personally prefer second way, seems cleaner. Does anyone has any reason to prefer one technique over the other?
There's also "class_method" method, but I never used it, it has quite complex implementation and seem to do a lot more than previous 2.
self.method is the simplest option when you just need to create one method without dependencies or related logic.
class << self allows you to do far more than define methods on the metaclass. This is useful when you're defining methods which need to work with other parts of the metaclass (eg. aliasing existing method names).
For instance:
class Logger
class << self
attr_reader :payload
def log(message)
#payload = message
end
end
end
The module extension method comes in handy for method reuse and to group multiple coherent methods.
For instance:
module QueryMethods
def find
end
def where
end
end
module FactoryMethods
def build
end
def create
end
end
class BusinessModel
extend QueryMethods
extend FactoryMethods
end
First, the class << foo syntax opens up foo's singleton class (eigenclass). This allows you to specialise the behaviour of methods called on that specific object.
a = 'foo'
class << a
def inspect
'"bar"'
end
end
a.inspect # => "bar"
a = 'foo' # new object, new singleton class
a.inspect # => "foo"
class << self opens up self's singleton class, so that methods can be redefined for the current self object (which inside a class or module body is the class or module itself). Usually, this is used to define class/module ("static") methods
class << self is good at keeping all of your class methods in the same block. If methods are being added in def self.method from then there's no guarantee (other than convention and wishful thinking) that there won't be an extra class method tucked away later in the file.
def self.method is good at explicitly stating that a method is a class method, whereas with class << self you have to go and find the container yourself.
Which of these is more important to you is a subjective decision, and also depends on things like how many other people are working on the code and what their preferences are.
Pros of “class << self” style
Currently I am doing something like the following to have the Runnable classes' run method access methods of the ExecutionContext:
class ExecutionContext
def message(text)
puts "ExecutionContext.message: #{text}"
end
def answer?
puts "ExecutionContext.answer called"
true
end
end
class Controller
def do_run(context)
#context = context
run
#context = nil
end
def method_missing(mth, *args)
#context.send(mth, *args)
end
end
class Runnable < Controller
def run
if answer?
message "Runnable's block executing!"
end
end
end
runnable = Runnable.new
context = ExecutionContext.new
runnable.do_run(context)
The idea is that the Runnable class is written by "end users" who want to access functionality (such as the message and answer? methods) of an ExecutionContext instance which is provided from elsewhere. In fact, the Runnable class is all the "end user" is concerned about and it should be as small and simple as possible.
The code above works as expected in that all methods called by Runnable.run are provided by the ExecutionContext.
However I am wondering if there is a more elegant way to achieve the same thing, where I can avoid using method_missing and defining the temporary #context instance variable.
Ideally, I would like to modify the code to something similar to this (ExecutionContext stays the same):
class Controller
def do_run(context, runnable)
runnable.extend_from_instance(context)
runnable.run
end
end
class Runnable
def run
if answer?
message "Runnable's block executing!"
end
end
end
runnable = Runnable.new
context = ExecutionContext.new
Controller.new.do_run(context, runnable)
Is there such thing as extend_from_instance?
Update
Thanks everyone for looking into this.
Since it has come up in the two answers I got so far, I realised I should mention the following restrictions: Unfortunately neither Runnable nor ExecutionContext can be turned into modules. They need to be classes as their instances will behave differently for different cases (both will have their own instance variables etc).
I really need the run method to execute as if it was part of the ExecutionContext instance (context in the code).
Yes, you don't want to use method_missing in this case.
I don't really understand what you are trying to accomplish. Perhaps define_block is not the correct name...?
However, a module seems to have the functionality that you want.
module Messaging
def message(text)
puts "Provider.message: #{text}"
end
end
class Parent
include Messaging
...
EDIT: I think you want to use binding. It allows you to pass the current scope (context) to another scope.
def print_local binding
puts 'local is ' + binding.eval('local')
end
def foo
local = 'value'
print_local binding
end
foo
=> local is value
In this case, print_local wants to print foo's local variable. But it doesn't have access to it because local exists only in foo's scope (or context).
So, you can pass in foo's scope as binding.
See binding.
I think what B Seven said was correct. It makes the most sense to create the shared functionality in a Module and include that module in the class. However if you really want something like your extend_from_instance you can use Ruby's Object#extend method to dynamically extend the module in an instance of the class (though I think this is a bad idea). Working form your desired code:
module ExecutionContext
def message(text)
puts "ExecutionContext.message: #{text}"
end
def answer?
puts "ExecutionContext.answer called"
true
end
end
class Controller
def do_run(context_module, runnable)
runnable.extend(context_module)
runnable.run
end
end
class Runnable
def run
if answer?
message "Runnable's block executing!"
end
end
end
runnable = Runnable.new
Controller.new.do_run(ExecutionContext, runnable)
Again though, this is a bad idea and will probably come back to bite you in the long run.
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.
I can't seem to grasp the exact difference between these two "constructs". To my mind, the following small script should output the same thing three times:
class Example
puts self
class << self
puts self
end
instance_eval do
puts self
end
end
However, the output is:
Example
#<Class:Example>
Example
Here's my rationale:
Example is an instance of Class, so self in the class body refers to that;
class << obj sets self to whatever obj is in the given block, which in my case is the instance of Class that is Example (this is where I'm probably wrong);
instance_eval runs the block in the given instance, so, in my case it's pretty much the same as putting the code in the block directly in the class body.
My current guess is that class << self inserts a ghost class between Example and Class and sets self to that, but the output of #<Class:Example> is not confirming that at all.
So what is wrong with my rationale?
class << obj sets self to whatever obj is in the given block, which in my case is the instance of Class that is Example (this is where I'm probably wrong);
No, class << obj opens up the singleton class of obj. As you correctly pointed out, inside of a class declaration, self refers to the class itself, so, in this case, the "inner" self (i.e. the one being passed to puts) refers to the singleton class of Example.
In my opinion, class << self has been one of the most obnoxious bits of syntax in Ruby. People new to the language have little idea what it means, apart from cargo-cult conventions, and even those intimately familiar with the language have only a hazy understanding of what differentiates it from instance_method, as the two do seem to be remarkably similar.
Here's an example of two different ways of defining a class method:
class Example
class << self
def class_def
:class_def
end
end
instance_eval do
def instance_def
:instance_def
end
end
end
You can check that these work by calling the methods:
puts Example.class_def.inspect
# => :class_def
puts Example.instance_def.inspect
# => :instance_def
The difference is when you're dynamically creating methods using define_method since the binding does appear to be incorrect on the instance_eval version:
class Example
class << self
define_method(:class_def) do
:class_def
end
end
instance_eval do
define_method(:instance_def) do
:instance_def
end
end
end
This results in the instance_def method being defined, but not being bound to the class itself:
puts Example.class_def.inspect
# => :class_def
puts Example.instance_def.inspect
# => NoMethodError: undefined method ‘instance_def’ for Example:Class
The only reliable way to create dynamic methods is with class << self. The method instance_def appears to be created and discarded as it doesn't show up in Example.methods even inside that block.