attr_accessor and meta programming in Ruby [duplicate] - ruby

This question already has answers here:
Ruby: dynamically generate attribute_accessor
(3 answers)
Closed 6 years ago.
Hi I would like to initialize the attributes of an instance of a ruby object dinamically via some config file, I can do that pretty fast using the following code:
class ApiTester
def initialize(path= "api_test")
h = eval(File.open("#{path}/config.hash","r").read)
h.each do |k,v|
eval("##{k}=#{v.class == String ? "\"#{v}\"" : v }" )
end
end
end
How do I give the attribute "##{k}" the property attr_accessor?

class ApiTester
def initialize(path= "api_test")
h = { a: 1, b: 2 }
h.each do |k,v|
instance_variable_set("##{k}", v)
self.class.send(:attr_accessor, k)
end
end
end
api_tester = ApiTester.new
puts api_tester.a # => 1
puts api_tester.b # => 2
api_tester.a = 3
puts api_tester.a # => 3
By the way, you should probably create a .yaml file and use YAML::load_file, it is best practice to avoid eval if you can.

Related

implementation of operator overload (+=) in ruby [duplicate]

This question already has an answer here:
+= operator overloading in ruby
(1 answer)
Closed 3 years ago.
how to implement operator overloading (operator += )for two one-dimensional arrays? for b+=a in method def +# ??? end
class Vector
def initialize
#vector = Array.new
end
def set_vector=(vector)
#vector = vector
end
def get_vector
#vector
end
def +#
?????
end
end
a = Vector.new
a.set_vector = [1,3,4,5]
print a.get_vector
b = Vector.new
b.set_vector = [1,2]
print b.get_vector
a.get_vector += b.get_vector
puts "\n a new"
puts a
There's a more Ruby way of doing this that fixes the issue:
class Vector
attr_accessor :vector
def initialize(*values)
#vector = values
end
def [](i)
#vector[i]
end
def +(vector)
Vector.new(
*#vector.map.with_index do |v, i|
v + vector[i].to_i
end
)
end
end
Note that + is just a regular method, there's no +# method involved here, that's the unary + as in r = +v, but you want to return a new object so you can chain it, as in a + b + c, and never modify the original.
+= creates a new object and does the assignment, as in x = x + 1.
In practice:
v = Vector.new(1,2)
r = v + Vector.new(2,3)
# => #<Vector:0x00007fc6678955a0 #vector=[3, 5]>

I want to get keys names from hash which has same values in ruby [duplicate]

This question already has answers here:
Get all keys in hash with same value
(3 answers)
Closed 5 years ago.
{"Amar"=>20,"Benton"=>14,"John"=>32,"Sunny"=>28,"Edward"=>19,"Leon"=>12,"Ram"=>19,"David"=>28}
the above hash has name and age ,i want to get the names which has same age in ruby
try this.
hash = {"name1"=>12, "name2"=>13, "name3"=>12}
groups = {}
hash.each do |k, v|
groups[v] = groups[v] || []
groups[v].push(k)
end
hash = {"Amar"=>20,"Benton"=>14,"John"=>32,"Sunny"=>28,"Edward"=>19,"Leon"=>12,"Ram"=>19,"David"=>28}
hash.keys.group_by { |k| hash[k] }.values.select { |g| g.size > 1 }
# => [["Sunny", "David"], ["Edward", "Ram"]]
Sunny and David have the same age, and so do Edward and Ram.
One solution is to create a hash with ages as the key and an array of names as the values:
names = {"Amar"=>20,
"Benton"=>14,
"John"=>32,
"Sunny"=>28,
"Edward"=>19,
"Leon"=>12,
"Ram"=>19,
"David"=>28}
ages = {}
names.each do |key, value|
ages[value] ||= []
ages[value] << key
end
puts ages
#=> {20=>["Amar"], 14=>["Benton"], 32=>["John"], 28=>["Sunny", "David"], 19=>["Edward", "Ram"], 12=>["Leon"]}
Note that if you want to get all the people who are 19 years old, you can just use ages[19].
For your hash
names_hash = {"Amar"=>20,"Benton"=>14,
"John"=>32,"Sunny"=>28,
"Edward"=>19,"Leon"=>12,
"Ram"=>19,"David"=>28}
You can always define a method which gives the desired names for an age
def names_for_age(age, hash = {})
hash.inject({}) do |container, (k,v)|
container[v] ||= []
container[v] << k
container
end[age]
end
So, now you can get the names as
names_for_age(10, names_hash)

