Forum: Ruby Novice: self, @self.respond_to? and dynamically built methods

Posted by Steve Tu (stevet)
on 2013-02-01 12:36
I have been plodding through a Ruby tutorial and came across the
'require'
and 'load' methods(?). Prior to that, all of the tutorial examples I
played with had been bolted into one file. I had added in a 'zenity'
based menu option to drive the selection of the tutorial subjects.
Now I have split each of the tutorial subjects into separate files that
get 'loaded' or 'required'. The 'main' section is then just a  call to
the zenity menu driver, and calls to the respective methods.

All works fine...apart from...

I had an old tutorial module that looked at the passing of arguments
into Ruby from the command line. This example did little more than split
the arguments into the argument type and value and then displayed them.
I thought it would be nice then to revisit this and make the argument
type the name of a tutorial 'method' and the argument value any value
required by that tutorial method.

This seemed relatively straight forward, so I could now call my Ruby
module as:
./ptutorial.rb -s"HASHES" -o"1"

From my driver menu, I could then select the argument tutorial - and it
would split the two arguments into a variable containing a method name
(by downcasing and appending _test to the -s argument) and option
variable.

Now, to stop a call to a non existent method, I wanted to see if the
built method variable existed as a method.

What I thought I could then do, was to see if 'self' responded to the
method by using:
if self.respond_to?(method)

but it seems that 'self' does not respond to that method - although if I
call self.send(method, option) it appears to work fine (even though if I
look at the methods that self/@self supports via the methods method,
none of my 'local' tutorial methods appear).

I thought that it may be down to use of 'self' as opposed to '@self' but
both fail on the respond_to?, and seem to process the method ok on the
send.

What I want is to dynamically build a method name, then see if the
object (in this case 'self' or the current module) supports that method
and if so to then execute that method with any optional arguments.
Posted by Joel Pearson (virtuoso)
on 2013-02-01 12:39
Have you tried self.methods.include?
Posted by Steve Tu (stevet)
on 2013-02-01 14:17
Joel Pearson wrote in post #1094736:
> Have you tried self.methods.include?

Joel,
Just tried, but no joy.
BUT...
I tried adding the full method footprint into the self.respond_to? and 
that then seemed to work - seemed being the operative word, as I looked 
more at respond_to? and I read that the second argument is a flag for 
checking private methods. So, that implies that somehow, my 'local' 
methods have been classed as private? I can't see a private statement in 
my code at all - are methods in 'main' then classed as private by 
default?

That also then made me think whether what I'm trying to achieve (all be 
it in learning mode) is possible.
If respond_to? only checks if that method name is valid for the given 
object, I still have the problem of knowing what the method's footprint 
is.

In my case, I have a whole series of tutorial modules - mainly called 
foo_test (ie array_test, hash_test, range_test...etc) - which can then 
optionally take arguments - ie in my hash_test, there may be umpteen 
sub-subjects - each being triggered by the option passed (ie 
hash_test(1) will show me blah, hash_test(2)...)

So what I was after was dynamically building foo_test from the command 
line argument(s) and then calling the method, with or without arguments. 
But if respond_to? doesn't check the footprint, I still end up with a 
crash. Given that, is a better practice to simply call the method with 
and without the arguments, catching any error and simply not bothering 
to see if the object responds to that method?
Posted by unknown (Guest)
on 2013-02-01 14:38
(Received via mailing list)
Am 01.02.2013 12:36, schrieb Steve Tu:
>
>
> if self.respond_to?(method)
> What I want is to dynamically build a method name, then see if the
> object (in this case 'self' or the current module) supports that method
> and if so to then execute that method with any optional arguments.

To me, this does not sound like a use case for metaprogramming.
Why don't you use a method that takes the section as an argument?


But to your question: I assume you are talking about 'global' methods?
If so, I cannot reproduce your problem:


1.9.3p374 :001 > def test_method; puts 'test'; end
  => nil
1.9.3p374 :002 > self.methods.include?(:test_method)
  => true
1.9.3p374 :003 > test_method
test
  => nil
1.9.3p374 :004 > self.send(:test_method)
test
  => nil
1.9.3p374 :005 > self.respond_to?(:test_method)
  => true
