Forum: Ruby Making object methods available externally

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
C96b9e285e706bfb3c4cac218ca9b4f4?d=identicon&s=25 David Stanford (dthomas53)
on 2009-02-04 19:37
Hi all,

I'm having trouble figuring out a way of creating a method within a
class that's available not only to the other object methods (object
instances), but also externally, as if I were to use 'self.method'.
Below is an (admittedly, lame) example of what I'm referring to:

#############################

class CheesePhrase
  def initialize(phrase)
    @phrase = phrase
    say_it
  end

  def say_it(phrase=@phrase)
    puts "#{phrase}"
  end
end

#############################

The above code works fine if I create an object instance, and (if I
choose to do so) call the method 'say_it', like so:

some_cheese_phrase = CheesePhrase.new("I like cheese.")
some_cheese_phrase.say_it

However, what if I'd also like to call the method directly, without
creating a new instance? For example, simply with:

CheesePhrase.say_it("I like cheese, too.")

I can't do this without using 'self.say_it' in the method declaration.
Though, if I do that, I am no longer able to call the method in
'initialize' since 'self.say_it' is now a class method. I get the
following error:

NoMethodError: undefined method `say_it' for #<CheesePhrase:0x284372f4
@phrase="I like cheese">
        from (irb):4:in `initialize'
        from (irb):11:in `new'

I'm sure I must be doing something wrong, or my concept of how to
implement this is totally wrong. The only other thought I've had is to
create duplicate methods, but this seems to violate the famed DRY
principle. Any advice would be much appreciated.

Thanks in advance!

-David
Ef3aa7f7e577ea8cd620462724ddf73b?d=identicon&s=25 Rob Biedenharn (Guest)
on 2009-02-04 19:57
(Received via mailing list)
On Feb 4, 2009, at 1:37 PM, David Stanford wrote:

>  def initialize(phrase)
> <snip/>
> Thanks in advance!
>
> -David

class CheesePhrase
   def initialize(phrase)
     @phrase = phrase
     say_it
   end

   def self.say_it(phrase)
     puts phrase
   end
   def say_it
     self.class.say_it(@phrase)
   end
end

irb> some_cheese_phrase = CheesePhrase.new("I like cheese.")
I like cheese.
=> #<CheesePhrase:0x629d0 @phrase="I like cheese.">
irb> some_cheese_phrase.say_it
I like cheese.
=> nil
irb> CheesePhrase.say_it("I like cheese, too.")
I like cheese, too.
=> nil

-Rob

Rob Biedenharn    http://agileconsultingllc.com
Rob@AgileConsultingLLC.com
C96b9e285e706bfb3c4cac218ca9b4f4?d=identicon&s=25 David Stanford (dthomas53)
on 2009-02-04 20:04
Rob Biedenharn wrote:
> On Feb 4, 2009, at 1:37 PM, David Stanford wrote:

> class CheesePhrase
>    def initialize(phrase)
>      @phrase = phrase
>      say_it
>    end
>
>    def self.say_it(phrase)
>      puts phrase
>    end
>    def say_it
>      self.class.say_it(@phrase)
>    end
> end

> -Rob
>
> Rob Biedenharn    http://agileconsultingllc.com
> Rob@AgileConsultingLLC.com

Thanks a lot, Rob. Is this really the standard way of implementing
something like this? It just seems a little redundant, no?

Thanks!

-David
A745f7d401d0a9bf80a5d5d94c961a05?d=identicon&s=25 unknown (Guest)
on 2009-02-04 21:05
(Received via mailing list)
Hi David,
i don't understand well what you need. Is this that you are looking for:
class CheesePhrase
  def initialize(phrase)
    @phrase = phrase
    CheesePhrase.say_it(phrase)
  end
  def CheesePhrase.say_it(phrase=@phrase)
    puts "#{phrase}"
  end
end

a = CheesePhrase.new('I like cheese')
a = CheesePhrase.say_it('I like too much cheese);

On Thu, 5 Feb 2009 03:37:30 +0900, David Stanford
<dthomas53@hotmail.com>
Ef3aa7f7e577ea8cd620462724ddf73b?d=identicon&s=25 Rob Biedenharn (Guest)
on 2009-02-04 21:08
(Received via mailing list)
On Feb 4, 2009, at 2:03 PM, David Stanford wrote:
>>     puts phrase
>
> Thanks a lot, Rob. Is this really the standard way of implementing
> something like this? It just seems a little redundant, no?
>
> Thanks!
>
> -David


When you need it, yes. However, it isn't typical that you'd do
something like this.  Either the method belongs on the instance or it
belongs on the class itself.  Occasionally, for convenience, you want
a class method to be called on an instance and then you'd likely do it
this way. Your example is simple (and, being an example, somewhat
contrived), but illustrates a point.  You can call #say_it with no
parameter because the instance variable is there so an instance
*knows* what to say, but to call .say_it on the class, you need to
pass the parameter.

-Rob

Rob Biedenharn    http://agileconsultingllc.com
Rob@AgileConsultingLLC.com
8f6f95c4bd64d5f10dfddfdcd03c19d6?d=identicon&s=25 Rick Denatale (rdenatale)
on 2009-02-04 22:06
(Received via mailing list)
On Wed, Feb 4, 2009 at 3:06 PM, Rob Biedenharn
<Rob@agileconsultingllc.com>wrote:

>
> When you need it, yes. However, it isn't typical that you'd do something
> like this.  Either the method belongs on the instance or it belongs on the
> class itself.  Occasionally, for convenience, you want a class method to be
> called on an instance and then you'd likely do it this way. Your example is
> simple (and, being an example, somewhat contrived), but illustrates a point.
>  You can call #say_it with no parameter because the instance variable is
> there so an instance *knows* what to say, but to call .say_it on the class,
> you need to pass the parameter.
>

Ruby does have a method Module#module_function which can be used in
cases
similar to this. For a rather silly example:

module Test
  def foo
    puts "Foo"
  end

  module_function :foo
end

Test.foo

begin
  foo
rescue Exception => ex
  puts "oops! #{ex}"
end

self.extend Test
foo

when run produces:

Foo
oops! undefined local variable or method `foo' for main:Object
Foo

