How do I implement the '<<' to have the same behavior when used as a chainable method?
class Test
attr_accessor :internal_array
def initialize
#internal_array = []
end
def <<(item)
#internal_array << :a
#internal_array << item
end
end
t = Test.new
t << 1
t << 2
t << 3
#t.internal_array => [:a, 1, :a, 2, :a, 3]
puts "#{t.internal_array}" # => a1a2a3
t = Test.new
t << 1 << 2 << 3
#t.internal_array => [:a, 1, 2, 3]
puts "#{t.internal_array}" # => a123 , Why not a1a2a3?
I want both cases giving the same result.
Add self as the last line in the << method to return it. As is, you're implicitly returning the array, not the instance.
Explanation of the answer above:
When a method is chained, the next method is applied to the result of the first method.
For exemplo:
class A
def method1
B.new
end
end
class B
def method2
C.new
end
end
class C
def method3
puts "It is working!!!"
end
end
The code below will work
A.new.method1.method2.method3
but the this will not
A.new.method1.method3.method2
because an instance of class B, which is the result of A.new.method1 doesn't implement method3. This is the same of:
(((A.new).method1).method3).method2
The code used in the question above, was a little bit more trick, because both, Test and Array had the method <<. But I want Test#<< to return self, and not the #internal_array that was being returned.
Related
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.
I have the following code where I set attr_reader and attr_writer manually.
class Pairs
attr_reader :pair, :asks, :bids, :isFrozen, :seq, :main_currency, :sub_currency
attr_writer :pair, :asks, :bids, :isFrozen, :seq
def initialize (key, args)
#pair = key
#main_currency, #sub_currency = key.split('_')
args.each {|k,v|
if numeric?(v) then v=v.to_f end
self.instance_variable_set("##{k}".to_sym, v)
}
end
private
def numeric?(string)
Float(string) != nil rescue false
end
end
Is there a way to automatically set them based on the keys of the arguments, like I'm automatically filling #k with v? Can I set attr_reader for each #k?
I suppose something like:
self.attr_reader("##{k}")
or even better for all objects of the class, something like:
Pairs << attr_reader("##{k}")
I am going to assume that you may be creating this with many keys specific to different Hash if this is the case then rather than clutter the individual instances with unneeded readers for non existent keys let's use the singleton_class for this.
So your final Pairs class could look something like
class Pairs
attr_reader :main_currency, :sub_currency
attr_accessor :pair, :asks, :bids, :isFrozen, :seq
def initialize (key, args)
#pair = key
#main_currency, #sub_currency = key.split('_')
args.each do |k,v|
singleton_class.send(:attr_reader,k)
instance_variable_set("##{k}", convert_numeric(v))
end
# Alternatively:
# args.each do |k,v|
# val = convert_numeric(v)
# define_singleton_method(k) {val}
# end
end
private
def convert_numeric(val)
Float(Rational(val)) rescue val
end
end
TL;DR
For Example: (using #mudasobwa's approach)
class C
def extend_self_with_reader name
self.class.send :attr_reader, name
end
def initialize *keys
keys.each(&method(:extend_self_with_reader))
end
end
This causes subsequent readers to clutter the instance and bleed across instances:
a = C.new(:a,:b)
a.a #=> nil
b = C.new
b.a #=> nil
c = C.new(:r)
c.a #=> nil
c.r #=> nil
a.methods.sort - Object.methods
#=> [:a, :b, :extend_self_with_reader, :r]
a.r #=> nil (hmmmmm)
Instead localize these readers buy using the singleton_class of the instance like:
class C
def initialize *keys
singleton_class.send(:attr_reader, *keys)
end
end
Then
a = C.new(:a,:b)
a.a #=> nil
b = C.new
b.a #=> NoMethodError: undefined method `a'
c = C.new(:r)
c.a #=> NoMethodError: undefined method `a'
c.r #=> nil
a.r #=> NoMethodError: undefined method `r'
a.methods.sort - Object.methods
#=> [:a,:b]
b.methods.sort - Object.methods
#=> []
Using the singleton_class localizes these readers to the instance of the object rather than bleeding them into the Class definition. If attr_reader is not a requirement then this would also be sufficient:
keys.each {|k| define_singleton_method(k) {}}
I doubt I understood the question, but from what I get you want to dynamically extend your class with attribute readers at runtime.
This method would do:
def extend_self_with_reader name
self.class.send :attr_reader, name
end
Test:
class C
def extend_self_with_reader name
self.class.send :attr_reader, name
end
def initialize *keys
puts keys.inspect
keys.each(&method(:extend_self_with_reader))
end
end
cc = C.new(*%i|a b c|)
cc.a #⇒ nil
Perhaps look into define_method.
I'm not 100% sure that I understand the problem, but check this out:
hash = { a: 1, b: 2, c: 3 }
hash.keys.each do |key|
define_method(key) do
hash[key]
end
end
There are now methods for a, b, and c:
a => 1
b => 2
c => 3
That essentially makes an attr_reader for all the keys in the hash. You could do something similar for an attr_writer.
I'd like my SomeArray#map to return an "array" of the SomeArray class.
class SomeArray < Array
def map
SomeArray.new(super)
end
end
some_array = SomeArray.new(["foo", "bar", "baz"])
p some_array.class #=> SomeArray
p some_array.map { |e| e }.class #=> SomeArray
Except now I also want to be able to use the Enumerator#with_index instance method. So ideally something like this would work:
some_array.map.with_index { |e, i| e }.class #=> SomeArray
How would that work?
I've tried:
class SomeArray < Array
def map
SomeArray.new(super)
end
def with_index(offset = 0)
super
end
end
some_array = SomeArray.new(["foo", "bar", "baz"])
p some_array.class #=> SomeArray
p some_array.map.with_index { |e, i| e }.class #=> no implicit conversion of Enumerator into Integer (TypeError)
But it's not working.
I think the problem here is that you're treating enumerable and array as if they're the same, which they're not.
Specifically, this in the map call: SomeArray.new(super).
I can reproduce your error:
[6] pry(main)> Array.new [1].map
TypeError: no implicit conversion of Enumerator into Integer
Now, when you pass a block to map, it works:
Array.new([1].map { |x| x })
=> [1]
But in your map.with_index you're not doing this.
You can do something like this:
module Foo
include Enumerable
def map
puts "calling my map"
super
end
def with_index
puts "calling my with_index"
super
end
end
class MyArr < Array
include Foo
end
puts MyArr.new([1]).map.with_index { |x, y| [x,y] }
# calling my map
# calling my with_index
# 1
# 0
Which kind of begs the question of why you're writing this class that just calls super. But in the case that you want to modify the default functionality of enumerable, this is one way.
The main problem is that map is an Array method while with_index is an Enumerator method.
Array methods are defined to just call super, and convert the output array to SomeArray.
Enumerator methods are defined with a to_enum first, convert the output to an Array and then to SomeArray.
It probably isn't the best structure for what you want to do, and isn't very efficient either. It's just a proof a concept!
class SomeArray < Array
# Array methods are overwritten here :
[:map, :select, :reject].each do |array_method_name|
define_method array_method_name do |*p, &block|
SomeArray.new(super(*p, &block).to_a)
end
end
# Enumerator methods are defined for SomeArray here :
[:with_index, :with_object].each do |enum_method_name|
define_method enum_method_name do |*p, &block|
SomeArray.new(to_enum.public_send(enum_method_name, *p, &block).to_a)
end
end
end
some_array = SomeArray.new(%w(foo bar baz biz))
p some_array.map { |s| s * 2 }.with_index.select { |_, i| i.even? }
#=> [["foofoo", 0], ["bazbaz", 2]]
p some_array.map { |s| s * 2 }.with_index.select { |_, i| i.even? }.class
#=> SomeArray
I'm stuck with this exercise from exercism.io:
part of sum_of_multiples_test.rb
...
def test_sum_to_1000
skip
assert_equal 233168, SumOfMultiples.to(1000)
end
def test_configurable_7_13_17_to_20
assert_equal 51, SumOfMultiples.new(7, 13, 17).to(20)
end
...
sum.rb
class SumOfMultiples
def initialize(*args)
#args = args ||= [3,5]
end
def to(max)
ary = []
return 0 if max < 2
#args.each do |m|
for i in 0..max-1
ary << i if i % m == 0
end
end
ary.uniq!.inject(:+)
end
end
If I use class method self.to, it can't see my instance variable #args, if I use
instance method "def to" first test don't pass. Is there a way to somehow "merge" both?
Add another method to your class:
def self.to(max)
new.to(max)
end
You can now call each of these and the result will be the same:
SumOfMultiples.to(1000)
SumOfMultiples.new.to(1000)
How can I call a nested hash of methods names on an object?
For example, given the following hash:
hash = {:a => {:b => {:c => :d}}}
I would like to create a method that, given the above hash, does the equivalent of the following:
object.send(:a).send(:b).send(:c).send(:d)
The idea is that I need to get a specific attribute from an unknown association (unknown to this method, but known to the programmer).
I would like to be able to specify a method chain to retrieve that attribute in the form of a nested hash. For example:
hash = {:manufacturer => {:addresses => {:first => :postal_code}}}
car.execute_method_hash(hash)
=> 90210
I'd use an array instead of a hash, because a hash allows inconsistencies (what if there is more than one key in a (sub)hash?).
object = Thing.new
object.call_methods [:a, :b, :c, :d]
Using an array, the following works:
# This is just a dummy class to allow introspection into what's happening
# Every method call returns self and puts the methods name.
class Thing
def method_missing(m, *args, &block)
puts m
self
end
end
# extend Object to introduce the call_methods method
class Object
def call_methods(methods)
methods.inject(self) do |obj, method|
obj.send method
end
end
end
Within call_methods we use inject in the array of symbols, so that we send every symbol to the result of the method execution that was returned by the previous method send. The result of the last send is automatically returned by inject.
There's a much simpler way.
class Object
def your_method
attributes = %w(thingy another.sub_thingy such.attribute.many.method.wow)
object = Object.find(...)
all_the_things << attributes.map{ |attr| object.send_chain(attr.split('.')) }
end
def send_chain(methods)
methods.inject(self, :try)
end
end
There is no predefined method, but you can define your own method for that:
class Object
def send_chain(chain)
k = chain.keys.first
v = chain.fetch(k)
r = send(k)
if v.kind_of?(Hash)
r.send_chain(v)
else
r.send(v)
end
end
end
class A
def a
B.new
end
end
class B
def b
C.new
end
end
class C
def c
D.new
end
end
class D
def d
12345
end
end
chain = { a: { b: { c: :d } } }
a = A.new
puts a.send_chain(chain) # 12345
Tested with http://ideone.com/mQpQmp