Ruby Static method with local scope - ruby

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

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 prepend doesn't work for class method

I'm trying to use prepend method in ruby to overwrite methods of a class, here is how my code works:
module PrependedModule
def self.klass_method
puts 'PrependedModule klass_method'
end
def instance_method_a
puts 'PrepenedModule instance method'
end
end
class SomeClass
prepend PrependedModule
def self.klass_method
puts 'SomeClass klass_method'
end
def instance_method_a
puts 'SomeClass instance_method'
end
end
SomeClass.klass_method
SomeClass.new.instance_method_a
#output:
#SomeClass klass_method
#PrependedModule instance method
#expected:
#PrependedModule klass_method
#PrependedModule instance method
The output of this script is shown above, as we can see, the instance method instance_method_a was overwritten by module PrependedModule, but not the class method klass_method, when I called klass_method on SomeClass, it still executes it's origin method, instead of the one defined in PrependedModule.
I'm confused about this and don't know what happend with class methods when using prepend. It would be a great help if anyone can solve this question for me.
Singleton classes do not work that way. You need to explicitly prepend the methods to the eigenclass of SomeClass:
module PrependedModule
module ClassMethods
def klass_method
puts 'PrependedModule klass_method'
end
end
def instance_method_a
puts 'PrepenedModule instance method'
end
end
class SomeClass
# prepending to the class
prepend PrependedModule
class << self
# prepending to the eigenclass
prepend PrependedModule::ClassMethods
end
def self.klass_method
puts 'SomeClass klass_method'
end
def instance_method_a
puts 'SomeClass instance_method'
end
end
With include or prepend you can only gain access to a module's instance methods1. You therefore might ask if there is any reason to define module methods on a module. The answer is a resounding "yes". There are two reasons you might want to do that.
The first has nothing to do with including or prepending the module. You need only look at the module Math to understand why you might want to do that. All methods defined on Math are module methods. These constitute a library of useful functions. They are of course methods, but since all have Math as their receiver, they behave like functions in non-OOP languages.
The second reason is that you might want to define a callback method (aka hook method) on a module that is to be included or prepended by another module. The main ones are Module#included, Module#prepended, Module#extended, Class#inherited and BasicObject#method_missing. The last of these is an instance method; the others are module methods. An example of Module#prepended is given below.
#mudasoba has shown how to confine instance methods to a sub-module Sub of Mod so that Mod::Sub can be prepended (or included) to a class's (or module's) singleton class. A commonly-used pattern for doing that employs the callback Module#prepended. It follows.
module PrependedModule
module ClassMethods
def klass_method
puts 'PrependedModule klass_method'
end
end
def instance_method_a
puts 'PrepenedModule instance method'
end
def self.prepended(mod)
mod.singleton_class.prepend(ClassMethods)
end
end
class SomeClass
def self.klass_method
puts 'SomeClass klass_method'
end
def instance_method_a
puts 'SomeClass instance_method'
end
prepend PrependedModule
end
SomeClass.klass_method
# PrependedModule klass_method
SomeClass.new.instance_method_a
# PrepenedModule instance method
1 I've always found it curious that instance methods can be defined on modules (that are not classes), considering that such modules cannot have instances. True, these methods become instance methods in classes that include or prepend the module, but keep in mind that those modules can be included or prepended by other modules (that are not classes) as well. One might therefore expect such methods to have some name other than "instance method". Finding a suitable alternative would be a challenge, however, which is perhaps one reason why that nomenclature has persisted.
Someone can comment on when this was introduced, but my personal experience and also suggested by https://stackoverflow.com/users/256970/cary-swoveland in comments section of the selected answer, you can always do
class Source
def self.hello
puts "hello"
end
end
module Extension
def hello
# you're also allowed to call super from here
puts "world"
end
end
Source.singleton_class.prepend Extension
Now if you call Source.hello, the method from the Extension module will be called.
This is also valid for overriding Module's static/class methods But I've mentioned the code explicitly because I've rarely come accross this solution on S/O.
module Source
def self.hello
puts "hello"
end
end
module Extension
def hello
# you're also allowed to call super from here
puts "world"
end
end
Source.singleton_class.prepend Extension
class Foo
singleton_class.prepend ClassMethods
def self.hello
puts "hi"
end
end
module ClassMethods
def hello
puts "ho"
end
end