What module_function does is to copy one or more instance methods and
make
them singleton methods of the module.  It only works for modules not
classes.

The canonical use case is in the Math module so that you can either
write:

Math.sin(.2)

or

class X
   include Math

   def compute(val)
        2 + sin(val)
   end
end
--
Rick DeNatale

Blog: http://talklikeaduck.denhaven2.com/
Twitter: http://twitter.com/RickDeNatale
3131fcea0a711e5ad89c8d49cc9253b4?d=identicon&s=25 Julian Leviston (Guest)
on 2009-02-05 01:36
(Received via mailing list)
Put "self." in front of the method name in it's definition. This is
called a class method. If you need both, have both.

Blog: http://random8.zenunit.com/
Learn rails: http://sensei.zenunit.com/
C96b9e285e706bfb3c4cac218ca9b4f4?d=identicon&s=25 David Stanford (dthomas53)
on 2009-02-05 23:32
Thanks for all the responses, guys.

Judging from everyone's response, it sounds like what I'm trying to do
isn't typically done. My mindset was, first, to simply create a standard
class. While creating one of the object methods for that class, I
realized that the same method code would be useful to call directly,
without having to create an object instance; so, as I said, I was
thinking there must be some way to do this, without repeating the same
(or a similar piece) of code somewhere else.

So, thanks again!
A246f7c0ce5f2909483d358bd9e83e4e?d=identicon&s=25 Mike Gold (mikegold)
on 2009-02-06 15:50
Rick Denatale wrote:
> Ruby does have a method Module#module_function which can be used in
> cases similar to this. For a rather silly example:
>
> module Test
>   def foo
>     puts "Foo"
>   end
>
>   module_function :foo
> end

To make a minor point, I see this use of module_function often, even in
the standard library.  I don't like it.

In addition to the module_function method, there's the module_function
scoping state, like public or private or protected, which is likewise
enabled by calling module_function without arguments.  Therefore we can
make a D.R.Y. equivalent:

module Test
  module_function

  def foo
    puts "Foo"
  end
end
E0d864d9677f3c1482a20152b7cac0e2?d=identicon&s=25 Robert Klemme (Guest)
on 2009-02-06 15:59
(Received via mailing list)
2009/2/5 David Stanford <dthomas53@hotmail.com>:

