Forum: Ruby struct equivalent with rails-y keyword args hash?

Ce8b03e5750097942c58e12b46724312?d=identicon&s=25 Giles Bowkett (Guest)
on 2008-01-09 06:03
(Received via mailing list)
I have some code which uses Structs. I'd rather use keyword args. I'm
generating data objects to store MIDI note data, and it's kind of
hideously unreadable:

Note.new(2, 43, 0.25, 127, now += interval)

This would be much nicer:

Note.new(:channel => 2,
         :note => 43,
         :duration => 0.25,
         :velocity => 127,
         :time => now += interval)

(God I wish I could configure bloody Gmail to use monospaced fonts.)

Anyway, is there an easy equivalent to Struct which, instead of taking
stuff in sequence, takes a Hash of options, in a Rails-y style? Like a
HashStruct? Does Facets have such a thing, maybe haps? If not, is the
basic code for Struct in Ruby, and/or is a class like Struct but with
Hashes easy to build?

--
Giles Bowkett

Podcast: http://hollywoodgrit.blogspot.com
Blog: http://gilesbowkett.blogspot.com
Portfolio: http://www.gilesgoatboy.org
Tumblelog: http://giles.tumblr.com
1bac2e65d64faf472cf2ebc94f0f5ee0?d=identicon&s=25 ara howard (Guest)
on 2008-01-09 06:10
(Received via mailing list)
On Jan 8, 2008, at 10:02 PM, Giles Bowkett wrote:

> Anyway, is there an easy equivalent to Struct which, instead of taking
> stuff in sequence, takes a Hash of options, in a Rails-y style? Like a
> HashStruct? Does Facets have such a thing, maybe haps? If not, is the
> basic code for Struct in Ruby, and/or is a class like Struct but with
> Hashes easy to build?



i use this alot in rails projects:

require 'attributes'

class OpenObject
   alias_method "__inspect__", "inspect"
   alias_method "__to_s__", "to_s"
   alias_method "__instance_eval__", "instance_eval"

     instance_methods.each{|m| undef_method m unless m[%r/__/]}

   alias_method "instance_eval", "__instance_eval__"
   alias_method "inspect", "__inspect__"
   alias_method "to_s", "__to_s__"

   def method_missing m, *a, &b
     super if(a.empty? and b.nil?)
     m = m.to_s
     setter = m.delete!('=') || a.first || b
     if setter
       if a.empty?
         __attribute__ m, &b
       else
         __attribute__ m => a.shift
       end
     else
       nil
     end
   end

   def configure kvs = {}, &b
     kvs.each{|k,v| __attribute__ k => v}
     __instance_eval__ &b if b
   end
   alias_method "initialize", "configure"
   alias_method "eval", "configure"

   def __singleton_class__ &b
     sc =
       class << self
         self
       end
     b ? sc.module_eval(&b) : sc
   end

   def extend *a, &b
     if b
       __singleton_class__ &b
       self
     else
       super
     end
   end
   alias_method "extending", "extend"

   def attributes *a, &b
     __attributes__ *a, &b
   end

   def to_hash
     __attributes__.inject(Hash.new){|h,a| h.update a => __send__(a)}
   end

   def as_query
     require 'cgi'
     e = lambda{|x| CGI.escape x.to_s}
     to_hash.map{|k,v| "#{ e[k] }=#{ e[v] }"}.join("&")
   end

   def respond_to? *a, &b
     true
   end
end
def openobject(*a, &b) OpenObject.new(*a, &b) end

class Object
   def Open *a, &b
     OpenObject.new(*a, &b)
   end
end



it's all that and so much more.


a @ http://codeforpeople.com/
86e33dee4a89a8879a26487051c216a8?d=identicon&s=25 Michael Fellinger (Guest)
on 2008-01-09 06:38
(Received via mailing list)
On Jan 9, 2008 2:02 PM, Giles Bowkett <gilesb@gmail.com> wrote:
>          :duration => 0.25,
>          :velocity => 127,
>          :time => now += interval)
>
> (God I wish I could configure bloody Gmail to use monospaced fonts.)
>
> Anyway, is there an easy equivalent to Struct which, instead of taking
> stuff in sequence, takes a Hash of options, in a Rails-y style? Like a
> HashStruct? Does Facets have such a thing, maybe haps? If not, is the
> basic code for Struct in Ruby, and/or is a class like Struct but with
> Hashes easy to build?

One possibility, method for Note only:

manveru@pi ~ % irb
class Note < Struct.new(:channel, :note, :duration, :velocity, :time)
  def self.create(hash)
    new(*hash.values_at(*members.map{|m| m.to_sym}))
  end
end
# nil
Note.create(:channel => 1, :note => 2, :duration => 3, :velocity => 3,
:time => Time.now)
# #<struct Note channel=1, note=2, duration=3, velocity=3, time=Wed
Jan 09 14:36:21 +0900 2008>


Second one, method for all Structs:

manveru@pi ~ % irb
class Struct
  def self.create(hash)
    new(*hash.values_at(*members.map{|m| m.to_sym}))
  end
