Hi,
That’s not how you should use method_missing. Note that this method is
called every time a method can’t be found in the standard lookup path.
So it makes no sense to define it like a normal method with specific
functionality. This will lead to unexplicable behaviour and strange
errors. In your case, every method that doesn’t “exist” is assumed to be
a “select” method, even if it has absolutely nothing to do with it (like
a typo).
If you want to use method_missing at all, then only let it catch
specific method names and raise an error otherwise. However, in your
case it simply makes no sense. You’re putting so much effort in parsing
the method name that you’ve almost created your own “language in a
language” with its own method calling syntax. That’s definitely not the
purpose of method_missing. Use a normal method and pass the data through
the arguments (like you always do).
Your class structure is also rather strange. What is “myclass”? I don’t
understand the purpose of this method(?), and you seem to be using it in
completely different contexts. If you tell us exactly what you have and
what you want to do, we’ll be able to help you better.
Using a similar example, I basically see two possibilities that make
sense:
Either you simply define a method that takes a block and passes it to
@cars.select:
#----------------------------------------
class Car
def self.create_test_data
@cars = [‘red’, ‘black’, ‘white’].map {|color| Car.new color}
end
save block in parameter (as a Proc object)
def self.select_cars &block
# when there’s no block given, you probably want all cars
if block_given?
@cars.select &block # convert Proc object to block again
else
@cars
end
end
attr_reader :color
def initialize color
@color = color
end
end
Car.create_test_data
p Car.select_cars {|car| car.color == ‘red’}
#----------------------------------------
Or if you insist on the pattern “attribute-operator-value”, pass a hash
to the method:
#----------------------------------------
class Car
def self.create_test_data
@cars = [‘red’, ‘black’, ‘white’].map {|color| Car.new color}
end
def self.select_cars filter = {}
if filter.has_key? :by
attribute, operator, value =
filter.values_at(:by, :is, :value)
@cars.select do |car|
car.public_send(attribute).public_send operator, value
end
else
@cars
end
end
attr_reader :color
def initialize color
@color = color
end
end
Car.create_test_data
p Car.select_cars by: ‘color’, is: ‘==’, value: ‘red’
#----------------------------------------
This should also answer your original question: You cannot assemble Ruby
code within Ruby code in the sense of
car.“color” “==” “red”
or something. That’s not how Ruby (or most other languages) work. If you
want to dynamically call a method, you need to call “send” or
“public_send” (which will only call public methods).
I think this is generally going in a wrong direction. You’re somehow on
the road to this kind of “meta-metaprogramming” where you basically use
your own syntax within Ruby and then parse it again to Ruby code. Don’t
do that, especially when you’re a beginner. Ruby does have strong
metaprogramming abilities, but don’t get lost in them. They sometimes
lead away from the simple solutions up to a point where you’re working
around Ruby instead of actually using it.
When you want to call a method with parameters, then call a method with
parameters. Don’t pass a string that contains method call syntax that’s
parsed to a method name with parameters that’s passed to Ruby’s
metaprogramming methods that call a method will parameters.