Quick module include question.

Discussion in 'Ruby' started by Chris Eskow, Oct 9, 2005.

  1. Chris Eskow

    Chris Eskow Guest

    ------=_Part_18429_13903458.1128823698427
    Content-Type: text/plain; charset=ISO-8859-1
    Content-Transfer-Encoding: quoted-printable
    Content-Disposition: inline

    Hi, I'm semi-new to Ruby and I have a question.

    I have a class, World, that basically looks like this:

    class World

    # Convenience method that takes one or more symbols and defines an
    # accessor+mutator method for each one.
    def self.att *atts
    atts.each do |att|
    class_eval %{
    def #{att} val=3Dnil
    val =3D=3D nil ? @#{att} : @#{att} =3D val
    end
    }
    end
    end

    # Now I can define some attributes.
    att :title, :author

    # Other methods here...

    end

    ...which allows me to do things like this:

    world.title "My Title"
    world.title #=3D> "My Title"

    The att method creates an instance method that sets an attribute if
    an argument is given, or returns it if no arguments are given. This
    is nice and all, but now I want to use this att method with another
    class. Since it's very short I could just copy and paste it, but I'd
    rather use a module, Attributes, that I could include into any class:

    class World
    include Attributes
    att :title, :author
    end

    class Thing
    include Attributes
    att :name, :desc
    end

    I'm having trouble getting it to work, however. I have a feeling I
    have to use metaclasses or something, but I'm not quite sure how I'm
    suppose to do that. Everything I tried resulted in "undefined method
    `att'" errors. Any thoughts?

    Chris

    ------=_Part_18429_13903458.1128823698427--
     
    Chris Eskow, Oct 9, 2005
    #1
    1. Advertising

  2. En r=E9ponse =E0 Chris Eskow :
    > Hi, I'm semi-new to Ruby and I have a question.
    >=20
    > I have a class, World, that basically looks like this:
    >=20
    > class World
    >=20
    > # Convenience method that takes one or more symbols and defines an
    > # accessor+mutator method for each one.
    > def self.att *atts
    > atts.each do |att|
    > class_eval %{
    > def #{att} val=3Dnil
    > val =3D=3D nil ? @#{att} : @#{att} =3D val
    > end
    > }
    > end
    > end
    >=20
    > # Now I can define some attributes.
    > att :title, :author
    >=20


    This looks a lot like the traits library. You might want to look at it.

    >=20
    > The att method creates an instance method that sets an attribute if
    > an argument is given, or returns it if no arguments are given. This
    > is nice and all, but now I want to use this att method with another
    > class. Since it's very short I could just copy and paste it, but I'd
    > rather use a module, Attributes, that I could include into any class:
    >=20
    > class World
    > include Attributes
    > att :title, :author
    > end
    >=20
    > class Thing
    > include Attributes
    > att :name, :desc
    > end
    >=20
    > I'm having trouble getting it to work, however. I have a feeling I
    > have to use metaclasses or something, but I'm not quite sure how I'm
    > suppose to do that. Everything I tried resulted in "undefined method
    > `att'" errors. Any thoughts?
    >=20


    What you need is the att method to become a class method rather than an=20
    instance method, so that you can use it like one uses attr_accessor. To=20
    do that, simply use "extend" instead of "include":
    class World
    extend Attributes
    att :title, :author
    end

    Another way that uses metaclasses indeed is to include your module in=20
    the metaclass of the class:
    class OtherWorld
    class <<self
    include Attributes
    end
    att :title, :author
    end

    I'm not aware of any difference between those two ways.
    --=20
    Christophe Grandsire.

    http://rainbow.conlang.free.fr

    You need a straight mind to invent a twisted conlang.
     
    Christophe Grandsire, Oct 9, 2005
    #2
    1. Advertising

  3. Chris Eskow <> wrote:
    > Hi, I'm semi-new to Ruby and I have a question.
    >
    > I have a class, World, that basically looks like this:
    >
    > class World
    >
    > # Convenience method that takes one or more symbols and defines an
    > # accessor+mutator method for each one.
    > def self.att *atts
    > atts.each do |att|
    > class_eval %{
    > def #{att} val=nil
    > val == nil ? @#{att} : @#{att} = val
    > end
    > }
    > end
    > end
    >
    > # Now I can define some attributes.
    > att :title, :author
    >
    > # Other methods here...
    >
    > end
    >
    > ..which allows me to do things like this:
    >
    > world.title "My Title"
    > world.title #=> "My Title"
    >
    > The att method creates an instance method that sets an attribute if
    > an argument is given, or returns it if no arguments are given. This
    > is nice and all, but now I want to use this att method with another
    > class. Since it's very short I could just copy and paste it, but I'd
    > rather use a module, Attributes, that I could include into any class:
    >
    > class World
    > include Attributes
    > att :title, :author
    > end
    >
    > class Thing
    > include Attributes
    > att :name, :desc
    > end
    >
    > I'm having trouble getting it to work, however. I have a feeling I
    > have to use metaclasses or something, but I'm not quite sure how I'm
    > suppose to do that. Everything I tried resulted in "undefined method
    > `att'" errors. Any thoughts?


    You're basically reimplementing attr_accessor, attr_reader, attr_writer and
    attr. These come predefined and you can even use them on an instance level:

    >> o=Object.new

    => #<Object:0x10192dc8>
    >> class<<o
    >> attr_accessor :name
    >> end

    => nil
    >> o.name="foo"

    => "foo"
    >> o.name

    => "foo"

    Of course you can wrap that in an instance method:

    >> class Object
    >> def att(*a)
    >> cl=class<<self;self;end
    >> a.each {|at| cl.send:)attr_accessor,at)}
    >> end
    >> end

    => nil
    >> o=Object.new

    => #<Object:0x10164808>
    >> o.att :name

    => [:name]
    >> o.name="foo"

    => "foo"
    >> o.name

    => "foo"

    Also note that if you use OpenStruct you don't even need to define
    attributes - you can just use them.

    >> require 'ostruct'

    => true
    >> o=OpenStruct.new

    => <OpenStruct>
    >> o.name="bar"

    => "bar"
    >> o.name

    => "bar"


    Kind regards

    robert
     
    Robert Klemme, Oct 9, 2005
    #3
  4. Chris Eskow

    Chris Eskow Guest

    ------=_Part_27645_15304138.1128911235631
    Content-Type: text/plain; charset=ISO-8859-1
    Content-Transfer-Encoding: quoted-printable
    Content-Disposition: inline

    To Christophe Grandsire:

    I tried both suggestions but I still can't get them to work. Could you
    elaborate futher?

    To Robert Klemme:

    I know about attr_*, but this is a bit different: instead of using
    "attr=3Dval" to set an attribute, this uses "attr val" (val being an
    argument). I'd rather do it this way because the seemingly miniscule
    improvement in looks gets a lot nicer looking in the particular instance
    that I'm using it in:

    world do
    title "A Title"
    author "An Author"
    end

    world is a method that creates a World object and instance_evals the block.
    Now, if I were to just use "attr_accessor :title, :author", I would have to
    do this:

    world do
    @title =3D "A Title"
    @author =3D "An Author"
    end

    That's a lot uglier and a bit more obtrusive. If you haven't guessed
    already, I'm making a text adventure. I'd like the story-definition DSL to
    look as clean as possible. Yes, I know about the recent Ruby Quiz entry, bu=
    t
    I'm trying not to look at the solutions' code too much or else I'd be
    tempted to copy... ;-)

    Chris

    ------=_Part_27645_15304138.1128911235631--
     
    Chris Eskow, Oct 10, 2005
    #4
    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. JKop
    Replies:
    11
    Views:
    894
  2. Andreas Bogenberger
    Replies:
    3
    Views:
    932
    Andreas Bogenberger
    Feb 22, 2008
  3. Brian Buckley

    include Module in another Module

    Brian Buckley, Feb 19, 2007, in forum: Ruby
    Replies:
    3
    Views:
    126
    Marcello Barnaba
    Feb 20, 2007
  4. Iñaki Baz Castillo
    Replies:
    3
    Views:
    154
    Iñaki Baz Castillo
    May 2, 2008
  5. Ittay Dror

    include module in module

    Ittay Dror, Dec 8, 2008, in forum: Ruby
    Replies:
    2
    Views:
    101
    Robert Klemme
    Dec 8, 2008
Loading...

Share This Page