Attribute writers

Discussion in 'Ruby' started by Michael Judge, Jan 6, 2006.

  1. I'm working on a survey system in Ruby and am having a style crisis with
    a simple bit of code that has left me paralyzed and unable to work.

    Respondent data in research surveys is traditionally stored as big
    strings of text like this: "123145992348241110111 11 11 1 1111 3331 1"

    This was a made up example, in the real world however, there's sometimes
    hundreds of thousands of characters in each respondent's data string.
    In the stats and analysis section of my program, I'm working out how to
    access subsets of these strings in an object-oriented way.

    Something like this appeals to me and is very readable:

    if currentRespondent.ascii(0..2) == 123
    currentRespondent.ascii(0..2) = 777
    end

    How would one actually implement this?

    class Respondent
    def ascii(range)
    return @ascii[range]
    end

    # doesn't work
    def ascii(range)=(value)
    @ascii[range] = value
    end
    end

    currentRespondent = Respondent.new

    This is broken code, as Ruby doesn't like me trying to do "def
    a(b)=(c)". There's other ways to accomplish the same purpose (e.g. def
    setter(range,value)) but I'm obsessed with the attribute writer form --
    which is just so dang cool.

    Anyone have some light they could spare?

    Argh. I've been struggling with this for a week!

    --
    Posted via http://www.ruby-forum.com/.
    Michael Judge, Jan 6, 2006
    #1
    1. Advertising

  2. Michael Judge

    Ross Bamford Guest

    On Fri, 06 Jan 2006 09:16:50 -0000, Michael Judge
    <> wrote:

    > I'm working on a survey system in Ruby and am having a style crisis with
    > a simple bit of code that has left me paralyzed and unable to work.
    >
    > Respondent data in research surveys is traditionally stored as big
    > strings of text like this: "123145992348241110111 11 11 1 1111 3331 1"
    >
    > This was a made up example, in the real world however, there's sometimes
    > hundreds of thousands of characters in each respondent's data string.
    > In the stats and analysis section of my program, I'm working out how to
    > access subsets of these strings in an object-oriented way.
    >
    > Something like this appeals to me and is very readable:
    >
    > if currentRespondent.ascii(0..2) == 123
    > currentRespondent.ascii(0..2) = 777
    > end
    >
    > How would one actually implement this?
    >


    Is this any help?

    irb(main):006:0> s = "123145992348241110111 11 11 1 1111 3331 1"
    => "123145992348241110111 11 11 1 1111 3331 1"
    irb(main):007:0> s[0..2]
    => "123"
    irb(main):008:0> s[0..2] = '777'
    => "777"
    irb(main):009:0> s
    => "777145992348241110111 11 11 1 1111 3331 1"

    > class Respondent
    > def ascii(range)
    > return @ascii[range]
    > end
    >
    > # doesn't work
    > def ascii(range)=(value)
    > @ascii[range] = value
    > end
    > end
    >
    > currentRespondent = Respondent.new
    >
    > This is broken code, as Ruby doesn't like me trying to do "def
    > a(b)=(c)". There's other ways to accomplish the same purpose (e.g. def
    > setter(range,value)) but I'm obsessed with the attribute writer form --
    > which is just so dang cool.
    >
    > Anyone have some light they could spare?
    >
    > Argh. I've been struggling with this for a week!
    >


    As far as I'm aware you can't do indexed attribute writers. You can of
    course do:

    def []=(idx, val)
    puts "at #{idx} = #{val}"
    end

    But Ruby won't have it if you prefix the [] with a name.

    What I think I'd do is just remove the writer (for this purpose) and have
    ascii return a String. The you can do:

    if currentRespondent[0..2] == '777'
    currentRespondent.ascii[0..2] = "123"
    end

    (since String's [] is pretty flexible as I showed above)

    But I have a feeling I may have missed something in your question. Is that
    helpful?

    --
    Ross Bamford -
    Ross Bamford, Jan 6, 2006
    #2
    1. Advertising

  3. Michael Judge

    Hal Fulton Guest

    Michael Judge wrote:
    > I'm working on a survey system in Ruby and am having a style crisis with
    > a simple bit of code that has left me paralyzed and unable to work.
    >
    > Respondent data in research surveys is traditionally stored as big
    > strings of text like this: "123145992348241110111 11 11 1 1111 3331 1"
    >
    > This was a made up example, in the real world however, there's sometimes
    > hundreds of thousands of characters in each respondent's data string.
    > In the stats and analysis section of my program, I'm working out how to
    > access subsets of these strings in an object-oriented way.
    >
    > Something like this appeals to me and is very readable:
    >
    > if currentRespondent.ascii(0..2) == 123
    > currentRespondent.ascii(0..2) = 777
    > end
    >
    > How would one actually implement this?


    I'd suggest doing a little metaprogramming and making the final code
    a little easier.

    Here is a very rough outline -- if you like it I can help flesh out
    the details:


    class Respondent

    Fields = { :this => 0..2,
    :that => 3..6,
    :etc => 7..10 }

    Fields.each_pair do |field,range|
    # add a reader basically doing @resp[range]
    # add a writer basically doing @resp[range]=val
    end

    def initialize(str)
    @resp = str
    end

    end


    resp = Respondent.new(whatever)
    if resp.this == Something
    resp.this = SomethingElse
    end
    do_whatever if resp.that != resp.this


    Make sense?

    Hal
    Hal Fulton, Jan 6, 2006
    #3
  4. Michael Judge

    Hal Fulton Guest

    Michael Judge wrote:
    > I'm working on a survey system in Ruby and am having a style crisis with
    > a simple bit of code that has left me paralyzed and unable to work.


    Here is the rest of my solution.

    Does this help?


    Hal


    class Respondent

    Fields = { :this => 0..2,
    :that => 3..6,
    :etc => 7..10 }

    Fields.each_pair do |field,range|
    # add a reader basically doing @resp[range]
    define_method(field) { @resp[range] }
    # add a writer basically doing @resp[range]=val
    define_method((field.to_s+"=").to_sym) do |val|
    raise "Wrong field length" if val.size != range.to_a.size
    @resp[range] = val
    end
    end

    def initialize(str)
    @resp = str
    end

    end


    resp = Respondent.new("abcdefghij")

    puts resp.this
    puts resp.that
    puts resp.etc

    resp.that = "foob"

    puts resp.this
    puts resp.that
    puts resp.etc

    resp.that = "foo" # error!
    Hal Fulton, Jan 6, 2006
    #4
  5. Michael Judge wrote:
    > I'm working on a survey system in Ruby and am having a style crisis
    > with a simple bit of code that has left me paralyzed and unable to
    > work.
    >
    > Respondent data in research surveys is traditionally stored as big
    > strings of text like this: "123145992348241110111 11 11 1 1111 3331
    > 1"
    >
    > This was a made up example, in the real world however, there's
    > sometimes hundreds of thousands of characters in each respondent's
    > data string. In the stats and analysis section of my program, I'm
    > working out how to access subsets of these strings in an
    > object-oriented way.
    >
    > Something like this appeals to me and is very readable:
    >
    > if currentRespondent.ascii(0..2) == 123
    > currentRespondent.ascii(0..2) = 777
    > end
    >
    > How would one actually implement this?
    >
    > class Respondent
    > def ascii(range)
    > return @ascii[range]
    > end
    >
    > # doesn't work
    > def ascii(range)=(value)
    > @ascii[range] = value
    > end
    > end
    >
    > currentRespondent = Respondent.new
    >
    > This is broken code, as Ruby doesn't like me trying to do "def
    > a(b)=(c)". There's other ways to accomplish the same purpose (e.g.
    > def setter(range,value)) but I'm obsessed with the attribute writer
    > form -- which is just so dang cool.
    >
    > Anyone have some light they could spare?
    >
    > Argh. I've been struggling with this for a week!


    Hm, I wouldn't use a string internally at all - at least not if you are
    trying to do it OO. This is what I'd do:

    class Results
    include Enumerable

    def initialize(from_string = "")
    @fields = from_string.split(/\s+/).map {|s| s.to_i}
    end

    def field() @fields end

    def each(&b)
    @fields.each(&b)
    self
    end

    def size() @fields.size end
    def empty?() @fields.empty? end

    def to_str
    @fields.map {|d| d.to_s}.join ' '
    end

    alias :to_s :to_str

    def to_a() @fields end
    end

    You could also simply subclass Array and create appropriate constructors
    and to_s, to_str methods:

    class Results < Array
    def initialize(from_string = "")
    super( from_string.split(/\s+/).map! {|s| s.to_i} )
    end

    def to_str
    map {|d| d.to_s}.join ' '
    end

    alias :to_s :to_str
    end

    HTH

    Kind regards

    robert
    Robert Klemme, Jan 6, 2006
    #5
  6. I want to say thanks for everyone's responses. It took me a couple days
    to go through them all and it helped me learn quite a bit.

    * Robert's example with Enumerable was neat.
    * Hal's use of symbols was cool and interesting.
    * Ross's use of a simple string probably made the most sense for my
    situation

    In the end I decided that simplest was best and subclassed String for
    each respondent: adding save, load, and changed? methods.

    respondents.each do |respondent|
    respondent.open
    respondent[0..2] = 213
    respondent.close
    end


    I've been struggling with object oriented programming for years with
    Perl, and only now (with Ruby) do I feel like I'm starting to really
    "get" it. It's all about creating an interface that makes sense to use
    from a larger perspective. The object hides the gritty details.

    Like with the above interface, I could load the respondent from memory,
    from database, or disk and it wouldn't matter at all to the rest of the
    program. That's sexy and makes my life easier.

    Thanks again for everyone's help. I certainly became a better
    programmer after playing with your examples.

    Regards,

    Mike Judge

    --
    Posted via http://www.ruby-forum.com/.
    Michael Judge, Jan 8, 2006
    #6
    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. Andy Fish

    closing writers and outputstreams

    Andy Fish, Nov 13, 2003, in forum: Java
    Replies:
    1
    Views:
    493
    Thomas Weidenfeller
    Nov 13, 2003
  2. Duke Hamacher

    APIs for SAX writers

    Duke Hamacher, Oct 12, 2004, in forum: XML
    Replies:
    3
    Views:
    376
    Donald Roby
    Oct 14, 2004
  3. M.Kumar

    Advanced C++ writers

    M.Kumar, Aug 1, 2004, in forum: C++
    Replies:
    3
    Views:
    393
    Julie
    Aug 2, 2004
  4. Scott Abel
    Replies:
    0
    Views:
    430
    Scott Abel
    Jul 5, 2006
  5. Stani
    Replies:
    0
    Views:
    279
    Stani
    Jul 15, 2005
Loading...

Share This Page