Posted by Steve Tu (stevet)
on 2013-02-01 16:03
unknown wrote in post #1094752:
> Am 01.02.2013 12:36, schrieb Steve Tu:
>>
>>
>> if self.respond_to?(method)
>> What I want is to dynamically build a method name, then see if the
>> object (in this case 'self' or the current module) supports that method
>> and if so to then execute that method with any optional arguments.
>
> To me, this does not sound like a use case for metaprogramming.
> Why don't you use a method that takes the section as an argument?
>
>
> But to your question: I assume you are talking about 'global' methods?
> If so, I cannot reproduce your problem:
>
>
> 1.9.3p374 :001 > def test_method; puts 'test'; end
>   => nil
> 1.9.3p374 :002 > self.methods.include?(:test_method)
>   => true
> 1.9.3p374 :003 > test_method
> test
>   => nil
> 1.9.3p374 :004 > self.send(:test_method)
> test
>   => nil
> 1.9.3p374 :005 > self.respond_to?(:test_method)
>   => true

The code I have is now split into separate files that are 'load' or 
'require'd. The argument test file contains:
 #-----------------------------------------------------------------------
 #   F O R M A L   A R G U M E N T S
 #-----------------------------------------------------------------------
  def arguments_test
    require 'getoptlong'

    # The parameters can be in any order
    STDOUT.puts "Arguments "+ARGV.length.to_s
    unless ARGV.length >= 1
        `zenity --info --title="Usage ... " --text="Usage: 
./ptutorial.rb -s\"Subject\" -o\"Options\""`
        exit
    end

    subject = option =  ''
    # specify the options we accept and initialize
    # the option parser
    opts = GetoptLong.new(
                [ "--subject", "-s", GetoptLong::REQUIRED_ARGUMENT ],
                [ "--option", "-o", GetoptLong::OPTIONAL_ARGUMENT ],
            )
    # process the parsed options
    opts.each do |opt, arg|
        case opt
            when '--subject'
                subject = arg
            when '--option'
                option = arg
        end
    end

    STDOUT.puts "(#{self}) - (#{self.to_s}) Supports (#{self.methods})"
    STDOUT.puts "No Class Supports (#{methods})"


    method  = "#{subject.to_s.chomp.downcase}_test"
    if self.respond_to?(method)
        self.send(method)
    else
        if self.respond_to?(method,TRUE)
            self.send(method,option)
        else
            `zenity --info --title="Arguments ... " --text="Args: 
(#{@self}).... Does Not Support #{method}"`
        end
    end

    `zenity --info --title="Arguments ... " --text="Args: Subject 
(#{subject}) Option (#{option})"`

  end


What I now end up with is the 'private' respond_to? test working - which 
is one point I don't understand (ie what determines a method's 
visibility - I thought it had to be explicitly defined or was assumed as 
public?). That then lead onto the question re how to determine the 
footprint of the method being called, as the code above works if the 
method passed accepts a single parameter. If the method accepts o 
parameters then the code fails anyway - and hence the question re 
whether it is simply then better to just attempt the method call and 
trap the errors thrown, rather than trying to see if the method is 
supported in the first place.
Am I making any sense at all?
Posted by unknown (Guest)
on 2013-02-01 17:00
(Received via mailing list)
Am 01.02.2013 16:06, schrieb Steve Tu:

> The code I have is now split into separate files that are 'load' or
> 'require'd. The argument test file contains:
>   #-----------------------------------------------------------------------
>   #   F O R M A L   A R G U M E N T S
>   #-----------------------------------------------------------------------
>    def arguments_test
>      require 'getoptlong'

I like optparse.

>
>      # The parameters can be in any order
>      STDOUT.puts "Arguments "+ARGV.length.to_s

puts uses stdout by default, so you can just use `puts'.

>                  [ "--subject", "-s", GetoptLong::REQUIRED_ARGUMENT ],
>      end
>
>      STDOUT.puts "(#{self}) - (#{self.to_s}) Supports (#{self.methods})"

#{self} sends to_s implicitly, so #{self} and #{self.to_s} are the same

>              `zenity --info --title="Arguments ... " --text="Args:
> What I now end up with is the 'private' respond_to? test working - which
> is one point I don't understand (ie what determines a method's
> visibility - I thought it had to be explicitly defined or was assumed as
> public?).

