With the following code, in what way can I access #arr from Child?
class Parent
class << self
def create_singleton_variable
#arr = [1,2,3]
end
def arr
#arr
end
end
end
class Child < Parent
def get
puts self.arr
end
def self.get
puts self.arr
end
end
p "class method call #{Child.get}"
#=> ➜ ruby child.rb
#=> "class method call "
c = Child.new
p "instance call #{c.get}"
#=> ➜ ruby child.rb
#=> Traceback (most recent call last):
#=> 1: from child.rb:24:in `<main>'
#=> child.rb:15:in `get': undefined method `arr' for #<Child:0x00007fe0eb02e7d8> (NoMethodError)
I've tried many other ways as well, but don't feel the need to post them here.
edit to the question, since it appears I do need a bit more context:
I'm attempting to prepend a module into the Thor framework. I want to then access this bit of code
module ThorExtensions
module Thor
module CompletionGeneration
def self.prepended(base)
base.singleton_class.prepend(ClassMethods)
end
module ClassMethods
def completion
puts "Start Completion"
p self
p self.superclass
p self.class.superclass.subcommands
puts "End Completion"
end
end
end
end
end
results in
Start Completion
Debug
Thor
bundler: failed to load command: exe/pt (exe/pt)
NoMethodError: undefined method `subcommands' for Module:Class
/Users/tyler.thrailkill/Documents/code/backend/pt-cli/lib/thor_extensions/completion_generation.rb:13:in `completion'
/Users/tyler.thrailkill/Documents/code/backend/pt-cli/lib/debug/debug.rb:24:in `<class:Debug>'
/Users/tyler.thrailkill/Documents/code/backend/pt-cli/lib/debug/debug.rb:4:in `<top (required)>'
/Users/tyler.thrailkill/Documents/code/backend/pt-cli/lib/pt.rb:5:in `require'
/Users/tyler.thrailkill/Documents/code/backend/pt-cli/lib/pt.rb:5:in `<top (required)>'
exe/pt:13:in `require'
exe/pt:13:in `<top (required)>'
which of course is not what I want. It appears that maybe my issue is with prepending?
Edit 2
I seem to have done a terrible job of explaining my issue with prepending. Here is a fully working example showing my issue. I believe this is due to how prepending something to a class essentially creates another Class in the call stack that is called first. My hope is that I'm actually still able to access this method somehow.
class Parent
class << self
def create_singleton_variable
#arr = [1,2,3]
puts "arr is initialized #{#arr}"
end
# ... lots of code here.
def arr
puts "arr is #{#arr.inspect}"
#arr
end
end
end
module CompletionGeneration
def self.prepended(base)
base.singleton_class.prepend(ClassMethods)
end
module ClassMethods
def completion
puts "self.superclass.arr == #{self.superclass.arr.inspect}" # unable to access superclass arr
puts "self.class.superclass.arr == #{self.class.superclass.arr}" # likewise, unable to access Child's metaclass superclass
rescue Exception => e
# do nothing, this is just so you can see arr is actually initialized in the context of the Child
p e
end
end
end
Parent.prepend CompletionGeneration
class Child < Parent
create_singleton_variable
completion
arr
end
Child.new
results in the output
➜ ruby child.rb
arr is initialized [1, 2, 3]
arr is nil
self.superclass.arr == nil
#<NoMethodError: undefined method `arr' for Module:Class>
arr is [1, 2, 3]
This code should be simply copy and pastable as is.
Here is your code, slightly modified.
class Parent
def self.create_singleton_variable
#arr = [1,2,3]
end
def self.arr
puts "self = #{self} in the getter for #arr"
#arr
end
end
class Child < Parent
def get
puts self.arr
end
def self.get
puts self.arr
end
end
I have written Parent in the more conventional way. Except for the addition of the puts statement, it is equivalent to that contained in the question.
First, a head-slapper: Kernel#puts-anything returns nil. You need to remove puts from both methods:
class Child < Parent
def get
self.arr
end
def self.get
self.arr
end
end
Parent.create_singleton_variable
#=> [1, 2, 3]
Child.get.nil?
self = Child in the getter for #arr
#=> true
We see that within the getter arr, invoked by Child's class method get, self equals Child, so the method looks for a class instance variable #arr of Child not of Parent. As no such instance variable has been initialized, nil is returned.
You need the following.
class Parent
class << self
def create_singleton_variable
#arr = [1,2,3]
end
def arr
puts "self = #{self} in the getter for #arr"
#arr
end
end
end
class Child < Parent
def get
self.class.superclass.arr
end
def self.get
superclass.arr
end
end
The crucial difference to that given in the question is that Class#superclass changes the scope (i.e., self) to Parent.
We see the desired result is obtained.
Child.get
self = Parent in the getter for #arr
#=> [1, 2, 3]
Child.new.class.superclass.arr
self = Parent in the getter for #arr
#=> [1, 2, 3]
A common misconception is that the Child class method defined def self.get; self.arr; end invokes the getter Parent::arr, and therefore returns the value of Parent's instance variable #arr. It is Child::arr that is invoked, however, that method having been inherited from Parent, and it is Child's class instance variable #arr that is being retrieved, a subtle, but important, distinction.
Edit 2
The first observation is that Parent can be written in the more conventional (and completely equivalent) way.
class Parent
def self.create_singleton_variable
#arr = [1,2,3]
puts "arr is initialized #{#arr}"
end
def self.arr
puts "arr is #{#arr.inspect}"
#arr
end
end
Regardless of how its written, self will equal Parent when either class method is involked on parent. The first, therefore, will create the class instance variables #arr.
Parent.methods(false)
#=> [:create_singleton_variable, :arr]
Parent.instance_variables
#=> []
Parent.ancestors
#=> [Parent, Object, Kernel, BasicObject]
Now let's create a class variable for Parent.
Parent.create_singleton_variable
# arr is initialized [1, 2, 3]
Parent.instance_variables
#=> [:#arr]
Now let me change the value of #arr.
Parent.instance_variable_set(:#arr, ['dog', 'cat'])
#=> ["dog", "cat"]
Parent.arr
# arr is ["dog", "cat"]
#=> ["dog", "cat"]
Next, create the class Child, but do not yet prepend the module.
class Child < Parent
create_singleton_variable
arr
end
arr is initialized [1, 2, 3]
arr is [1, 2, 3]
Child.ancestors
#=> [Child, Parent, Object, Kernel, BasicObject]
Child.instance_variables
#=> [:#arr]
Child.instance_variable_get(:#arr)
#=> [1, 2, 3]
There are no surprises. Next load the module.
module CompletionGeneration
def self.prepended(base)
base.singleton_class.prepend(ClassMethods)
end
module ClassMethods
def completion
puts "self=#{self}"
puts "superclass=#{superclass}"
puts "self.class=#{self.class}"
puts "self.class.superclass == #{self.class.superclass}"
puts "superclass.arr == #{superclass.arr.inspect}"
puts "self.class.superclass.arr == #{self.class.superclass.arr}"
rescue Exception => e
# do nothing, this is just so you can see arr is actually
# initialized in the context of the Child
puts "Exception => e=#{e}"
end
end
end
(Note self. is not needed in "superclass.arr == #{superclass.arr.inspect}") Now prepend this module to Parent.
Parent.prepend CompletionGeneration
Parent.ancestors
#=> [CompletionGeneration, Parent, Object, Kernel, BasicObject]
Parent.methods.include?(:completion)
#=> true
Child.ancestors
#=> [Child, CompletionGeneration, Parent, Object, Kernel, BasicObject]
Child.methods.include?(:completion)
#=> true
The callback modules method CompletionGeneration::prepended is fired with base equal to Parent, causing Parent's singleton class to prepend ClassMethods, thereby adding the class method Parent::completion. Since Parent did not previously have a method by that name using prepend or include would have the same effect. Further, instead of Parent.singleton_class.include ClassMethods, one could have used the included(base) callback instead, and executed Parent.extend ClassMethods. Perhaps prepend is being used here for a general case where Parent may have a class method by that name.1
Now execute the following.
Child.completion
self=Child
superclass=Parent
self.class=Class
self.class.superclass == Module
arr is ["dog", "cat"]
superclass.arr == ["dog", "cat"]
Exception => e=undefined method `arr' for Module:Class
The exception was raised when
puts "self.class.superclass.arr == #{self.class.superclass.arr}"
was being executed. As that amounts to
puts "self.class.superclass.arr == #{Module.arr}"
but of course Module has no module method arr.
1 In view of Child.ancestors, prepending Parent with the module only causes Parent's children to include (rather than prepend) the module; that is, if a child already has a method completion before the prepending, that method will not be preempted by the the module's method by the same name.
Related
I would like to create an object that accepts method names and prints them out. I should be able to call any method on it. For example,
obj.hello("was")
# => called hello with argument 'was'
obj.ok(["df", 1])
# => called ok with argument ["df", 1]
I don't want to define hello or ok in advance.
Is that possible?
Easy:
class Noop
def method_missing(m, *args)
puts "#{m} #{args.inspect}"
end
end
Noop.new.foo
# => foo []
Noop.new.bar(1,2,3)
# => bar [1, 2, 3]
method_missing is called on every Ruby object when you call a method that does not exist. It usually ends up being handled by Object (the superclass of everything) which raises NoMethodError.
Note that this does not apply to the methods provided by its ancestors (Class, Module, Object, Kernel, BasicObject) which you can inspect by:
class Noop
puts self.instance_methods.inspect
puts self.methods.inspect
def method_missing(m, *args)
puts "#{m} #{args.inspect}"
end
end
Is there a concise way to limit method visibility within the module while including it? In other words, I'd like to limit polluting the class with helper methods only used in the included module.
module Bar
def call
hide_me
end
private
# make this method only callable within this module
def hide_me
'visible'
end
end
class Foo
include Bar
def unrelated_method
hide_me
end
end
# that's ok
Foo.new.call #=> 'visible'
# that's not
Foo.new.unrelated_method #=> 'visible'
I'm ok with calling it via Bar.instance_method(:hide_me).bind(self).call, I just don't want to worry about accessing or redefining a helper method from some module.
You can wrap a class into the module and use private methods within the class, like so:
module Bar
def call
BarClass.new.call
end
class BarClass
def call
hide_me
end
private
def hide_me
puts "invisible"
end
end
end
class Foo
include Bar
def call_bar
call
end
def this_method_fails
hide_me
end
end
One can do what you want by including the module to the class and then undefining the unwanted included methods.
First construct a module.
module M
def cat
puts "meow"
end
def dog
puts "woof"
end
def owl
puts "who?"
end
private
def frog
puts "ribbit"
end
def pig
puts "oink"
end
end
Confirm the methods now exist.
M.instance_methods(false)
#=> [:cat, :dog, :owl]
M.private_instance_methods(false)
#=> [:frog, :pig]
Create the class, including the module M.
class C
def froggie
frog
end
def cat
puts "meow-meow"
end
include M
end
Check the instance methods.
C.instance_methods & [:cat, :dog, :owl, :froggie]
#=> [:cat, :froggie, :dog, :owl]
C.private_instance_methods & [:frog, :pig]
#=> [:frog, :pig]
and confirm :cat is owned by C and not by M.
C.instance_method(:cat).owner
#=> C
Now use the method Module#undef_method to undefine the unwanted methods from the module.
class C
[:cat, :owl, :pig].each { |m|
undef_method(m) unless instance_method(m).owner == self }
end
The unless... clause is needed so that the instance method :cat defined in C is not undefined.
Confirm the methods were undefined.
C.instance_methods & [[:cat, :dog, :owl, :froggie]
#=> [:cat, :froggie, :dog]
C.private_instance_methods & [:frog, :pig]
#=> [:frog]
Execute the methods.
c = C.new
c.cat
#=> "meow-meow"
c.dog
#=> "woof"
c.froggie
#=> "ribbit"
I have a class that have an instance method, that returns a hash. I can't change the code of that class directly, but I can extend it with modules. I need to add some new keys to the returning hash of the method. Something like this:
class Processor
def process
{ a: 1 }
end
end
module ProcessorCustom
def process
super.merge(b: 2) # Not works :(
end
end
Processor.send :include, ProcessorCustom
processor = Processor.new
processor.process # returns { a: 1 }, not { a: 1, b: 2 }
How can I do that? Thanks.
You could call prepend instead of include:
Processor.prepend(ProcessorCustom)
processor = Processor.new
processor.process
#=> {:a=>1, :b=>2}
prepend and include result in different ancestor order:
module A; end
module B; end
module C; end
B.ancestors #=> [B]
B.include(C)
B.ancestors #=> [B, C]
B.prepend(A)
B.ancestors #=> [A, B, C]
Alternatives
Depending on your use-case, you could also extend a specific instance: (this doesn't affect other instances)
processor = Processor.new
processor.extend(ProcessorCustom)
processor.process
#=> {:a=>1, :b=>2}
Or use SimpleDelegator to implement a decorator pattern:
require 'delegate'
class ProcessorCustom < SimpleDelegator
def process
super.merge(b: 2)
end
end
processor = ProcessorCustom.new(Processor.new)
processor.process #=> {:a=>1, :b=>2}
I think the first option to consider would be the one requiring the least work by the reader to comprehend; and in Object Oriented software that would be a subclass to specialize the behavior of the superclass. I would deviate from this if, and only if, there were a compelling reason to do so.
How about this?:
#!/usr/bin/env ruby
class Processor
def foo
{ x: 3 }
end
end
class MyProcessor < Processor
def foo
super.merge({ y: 7 })
end
end
p MyProcessor.new.foo # outputs: {:x=>3, :y=>7}
I think it's better to create a proxy than to pollute the original class.
class Proxy
def initialize(target)
#target = target
end
# add some syntactic sugar
singleton_class.class_eval { alias [] new }
def process
#target.process.merge!(b: 2)
end
end
Proxy[Processor.new].process #=> {a: 1, b: 2}
You can even create your own dynamic proxy.
class DynamicProxy < BasicObject
def initialize(target)
#target = target
end
# again, add some syntactic sugar
singleton_class.class_eval { alias [] new }
def method_missing(name, *args, &block)
super unless #target.respond_to?(name)
# Do something before calling the target method
result = #target.send(name, *args, &block)
# Do something after calling the target method
end
def respond_to_missing?(name, include_private = false)
#target.respond_to?(name, include_private)
end
end
To hide the detail of creating Processor instance, you can make a simple factory to handle the creation.
module ProcessorFactory
def self.create
DynamicProxy[Processor.new]
end
end
Then you can do your job
ProcessorFactory.create.process
I want a child class to inherit a class-level instance variable from its parent, but I can't seem to figure it out. Basically I'm looking for functionality like this:
class Alpha
class_instance_inheritable_accessor :foo #
#foo = [1, 2, 3]
end
class Beta < Alpha
#foo << 4
def self.bar
#foo
end
end
class Delta < Alpha
#foo << 5
def self.bar
#foo
end
end
class Gamma < Beta
#foo << 'a'
def self.bar
#foo
end
end
And then I want this to output like this:
> Alpha.bar
# [1, 2, 3]
> Beta.bar
# [1, 2, 3, 4]
> Delta.bar
# [1, 2, 3, 5]
> Gamma.bar
# [1, 2, 3, 4, 'a']
Obviously, this code doesn't work. Basically I want to define a default value for a class-level instance variables in the parent class, which its subclasses inherit. A change in a subclass will be the default value then for a sub-subclass. I want this all to happen without a change in one class's value affecting its parent or siblings. Class_inheritable_accessor gives exactly the behavior I want... but for a class variable.
I feel like I might be asking too much. Any ideas?
Rails has this built into the framework as a method called class_attribute. You could always check out the source for that method and make your own version or copy it verbatim. The only thing to watch out for is that you don't change the mutable items in place.
What I did in my project for using resque is to define a base
class ResqueBase
def self.inherited base
base.instance_variable_set(:#queue, :queuename)
end
end
In the other child jobs, the queue instance will be set by default.
Hope it can help.
Use a mixin:
module ClassLevelInheritableAttributes
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def inheritable_attributes(*args)
#inheritable_attributes ||= [:inheritable_attributes]
#inheritable_attributes += args
args.each do |arg|
class_eval %(
class << self; attr_accessor :#{arg} end
)
end
#inheritable_attributes
end
def inherited(subclass)
#inheritable_attributes.each do |inheritable_attribute|
instance_var = "##{inheritable_attribute}"
subclass.instance_variable_set(instance_var, instance_variable_get(instance_var))
end
end
end
end
Including this module in a class, gives it two class methods: inheritable_attributes and inherited.
The inherited class method works the same as the self.included method in the module shown. Whenever a class that includes this module gets subclassed, it sets a class level instance variable for each of declared class level inheritable instance variables (#inheritable_attributes).
I have some difficulties for using Ruby block, passing in a method.
As in the following case, I would like to display each element of #array, from Box instance (using .each method):
class Box
def initialize
#array = [:foo, :bar]
end
def each(&block)
# well, hm..
end
end
a = Box.new
a.each { |element| puts element }
You really just need to delegate to the each method on #array and pass it the block. Additionally, you can include the Enumerable mix-in to gain access to the methods it provides (e.g. map, inject, etc...):
class Box
include Enumerable
def initialize
#array = [:foo, :bar]
end
def each(&block)
#array.each(&block)
end
end
More information on the Enumerable module is available in the documentation.
For this simple example, you actually aren't required to pass the block explicitly:
def each
#array.each{|e| yield e}
end
Passing the block (which is a Proc object) explicitly allows you to test it for things, like the number of arguments that it expects:
class Box
...
def each(&block)
#array.each do |e|
case block.arity
when 0
yield
when 1
yield e
when 2
yield e, :baz
else
yield
end
end
end
end
a = Box.new
a.each { puts "nothing" } # displays "nothing, nothing"
a.each { |e| puts e } # displays "foo, bar"
a.each { |e1, e2| puts "#{e1} #{e2}" } # displays "foo baz, bar baz"