Class best way for getters?

i have a class "HFSFile" initialized by a parsed string
after initialization i get a Hash @attr with keys like :name,
:creation_date etc...

what is the best way to write a getter for each individual key ?

the obvious would be for example :

def name
  @attr[:name]
end

or does exist a more clever way to get all of those getters ?
(same name for the getter that the key symbol)

another way :

def [a_key]
return nil unless @attr.keys.include?(a_key)
return @attr[a_key]
end

something like that ?

···

--
« Je suis sûr que la gaîté ambiguë du drôle de garçon qui a bu du
whisky et fait le zèbre sur un vieux caïman à Noël dans le cañon, a été
bénéfique à l'âme du poète blême, ainsi qu'à son cœur & cætera ! »
(© Jean-Paul Blanc)

def ( a_key )
    return @attr[a_key]
  end

but yes, that's a good option. By default hashes return nil when you
dereference a key that doesn't exist, so unless you've changed that
(by creating it with Hash.new(...) or similar) then you can leave that
first line out.

Ben

···

2010/5/15 Une Bévue <unbewusst.sein@google.com.invalid>:

def [a_key]
return nil unless @attr.keys.include?(a_key)
return @attr[a_key]
end

depends (I know this is too long, but anyway)

if no new keys can be added to @atts it is quite easy
http://pastie.org/961685

if keys can be added one might do this
http://pastie.org/961691

But it starts to become really interesting if methods can disappear.
Of course we could just use method_missing without defining a getter,
but having an observer on @atts that would add remove methods on the
receiver would be quite an elegant approach.
Unfamiliar with built in observers I have made a sketch of how I would
like to implement such a functionality here
http://pastie.org/961738

HTH
R.

···

2010/5/15 Une Bévue <unbewusst.sein@google.com.invalid>:

--
« Je suis sûr que la gaîté ambiguë du drôle de garçon qui a bu du
whisky et fait le zèbre sur un vieux caïman à Noël dans le cañon, a été
bénéfique à l'âme du poète blême, ainsi qu'à son cœur & cætera ! »
(© Jean-Paul Blanc)

--
The best way to predict the future is to invent it.
-- Alan Kay

i have a class "HFSFile" initialized by a parsed string
after initialization i get a Hash @attr with keys like :name,
:creation_date etc...

what is the best way to write a getter for each individual key ?

the obvious would be for example :

def name
  @attr[:name]
end

or does exist a more clever way to get all of those getters ?
(same name for the getter that the key symbol)

You can use this:

irb(main):017:0> o = Object.new
=> #<Object:0xa1feb18>
irb(main):018:0> ha.each {|k,| class<<o;self;end.class_eval {attr k}}
=> {:foo=>2, :bar=>3}
irb(main):019:0> o.foo
=> nil
irb(main):020:0> o.bar
=> nil
irb(main):021:0>

Or define method_missing to fetch data from the Hash.

def method_missing(s,*a,&b)
   if a.empty? && @attr.has_key? s
     @attr[s]
   else
     super
   end
end

I would not do it though as it is not too fast.

another way :

def [a_key]
return nil unless @attr.keys.include?(a_key)
return @attr[a_key]
end

something like that ?

You could use OpenStruct

irb(main):001:0> require 'ostruct'
=> true
irb(main):002:0> ha = {:foo => 2, :bar => 3}
=> {:foo=>2, :bar=>3}
irb(main):003:0> os = OpenStruct.new(ha)
=> #<OpenStruct foo=2, bar=3>
irb(main):004:0> os.foo
=> 2
irb(main):005:0> os.bar
=> 3
irb(main):006:0>

But the question remains, whether this is a good idea. Think about it: either you do not know keys beforehand. Then you can only use generic algorithms (i.e. query all keys and print their values). Or you do know keys beforehand and then you want to explicitly access individual keys (e.g. obj.name).

If you know the names you can use Struct, e.g.

irb(main):011:0> St = Struct.new :foo, :bar
=> St
irb(main):012:0> s = St[*S.members.map{|m| ha[m]}]
=> #<struct St foo=2, bar=3>
irb(main):013:0> s.foo
=> 2
irb(main):014:0> s.bar
=> 3
irb(main):015:0>

Kind regards

  robert

···

On 05/15/2010 04:42 PM, Une Bévue wrote:

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

I wrote a gem to handle this kind of situation... have a look at

class HFSFile < Valuable
  has_value :name
end

would create a getter, setter, and accept an attributes hash like...

