Ghost methods and dynamic methods

I am a newbie to ruby please explain your answers thoroughly. I am
having a lot of difficulties with the syntax. Thanks for the help.

I want to call this missing method:

redcars= myclass.select_cars_where_color("==", “red”)

My missing method should look like this, but I cannot get it work:

def self.method_missing(method, *args)
myclass.cars #populate a list of cars
prop = method.to_s.split(“where”).last
puts prop.sub!(/^_/, ‘’) #print out the query part for the method.
#for example this could be “color”
puts args.join(’,’) #for example this could be ==, red

#this is the part I cannot get it right:
#I want to have:
#@cars.select { |el| el.color == red}
#I tried this unsuccessfully:
#@students.select { |el|

el.send("#{method.to_s.split(“where”).last.sub!(/^_/, ‘’)}")
args.first.to_s args.last.to_s }
#how do I construct this line in my missing method from the above
code?
end

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.

class Car
attr_accessor :color, :model

def initialize(color, model)
@color = color
@model = model
end
end

class MyClass
cars = [
Car.new(‘red’, ‘A’),
Car.new(‘green’, ‘B’),
Car.new(‘red’, ‘C’),
]

@cars = cars

class <<self
attr_accessor :cars
end

def self.method_missing(meth_name, *args)
meth_name_s = meth_name.to_s

if meth_name_s.include?('cars_where')
  prop = meth_name_s.split("where_").last
else
  super
end

@cars.select do |car|
  car.send(prop).send(*args)
end

end

end

redcars = MyClass.select_cars_where_color(’==’, ‘red’)
p redcars

–output:–
[#<Car:0x00000100907378 @color=“red”, @model=“A”>,
#<Car:0x00000100907288 @color=“red”, @model=“C”>]

Actually, you don’t need this:

class <<self
attr_accessor :cars
end

Would this also work?

fastcars = MyClass.select_cars_where_color(’>’, ‘200’)

dis guy wrote in post #1079363:

Would this also work?

fastcars = MyClass.select_cars_where_color(’>’, ‘200’)

Haven’t you read anything of my long posting? :frowning:

It’s a pity that 7stud just corrected some technical errors (and added
one of his own, namely the call to “super” without a “return”) instead
of actually giving you advice.

Passing parameters through the method name using method_missing is
simply a bad idea for several reasons: You duplicate core
functionalities of the language, it’s complicated and error-prone, and
you force the code users to adhere to a specific (but undocumented)
naming scheme to get the method working. Yes, you can do that (as
7stud showed), but it’s far from best practices or even common sense. To
bring this idea to the extremes, why not pass all arguments through
the name?

redcars = MyClass.select_cars_where_color_is_red

Or even

redcars_assign_MyClass_select_cars_where_color_is_read

dis guy wrote in post #1079363:

Would this also work?

fastcars = MyClass.select_cars_where_color(’>’, ‘200’)

top_speed = 250

result = top_speed.send(’>’, 200)
p result

–output:–
true

On Thu, Oct 11, 2012 at 2:27 AM, dis guy [email protected] wrote:

I am a newbie to ruby please explain your answers thoroughly. I am
having a lot of difficulties with the syntax. Thanks for the help.

I want to call this missing method:

redcars= myclass.select_cars_where_color(“==”, “red”)

Why do you want to do that? The proper way to code that selection would
be

redcars = myclass.select {|car| car.color == “red”}

Kind regards

robert

On 11 October 2012 17:43, Jan E. [email protected] wrote:

redcars_assign_MyClass_select_cars_where_color_is_read

I believe you mean redcars_assign_MyClass_select_cars_where_color_is_red

redcars_assign_MyClass_select_cars_where_color_is_read is something
completely different.


Matthew K., B.Sc (CompSci) (Hons)
http://matthew.kerwin.net.au/
ABN: 59-013-727-651

“You’ll never find a programming language that frees
you from the burden of clarifying your ideas.” - xkcd