General Ruby Programming questions

Discussion in 'Ruby' started by Simon Kitching, Oct 15, 2003.

  1. Hi,

    I'm writing a Ruby app at the moment, and have some questions I hope you
    can help me with:

    (a) string->class

    I have a string containing the name of a class, and want to get the
    corresponding Class object. My current approach is:
    Module.class_eval(classname)

    Is this the best approach?

    (b) changing "require" searchpath

    The $: variable holds the path used to locate files when the "require"
    command is used. However this variable is read-only. In Perl, it is
    possible to modify the equivalent path; can this be done in ruby?

    The reason I want this is that I want to run my test harness from one
    directory, and pick up my in-development code from some other directory.
    Using "require '../xmldigester.rb'" doesn't work, because all the
    requires in that required file resolve against $:.

    (c) how invoke "hidden" methods?

    I have a class Digester which responds to SAX events generated by REXML.
    However I don't want methods like "start_element" to be public on
    Digester, because this is just untidy: these methods are "internal" to
    the Digester class, and should not be available for ordinary users of
    this class.

    In Java, I would declare package-scope methods on the Digester class,
    then write an adapter class which implements SaxContentHandler, and
    forwards calls to its methods to the appropriate Digester object
    methods.

    However Ruby doesn't have "package-scope" methods. Any idea how I
    achieve a similar effect in Ruby?

    (d) iterating in reverse

    I have an Array object, and wish to iterate over its members in reverse.
    I would prefer not to have to create a new Array object in order to do
    this iteration. Is there any way other than writing
    for(i=array.length-1; i>=0; --i) ....


    Thanks,

    Simon
     
    Simon Kitching, Oct 15, 2003
    #1
    1. Advertising

  2. Simon Kitching wrote:

    > Hi,


    Moin!

    > I have a string containing the name of a class, and want to get the
    > corresponding Class object. My current approach is:
    > Module.class_eval(classname)
    > Is this the best approach?


    It isn't -- an user might inject arbitary code. Use this instead:

    irb(main):001:0> Object.const_get('Class')
    => Class

    > (b) changing "require" searchpath
    > The $: variable holds the path used to locate files when the "require"
    > command is used. However this variable is read-only. In Perl, it is
    > possible to modify the equivalent path; can this be done in ruby?


    It might be read-only, but that doesn't mean that you can't change the
    object which it refers to:

    irb(main):002:0> $:.push('new')
    => [..., 'new']

    > (c) how invoke "hidden" methods?


    Simply send the message a method invocation would produce yourself:

    irb(main):003:0> 5.send:)rand)
    => 0.748920713318512

    > (d) iterating in reverse
    > I have an Array object, and wish to iterate over its members in reverse.
    > I would prefer not to have to create a new Array object in order to do
    > this iteration. Is there any way other than writing


    irb(main):004:0> [1, 2, 3].reverse_each { |item| puts item }
    3
    2
    1

    Regards,
    Florian Groß
     
    Florian Gross, Oct 16, 2003
    #2
    1. Advertising

  3. On Thursday, October 16, 2003, 9:00:04 AM, Florian wrote:

    > Simon Kitching wrote:


    >> Hi,


    > Moin!


    >> I have a string containing the name of a class, and want to get the
    >> corresponding Class object. My current approach is:
    >> Module.class_eval(classname)
    >> Is this the best approach?


    > It isn't -- an user might inject arbitary code. Use this instead:


    irb(main):001:0>> Object.const_get('Class')
    =>> Class

    That won't work for class names like "Test::Unit::TestCase". This is
    discussed on the Wiki. Search "class" and you'll find it I'm sure.

    > [...]


    Gavin
     
    Gavin Sinclair, Oct 16, 2003
    #3
  4. Hi Florian..

    On Thu, 2003-10-16 at 12:00, Florian Gross wrote:
    > Moin!


    ?? This isn't a greeting I've heard of before...

    Anyway, Kia Ora!

    > > (c) how invoke "hidden" methods?

    >
    > Simply send the message a method invocation would produce yourself:
    >
    > irb(main):003:0> 5.send:)rand)
    > => 0.748920713318512


    Whoa! So private methods can be called after all???!

    Not the end of the world, I guess. After all, Python doesn't support
    private methods at all, right? Still a bit odd in my opinion.

    Is there any reason why Ruby allows private methods to be invoked like
    this? (rather than provide a tidy alternative for problems like mine).

    Anyway, you answered all my questions in one swoop - thanks!


    Regards,

    Simon
     
    Simon Kitching, Oct 16, 2003
    #4
  5. Simon Kitching

    Eric Hodel Guest

    --gRZ38brEgCoUohoa
    Content-Type: text/plain; charset=us-ascii
    Content-Disposition: inline
    Content-Transfer-Encoding: quoted-printable

    Florian Gross () wrote:

    > Simon Kitching wrote:
    >
    > >(b) changing "require" searchpath
    > >The $: variable holds the path used to locate files when the "require"
    > >command is used. However this variable is read-only. In Perl, it is
    > >possible to modify the equivalent path; can this be done in ruby?

    >=20
    > It might be read-only, but that doesn't mean that you can't change the=20
    > object which it refers to:
    >=20
    > irb(main):002:0> $:.push('new')
    > =3D> [..., 'new']


    You really want to unshift here, the Array is read front to back, so new
    libs that override base libs won't get require'd.

    > >(d) iterating in reverse=20
    > >I have an Array object, and wish to iterate over its members in reverse.
    > >I would prefer not to have to create a new Array object in order to do
    > >this iteration. Is there any way other than writing

    >=20
    > irb(main):004:0> [1, 2, 3].reverse_each { |item| puts item }
    > 3
    > 2
    > 1


    You can also use reverse. An Array is smart, reverse takes constant time.

    [1,2,3].reverse.map do |i| i * 2 end

    --=20
    Eric Hodel - - http://segment7.net
    All messages signed with fingerprint:
    FEC2 57F1 D465 EB15 5D6E 7C11 332A 551C 796C 9F04


    --gRZ38brEgCoUohoa
    Content-Type: application/pgp-signature
    Content-Disposition: inline

    -----BEGIN PGP SIGNATURE-----
    Version: GnuPG v1.2.2 (FreeBSD)

    iD8DBQE/jdeZMypVHHlsnwQRAr3HAKCKrbnPdL6qsRPy4hO4Bfe0OvXqwACg39Ja
    BnyNvXWDbaq1d1ia1wzhoks=
    =k36U
    -----END PGP SIGNATURE-----

    --gRZ38brEgCoUohoa--
     
    Eric Hodel, Oct 16, 2003
    #5
  6. Simon Kitching

    Eric Hodel Guest

    --Y46ssxGX9/CNNfN6
    Content-Type: text/plain; charset=us-ascii
    Content-Disposition: inline
    Content-Transfer-Encoding: quoted-printable

    Simon Kitching () wrote:

    > > > (c) how invoke "hidden" methods?

    > >=20
    > > Simply send the message a method invocation would produce yourself:
    > >=20
    > > irb(main):003:0> 5.send:)rand)
    > > =3D> 0.748920713318512

    >=20
    > Whoa! So private methods can be called after all???!
    >=20
    > Not the end of the world, I guess. After all, Python doesn't support
    > private methods at all, right? Still a bit odd in my opinion.
    >
    > Is there any reason why Ruby allows private methods to be invoked like
    > this? (rather than provide a tidy alternative for problems like mine).


    I believe I'm giving a fairly accurate paraphrase of matz when I say
    that Ruby doesn't prevent you from writing sloppy code, it just makes it
    look ugly enough so that you don't do it.

    Scary things tend to look scary in Ruby.
    Evil things tend to look evil in Ruby.

    --=20
    Eric Hodel - - http://segment7.net
    All messages signed with fingerprint:
    FEC2 57F1 D465 EB15 5D6E 7C11 332A 551C 796C 9F04


    --Y46ssxGX9/CNNfN6
    Content-Type: application/pgp-signature
    Content-Disposition: inline

    -----BEGIN PGP SIGNATURE-----
    Version: GnuPG v1.2.2 (FreeBSD)

    iD8DBQE/jdiEMypVHHlsnwQRAtdxAKDERec4ZwpSgFymVIij+yiDJlLrbQCgtFiW
    B5EVDSLj/Ur4zC2z9mRwm8E=
    =DYTC
    -----END PGP SIGNATURE-----

    --Y46ssxGX9/CNNfN6--
     
    Eric Hodel, Oct 16, 2003
    #6
  7. > [Simon wrote:]
    >
    >> > (c) how invoke "hidden" methods?

    >>
    >> Simply send the message a method invocation would produce yourself:
    >>
    >> irb(main):003:0> 5.send:)rand)
    >> => 0.748920713318512

    >
    > Whoa! So private methods can be called after all???!
    >
    > Not the end of the world, I guess. After all, Python doesn't support
    > private methods at all, right? Still a bit odd in my opinion.
    >
    > Is there any reason why Ruby allows private methods to be invoked like
    > this? (rather than provide a tidy alternative for problems like mine).


    It may help to think of it not as a method being invoked, rather a message
    being sent. The object decides how it wishes to respond to that message.

    After all, you can't invoke a method that doesn't exist, but you can get a
    response back from a message that doesn't have a corresponding method, if
    you get my drift.

    class X
    def method_missing(sym, *args)
    5
    end
    end

    x = X.new
    x.foo # -> 5
    x.bar # -> 5
    x.quux # -> 5
    x.method:)foo) # -> NameError: undefined method 'foo'
    x.nil? # -> false

    Gavin
     
    Gavin Sinclair, Oct 16, 2003
    #7
  8. Simon Kitching

    James Britt Guest

    Eric Hodel wrote:

    > Simon Kitching () wrote:

    ...
    >>Is there any reason why Ruby allows private methods to be invoked like
    >>this? (rather than provide a tidy alternative for problems like mine).

    >
    >
    > I believe I'm giving a fairly accurate paraphrase of matz when I say
    > that Ruby doesn't prevent you from writing sloppy code, it just makes it
    > look ugly enough so that you don't do it.
    >


    And if you really want to trap such calls you can override 'send':

    module SafeSend
    alias evil_unsafe_send send
    def send( m, *args )
    msg = "No sending of private messages!"
    raise msg if self.private_methods.include? m.to_s
    evil_unsafe_send( m, *args )
    end
    end


    class Foo
    include SafeSend
    # ...
    end
     
    James Britt, Oct 16, 2003
    #8
  9. On Thu, 2003-10-16 at 13:06, Gavin Sinclair wrote:
    > > [Simon wrote:]
    > >
    > >> > (c) how invoke "hidden" methods?
    > >>
    > >> Simply send the message a method invocation would produce yourself:
    > >>
    > >> irb(main):003:0> 5.send:)rand)
    > >> => 0.748920713318512

    > >
    > > Whoa! So private methods can be called after all???!
    > >
    > > Not the end of the world, I guess. After all, Python doesn't support
    > > private methods at all, right? Still a bit odd in my opinion.
    > >
    > > Is there any reason why Ruby allows private methods to be invoked like
    > > this? (rather than provide a tidy alternative for problems like mine).

    >
    > It may help to think of it not as a method being invoked, rather a message
    > being sent. The object decides how it wishes to respond to that message.
    >
    > After all, you can't invoke a method that doesn't exist, but you can get a
    > response back from a message that doesn't have a corresponding method, if
    > you get my drift.


    Yes, that's clear.

    However I could still rephrase my original question as:

    Why doesn't ruby throw a NoMethodError exception when a message is
    sent to a method which is private using the "send" feature?

    The purpose of marking a method as private is to indicate that the only
    valid invocation of the code within that method is when the invoker is
    another method on the same class (java convention) or the same object
    (ruby convention).

    Quite often private methods don't preserve class invariants. And
    exposing methods that can break class invariants to the outside world is
    never a good idea.

    As I said, it's no big deal. Perl, javascript and (as far as I know)
    Python don't support the concept of private methods at all.

    However I am curious to know if there is a real justification for
    allowing private methods to be invoked via "send", or if it just an
    accident that this works. Ruby takes encapsulation so seriously in many
    other ways...

    Cheers,

    Simon

    PS: I did eventually find that class lookup implementation on the wiki.
    I think it would be really nice to have this in the std libs (eg as a
    class method on the Class or Object classes).

    http://www.rubygarden.org/ruby?FindClassesByName

    def get_class(name)
    name.split(/::/).inject(Object) {|p,n|
    p.const_get(n)
    }
    end
     
    Simon Kitching, Oct 16, 2003
    #9
  10. On Thu, 2003-10-16 at 13:14, James Britt wrote:
    > Eric Hodel wrote:
    >
    > > Simon Kitching () wrote:

    > ...
    > >>Is there any reason why Ruby allows private methods to be invoked like
    > >>this? (rather than provide a tidy alternative for problems like mine).

    > >
    > >
    > > I believe I'm giving a fairly accurate paraphrase of matz when I say
    > > that Ruby doesn't prevent you from writing sloppy code, it just makes it
    > > look ugly enough so that you don't do it.
    > >

    >
    > And if you really want to trap such calls you can override 'send':
    >
    > module SafeSend
    > alias evil_unsafe_send send
    > def send( m, *args )
    > msg = "No sending of private messages!"
    > raise msg if self.private_methods.include? m.to_s
    > evil_unsafe_send( m, *args )
    > end
    > end
    >
    >
    > class Foo
    > include SafeSend
    > # ...
    > end


    Hah! Consider my questions answered :)

    I love the way Ruby can override default behaviours.....

    Regards,

    Simon
     
    Simon Kitching, Oct 16, 2003
    #10
  11. > [Simon wrote:]
    >
    > PS: I did eventually find that class lookup implementation on the wiki.
    > I think it would be really nice to have this in the std libs (eg as a
    > class method on the Class or Object classes).
    >
    > http://www.rubygarden.org/ruby?FindClassesByName
    >
    > def get_class(name)
    > name.split(/::/).inject(Object) {|p,n|
    > p.const_get(n)
    > }
    > end


    I'll add this to extensions.rubyforge.org within a few days. At first
    blush, Kernel seems the right place to put it.

    Gavin
     
    Gavin Sinclair, Oct 16, 2003
    #11
  12. Simon Kitching <> writes:

    > Hi Florian..
    >
    > On Thu, 2003-10-16 at 12:00, Florian Gross wrote:
    > > Moin!

    >
    > ?? This isn't a greeting I've heard of before...


    It's (Northern) German for good morning, although AFAIK, most people
    there use it as a replacement for hello ("Moin Moin!") any time of
    the day.

    You might also encounter

    Guten Morgen! "high", formal german (good morning)
    Guten Tag! "high", formal german (hello)
    Gruess Gott! southern Germany, esp. Bavaria (hello)
    Gruess Dich! southern Germany, esp. Bavaria (hello, among friends)
    Servus! southern Germany (hello, among friends)
    Gruezi! Switzerland (hello)
    ....

    As you might guess, I'm living in Bavaria :)

    > Anyway, Kia Ora!


    What language is this?

    kind regards
    frank

    --
    Frank Schmitt
    4SC AG phone: +49 89 700763-0
    e-mail: frankNO DOT SPAMschmitt AT 4sc DOT com
     
    Frank Schmitt, Oct 16, 2003
    #12
  13. W li¶cie z czw, 16-10-2003, godz. 02:14, James Britt pisze:

    > And if you really want to trap such calls you can override 'send':


    And then if someone really wants to invoke a private method, he will
    just add a public forwarding method.

    You could try to protect against all that, but what for?

    --
    __("< Marcin Kowalczyk
    \__/
    ^^ http://qrnik.knm.org.pl/~qrczak/
     
    Marcin 'Qrczak' Kowalczyk, Oct 16, 2003
    #13
  14. W li¶cie z czw, 16-10-2003, godz. 01:26, Eric Hodel pisze:

    > You can also use reverse. An Array is smart, reverse takes constant time.


    VALUE
    rb_ary_reverse(ary)
    VALUE ary;
    {

    [...]

    while (p1 < p2) {
    tmp = *p1;
    *p1++ = *p2;
    *p2-- = tmp;
    }

    Doesn't look like constant time for me.

    --
    __("< Marcin Kowalczyk
    \__/
    ^^ http://qrnik.knm.org.pl/~qrczak/
     
    Marcin 'Qrczak' Kowalczyk, Oct 16, 2003
    #14
  15. "Marcin 'Qrczak' Kowalczyk" <> schrieb im Newsbeitrag
    news:1066301968.17940.18.camel@qrnik...
    > W li¶cie z czw, 16-10-2003, godz. 01:26, Eric Hodel pisze:
    >
    > > You can also use reverse. An Array is smart, reverse takes constant

    time.
    >
    > VALUE
    > rb_ary_reverse(ary)
    > VALUE ary;
    > {
    >
    > [...]
    >
    > while (p1 < p2) {
    > tmp = *p1;
    > *p1++ = *p2;
    > *p2-- = tmp;
    > }
    >
    > Doesn't look like constant time for me.


    I think he meant reverse_each which has no overhead compared to each, i.e.
    both are O(n).

    Regards

    robert
     
    Robert Klemme, Oct 16, 2003
    #15
  16. Simon Kitching

    James Britt Guest

    Marcin 'Qrczak' Kowalczyk wrote:
    > W li¶cie z czw, 16-10-2003, godz. 02:14, James Britt pisze:
    >
    >
    >>And if you really want to trap such calls you can override 'send':

    >
    >
    > And then if someone really wants to invoke a private method, he will
    > just add a public forwarding method.
    >
    > You could try to protect against all that, but what for?



    I suspect that 'send' is most used when a specific method name is
    unknown until run time, and you want to dynamicaly invoke a method.
    However, there's the chance one could inadvertantly call a private
    method; adding some protective measures to 'send' would help block this.

    Sure it's true that one can always hack around such measures, but the
    idea is to raise the bar. (And I suppose some combinbation of
    overriding 'method_added' and inspecting the call stack could be thrown
    in to make things harder.)

    James
     
    James Britt, Oct 16, 2003
    #16
  17. Simon Kitching

    Christoph Guest

    Simon Kitching wrote:
    ....
    > Is there any reason why Ruby allows private methods to be invoked like
    > this? (rather than provide a tidy alternative for problems like mine).


    If I remember correctly Matz once said something to the effect
    that forcing people to use obscure syntacs like

    obj.__send__:)my_meth,my_arg)

    or the actually faster

    obj.instance_eval { my_meth(my_arg) }

    is mend as a deterrent for using private methods capriciously.
    As Marcin 'Qrczak' Kowalczyk already pointed out you can

    always wrap a private method in a public method anyway -
    or you can simply publicize a private method with Module::public.
    (incidentally Module::public itself is a private method:)

    ---
    class A
    end

    class << A
    public :include
    end

    module M
    end

    A.include M
    ---

    /Christoph
     
    Christoph, Oct 16, 2003
    #17
  18. Simon Kitching

    Eric Hodel Guest

    --tqI+Z3u+9OQ7kwn0
    Content-Type: text/plain; charset=us-ascii
    Content-Disposition: inline
    Content-Transfer-Encoding: quoted-printable

    Marcin 'Qrczak' Kowalczyk () wrote:

    > W li?cie z czw, 16-10-2003, godz. 01:26, Eric Hodel pisze:
    >=20
    > > You can also use reverse. An Array is smart, reverse takes constant ti=

    me.
    >=20
    > Doesn't look like constant time for me.


    Ah yes, you're right. My sample must have been too small to show the
    difference.

    --=20
    Eric Hodel - - http://segment7.net
    All messages signed with fingerprint:
    FEC2 57F1 D465 EB15 5D6E 7C11 332A 551C 796C 9F04


    --tqI+Z3u+9OQ7kwn0
    Content-Type: application/pgp-signature
    Content-Disposition: inline

    -----BEGIN PGP SIGNATURE-----
    Version: GnuPG v1.2.2 (FreeBSD)

    iD8DBQE/kvuBMypVHHlsnwQRAj3cAKDfQjCxPbumz2g9cz+ccQqycWSRhwCfSSHQ
    34TtTxwPUGUqMJ4qYFlO0CE=
    =iHjn
    -----END PGP SIGNATURE-----

    --tqI+Z3u+9OQ7kwn0--
     
    Eric Hodel, Oct 19, 2003
    #18
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. David
    Replies:
    6
    Views:
    476
    David
    Feb 13, 2004
  2. Superdude
    Replies:
    8
    Views:
    411
  3. David Espada
    Replies:
    2
    Views:
    169
    David Espada
    Jul 1, 2004
  4. rtilley
    Replies:
    5
    Views:
    120
    Alex Combas
    Mar 4, 2006
  5. Vinh Chuc

    Some General Questions about Ruby

    Vinh Chuc, Apr 4, 2007, in forum: Ruby
    Replies:
    7
    Views:
    122
    Vinh Chuc
    Apr 5, 2007
Loading...

Share This Page