How to implement injection in Ruby? - ruby-on-rails

I need to be able to use this call:
h = x.inject({}) {|a, b| a[b.one] = b.two; a}
Where x is a sequence of Couple objects (these just contain two number fields, one and two).
I am not sure how to implement the inject method in Couple.

Define an #each method in Couple, then include Enumerable in it.
class Couple
def each
yield "a"
yield "b"
end
include Enumerable
end
couple = Couple.new
couple.inject("") { |str, obj| str + obj }
# => "ab"
http://www.ruby-doc.org/core-1.9.3/Enumerable.html

Related

Having a hash, set object properties in Ruby

Having a hashmap, such as:
{:foo => 1, :bar => 2}
in Ruby, is there an easy way to assign those values as properties of the object, to automatically cause this to happen:
obj.foo = 1
obj.bar = 2
To be precise, some Ruby-idiomatic way of doing:
hashmap.each { |k,v| obj.send("#{k}=", v) }
obj is an object that doesn't inherit ActiveModel and it's not a Struct and I can't control it's type as it's coming from a third party library.
I'm using Rails, so if the answer comes from Rails, that's acceptable.
What you have there is (almost) the most concise, readable, idiomatic solution already:
hashmap.each { |k,v| obj.send("#{k}=", v) }
There is only one thing left to improve:
hashmap.each { |k,v| obj.public_send("#{k}=", v) }
Use public_send instead of send to make it clear to others that you are using it only to pass a method name dynamically and not to circumvent access restrictions.
Maybe you could create an OpenStruct from your hash, do whatever you need with the OpenStruct and its attributes, then convert it back into a hash?
require 'ostruct'
h = {:foo => 1, :bar => 2}
o = OpenStruct.new(h)
o.foo # Output: => 1
o.bar # Output: => 2
# if necessary, convert it back into a hash
h = o.to_h
From the Ruby docs:
An OpenStruct is a data structure, similar to a Hash, that allows the
definition of arbitrary attributes with their accompanying values.
This is accomplished by using Ruby’s metaprogramming to define methods
on the class itself.
An OpenStruct utilizes Ruby’s method lookup structure to and find and
define the necessary methods for properties. This is accomplished
through the method method_missing and define_method.
This should be a consideration if there is a concern about the
performance of the objects that are created, as there is much more
overhead in the setting of these properties compared to using a Hash
or a Struct.
Doing this on mobile so let's give this a try:
class Something
attr_reader :foo, :bar
def initialize(hash = {})
foo = hash[:foo]
bar = hash[:bar]
end
end
obj = Something.new({foo: 1, bar: 2})
obj.foo = 1
obj.bar =2
class Klass
def initialize(h)
h.each { |iv, val| instance_variable_set("##{iv}", val) }
end
end
k = Klass.new(:foo => 1, :bar => 2)
#=> #<Klass:0x007ff1d9073118 #foo=1, #bar=2>
You can use method_missing if you need to set data dynamically.
class Foo
def method_missing(sym, *args)
super unless instance_variables.include?("##{sym}".to_sym)
instance_variable_get("##{sym}")
end
end
obj = Foo.new
h = {:foo => 1, :bar => 2}
h.each { |k, v| obj.instance_variable_set("##{k}", v) }
obj.foo
# => 1
obj.bar
# => 2
If you are using Rails, a minor improvement on your suggested solution would be to use Object#try from Active support extensions
hashmap.each {|k,v| obj.try "#{k}=", v }
As per documentation,
Invokes the public method whose name goes as first argument just like
public_send does, except that if the receiver does not respond to it
the call returns nil rather than raising an exception.

Store model name in an array rails