> Judging from everyone's response, it sounds like what I'm trying to do
> isn't typically done.

Agreed.

> My mindset was, first, to simply create a standard
> class. While creating one of the object methods for that class, I
> realized that the same method code would be useful to call directly,
> without having to create an object instance; so, as I said, I was
> thinking there must be some way to do this, without repeating the same
> (or a similar piece) of code somewhere else.

In that case I would have the method definition *only* at class level
since apparently it is independent of instance state.  I wouldn't even
have it as instance method at all.  That avoids confusion.  If you
need to invoke the method from instance methods the
self.class.a_method() idiom works well IMHO.

Kind regards

robert
Bec38d63650c8912b6ba9b557fb953b9?d=identicon&s=25 Roger Pack (rogerdpack)
on 2009-02-07 15:24
David Stanford wrote:
> Hi all,
>
> I'm having trouble figuring out a way of creating a method within a
> class that's available not only to the other object methods (object
> instances), but also externally, as if I were to use 'self.method'.

you could do something like
module X
 def shared_method
 end
end
class Y
 include X
 extend X
end
?
-=r
31e038e4e9330f6c75ccfd1fca8010ee?d=identicon&s=25 Gregory Brown (Guest)
on 2009-02-07 15:44
(Received via mailing list)
On Thu, Feb 5, 2009 at 5:31 PM, David Stanford <dthomas53@hotmail.com>
wrote:
> Thanks for all the responses, guys.
>
> Judging from everyone's response, it sounds like what I'm trying to do
> isn't typically done. My mindset was, first, to simply create a standard
> class. While creating one of the object methods for that class, I
> realized that the same method code would be useful to call directly,
> without having to create an object instance; so, as I said, I was
> thinking there must be some way to do this, without repeating the same
> (or a similar piece) of code somewhere else.

Probably s bit extreme for one method, but say you had 10 you wanted
to do this with:

module Foo
  def self.included(base)
     base.extend(self)
  end

  def method1; end
  def method2; end
  #...
end

class A
  include Foo # no need for manual extend call
end

Not tested or necessarily recommended. :)

-greg
D7463bd611f227cfb2ef4da4a978a203?d=identicon&s=25 Christopher Dicely (Guest)
on 2009-02-07 16:43
(Received via mailing list)
On Fri, Feb 6, 2009 at 6:58 AM, Robert Klemme
<shortcutter@googlemail.com> wrote:
>> without having to create an object instance; so, as I said, I was
>> thinking there must be some way to do this, without repeating the same
>> (or a similar piece) of code somewhere else.
>
> In that case I would have the method definition *only* at class level
> since apparently it is independent of instance state.  I wouldn't even
> have it as instance method at all.  That avoids confusion.  If you
> need to invoke the method from instance methods the
> self.class.a_method() idiom works well IMHO.


I think that depends on why the method is useful at both places; one
thing I've seen occasionally is the use case where it is useful at the
class level because there are lots of times you'd like to create an
instance, call the method, and then be done with the instance, so it
makes sense to have something like:

class Foo
  def initialize(init_arg1, ..., init_argn)
    # setup
  end

  def action(action_arg1, ..., action_argn)
    # do the action
  end

  def self.action(init_arg1, ..., init_argn, action_arg1, ...,
action_argn)
    new(init_arg1, ..., init_argn).action(action_arg1, ..., action_argn)
  end
end

If that's the reason, using the CheesePhrase example:

class CheesePhrase
  # all the stuff in the original example
  def self.say_it(phrase)
    new(phrase).say_it
  end
end
1d53b088a989e069b94597c282eebbbc?d=identicon&s=25 Simon Krahnke (Guest)
on 2009-02-07 20:35
(Received via mailing list)
* David Stanford <dthomas53@hotmail.com> (2009-02-04) schrieb:

>>      puts phrase
>>    end
>>    def say_it
>>      self.class.say_it(@phrase)
>>    end
>> end

> Thanks a lot, Rob. Is this really the standard way of implementing
> something like this? It just seems a little redundant, no?

Where do you see the redundancy? Nothing is repeated.

mfg,                   simon .... l
This topic is locked and can not be replied to.