Sorry that I haven't made the title very clear but I honestly can't work out how to phrase my question any better; if some one can think of a better title, then please by all means change it.
Take a look at the following example:
module Foo
def method(value)
Bar.instance_exec(self.method(value))
end
class Bar
def self.method(value)
print value
end
end
end
Is that the only way to make is so Bar's method is available within the namespace of Foo without the need to call Bar or is there a more elegant way to do this? Perhaps method_missing might work but again, a bit cumbersome.
EDIT:
Further example
Module Foo
class SomeClassController < ApplicationController
def index
method(#user.id) #instead of Bar.method(#user.id)
end
end
I'm building an engine but I don't want to have to refer to Engine every time I want to use a method from it that should be available everywhere within that Main Name Space (Foo in this instance) AS there more like utility methods BUT they need to be done within the scope of Engine.
EDIT:
Turns out I wasn't using the code correctly
module Foo
def method(value)
return Bar.instance_exec(value) { |v| return self.method(value)}
end
class Bar
def self.method(value)
return (value + 59)
end
end
end
will the method inside of the block of instance_exec return outside of it so that def method within module Foo will return the correct value.
EDIT: I realise it is kind of vague but trust me when I say that instance_exec is exactly what I was searching for, that and instance_exec does in deed return outside of it's execution block.
The answer to my problem is as follows using a rails engine as the example to add a more understandable context
engine.rb:
module Foo
class Engine
class SomeClass
def method(value)
return (value + 59)
end
end
end
end
otherActions.rb
module Foo
def method(value)
return Engine.instance_exec(value){ |v| return self::SomeClass.method(v) }
end
end
Engine_Controller.rb
Module Foo
class EngineController < ApplicationController.rb
def index
#user.some_field = method(params[:user_input])
end
end
end
I think that should work well, I know everything Up to the usage of method within def index at least, that I'm not sure about but the main point of the question has been answered within otherActions.rb
Related
Say I have this class:
class Foo
def destroy_target(target)
Missile.launch(target)
end
end
I want to temporarily neuter the destructive power of Foo, for example for testing purposes, so I do this:
backup = Foo.instance_method(:destroy_target)
class Foo
def destroy_target(target)
Pillow.launch(target)
end
end
Here's my question: how do I "reattach" the original method to Foo, as if it was never overridden in the first place?
I realize I can do this:
class Foo
def destroy_target(target)
backup.bind(self).call(target)
end
end
But obviously this is not optimal, as I'm now wrapping the original function. I want to be able to detach and reattach the method an indefinite amount of times without adding any overhead.
Asked a different way; how do I attach a DetachedMethod to a class "properly", i.e. without defining a new method that calls the detached one.
Note: I am not interested in alternative ways of temporarily changing the functionality of a class. I specifically want to know how to replace a method with a different method, then restore the original method cleanly.
I tested your first example, and it seems to work fine. I couldn't find any side-effect, but it doesn't mean there isn't.
Did you consider refinements?
For a Class
class Missile
def self.launch(t)
puts "MISSILE -> #{t}"
end
end
class Pillow
def self.launch(t)
puts "PILLOW -> #{t}"
end
end
class Foo
def destroy_target(target)
Missile.launch(target)
end
end
module PillowLauncher
refine Foo do
def destroy_target(target)
Pillow.launch(target)
end
end
end
module Test
using PillowLauncher
Foo.new.destroy_target("Tatooine")
#=> PILLOW -> Tatooine
end
Foo.new.destroy_target("Tatooine")
#=> MISSILE -> Tatooine
It might bring the advantage of being a bit more standard and understandable than your example.
For a Module
If Foo is a Module, you cannot call refine Foo directly, you'd get a TypeError: wrong argument type Module (expected Class).
You can, however, refine its singleton_class :
module Foo
def self.destroy_target(target)
Missile.launch(target)
end
end
module PillowLauncher
refine Foo.singleton_class do
def destroy_target(target)
Pillow.launch(target)
end
end
end
module Test
using PillowLauncher
Foo.destroy_target('Tatooine')
#=> PILLOW -> Tatooine
end
Foo.destroy_target('Tatooine')
#=> MISSILE -> Tatooine
I'm not sure about your note :
I am not interested in alternative ways of temporarily changing
the functionality of a class. I specifically want to know how to
replace a method with a different method, then restore the original
method cleanly.
My proposed code seems to do both.
This appears to work:
Foo.instance_exec {
define_method(:destroy_target, backup)
}
But I'm not completely sure if this is side effect free. If somebody knows for sure I'd appreciate a comment.
This also appears to work if Foo is a module defined like this:
module Foo
extend self
def destroy_target(target)
Missile.launch(target)
end
end
I'm trying to make a DSL like configuration for classes that include a module but to have the configured variable available to both class and instance methods seems to require littering the module with access methods. Is there a more elegant way to do this?
module DogMixin
class << self
def included(base)
base.extend ClassMethods
end
end
module ClassMethods
def breed(value)
#dog_breed = value
end
def dog_breed
#dog_breed
end
end
end
class Foo
include DogMixin
breed :havanese
end
puts Foo.dog_breed
# not implemented but should be able to do this as well
f = Foo.new
f.dog_breed
Your example is a bit weird I think :)
Anyway, one way to avoid writing the accessors (the assignment - accessor is problematic in my eyes - especially in the given example) is to define constants, as in the example below. If however you need runtime-assignments, please edit your question (and thus render this answer invalid :) except you want to mess with runtime constant assignment, which is possible but messy).
module DogMixin
# **include** DogMixin to get `Class.dog_breed`
class << self
def included(base)
def base.dog_breed
self::DOG_BREED || "pug"
end
end
end
# **extend** DogMixin to get `instance.dog_breed`
def dog_breed
self.class.const_get(:DOG_BREED) || "pug"
end
end
class Foomer
DOG_BREED = 'foomer'
extend DogMixin
include DogMixin
end
f = Foomer.new
puts Foomer.dog_breed
puts f.dog_breed
# If I understand you correctly, this is the most important (?):
f.dog_breed == Foomer.dog_breed #=> true
It took some reading of (In Ruby) allowing mixed-in class methods access to class constants to get the Instance-And-Class Constant lookup from a module, but it works. I am not sure if I really like the solution though. Good question, although you could add a little detail.
I'm trying to figure out how to get the name of the class that called a module function in a plugin-based application of mine.
caller seems to give me a file/line number, which is workable, but seems a bit hacky and not idiomatic.
Example code:
module AwesomeModule
def self.get_caller
puts #some unknown code here
end
end
class AwesomeClass
def initialize
AwesomeModule::get_caller
end
end
a = AwesomeClass.new # ideal return => "AwesomeClass"
You typically use ruby modules by including them. Try this:
module AwesomeModule
def get_caller
self.class
end
end
class AwesomeClass
include AwesomeModule
def initialize
get_caller
end
end
a = AwesomeClass.new # "AwesomeClass"
Also, note that in your question get_caller is being called on the AwesomeModule module itself, further complicating the issue.
I want to create an instance variable when a certain method is called and also set it to a default value. The instance should then only be able to get the variable but not set it. Here's a piece of code to showcase my problem:
class Foo
def self.bar(var)
attr_reader name.to_sym
instance_variable_set("##{var.to_s}",[var.to_s])
end
end
class Bar < Foo
bar :foo
end
puts Bar.new.foo
When I run this code I expect to get ["foo"] but instead I get nil. It seems to be a problem of scope but after fiddling around with the code for some time I just don't seem to get it right.
Edit:
I just found an exceptionally good article (read till the end) which addresses this problem very concisely. Click Me
This is I think the sort of pattern you are trying to create:
class Foo
def self.all_properties
#all_properties ||= []
end
def self.property( pr_name )
attr_reader pr_name.to_sym
instance_variable_set( "#default_#{pr_name.to_s}", [pr_name.to_s] )
all_properties << pr_name.to_sym
end
def set_default_properties
self.class.all_properties.each do | pr_name |
default = self.class.instance_variable_get( "#default_#{pr_name.to_s}" )
instance_variable_set( "##{pr_name}", default.clone )
end
end
def initialize
set_default_properties
end
end
class Bar < Foo
property :foo
end
p Bar.new.foo
It is more complex than you might initially assume. You have to put the list of managed properties, and their default values somewhere. I have done this above with the class instance variables #all_properties and #default_foo. You can get them forwarded into each instance with a little more meta-programming than you have in the question - basically a copy from defaults stashed in the class when it was defined has to be made during instantiation. Why? Because the class definition is not re-run during instantiation, it happens just once beforehand (unless you start modifying the class on-the-fly in the constructor - but that would be unusual!)
Note that the clone in the code above is not quite enough to prevent instances interfering with each other. I haven't implemented a deep clone (left as exercise to anyone reading the code), but the following variation of that line will work for the code as it stands, because the structure of default data is always the same:
instance_variable_set( "##{pr_name}", [ default[0].clone ] )
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.