HFSFile.new(:name => 'fu')

or just

file = HFSFile.new

file.name

=> nil

unless you want to specify a default...

class HFSFile < Valuable
  has_value :name, :default => 'anything'
end

file = HSFile.new
file.name

=> 'anything'

jw

···

On May 15, 9:42 am, unbewusst.s...@google.com.invalid (Une Bévue) wrote:

i have a class "HFSFile" initialized by a parsed string
after initialization i get a Hash @attr with keys like :name,
:creation_date etc...

what is the best way to write a getter for each individual key ?

the obvious would be for example :

def name
@attr[:name]
end

or does exist a more clever way to get all of those getters ?
(same name for the getter that the key symbol)

another way :

def [a_key]
return nil unless @attr.keys.include?(a_key)
return @attr[a_key]
end

something like that ?

--
« Je suis sûr que la gaîté ambiguë du drôle de garçon qui a bu du
whisky et fait le zèbre sur un vieux caïman à Noël dans le cañon, a été
bénéfique à l'âme du poète blême, ainsi qu'à son c½ur & cætera ! »
(© Jean-Paul Blanc)

You can also remove the "return" and leave it like this:

  def (key)
    @attr[key]
  end

···

On Sat, May 15, 2010 at 12:27 PM, Ben Bleything <ben@bleything.net> wrote:

2010/5/15 Une Bévue <unbewusst.sein@google.com.invalid>:

def [a_key]
return nil unless @attr.keys.include?(a_key)
return @attr[a_key]
end

def ( a_key )
return @attr[a_key]
end

but yes, that's a good option. By default hashes return nil when you
dereference a key that doesn't exist, so unless you've changed that
(by creating it with Hash.new(...) or similar) then you can leave that
first line out.

Ben

--
Luis Parravicini
http://ktulu.com.ar/blog/

First, thanks a lot for your answer !

Thats's my case, i know in advance all of the keywords.
In fact i get a String as input, something like :
"name: the_name_value, modification date: the date & time in default
lang, ..."
and i populate a hash :
h[:name] = the_name_value
h[:modification_date] = DateTime of("the date & time in default lang")
et c...

Then, I'll have a look on Struct.

···

Robert Klemme <shortcutter@googlemail.com> wrote:

But the question remains, whether this is a good idea. Think about it:
either you do not know keys beforehand. Then you can only use generic
algorithms (i.e. query all keys and print their values). Or you do know
keys beforehand and then you want to explicitly access individual keys
(e.g. obj.name).

If you know the names you can use Struct, e.g.

--
« Je suis sûr que la gaîté ambiguë du drôle de garçon qui a bu du
whisky et fait le zèbre sur un vieux caïman à Noël dans le cañon, a été
bénéfique à l'âme du poète blême, ainsi qu'à son cœur & cætera ! »
(© Jean-Paul Blanc)

fine, thanks a lot, it was what i was looking for !

···

Johnathon Wright <jw@mustmodify.com> wrote:

I wrote a gem to handle this kind of situation... have a look at
http://valuable.mustmodify.com/

class HFSFile < Valuable
  has_value :name
end

would create a getter, setter, and accept an attributes hash like...

HFSFile.new(:name => 'fu')

or just

file = HFSFile.new
>> file.name
=> nil

unless you want to specify a default...

class HFSFile < Valuable
  has_value :name, :default => 'anything'
end

>> file = HSFile.new
>> file.name
=> 'anything'

--
« L'amour, c'est quand on rencontre quelqu'un
   qui vous donne de vos nouvelles. »
   (André Breton)

i had a first look and i wonder how to use it in order to get a new
class "HFSFile" which can be used such a way, using OpenStruct :

hfsf = HFSFile.new(a_path)
p hfsf.path
p hfsf.modification_date
...

because i do :

class HFSFile
  def initialize(a_path)
    @attr = {}
    @attr[:path] = a_path
    @attr[:modification_date] = ...
    ...
    @data = OpenStruct.new(@attr)
  end
  def attributes
    return @attr
  end
  def data
    return @data
  end
end

what is ennoying me in that case is in that i do have to use :
hfsf = HFSFile.new(a_path)
p hfsf.data.path
p hfsf.data.modification_date
------^^^^^^-----------------
...
where i would prefer having self being an OpenStruct (extension ?) but i
think i can't do :
self = OpenStruct.new(@attr)

???

···

Une Bévue <unbewusst.sein@google.com.invalid> wrote:

Then, I'll have a look on Struct.

