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 ?

2010/5/15 Une Bévue [email protected]:

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

def
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

On Sat, May 15, 2010 at 12:27 PM, Ben B. [email protected]
wrote:

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

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

def
@attr[key]
end

2010/5/15 Une Bévue [email protected]:
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.

Robert K. [email protected] 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.

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.

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

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)
=> #
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]}]
=> #
irb(main):013:0> s.foo
=> 2
irb(main):014:0> s.bar
=> 3
irb(main):015:0>

Kind regards

robert

2010/5/16 Une Bévue [email protected]:

self = OpenStruct.new(@attr)
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 )
})

Une Bévue [email protected] 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)

???

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


end
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)

???

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

Robert K. [email protected] wrote:

   o[field] = val

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.

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”.

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, [email protected] (Une Bévue)

Johnathon W. [email protected] wrote:

end

file = HSFile.new
file.name
=> ‘anything’

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