Converting an array of numbers to characters ruby [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
The to_chr function is supposed to return the encrypted array but converted to characters. I have tried many things and commented out the ones that didn't work.
class Encrypt
def initialize(code, string)
#code = code
#string = string
#encrypted = []
end
def to_byte
#string.each_byte do |c|
#encrypted.push(c + #code)
end
print #encrypted
end
def to_chr
n = #encrypted.length
# n.times do |i|
# #encrypted.push(i.chr)
# end
print #encrypted[0].chr
# #encrypted.each do |x|
# #encrypted.push(x.chr)
# end
# print #encrypted
end
end
goop = Encrypt.new(2, "hello")
goop.to_chr
#=> in `to_chr': undefined method `chr' for nil:NilClass (NoMethodError)
You create instance of Encrypted method, but you set #code = 2, #string = "Hello" and #encrypted = []. So if you call #encrypted[0], ruby return nil.
So you can modify your class like this:
class Encrypt
def initialize(code, string)
#code, #string, #encrypted = code, string, []
end
def to_byte
#string.each_byte { |c| #encrypted << c + #code }
end
def to_chr
to_byte if #encrypted.empty?
#encrypted.map(&:chr)
end
end
goop = Encrypt.new(2, "hello")
p goop.to_chr
# => ["j", "g", "n", "n", "q"]
I hope this helps
def to_chr
#encrypted.each do |i|
print i.chr
end
print "\n"
end
Make sure to call to_byte prior to to_chr
goop = Encrypt.new(2, "hello")
goop.to_byte
goop.to_chr
returns:
jgnnq

setter method return self not work, it's a bug? [duplicate]

This question already has answers here:
Is it possible to have class.property = x return something other than x?
(3 answers)
Closed 8 years ago.
I want to iterate an array of strings, and assign each of them to a fresh instance of class User, and I expect that I will got an array of User objects:
class User
def name=(name)
#name = name
self
end
end
original_array = ["aaa", "bbb", "bbb"]
result = original_array.collect { |str| User.new.name = str }
but the result is an array of strings!
puts result.inspect # => ["aaa", "bbb", "bbb"]
puts result === original_array # => true
I have no idea of where I went wrong?
What's wrong here is that User.new.name = str returns str, so the value of str gets collected.
Why does it return str? Because, opposed to any other Ruby method, every Ruby setter method returns the passed value, regardless the returned value in the method. For more infos about this behaviour you can check this other SO answer.
Below a IRB-ready Proof of Concept:
def name=(name)
#name = 'another value'
end
returned_value = (self.name = 'a value')
returned_value #=> 'a value'
#name #=> 'another value'
What you want can be done in this ways:
This syntax is valid for any Ruby object, as it uses Object#tap:
User.new.tap { |v| v.name = str }
If User is an ActiveRecord model, as I guess, you can use one of these slightly shorter syntaxes:
User.new name: str
User.new { |v| v.name = str }

Make an array in Ruby

I am very beginner in Ruby and probably the question is too easy but well, I've already spent some time on it and couldn't find a solution.
My Ruby script takes a number (ex 10) and a name (ex Vincent). What I want is to make an array looking like
Vincent0
Vincent1..
Vincent9
I can't figure a way to make it..
def arrayfy(string, number)
arr = []
0.upto(number-1) do |i|
arr << "#{string}#{i}"
end
return arr
end
Update: To add these as variables to the class
class Foo
def arrayfy(string, number)
0.upto(number-1) do |i|
var_string = "##{string}#{i}"
var_symbol = var_string.to_sym
self.instance_variable_set(var_symbol, "")
end
end
end
Array.new(10) {|i| "Vincent#{i}"}
gives you
["Vincent0", "Vincent1", "Vincent2", "Vincent3", "Vincent4", "Vincent5",
"Vincent6", "Vincent7", "Vincent8", "Vincent9"]
The documentation for Array is available at http://www.ruby-doc.org/core/classes/Array.html (googling for Array RDoc will give you the URL).
The bit in the braces ({|i| "Vincent#{i}"}) is called a block. You'll definitely want to learn about them.
Using Array.new with a block (docs):
def create_array(count, name)
Array.new(10) { |i| "#{name}#{i} }
end
Using Enumerable#reduce (docs):
def create_array(count, name)
(0...count).reduce([]) { |m,i| m << "#{name}#{i}" }
end
Or using Enumerable#each_with_object (docs):
def create_array(count, name)
(0...count).each_with_object([]) { |i,a| a << "#{name}#{i}" }
end
Using it:
# Using the array (assigning to variables)
array = create_array(10, 'Vincent') # => ['Vincent0', 'Vincent1', 'Vincent2' ...]
name = array[1] # => 'Vincent1'
Just for the record, a solution in a more functional style:
>> def arrayify(str, n)
.. ([str] * n).zip(0...n).map(&:join)
.. end
#=> nil
>> arrayify('Vincent', 10)
#=> ["Vincent0", "Vincent1", "Vincent2", "Vincent3", "Vincent4", "Vincent5", "Vincent6", "Vincent7", "Vincent8", "Vincent9"]
def array_maker(number, string)
result = []
for i in 0..number do
result << "#{string}#{i}"
end
result
end

Resources