Forum: Ruby Re: Adding in class attribute with a Module

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.
TRANS (Guest)
on 2006-04-10 19:01
(Received via mailing list)
> Is it possible to add class attributes (cattr_accessor) using a module?
> If so could you give an example?

There is no "Ruby Way" to do this b/c Ruby's module methods aren't
inherited by classes that include them. Though I've asked for a way
man times now!

It might seem easy to glaze by this, thinking nothing of
cattr_accessor, a seemingly esoteric method since it's not included in
core Ruby, but you will find it in both Rail's Active Support and Ruby
Facets. And in fact, you've brought up a nice example of how this play
out b/c cattr_accessor and it's ilk really don't do anything very
esoteric under the hood. I'm going to layout what cattr_accessor
essentially does* so all can see this for what it is and what happens
when you try to REUSE structures of this nature.

For a class, it is simply:

  class C
    def self.m ; @m ; end
    def self.m=(x) ; @m=x ; end
    def m ; self.class.m ; end
    def m=(x) ; self.class.m=x ; end
  end

Hence

  C.m = 10
  C.m  #=> 10
  c = C.new
  c.m  #=> 10
  c.m = 20
  c.m  #=> 20
  C.m  #=> 20

While you can subclass C with this functionality readily, eg 'class N
< C' works as you'd expect, try MODULARIZING this functionality. The
cooresponding

  module M
    def self.m ; @m ; end
    def self.m=(x) ; @m=x ; end
    def m ; self.class.m ; end
    def m=(x) ; self.class.m=x ; end
  end

does not work at all. You can't include it in a class, you can't
extend a class with it, nor both --it completely bombs. Feel free to
maniputlate the code as well --shame as that is, while you can get
closer, nothing completely works --there's alwasy something wrong.
Only ugly meta-programming hacks will give the sought behavior --and
even then their are corner-case complications.

T.


* I'm using an instance var in this case rather than a class var for
simplicity sake, but it makes no difference to the problem itself.
Gregory S. (Guest)
on 2006-04-10 19:19
(Received via mailing list)
On Mon, Apr 10, 2006 at 11:58:43PM +0900, TRANS wrote:
} > Is it possible to add class attributes (cattr_accessor) using a
module?
} > If so could you give an example?
}
} There is no "Ruby Way" to do this b/c Ruby's module methods aren't
} inherited by classes that include them. Though I've asked for a way
} man times now!
[...]
} Only ugly meta-programming hacks will give the sought behavior --and
} even then their are corner-case complications.

Does this count as ugly meta-programming?

module M
  def self.included(klass)
    class << klass
      attr_accessor :m
    end
  end
end

} T.
--Greg
zdennis (Guest)
on 2006-04-10 20:06
(Received via mailing list)
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

TRANS wrote:
> core Ruby, but you will find it in both Rail's Active Support and Ruby
>     def self.m=(x) ; @m=x ; end
>   c.m = 20
>   c.m  #=> 20
>   C.m  #=> 20
>

On a total side note, why do you have cattr_* methods affecting instance
methods as well ?

  class C
    cattr_accessor :m
  end

  C.m = 10
  c = C.new
  puts c.m
  # => 10

I would expect it to work for "C.m" and "C.m=" but not for "c.m" I would
expect:

  NoMethodError: undefined method `m' for #<C:0xb7d3103c>
        from /home/zdennis/.irbrc:4:in `method_missing'
        from (irb):5
        from :0

Perhaps I have wrong expectation of cattr_* . I am just asking at this
point though to better understand thought behind it.

Zach


-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.1 (GNU/Linux)
Comment: Using GnuPG with Thunderbird - http://enigmail.mozdev.org

iD8DBQFEOoKXMyx0fW1d8G0RAmiHAJ9KTcVR3wogDAWDEkvYEYtBJAJKFACcC/9T
Rxg1qQquejXwvmS6hH57aB0=
=Eq3D
-----END PGP SIGNATURE-----
Robert D. (Guest)
on 2006-04-10 21:20
(Received via mailing list)
On 4/10/06, Gregory S. <removed_email_address@domain.invalid> wrote:
> } Only ugly meta-programming hacks will give the sought behavior --and
> end
It is much prettier than the following ugly meta-programming, but I have
no
clue how it works !!!
Could you explain please?
Thx
Robert

} T.
> --Greg




