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:
    #@cars.select { |el| el."@#method.to_s.split("where").last.sub!
#(/^_/, '')" args.first args.last }
    #how do I construct this line in my missing method from the above
code?
  end

···

--
Posted via http://www.ruby-forum.com/.

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.

--
Posted via http://www.ruby-forum.com/.

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)
    prop = meth_name.to_s.split("where").last[1..-1]

    @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">]

···

--
Posted via http://www.ruby-forum.com/.

Actually, you don't need this:

  class <<self
    attr_accessor :cars
  end

···

--
Posted via http://www.ruby-forum.com/.

Would this also work?

fastcars = MyClass.select_cars_where_color('>', '200')

···

--
Posted via http://www.ruby-forum.com/.

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 Thu, Oct 11, 2012 at 2:27 AM, dis guy <lists@ruby-forum.com> 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")

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

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. Yes, you can get it working (as 7stud showed), but
it's not how to do it. 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

···

--
Posted via http://www.ruby-forum.com/\.

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

···

--
Posted via http://www.ruby-forum.com/\.

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.

···

On 11 October 2012 17:43, Jan E. <lists@ruby-forum.com> wrote:

redcars_assign_MyClass_select_cars_where_color_is_read

--
  Matthew Kerwin, 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