Getting the name of the calling class in Ruby

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.

Best way to add methods to a Class in execution time

I have to add methods to Class in execution time.
class ExtendableClass
end
The methods to add are declared in independent Classes.
module ExtensionClassOne
def method_one
end
end
module ExtensionClassTwo
def method_two
end
end
I'm looking for an (elegant) mechanism to add all the extension class methods into the ExtendableClass.
Approach 1
I'm thinking in explicily include the extension classes like:
ExtendableClass.send( :include, ExtensionClassOne )
ExtendableClass.send( :include, ExtensionClassTwo )
but it looks a little forced to have to call this private method every time I define a new extension class.
Approach 2
So I was looking for an automatic way to include this methods into my ExtendableClass class.
I'm thinking in declare an specific ancestor for this extension classes:
class ExtensionClassOne < Extension
def method_one
end
end
and then I'd need a mechanism to know all the childs of a class... something like the oposite of ancestors.
Once I have this list I can easily ExtendableClass.include all the list of classes. Even if I have to call to the private method here.
Approach 3
Also inheriting from the Extension class and detect in declaration time when this class is used as ancestor. In the way that the ActiveSupport.included method works, like an event binding. Then make the include there.
Any solution for implement approach 2 or approach 3? Do you recommend approach 1? New approachs?
#fguillen, you are right that the "explicit way is the cleanest approach". Since that is so, why don't you use the most "explicit" code which could be imagined:
class Extendable
end
class Extendable
def method_one
puts "method one"
end
end
class Extendable
def method_two
puts "method two"
end
end
...In other words, if you are defining a module which will be automatically included in a class as soon as it is defined, why bother with the module at all? Just add your "extension" methods directly to the class!
Approach 4 would be to define a macro on class level in Object
class Object
def self.enable_extension
include InstanceExtension
extend ClassExtension
end
end
and calling this macro in all your classes you want to be extended.
class Bacon
enable_extension
end
Car.enable_extension
This way,
you don't have to use #send to circumvent encapsulation (Approach 1)
you can inherit from any Class you want, because everything inherits from Object anyway (except 1.9's BasicObject)
the usage of your extension is declarative and not hidden in some hook
Downside: you monkeypatch build-in Classes and may break the world. Choose long and decriptive names.
Edit: Given your answer to my comment on the question I suppose this is not what you wanted. I see no problem with your "Approach 1" in this case; it's what I'd do. Alternatively, instead of using send to bypass the private method, just re-open the class:
class ExtendableClass
include ExtensionOne
end
Assuming I understand what you want, I'd do this:
module DelayedExtension
def later_include( *modules )
(#later_include||=[]).concat( modules )
end
def later_extend( *modules )
(#later_extend||=[]).concat( modules )
end
def realize_extensions # better name needed
include *#later_include unless !#later_include || #later_include.empty?
extend *#later_extend unless !#later_extend || #later_extend.empty?
end
end
module ExtensionOne
end
module ExtensionTwo
def self.included(klass)
klass.extend ClassMethods
end
module ClassMethods
def class_can_do_it!; end
end
end
class ExtendableClass
extend DelayedExtension
later_include ExtensionOne, ExtensionTwo
end
original_methods = ExtendableClass.methods
p ExtendableClass.ancestors
#=> [ExtendableClass, Object, Kernel, BasicObject]
ExtendableClass.realize_extensions
p ExtendableClass.ancestors
#=> [ExtendableClass, ExtensionOne, ExtensionTwo, Object, Kernel, BasicObject]
p ExtendableClass.methods - original_methods
#=> [:class_can_do_it!]
The included method is actually a hook. It is called whenever you are inherited from:
module Extensions
def someFunctionality()
puts "Doing work..."
end
end
class Foo
def self.inherited(klass)
klass.send(:include, Extensions) #Replace self with a different module if you want
end
end
class Bar < Foo
end
Bar.new.someFunctionality #=> "Doing work..."
There is also the included hook, which is called when you are included:
module Baz
def self.included(klass)
puts "Baz was included into #{klass}"
end
end
class Bork
include Baz
end
Output:
Baz was included into Bork
A very tricky solution, I think too much over-engineering, would be to take the inherited hook that #Linux_iOS.rb.cpp.c.lisp.m.sh has commented and keep all and every child class in a Set and combined it with the #Mikey Hogarth proposition of method_missing to look for all this child class methods every time I call a method in the Extendable class. Something like this:
# code simplified and no tested
# extendable.rb
class Extendable
##delegators = []
def self.inherited( klass )
##delegators << klass
end
def self.method_missing
# ... searching in all ##delegators methods
end
end
# extensions/extension_one.rb
class ExtensionOne < Extendable
def method_one
end
end
But the logic of the method_missing (and respond_to?) is gonna be very complicate and dirty.
I don't like this solution, just let it here to study it like a possibility.
After a very interesting propositions you have done I have realized that the explicit way is the cleanest approach. If we add a few recommendations taking from your answers I think I'm gonna go for this:
# extendable.rb
class Extendable
def self.plug( _module )
include( _module )
end
end
# extensions/extension_one.rb
module ExtensionOne
def method_one
puts "method one"
end
end
Extendable.plug( ExtensionOne )
# extensions/extension_two.rb
module ExtensionTwo
def method_two
puts "method two"
end
end
Extendable.plug( ExtensionTwo )
# result
Extendable.new.method_one # => "method one"
Extendable.new.method_two # => "method two"

Ruby Metaprogramming

I'm trying to write a DSL that allows me to do
Policy.name do
author "Foo"
reviewed_by "Bar"
end
The following code can almost process it:
class Policy
include Singleton
def self.method_missing(name,&block)
puts name
puts "#{yield}"
end
def self.author(name)
puts name
end
def self.reviewed_by(name)
puts name
end
end
Defining my method as class methods (self.method_name) i can access it using the following syntax:
Policy.name do
Policy.author "Foo"
Policy.reviewed_by "Bar"
end
If i remove the "self" from the method names, and try to use my desired syntax, then i receive an error "Method not Found" in the Main so it could not find my function until the module Kernel. Its ok, i understand the error. But how can i fix it? How can i fix my class to make it work with my desired syntax that?
In order to control what self is in the scope of the block (since author resolves to self.author), you can use instance_eval.
class Policy
def self.name(&block)
PolicyNameScope.new(block)
end
class PolicyNameScope
def initialize(block)
instance_eval(&block)
end
def author(author)
#author = author
end
def reviewed_by(reviewed_by)
#reviewed_by = reviewed_by
end
end
end
policy = Policy.name do
author "Dawg"
reviewed_by "Dude"
end
p policy
# => #<Policy::PolicyNameScope:0x7fb81ef9f910 #reviewed_by="Dude", #author="Dawg">
The PolicyNameScope class has the instance methods that are allowed in the name block. This is so that methods from Policy isn't available inside the block, making the DSL a whole lot tighter.
Since your example is out of context I can't help you any further - this code by itself doesn't seem very useful.
The above answer suggested by August is correct, however if you want to use your Constructor for some other purpose, then the above method fails. Therefore, in that situation you have to use some other method other than that described above. Here's the solution that I have prepared without using the class constructor.
class Policy
def self.method_missing(name,&block)
self.class_eval(&block)
end
def self.author(name)
p name
end
def self.reviewed_by(name)
p name
end
end
Policy.name do
author "Foo"
reviewed_by "Bar"
end
This is not an optimized solution but a solution to your stated problem.

Resources