Seems 'global' methods behave differently, they are private methods
of `Object', see
http://stackoverflow.com/questions/8799704/are-rub...

> That then lead onto the question re how to determine the
> footprint of the method being called, as the code above works if the
> method passed accepts a single parameter. If the method accepts o
> parameters then the code fails anyway - and hence the question re
> whether it is simply then better to just attempt the method call and
> trap the errors thrown, rather than trying to see if the method is
> supported in the first place.
> Am I making any sense at all?

You could use an optional argument hash.


Still, this approach seems a bit strange to me.
Ever thought about defining classes?
Posted by Steve Tu (stevet)
on 2013-02-03 09:45
unknown wrote in post #1094776:
> Am 01.02.2013 16:06, schrieb Steve Tu:
>
>> The code I have is now split into separate files that are 'load' or
>> 'require'd. The argument test file contains:
>>   #-----------------------------------------------------------------------
>>   #   F O R M A L   A R G U M E N T S
>>   #-----------------------------------------------------------------------
>>    def arguments_test
>>      require 'getoptlong'
>
> I like optparse.
>
>>
>>      # The parameters can be in any order
>>      STDOUT.puts "Arguments "+ARGV.length.to_s
>
> puts uses stdout by default, so you can just use `puts'.
>
>>                  [ "--subject", "-s", GetoptLong::REQUIRED_ARGUMENT ],
>>      end
>>
>>      STDOUT.puts "(#{self}) - (#{self.to_s}) Supports (#{self.methods})"
>
> #{self} sends to_s implicitly, so #{self} and #{self.to_s} are the same
>
>>              `zenity --info --title="Arguments ... " --text="Args:
>> What I now end up with is the 'private' respond_to? test working - which
>> is one point I don't understand (ie what determines a method's
>> visibility - I thought it had to be explicitly defined or was assumed as
>> public?).
>
> Seems 'global' methods behave differently, they are private methods
> of `Object', see
> 
http://stackoverflow.com/questions/8799704/are-rub...
>
>> That then lead onto the question re how to determine the
>> footprint of the method being called, as the code above works if the
>> method passed accepts a single parameter. If the method accepts o
>> parameters then the code fails anyway - and hence the question re
>> whether it is simply then better to just attempt the method call and
>> trap the errors thrown, rather than trying to see if the method is
>> supported in the first place.
>> Am I making any sense at all?
>
> You could use an optional argument hash.
>
>
> Still, this approach seems a bit strange to me.
> Ever thought about defining classes?

I am following a tutorial, so am picking up each example as is. I am 
then trying to modify those examples (on a limited basis), just to make 
sure I understand the lesson and what it implies. I may revisit older 
examples if I see a new facility that I think applies - as here, where I 
thought I could bypass my menu and use the passed arguments to 
dynamically call the tutorial lesson.
I just get a bit lost as to Ruby's built in methods and what they're 
meant to do (I can see WHAT they do, but the implication of 
when/where/why sometimes loses me). Here, for example, you can see if a 
method is available by respond_to? and eval and send will then execute a 
'prepared' string as a method. But if I can't tell what the footprint is 
of the method what is the point in being able to dynamically execute a 
method (ie I could end up calling a methods as blah() when it should be 
called as blah(int, string, int))? What then is dynamic method execution 
for, if I can't tell how the method is to be called in detail?

Thanks for the info on 'global' methods by the way - that explains the 
why I was seeing my 'respond_to?' fail - and also why I thought it 
worked when I called it as respond_to?("hashes_test","1").
Posted by unknown (Guest)
on 2013-02-03 11:24
(Received via mailing list)
Am 03.02.2013 09:45, schrieb Steve Tu:
> 'prepared' string as a method. But if I can't tell what the footprint is
> of the method what is the point in being able to dynamically execute a
> method (ie I could end up calling a methods as blah() when it should be
> called as blah(int, string, int))? What then is dynamic method execution
> for, if I can't tell how the method is to be called in detail?

As I mentioned, one approach could be to use an argument hash.
But: I think as a novice you should postpone metaprogramming
(constructing method names etc.) for some time.

First thing you should do is to drop the 'global' methods
and start learning about classes.
Please log in before posting. Registration is free and takes only a minute.
Existing account (Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
No account? Register here.