--
« Je suis sûr que la gaîté ambiguë du drôle de garçon qui a bu du
whisky et fait le zèbre sur un vieux caïman à Noël dans le cañon, a été
bénéfique à l'âme du poète blême, ainsi qu'à son cœur & cætera ! »
(© Jean-Paul Blanc)

no, but you could forward to the OpenStruct.new( @attr )

require 'forwardable'

class ...
   extend Forwardable

attr = @attr = OpenStruct.new(@attr)
extend( Module::new{
  extend Forwardable
  def_delegators( ;@attr, *attr.keys )
})

···

2010/5/16 Une Bévue <unbewusst.sein@google.com.invalid>:

self = OpenStruct.new(@attr)

There is no need for delegation here: you only need one instance and the way through Hash or OpenStruct seems a detour here - at least if the set of fields is fixed. Then you can do

HFSFile = Struct.new :name, :modification_date do
   def self.parse(s)
     o = new
     s.scan /\s*([\w\s]+):\s*([^,]+),?/ do
       field = normalize($1)
       val = $2.strip
       o[field] = val
     end
     o
   end

   def self.normalize(k)
     k.strip.gsub /\s+/, '_'
   end
end

Now you can do:

irb(main):033:0> HFSFile.parse "name: the_name_value, modification date: the date"
=> #<struct HFSFile name="the_name_value", modification_date="the date">
irb(main):034:0>

You'll get a NameError if you parse fields from the String that are not present in the struct. If you need to cope with additional fields you need to either ignore them or store them in an additional Hash member. You can use HFSFile.members (or just members in method parse()) to test for field presence.

Kind regards

  robert

···

On 16.05.2010 09:25, Une Bévue wrote:

Une Bévue<unbewusst.sein@google.com.invalid> wrote:

Then, I'll have a look on Struct.

i had a first look and i wonder how to use it in order to get a new
class "HFSFile" which can be used such a way, using OpenStruct :

hfsf = HFSFile.new(a_path)
p hfsf.path
p hfsf.modification_date
...

because i do :

class HFSFile
   def initialize(a_path)
     @attr = {}
     @attr[:path] = a_path
     @attr[:modification_date] = ...
     ...
     @data = OpenStruct.new(@attr)
   end
   def attributes
     return @attr
   end
   def data
     return @data
   end
end

what is ennoying me in that case is in that i do have to use :
hfsf = HFSFile.new(a_path)
p hfsf.data.path
p hfsf.data.modification_date
------^^^^^^-----------------
...
where i would prefer having self being an OpenStruct (extension ?) but i
think i can't do :
self = OpenStruct.new(@attr)

???

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

OK, i see it's really fine of u helping that way !

in fact i do have a number of keys (18).

and a new prob arroses : i'd like to add new keys depending on the file
type (the input is a file path).

if the file path refers to an alias file (Mac OS X) one of the keys ( a
boolean) give me that allready but, in that case, i'd like to add a new
key :alias_broken (the original item of the alias has been deleted) i
suppose i could do that easily :

self.alias_broken = true|false

and in ruby generally the methods returning a boolean are named with an
ending "?", a good habit.

obiously, i know when this is a boolean because the string value is
"true"|"false".

···

Robert Klemme <shortcutter@googlemail.com> wrote:

There is no need for delegation here: you only need one instance and the
way through Hash or OpenStruct seems a detour here - at least if the set
of fields is fixed. Then you can do

HFSFile = Struct.new :name, :modification_date do
   def self.parse(s)
     o = new
     s.scan /\s*([\w\s]+):\s*([^,]+),?/ do
       field = normalize($1)
       val = $2.strip
       o[field] = val
     end
     o
   end

   def self.normalize(k)
     k.strip.gsub /\s+/, '_'
   end
end

Now you can do:

irb(main):033:0> HFSFile.parse "name: the_name_value, modification date:
the date"
=> #<struct HFSFile name="the_name_value", modification_date="the date">
irb(main):034:0>

You'll get a NameError if you parse fields from the String that are not
present in the struct. If you need to cope with additional fields you
need to either ignore them or store them in an additional Hash member.
You can use HFSFile.members (or just members in method parse()) to test
for field presence.

--
« Je suis sûr que la gaîté ambiguë du drôle de garçon qui a bu du
whisky et fait le zèbre sur un vieux caïman à Noël dans le cañon, a été
bénéfique à l'âme du poète blême, ainsi qu'à son cœur & cætera ! »
(© Jean-Paul Blanc)