Forum: Ruby Protected methods and class methods

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.
Gioele B. (Guest)
on 2006-01-20 22:13
(Received via mailing list)
Hi,

is there a way to call a protected method of a class from a static
method of
the same class? Or to acces an instance variable of a class from a
static
method of the same class?

Now I'm facing a problem. I use some static methods as "factory
methods", to
create "prefilled" class instances. These methods can't access the
protected
methods of the same class. Is this behavior intentional? Is there clean
solution to this problem? If not, how can I work around this problem?
This is
a public class, so I don't want to add other params to initialize.

I get this error

  irb(main):032:0> Info.fromBytes("\010\008")
  NoMethodError: protected method `length=' called for #<Info:0x300c3b70
@length=nil, @typeID=8>
          from (irb):23:in `fromBytes'

with this class

  class Info
    def initialize(typeID)
      @typeID = typeID
      @length = nil
    end

    attr_reader :typeID

    def length
      if @length.nil?
        @length = veryLongMathCalcs()
      end
      return @length
    end

    def Info.fromBytes(bytes)
      typeID = bytes[0, 1].unpack("c")[0]
      length = bytes[1, 1].unpack("c")[0]

      info = Info.new(typeID)
      info.length = length  ## <<< this is the error!

      return info
    end

  protected
    attr_writer :length

  end
Ross B. (Guest)
on 2006-01-21 03:24
(Received via mailing list)
On Sat, 2006-01-21 at 04:34 +0900, Gioele B. wrote:

> Now I'm facing a problem. I use some static methods as "factory methods", to
> create "prefilled" class instances. These methods can't access the protected
> methods of the same class. Is this behavior intentional?

I believe so. Because the class Info is itself an instance of Class,
while the instance is an instance of Info, which is entirely unrelated
to Class (apart from the common ancestor, Object). So there's no reason
for protected instance methods on Info to be available to methods on the
class itself.

(I think static methods are usually referred to as class methods in
Ruby. *nothing* is static here).

Access modifiers can be pretty surprising depending where you're coming
from. Check out (if you haven't already):

http://www.whytheluckystiff.net/ruby/pickaxe/html/...

> Is there clean solution to this problem? If not, how can I work around this
> problem? This is a public class, so I don't want to add other params
> to initialize.

> I get this error
>
>   irb(main):032:0> Info.fromBytes("\010\008")
>   NoMethodError: protected method `length=' called for #<Info:0x300c3b70
> @length=nil, @typeID=8>
>           from (irb):23:in `fromBytes'
>
> with this class
>
>   [ ... snipped ...]

Maybe try this workaround. I've also changed a few things to be more
'Rubyish', but you can just ignore that if you like :)

class Info
  def initialize(type_id)
    @type_id = type_id
    @length = nil
  end

  attr_reader :type_id

  def length
    @length ||= very_long_math_calcs
  end

  def Info.from_bytes(bytes)
    type_id = bytes[0,1].unpack("c")[0]
    len = bytes[1,1].unpack("c")[0]

    info = Info.new(type_id)
    info.instance_eval { self.length = len }  ## <<< changed!

    # or (if you don't care about encapsulation here):
    # info.instance_eval { @length = len }

    info
  end

  # maybe don't need this now?
  protected
  attr_writer :length
end

# Noticed a potential error in your input - \008 isn't valid octal :)
p Info.from_bytes("\010\015")
#=> #<Info:0xb7f7b228 @length=13, @type_id=8>

Hope that helps,
Ross
Gioele B. (Guest)
on 2006-01-22 19:59
(Received via mailing list)
On Saturday 21 January 2006 01:46, Ross B. wrote:
> On Sat, 2006-01-21 at 04:34 +0900, Gioele B. wrote:
> > Now I'm facing a problem. I use some static methods as "factory methods",
> > to create "prefilled" class instances. These methods can't access the
> > protected methods of the same class. Is this behavior intentional?
>
> I believe so. Because the class Info is itself an instance of Class,
> while the instance is an instance of Info, which is entirely unrelated
> to Class (apart from the common ancestor, Object). So there's no reason
> for protected instance methods on Info to be available to methods on the
> class itself.
Instead it makes sense to me to be able to use Info protected methods
from
Info class methods. I see no need for 'Class' methods to be able to use
protected members of an arbitrary class, but this is a particular case!
Both
methods are strictly related to Info (and its subclasses, but this is
just a
detail).

> (I think static methods are usually referred to as class methods in
> Ruby. *nothing* is static here).
Ah, my C++ heritage :)

> Maybe try this workaround.
>
>     info = Info.new(type_id)
>     info.instance_eval { self.length = len }  ## <<< changed!
>
> Hope that helps,
> Ross
Yes, it worked fine. Just I'm not so happy when I have to use *_eval :(
I feel
like cheating. Anyway it is better to have such a shortcut than not :)
Ross B. (Guest)
on 2006-01-22 21:59
(Received via mailing list)
On Mon, 2006-01-23 at 02:56 +0900, Gioele B. wrote:
> > class itself.
> Instead it makes sense to me to be able to use Info protected methods from
> Info class methods. I see no need for 'Class' methods to be able to use
> protected members of an arbitrary class, but this is a particular case! Both
> methods are strictly related to Info (and its subclasses, but this is just a
> detail).
>

Yes, I can see the logic in that, and I don't disagree that it'd
probably make sense in some respects, but now I've become more familiar
with Ruby I think I probably _would_ find it surprising if protected
methods worked that way. Things are often more simple (and elegant) than
you'd imagine I suppose...

> > Maybe try this workaround.
> >
> >     info = Info.new(type_id)
> >     info.instance_eval { self.length = len }  ## <<< changed!
> Yes, it worked fine. Just I'm not so happy when I have to use *_eval :( I feel
> like cheating. Anyway it is better to have such a shortcut than not :)

Definitely :)
This topic is locked and can not be replied to.