end
# nil
Note = Struct.new(:channel, :note, :duration, :velocity, :time)
# Note
Note.create(:channel => 1, :note => 2, :duration => 3, :velocity => 3,
:time => Time.now)
# #<struct Note channel=1, note=2, duration=3, velocity=3, time=Wed
Jan 09 14:37:20 +0900 2008>
E0d864d9677f3c1482a20152b7cac0e2?d=identicon&s=25 Robert Klemme (Guest)
on 2008-01-09 13:11
(Received via mailing list)
2008/1/9, Giles Bowkett <gilesb@gmail.com>:
>          :duration => 0.25,
>          :velocity => 127,
>          :time => now += interval)

Then just use that form.

Kind regards

robert
4299e35bacef054df40583da2d51edea?d=identicon&s=25 James Gray (bbazzarrakk)
on 2008-01-09 15:14
(Received via mailing list)
On Jan 8, 2008, at 11:02 PM, Giles Bowkett wrote:

>         :duration => 0.25,
>         :velocity => 127,
>         :time => now += interval)

One way is:

require "ostruct"

now      = Time.now
interval = 5
note     = OpenStruct.new( :channel  => 2,
                            :note     => 43,
                            :duration => 0.25,
                            :velocity => 127,
                            :time     => now + interval )
note.channel   # => 2
note.velocity  # => 127

__END__

James Edward Gray II
Ce8b03e5750097942c58e12b46724312?d=identicon&s=25 Giles Bowkett (Guest)
on 2008-01-09 19:15
(Received via mailing list)
several good suggestions but I think this is the one I'll run with:

> # Note
> Note.create(:channel => 1, :note => 2, :duration => 3, :velocity => 3,
> :time => Time.now)
> # #<struct Note channel=1, note=2, duration=3, velocity=3, time=Wed
> Jan 09 14:37:20 +0900 2008>

Ara's seems the most robust, but with so much going on and no specs it
makes me antsy; the OpenStruct looks good too but I want classes as
well as instances.

--
Giles Bowkett

Podcast: http://hollywoodgrit.blogspot.com
Blog: http://gilesbowkett.blogspot.com
Portfolio: http://www.gilesgoatboy.org
Tumblelog: http://giles.tumblr.com
E0d864d9677f3c1482a20152b7cac0e2?d=identicon&s=25 Robert Klemme (Guest)
on 2008-01-09 19:31
(Received via mailing list)
On 09.01.2008 19:13, Giles Bowkett wrote:
>> # nil
>
Folks, why do you run around implementing something?  The desired
behavior *is already present* with Struct.

Cheers

  robert
Ce8b03e5750097942c58e12b46724312?d=identicon&s=25 Giles Bowkett (Guest)
on 2008-01-09 19:38
(Received via mailing list)
> Folks, why do you run around implementing something?  The desired
> behavior *is already present* with Struct.

I tried your code example, and it didn't work.

class Note < Struct.new(:channel) ; end

Note.new(:channel => 2)
Note.channel # => "{:channel => 2}"

It just stores the hash.

--
Giles Bowkett

Podcast: http://hollywoodgrit.blogspot.com
Blog: http://gilesbowkett.blogspot.com
Portfolio: http://www.gilesgoatboy.org
Tumblelog: http://giles.tumblr.com
E0d864d9677f3c1482a20152b7cac0e2?d=identicon&s=25 Robert Klemme (Guest)
on 2008-01-10 08:25
(Received via mailing list)
On 09.01.2008 19:37, Giles Bowkett wrote:
> It just stores the hash.
Argh!!  You are right, I am wrong.  I'm sorry, should have payed
attention more closely to the output of #inspect.  I think I'll better
shut up now.

Cheers

  robert
E0d864d9677f3c1482a20152b7cac0e2?d=identicon&s=25 Robert Klemme (Guest)
on 2008-01-10 09:49
(Received via mailing list)
2008/1/10, Robert Klemme <shortcutter@googlemail.com>:
> >
> > It just stores the hash.
>
> Argh!!  You are right, I am wrong.  I'm sorry, should have payed
> attention more closely to the output of #inspect.  I think I'll better
> shut up now.

Hm, could not resist...  Here is another variant:

Note = Struct.new(:channel, :note, :duration, :velocity, :time) do
  def self.create(hash)
    new(*members.map {|me| hash[me.to_sym]})
  end
end

irb(main):016:0* now = interval = 1
=> 1
irb(main):017:0> n = Note.new(:channel => 2,
irb(main):018:1*         :note => 43,
irb(main):019:1*         :duration => 0.25,
irb(main):020:1*         :velocity => 127,
irb(main):021:1*         :time => now += interval)
=> #<struct Note channel={:channel=>2, :note=>43, :duration=>0.25,
:velocity=>127, :time=>2}, note=nil, duration=nil, ve
locity=nil, time=nil>
irb(main):022:0>

:-)

robert
D969a347edb3054c711946c0ed07ce2f?d=identicon&s=25 ronen barzel (ronen)
on 2011-11-18 13:19
This thread has been idle for a while, but in case anybody's still
watching...

I've long wanted the same thing as was originally requested, and I too
couldn't find a gem anywhere that has it.  So I wrote one.  See

      http://rubygems.org/gems/key_struct

Hope it's useful to others.  Hope if I've re-invented the wheel somebody
will point me to an existing gem.
Please log in before posting. Registration is free and takes only a minute.
Existing account

NEW: Do you have a Google/GoogleMail, Yahoo or Facebook account? No registration required!
Log in with Google account | Log in with Yahoo account | Log in with Facebook account
No account? Register here.