NAME
attributes.rb
INSTALL
gem install attributes
URIS
http://rubyforge.org/projects/codeforpeople/
http://codeforpeople.com/lib/ruby
SYNOPSIS
attributes.rb provides a set of attr_* like method with several user
friendly additions. attributes.rb is similar to the traits.rb
package but
sacrifices a few features for simplicity of implementation.
the implementation of attributes.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 N. and Florian
Gross along
with concepts from the original traits lib
key features provided by attributes 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 attributes they've defined and this
information is
available to client code
- a whole suite of methods is defined by calls to #attributes
including
getter, setter, query (var?) and banger (var! - which forces
re-initialization from the default value)
- ability to define multiple attributes at once using key =>
value pairs
- fast lookup of whether or not a class has defined a certain
attribute
- attributes can be defined on objects on a per singleton basis
as well
- getters acts as setters if an argument is given to them
all this in < 100 lines of code
HISTORY
5.0.0
- added support for block caching. for example
- simple block caching:
class Filter
attribute :process
end
(( filter = Filter.new )).process{|line| line.upcase}
lines.each do |line|
p filter.process.call(line)
end
- using block caching to delay block evaluation/class-factory:
module MigrationDSL
attribute :migration
def migration_class
model = self
Class.new(::ActiveRecord::Migration) do
singleton_class =
class << self
self
end
singleton_class.module_eval{ attribute :model =>
model }
singleton_class.module_eval &model.migration
end
end
end
class Table < ActiveRecord::Base
extend MigrationDSL
end
class Jobs < Table
migration do
def up
create_table model.table_name, :primary_key =>
model.primary_key do |t|
t.column ‘rockin’, :text
end
end
def down
create_table model.table_name
end
end
end
...
JobsMigration = Jobs.migration_class
4.1.0
- 4.0.0 introduced a bug where a query (foo?) would not
initialize a var -
4.1.0 fixes that
4.0.0
- removed dependancy on, and bundle of, pervasives
- faster. as fast as normal method definition.
- faster lookup for MyClass.attributes.include?(‘foobar’)
3.7.0
small patch to use ‘instance_variable_defined?’ instead of defined?
keyword
3.5.0
migrated to a pervasives based impl to attributes should work on
any
object - even blankslate objects
3.3.0
moved to an instance variable-less model using an module level
closure for
the attributes list
SAMPLES
<========< samples/a.rb >========>
~ > cat samples/a.rb
#
# basic usage is like attr, but note that attribute defines a
suite of methods
#
require ‘attributes’
class C
attribute 'a'
end
c = C.new
c.a = 42
p c.a #=> 42
p 'forty-two' if c.a? #=> 'forty-two'
#
# attributes works on object too
#
o = Object.new
o.attribute '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 ‘attributes’
class C
attribute :a => 42
attribute(: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 values may by given, plain names and key/val pairs
may be mixed.
#
require ‘attributes’
class C
attributes 'x', 'y' => 0b101000, 'z' => 0b10
end
c = C.new
c.x = c.y + c.z
p c.x #=> 42
~ > ruby samples/c.rb
42
<========< samples/d.rb >========>
~ > cat samples/d.rb
#
# a nice feature is that all attributes 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
# attribute traversal is very easy.
#
require ‘attributes’
class C
attributes %w( x y z )
def attributes
self.class.attributes
end
def initialize
attributes.each_with_index{|a,i| send a, i}
end
def to_hash
attributes.inject({}){|h,a| h.update a => send(a)}
end
def inspect
to_hash.inspect
end
end
c = C.new
p c.attributes
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 attributes is that getters can also be
setters.
# this allows incredibly clean looking code like
#
require ‘attributes’
class Config
attributes %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
#<Config:0x1fb58 @port=80, @host="codeforpeople.org">
<========< samples/f.rb >========>
~ > cat samples/f.rb
#
# of course attributes works as well at class/module level as at
instance
# level
#
require ‘attributes’
module Logging
Level_names = {
0 => 'INFO',
# ...
42 => 'DEBUG',
}
class << self
attribute 'level' => 42
attribute('level_name'){ Level_names[level] }
end
end
p Logging.level
p Logging.level_name
~ > ruby samples/f.rb
42
"DEBUG"
enjoy.