Extending an instance with methods from another instance of another class - ruby

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.

Related

Ruby: why doesn't calling super.method work from subclass

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]

Ruby: the role of `initialize`

I'm wondering if there's a convention / best practice for how initialize should be used when building Ruby classes. I've recently built a class as follows:
class MyClass
def initialize(file_path)
#mapped_file = map_file(file_path)
end
def map_file(file_path)
# do some processing and return the data
end
def run
#mapped_file.do_something
end
end
This uses initialize to do a lot of heavy lifting, before methods are subsequently called (all of which rely on #mapped_data).
My question is whether such processing should be handled outside of the constructor, with initialize used simply to store the instances' inputs. Would the following, for example, be preferable?
class MyClass
def initialize(file_path)
#file_path = file_path
end
def run
mapped_file.do_something_else do
etc_etc
end
end
def mapped_file(file_path)
#mapped_file ||= map_the_file_here
end
end
I hope this question isn't considered too opinion based, but will happily remove if it's deemed to be.
So, is there a 'correct' way to use initialize, and how would this fit with the scenarios above?
Any questions or comments, let me know.
As was mentioned in the comments, constructor is usually used to prepare the object, not do any actual work. More than a ruby convention, this a rule of thumb of almost all Object-Oriented languages.
What does "preparing the object" usually entail? Initializing members with default values, assigning passed arguments, calling the initializer of a super-class if such exists, etc.
In your case, this how I would rewrite your class:
class MyClass
def initialize(file_path)
#file_path = file_path
end
def map_file
#mapped_file ||= map_file_here(#file_path)
end
def run
map_file.do_something
end
end
Since run requires the file to be mapped, it always calls map_file first. But the internal map_file_here executes only once.

Changing ruby method context / calling a method with instance_exec

First, for the short version:
Isn't a method definition just a block? Why can't I do something like:
obj.instance_exec(&other_obj.method(:my_method))
with the goal of running some module method in the context of an instance of a separate class? The method is called, but it doesn't seem to be executed in the context of 'obj', despite the 'instance_exec' call.
The only way I can figure out how to accomplish this is to wrap all of the code of 'my_method' in a proc, then call in the following manner instead:
obj.instance_eval(&other_obj.my_method)
but I'd like to avoid encapsulating all of my module methods in procs.
Now, for the long version:
I'm attempting to create a modularized external provider system, where for any given class/method (generally controller methods,) I can call a corresponding method for a given provider (e.g. facebook).
Since there could be multiple providers, the provider methods need to be namespaced, but instead of simply including a bunch of methods like, for example, 'facebook_invitation_create', I'd like my InvitationsController instance to have a facebook member containing a create method - e.g.
class InvitationsController < ApplicationController
def create
...
# e.g. self.facebook.create
self.send(params[:provider]).create
...
end
end
Furthermore, I'd like the provider methods to not only function as if they were part of the controller itself - meaning they should have access to things like controller instance variables, params, session, etc. - but also to be (mostly) written as if they were part of the controller itself - meaning without any complex additional code as a result of being modularized.
I've created a simplified example below, in which MyClass has a greet method, which if called with a valid provider name (:facebook in this case), will call that providers greet method instead. In turn, the provider greet method accesses the message method of the including class, as if it were part of the class itself.
module Providers
def facebook
#facebook ||= FacebookProvider
end
module FacebookProvider
class << self
def greet
proc {
"#{message} from facebook!"
}
end
end
end
end
class MyClass
include Providers
attr_accessor :message
def initialize(message="hello")
self.message = message
end
def greet(provider=nil)
(provider.nil? or !self.respond_to?(provider)) ? message : instance_exec(&self.send(provider).greet)
end
end
This actually accomplishes almost everything I've previously stated, but I'm hung up on the fact that my provider functions need to be encapsulated in procs. I thought maybe I could simply call instance_exec on the method instead (after removing the proc encapsulation):
instance_exec(&self.send(provider).method(:greet))
...but then it seems like the instance_exec is ignored, as I get the error:
NameError: undefined local variable or method `message' for Providers::FacebookProvider:Module
Is there any way to call instance_exec on a defined method?
(I'm open to suggestions on how to better implement this as well...)
I think this is simpler than you might expect (and I realize that my answer is 2 years after you asked)
You can use instance methods from modules and bind them to any object.
module Providers
def facebook
#facebook ||= FacebookProvider
end
module FacebookProvider
def greet
"#{message} from facebook!"
end
end
end
class MyClass
include Providers
attr_accessor :message
def initialize(message="hello")
self.message = message
end
def greet(provider=nil)
if provider
provider.instance_method(:greet).bind(self).call
else
message
end
end
end
If your provider is a module, you can user instance_method to create an UnboundMethod and bind it to the current self.
This is delegation.
It's the basis for the casting gem which would work like this:
delegate(:greet, provider)
Or, if you opt-in to using method_missing from casting, your code could just look like this:
greet
But you'd need to set your delegate first:
class MyClass
include Providers
include Casting::Client
delegate_missing_methods
attr_accessor :message
def initialize(message="hello", provider=facebook)
cast_as(provider)
self.message = message
end
end
MyClass.new.greet # => "hello from facebook!"
I wrote about what delegation is and is not on my blog which is relevant to understanding DCI and what I wrote about in Clean Ruby
Maybe I'm not following along, but it seems like you are making this harder than it needs to be.
Why not implement a "dispatch" pattern in your class, where you have a hash of provider names and provider methods {:facebook=>"facebook_greet"} and then just "send" the incoming call to the correct handler via "Object#send" (http://ruby-doc.org/core-1.9.3/Object.html#method-i-send)? Send is very fast for dispatching methods, so unlike eval, you should get great performance.
Here's some code to demonstrate the way I'd solve it (assuming I am following along with what you're trying to accomplish):
module TwitterProvider
def providerInit(providers)
#providers[:twitter]="twitter_greet"
super(providers) if defined?(super)
end
def twitter_greet
"Hello Twitter User"
end
end
module FacebookProvider
def providerInit(providers)
providers[:facebook]="facebook_greet"
super(providers) if defined?(super)
end
def facebook_greet
"Hello Facebook User"
end
end
class MyClass
include FacebookProvider
include TwitterProvider
attr_accessor :message
def providerInit(providers)
super(providers) if defined?(super)
end
def initialize(message="hello")
#providers = {}
self.message = message
providerInit(#providers)
end
def greet(provider=nil)
if provider.nil? or !self.respond_to?(#providers[provider])
self.message
else
self.send(#providers[provider])
end
end
end
my_class = MyClass.new
puts my_class.greet
puts my_class.greet(:twitter)
puts my_class.greet(:facebook)
# Output:
# hello
# Hello Twitter User
# Hello Facebook User

Ruby Static method with local scope

The title sounds rediculous because it is. My biggest issue is actually trying to figure out what question to ask.
The goal: To be able to implement the code as described below OR to figure out what terminology I should be using to search for the correct answer.
The issue: I wish to have a system where classes register "processors" via a method within the class definition. eg:
class RunTheseMethodsWhenICallProcess
Include ProcessRunner
add_processor :a_method_to_run
add_processor :another_method_to_run
def a_method_to_run
puts "This method ran"
end
def another_method_to_run
puts "another method ran"
end
end
Module ProcessRunner
def process
processors.each {|meth| self.send(meth)}
end
end
My issues are mostly with understanding the scope and reference of the class to make them interact. As it stands, I have been able to add a static method 'add_processor' by calling class.extend(AClass) in the included method and adding in the class there.
The idea for this syntax was inspired by DataMappers 'property' and 'before' methods. Even with the code checked out, I am having a touch of trouble following it.
Thanks so much for any help you can offer.
If I got you right, the following will do what you want.
It initializes each class (or module) including ProcessRunner to have an empty array in ##processors. Additionally it adds class methods processors (a simple getter) and add_processor.
The process method had to be adjusted to use the class method. In fact, you could add a wrapper for this, but I think that would be to verbose for such a sample.
module ProcessRunner
module ClassMethods
def add_processor(processor)
processors << processor
end
def processors
class_variable_get :##processors
end
end
def self.included(mod)
mod.send :class_variable_set, :##processors, []
mod.extend ClassMethods
end
def process
self.class.processors.each {|meth| self.send(meth)}
end
end
class RunTheseMethodsWhenICallProcess
include ProcessRunner
add_processor :a_method_to_run
add_processor :another_method_to_run
def a_method_to_run
puts "This method ran"
end
def another_method_to_run
puts "another method ran"
end
end

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.

Resources