NAME fattr.rb INSTALL gem install fattrs URIS http://github.com/ahoward/fattr http://rubyforge.org/projects/codeforpeople/ http://codeforpeople.com/ SYNOPSIS fattr.rb is a "fatter attr" for ruby the implementation of fattr.rb borrows many of the best ideas from the metakoans.rb ruby quiz http://www.rubyquiz.com/quiz67.html in particular the solutions of Christian Neukirchen and Florian Gross along with concepts from my original traits.rb lib key features provided by fattrs are - ability to specify default values for attrs and definition time. values can be literal objects or blocks, which are evaluated in the context of self to initialize the variable - classes remember which fattrs they've defined and this information is available to client code - a whole suite of methods is defined by calls to #fattrs including getter, setter, query (var?) and banger (var! - which forces re-initialization from the default value/block) - ability to define multiple fattrs at once using key => value pairs - fast lookup of whether or not a class has defined a certain fattr - fattrs can be defined on objects on a per singleton basis - getters acts as setters if an argument is given to them - block caching, calling an fattr with a block sets the instance variable to that block - shortcuts for adding class/module level fattrs - class inheritable attributes all this in 156 lines of code SAMPLES <========< samples/a.rb >========> ~ > cat samples/a.rb # # basic usage is like attr, but note that fattr defines a suite of methods # require 'fattr' class C fattr 'a' end c = C.new c.a = 42 p c.a #=> 42 p 'forty-two' if c.a? #=> 'forty-two' # # fattrs works on object too # o = Object.new o.fattr 'answer' => 42 p o.answer #=> 42 ~ > ruby samples/a.rb 42 "forty-two" 42 <========< samples/b.rb >========> ~ > cat samples/b.rb # # default values may be given either directly or as a block which will be # evaluated in the context of self. in both cases (value or block) the # default is set only once and only if needed - it's a lazy evaluation. the # 'banger' method can be used to re-initialize a variable at any point whether # or not it's already been initialized. # require 'fattr' class C fattr :a => 42 fattr(:b){ Float a } end c = C.new p c.a #=> 42 p c.b #=> 42.0 c.a = 43 p c.a #=> 43 c.a! p c.a #=> 42 ~ > ruby samples/b.rb 42 42.0 43 42 <========< samples/c.rb >========> ~ > cat samples/c.rb # # multiple name=>default pairs can be given # require 'fattr' class C fattrs 'x' => 0b101000, 'y' => 0b10 end c = C.new z = c.x + c.y p z #=> 42 ~ > ruby samples/c.rb 42 <========< samples/d.rb >========> ~ > cat samples/d.rb # # a nice feature is that all fattrs are enumerated in the class. this, # combined with the fact that the getter method is defined so as to delegate # to the setter when an argument is given, means bulk initialization and/or # fattr traversal is very easy. # require 'fattr' class C fattrs %w( x y z ) def fattrs self.class.fattrs end def initialize fattrs.each_with_index{|a,i| send a, i} end def to_hash fattrs.inject({}){|h,a| h.update a => send(a)} end def inspect to_hash.inspect end end c = C.new p c.fattrs p c c.x 'forty-two' p c.x ~ > ruby samples/d.rb ["x", "y", "z"] {"x"=>0, "y"=>1, "z"=>2} "forty-two" <========< samples/e.rb >========> ~ > cat samples/e.rb # # my favourite element of fattrs is that getters can also be setters. # this allows incredibly clean looking code like # require 'fattr' class Config fattrs %w( host port) def initialize(&block) instance_eval &block end end conf = Config.new{ host 'codeforpeople.org' port 80 } p conf ~ > ruby samples/e.rb samples/e.rb:7: Use RbConfig instead of obsolete and deprecated Config. samples/e.rb:7:in `<main>': Config is not a class (TypeError) <========< samples/f.rb >========> ~ > cat samples/f.rb # # of course fattrs works as well at class/module level as at instance # level # require 'fattr' module Logging Level_names = { 0 => 'INFO', # ... 42 => 'DEBUG', } class << Logging fattr 'level' => 42 fattr('level_name'){ Level_names[level] } end end p Logging.level p Logging.level_name ~ > ruby samples/f.rb 42 "DEBUG" <========< samples/g.rb >========> ~ > cat samples/g.rb # # you can add class/module fattrs the 'normal' way or using the provided # shortcut method # require 'fattr' class C class << C fattr 'a' => 4 end Fattr 'b' => 2 end p [ C.a, C.b ].join ~ > ruby samples/g.rb "42" <========< samples/h.rb >========> ~ > cat samples/h.rb # # class variable inheritance is supported simply # require 'fattr' class A Fattr :x, :default => 42, :inheritable => true end class B < A end class C < B end p C.x #=> 42 A.x = 42.0 B.x = 'forty-two' p A.x #=> 42.0 p B.x #=> 'forty-two' p C.x #=> 42 C.x! # re-initialize from closest ancestor (B) p A.x #=> 42.0 p B.x #=> 'forty-two' p C.x #=> 'forty-two' ~ > ruby samples/h.rb 42 42.0 "forty-two" 42 42.0 "forty-two" "forty-two" <========< samples/i.rb >========> ~ > cat samples/i.rb # # you can retrieve all fattrs as a list, or a hash with values included # require 'fattr' class C fattr(:a) fattr(:b){ a.to_f } end o = C.new o.fattr(:c) o.fattr(:d){ self.c.upcase } o.a = 42 o.c = 'forty-two' p o.fattrs.to_hash #=> {"a"=>42, "b"=>42.0, "c"=>"forty-two", "d"=>"FORTY-TWO"} p o.fattrs #=> ["c", "d"] ~ > ruby samples/i.rb {"a"=>42, "b"=>42.0, "c"=>"forty-two", "d"=>"FORTY-TWO"} ["c", "d"] HISTORY 2.3.0 support for "object.fattrs.to_hash" 2.0.0: support class/module inheritable attributes 1.1.0: ruby19 testing. move to github. 1.0.2: added Fattr shortcut for adding class/module level fattrs class C Fattr 'children' => [] def C.inherited other (children << other).uniq! super end end class B < C end p C.children #=> B 1.0.0: port from attributes.rb retaining all the same features of that version of attributes.rb
Project
fattr
a "fatter attr" for ruby
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
Pull Requests
Development
Dependencies
Project Readme