class Module
>                 end
>         end
> end
>
>


--
Deux choses sont infinies : l'univers et la bêtise humaine ; en ce qui
concerne l'univers, je n'en ai pas acquis la certitude absolue.

- Albert Einstein
Gregory S. (Guest)
on 2006-04-10 21:41
(Received via mailing list)
On Tue, Apr 11, 2006 at 02:19:30AM +0900, Robert D. wrote:
} On 4/10/06, Gregory S. <removed_email_address@domain.invalid> wrote:
} >
} > On Mon, Apr 10, 2006 at 11:58:43PM +0900, TRANS wrote:
} > } > Is it possible to add class attributes (cattr_accessor) using a
} > module?
} > } > If so could you give an example?
} > }
} > } There is no "Ruby Way" to do this b/c Ruby's module methods aren't
} > } inherited by classes that include them. Though I've asked for a
way
} > } man times now!
} > [...]
} > } Only ugly meta-programming hacks will give the sought behavior
--and
} > } even then their are corner-case complications.
} >
} > Does this count as ugly meta-programming?
} >
} > module M
} >   def self.included(klass)
} >     class << klass
} >       attr_accessor :m
} >     end
} >   end
} > end
}
} It is much prettier than the following ugly meta-programming, but I
have no
} clue how it works !!!
} Could you explain please?

The first thing to understand is that when you define a class, e.g.
class
Foo; ... end, you are just setting a constant (Object::Foo, in this
case)
to a new instance of a Class. Just as you can add new methods to an
instance of any other kind of object with class << obj; ... end, you can
add new methods to Foo. When a module is included, its self.included
method
is called with the Class instance it is being included into as an
argument.
I am defining self.included to add an attribute accessor to that Class
instance.

} Thx
} Robert
[...]
--Greg
Robert D. (Guest)
on 2006-04-10 23:19
(Received via mailing list)
On 4/10/06, Gregory S. <removed_email_address@domain.invalid> wrote:
> } > } inherited by classes that include them. Though I've asked for a way
> } >       attr_accessor :m
> Foo; ... end, you are just setting a constant (Object::Foo, in this case)
> to a new instance of a Class. Just as you can add new methods to an
> instance of any other kind of object with class << obj; ... end, you can
> add new methods to Foo. When a module is included, its self.includedmethod
> is called with the Class instance it is being included into as an
> argument.


I see thank you about the explanation of that hook.

I am defining self.included to add an attribute accessor to that Class
> instance.



No of course not, because it is equivalent to

class <<self
     attr_accessor
end

which does not work.

} Thx
> } Robert
> [...]
> --Greg


Robert




--
Deux choses sont infinies : l'univers et la bêtise humaine ; en ce qui
concerne l'univers, je n'en ai pas acquis la certitude absolue.

- Albert Einstein
TRANS (Guest)
on 2006-04-11 01:22
(Received via mailing list)
> Does this count as ugly meta-programming?
>
> module M
>   def self.included(klass)
>     class << klass
>       attr_accessor :m
>     end
>   end
> end

Yep. That's Ugly Hack #1. What wrong with Ugly Hack #1, you ask?

  module N
    include M
  end

  class C
    include N
  end

  C.m
  NoMethodError: undefined method `m' for C:Class

T.
Gregory S. (Guest)
on 2006-04-11 18:30
(Received via mailing list)
On Tue, Apr 11, 2006 at 06:19:23AM +0900, TRANS wrote:
} > Does this count as ugly meta-programming?
} >
} > module M
} >   def self.included(klass)
} >     class << klass
} >       attr_accessor :m
} >     end
} >   end
} > end
}
} Yep. That's Ugly Hack #1. What wrong with Ugly Hack #1, you ask?
}
}   module N
}     include M
}   end
}
}   class C
}     include N
}   end
}
}   C.m
}   NoMethodError: undefined method `m' for C:Class

Yep, that's an issue. Well, you can do this:

module M
  OnIncludeProc = lambda do |klass|
    if klass.class == Module
      def klass.included(k)
        OnIncludeProc.call(k)
      end
    else
      class << klass
        attr_accessor :m
      end
    end
  end

  OnIncludeProc.call(self)

end

Yes, it's uglier. Yes, it conflicts with other #included tricks with
modules
that include the module. It does work, though.

} T.
--Greg
This topic is locked and can not be replied to.