Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 8 years ago.
Improve this question
I wanted to make a simple DSL where I could pass a bunch of methods to a block, relying on self as the implicit receiver. So basically here you can call the 'say' method on this class object, passing it 'things to say' as methods in the block. The last line returns ['Maria']. I was wondering if this is a good programming practice for creating DSLs and if there are any problems with this approach.
class SaySomething
def initialize
#said = []
end
def hey(name)
#said << name
end
def say(&block)
instance_eval(&block)
end
end
a = SaySomething.new
name = 'Maria'
a.say do
hey(name)
end
a.instance_eval { p #said } #=> produces ['Maria']
I would probably add an attr_accessor :said and then replace your last line with
a.said
#=> ['Maria']
Other than that your code looks fine to me. If you want to learn more about metaprogramming in Ruby, I can recommend the book "Eloquent Ruby".
The only problem with this approach is that any class variables will collide with variables in the same scope as the block. The usual approach is to provide instance evaluation, but also allow the user to specify the class as an argument as a fall back.
class Test
def test; "hello"; end
def say(&b)
if b.arity == 1
b.call(self)
else
instance_eval &b
end
end
end
t = Test.new
test = "fred"
t.say { p test } # "fred"
t.say { |t| p t.test } # "hello"
Related
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 8 years ago.
Improve this question
Is it possible in Ruby to define such setters and getters for class that can be used with [] or () or smth alike? E.g.
word.meaning[:english] = "ruby"
puts word.meaning[:german] # "Rubin"
Note that word.meaning must not be a hash! and :english, :german are kind of additional parameters for setter/getter meaning.
Yes, it can be done. You need to define a []= and [] methods.
In this example, I am using a Hash as the internal data structure - you are free to use whatever you like.
class Word
attr_reader :meaning
def initialize
#meaning = Meaning.new
end
end
class Meaning
attr_reader :h
def initialize
#h = {}
end
def []=(key, value)
#h[key] = value
end
def [](key)
#h[key]
end
end
Example:
word = Word.new
word.meaning[:english] = 'Hello'
word.meaning[:english] # => 'Hello'
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 8 years ago.
Improve this question
I have two files:
answer.rb
class Answer
def decision(string)
if string == 'Richard'
puts "Hello"
else
puts "I dont know you"
end
end
end
question.rb
require './answer'
class Question < Answer
puts "What is your name?"
response = gets.chomp
puts decision("#{response}")
end
How do I access the method in class Answer from class Question given the file is not enough? If I remove the the class Answer, then everything works.
To make your example work you need to call your code. For instance you can amend your Question class with the following code :
#question.rb
require './answer'
class Question < Answer
def ask
puts "What is your name?"
response = gets.chomp
puts decision(response)
end
end
Question.new.ask
The inheritance will be that your instance of question (i.e. Question.new) will be inherited from Answer => it will have both methods (in your case 'ask' and 'decision').
Just do
puts self.new.decision(response) # no need for string interpolation.
#decision is an instance method of Answer class, so it will be available as an instance method of the class Question. Now inside the class, self is set to the class itself, thus the bare method call like your, will throw error as no method found. Thus you have to create an instance of the class Answer or Question, and on that instance you have to call the method.
Complete code :
class Answer
def decision(string)
if string == 'Richard'
puts "Hello"
else
puts "I dont know you"
end
end
end
class Question < Answer
puts "What is your name?"
response = gets.chomp
puts self.new.decision(response)
end
Run the code:
(arup~>Ruby)$ ruby so.rb
What is your name?
arup
I dont know you
(arup~>Ruby)$
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 9 years ago.
Improve this question
I'm new to ruby and I'am building a little program on ruby alone,the problem is that I'am trying to launch it without success
Imagine that i have this code
#usr/bin/ruby
Class Whatever
def get_user_input
#user_input = gets.chomp
user_doing(#user_input)
end
def user_doing
#something
end
end
What I want is to call the get_user_input method as soon as i feed my rb file to ruby
I tried to call it on a initialize method
def initialize
get_user_input
end
I also tried to define it as a "class method"
def get_user_input
#user_input = gets.chomp
user_doing(#user_input)
end
but neither of them seems to work as when I'm start the rb file the program doesn't expect my input so how can i do this?.
You just define a class. What you did not call the method. Just add Whatever.new.get_user_input to your file.
#usr/bin/ruby
class Whatever
def initialize(input)
#input = input
end
def self.get_user_input
whatever = new(gets.chomp)
whatever.user_doing
end
def user_doing
puts "Input was: #{#input}"
end
end
Whatever.get_user_input
Btw: Your user_doing does not take args in the moment. You may want to check that.
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 9 years ago.
Improve this question
If I am editing the Array class, shouldn't I have to define each method with a self (e.g. self.sum). I'm not sure why this passes the rpsec tests for the 'Test-First' Ruby track without the self.method immediately following the def.
class Array
def sum
count = 0
self.each {|x| count += x}
count
end
def square
self.map {|x| x * x}
end
def square!
self.map! {|x| x * x}
end
end
These are "instance methods" - they operate on an instance of an Array, not the Array class itself. If you were to put self. before the name of each method when defining it, you would be defining a "class method", which wouldn't make any sense for these methods.
Although not necessary, the reason that self. works when invoking these methods from within the body of another one of the methods is that self is defined to be the "instance" at that point. This contrasts to when you're defining the methods with def, where self is the Array class.
Closed. This question is off-topic. It is not currently accepting answers.
Want to improve this question? Update the question so it's on-topic for Stack Overflow.
Closed 10 years ago.
Improve this question
Hi i have a code i would like to refactor
def gear_type
#gear_type ||= self.class.gear_types.find{|gears| gears["id"]==source["gear_type_id"]}["text"] if source["gear_type_id"]
end
def brand
#brand ||= self.class.brands.find{|node| node["id"]==source["brand_id"].to_s}["text"] if source['brand_id']
end
what is best way? to use eval or define method? i've tried this but there are some error i can't discover yet:
%w(gear_type brand).each do |meth|
define_method(meth){
instance_variable_get("##{meth}") rescue
instance_variable_set("##{meth}", self.class.send(meth.pluralize).find{|node| node["id"]==source["#{meth}_id"]}["text"]) if source["#{meth}_id"]
}
end
I'd just write a common finder method that you can parameterize:
def gear_type
#gear_type ||= generic_finder :gear_types, "gear_type_id"
end
def brand
#brand ||= generic_finder :brands, "brand_id"
end
def generic_finder(collection, primary_key)
self.class.send(collection).each do |object|
return object["text"] if object["id"] == source[primary_key]
end if source[primary_key]
nil
end
instance_variable_get("##{meth}") does not raise an error if the instance variable is not set, it returns nil. So you have to do almost the same you were doing:
%w(gear_type brand).each do |meth|
define_method(meth){
instance_variable_get("##{meth}") || instance_variable_set("##{meth}", self.class.send(meth.pluralize).find{|node| node["id"]==source["#{meth}_id"]}["text"]) if source["#{meth}_id"]
}
end
You should also refactor that line. It has to many stuff on it
%w(gear_type brand).each do |meth|
def source(meth)
#source ||= source["#{meth}_id"]
end
def class_meths(meth)
self.class.send(meth.pluralize)
end
def look_for(meth)
class_meths(meth).find{|node| node["id"] == source(meth)}["text"]
end
define_method(meth) do
value = instance_variable_get("##{meth}")
instance_variable_set("##{meth}", look_for(meth)) if !value && source(meth)
end
end
Here is a try. Not sure if it got better or not, but it's easier to read I think.
Oh! I just realized those methods probably won't be on the scope when the meta? method is called. But oh well, it's still a good example I think :)
It's probably cleaner just to use eval:
%w(gear_type brand).each do |meth|
eval <<-RUBY
def #{meth}
##{meth} ||= self.class.#{meth.plural}.find{|item| item["id"]==source["#{meth}_id"]}["text"] if source["#{meth}_id"]
end
RUBY
end