My requirement is something like this (but this doesn't work)
array=["Menu","Article","Comment"]
array.each do |x|
x.find 1 # the x is of class String
p x.id
end
each elements of the array is an model name in my application. The 'x' obtained inside the looping is of class string but i want it to be of a model.
I want to do certain same task on each of the models, doing something like this can reduce some 60 lines of code in my program. can anybody help..
You can use constantize like so:
array=["Menu","Article","Comment"]
array.each do |x|
klass = x.constantize
klass.find 1
p klass.id
end
http://apidock.com/rails/String/constantize
In ActiveSupport (part of Rails) there is a method constantize available on Strings which might help you:
array=["Menu","Article","Comment"]
array.each do |x|
instance = x.constantize.find 1
p instance.id
end
You can do it like this:
array=["Menu","Article","Comment"]
array.each do |x|
a = (Object.const_get x).find 1
p a.id
end

Ruby's bitwise shift operator "<<" confusion in conjuction with array

I am studying Ruby and can't figure this out. I have an exercise where I have to add a method into a Library class which contains games. Each game is an instance of a class Game. So the solution is the following:
class Library
attr_accessor :games
def initialize(games)
self.games = games
end
def has_game?(search_game)
for game in games
return true if game == search_game
end
false
end
def add_game(game)
#games << game
end
end
I can't understand how << works in this case. Is this a bitwise left shift? Does Library class just assumes that games is an array, I believe I can pass anything to the Library class when I am initialising, single game or an array of games?
When you have:
#games << game
<< is actually a method. If it is a method, you ask, why isn't it written in the normal way:
#games.<<(game)
? You could, in fact, write it that way and it would work fine. Many Ruby methods have names that are symbols. A few others are +, -, **, &, || and %. Ruby knows you'd prefer writing 2+3 instead of 2.+(3), so she let's you do the former (and then quietly converts it to the latter). This accommodation is often referred to as "syntactic sugar".
<< is one of #games' methods (and game is <<'s argument), because #games is the receiver of the method << (technically :<<). Historically, it's called the "receiver" because with OOP you "send" the method to the receiver.
To say that << is a method of #games means that << is an instance method of #games's class. Thus, we have:
#games.methods.include?(:<<) #=> true
#games.class.instance_methods.include?(:<<) #=> true
I expect #games is an instance of the class Array. Array's instance methods are listed here. For example:
#games = [1,2,3]
#games.class #=> [1,2,3].class => Array
#games << 4 #=> [1,2,3,4]
#games.<<(5) #=> [1,2,3,4,5]
On the other hand, suppose #games were an instance of Fixnum. For example:
#games = 7
#games.class #=> 7.class => Fixnum
which in binary looks like this:
#games.to_s(2) #=> "111"
Then:
#games << 2 #=> 28
28.to_s(2) #=> "11100"
because << is an instance method of the class Fixnum.
As a third example, suppose #games were a hash:
#games = { :a => 1 }
#games.class #=> { :a => 1 }.class => Hash
Then:
#games << { :b => 2 }
#=> NoMethodError: undefined method `<<' for {:a=>1}:Hash
The problem is clear from the error message. The class Hash has no instance method <<.
One last thing: consider the method Object#send. Recall that, at the outset, I said that methods are sent to receivers. Instead of writing:
#games = [1,2,3]
#games << 4 #=> [1,2,3,4]
you could write:
#games.send(:<<, 4) #=> [1, 2, 3, 4]
This is how you should think of methods and receivers. Because send is an instance method of the class Object, and all objects inherit Object's instance methods, we see that send is "sending" a method (:<<, which can alternatively be expressed as a string, "<<") and its arguments (here just 4) to the receiver.
There are times, incidentally, when you must use send to invoke a method. For one, send works with private and protected methods. For another, you can use send when you want to invoke a method dynamically, where the name of the method is the value of a variable.
In sum, to understand what method does in:
receiver.method(*args)
look for the doc for the instance method method in receiver's class. If you Google "ruby array", for example, the first hit will likely be the docs for the class Array.
It's not a bitwise left shift, it's "shovel" operator. You, probably, know that in Ruby operators are implemented as methods, i.e. when you write
1 + 1
what is actually going on:
1.+(1)
The same is true for "shovel" (<<) operator, it's just a method on Array class, that appends its arguments to the end of the array and returns the array itself, so it can be chained:
>> arr = []
=> []
>> arr << 1
=> [1]
>> arr
=> [1]
>> arr << 2 << 3
=> [1, 2, 3]
It looks like #games is an array. The shift operator for an array adds an element to the end of the array, similar to array#push.
ary << obj → ary
Append—Pushes the given object on to the end of this array. This expression returns the array itself, so several appends may be chained together.

Using injection in Ruby? [duplicate]

This question already has an answer here:
How to implement injection in Ruby?
(1 answer)
Closed 8 years ago.
I have a class called Hsh which basically simulates a hash. It has an array of Couple objects (which hold fields named one and two, one is an int another is a string name of that int).
I am supposed to be able to accept the following call:
h = x.inject({}) {|a, b| a[b.one] = b.two; a}
Where x is the Hsh object.
I am not sure how to implement the inject method within Hsh? Like, what would I write in:
def inject ????
??
??
end
All it's supposed to do is create a hash map.
you shouldn't really need to implement it, just implement Hsh#eachand include Enumerable, you'll get inject for free.
For your specific example something like this should work:
def inject accumulator
#I assume Hsh has some way to iterate over the elements
each do |elem|
accumulator = yield accumulator, elem
end
accumulator
end
But the real implementation of inject is a bit different (e.g. works without providing an accumulator, takes a symbol instead of a block etc)
require 'ostruct'
class Hsh
include Enumerable
def initialize
#arr = (0..9).map{ |i| OpenStruct.new(:one => i, :two => "#{i}")}
end
def each(&block)
#arr.each(&block)
end
end
p Hsh.new.inject({}) {|a, b| a[b.one] = b.two; a}
#=> {5=>"5", 0=>"0", 6=>"6", 1=>"1", 7=>"7", 2=>"2", 8=>"8", 3=>"3", 9=>"9", 4=>"4"}
In this particular case Hsh is actually an array, so unless you use it for something else such a complex code doesn't make sense, it can be done much easier:
p (0..9).map{ |i| OpenStruct.new(:one => i, :two => "#{i}")} \
.inject({}) {|a, b| a[b.one] = b.two; a}
#=> {5=>"5", 0=>"0", 6=>"6", 1=>"1", 7=>"7", 2=>"2", 8=>"8", 3=>"3", 9=>"9", 4=>"4"}
I have used OpenStruct instead of classes. See if this works for you
require 'ostruct'
class Hsh
attr_accessor :arr
def initialize
obj = OpenStruct.new
obj.one = 1
obj.two = "two"
#arr = [obj]
end
def inject(hash)
#arr.each do |arr|
yield hash, arr
end
hash
end
end
x = Hsh.new
p x.inject({}) {|a, b| a[b.one] = b.two} #prints {1 => "two"}

Object#tap question

Why does
a = [].tap do |x|
x << 1
end
puts "a: #{a}"
work as expected
a: [1]
but
b = [].tap do |x|
x = [1]
end
puts "b: #{b}"
doesn't
b: []
?
The reason why the second snippet does not change the array is the same why this snippet:
def foo(x)
x = [1]
end
a = []
foo(a)
does not change variable a. Variable x in your code is local to the scope of the block, and because of that you can assign anything to it, but the assignment won't be visible outside (Ruby is a pass-by-value language).
Of course, blocks have also closures on the local variables where they were declared, so this will work:
def foo(x)
yield(x)
end
b = []
foo(123) do |x|
b = [1]
end
p b # outputs [1]
The first method put 1 on the end of an empty array. In the same way you cant say that an empty array is equal to 1. Rather you would try and replicate it...
b = [].tap do |x|
x.unshift(1)
end
This is just an example yet have a look at the method call you can use on an Array by typing.
Array.methods.sort
All the best and Good luck
This is slightly unrelated -- but that [].tap idiom is horrible. You should not use it. Even many of the people who used it in rails code now admit it's horrible and no longer use it.
Do not use it.

Resources