lib/glue/property.rb in glue-0.20.0 vs lib/glue/property.rb in glue-0.21.0
- old
+ new
@@ -19,415 +19,415 @@
# Perhaps a sync is needed in evals (!!!!)
#++
class Property
- # If set to true, perform type checking on property set.
- # Useful when debugging.
+ # If set to true, perform type checking on property set.
+ # Useful when debugging.
- cattr_accessor :type_checking, false
+ cattr_accessor :type_checking, false
- # The symbol of the property.
-
- attr_accessor :symbol
-
- # The class of the property.
-
- attr_accessor :klass
+ # The symbol of the property.
+
+ attr_accessor :symbol
+
+ # The class of the property.
+
+ attr_accessor :klass
- # Additional metadata (like sql declaration, sql index, etc)
- # Here is a list of predefined metadata:
- #
- # [+:reader+]
- # create reader?
- #
- # [+:writer+]
- # create writer?
- #
- # [+:sql_index+]
- # create an sql index for the column this poperty maps to?
- #
- # You can use this mechanism to add your own, custom,
- # metadata.
-
- attr_accessor :meta
+ # Additional metadata (like sql declaration, sql index, etc)
+ # Here is a list of predefined metadata:
+ #
+ # [+:reader+]
+ # create reader?
+ #
+ # [+:writer+]
+ # create writer?
+ #
+ # [+:sql_index+]
+ # create an sql index for the column this poperty maps to?
+ #
+ # You can use this mechanism to add your own, custom,
+ # metadata.
+
+ attr_accessor :meta
- def initialize(symbol, klass, meta = {})
- @symbol, @klass = symbol, klass
- @meta = meta
- end
-
- def ==(other)
- return @symbol == other.symbol
- end
-
- def to_s
- return @symbol.to_s
- end
+ def initialize(symbol, klass, meta = {})
+ @symbol, @klass = symbol, klass
+ @meta = meta
+ end
+
+ def ==(other)
+ return @symbol == other.symbol
+ end
+
+ def to_s
+ return @symbol.to_s
+ end
end
# A collection of Property related utility methods.
module PropertyUtils
- # Add accessors to the properties to the given target
- # (Module or Class). For simplicity also create the
- # meta accessors.
- #
- # [+target+]
- # The target class or module
- #--
- # gmosx: Perhaps we 'll optimize this in the future.
- #++
+ # Add accessors to the properties to the given target
+ # (Module or Class). For simplicity also create the
+ # meta accessors.
+ #
+ # [+target+]
+ # The target class or module
+ #--
+ # gmosx: Perhaps we 'll optimize this in the future.
+ #++
- def self.enchant(target, force = false)
- unless target.instance_variables.include?('@__props')
- # FIXME: should be thread safe here!
- target.instance_variable_set('@__meta', Flexob.new)
- target.instance_variable_set('@__props', SafeArray.new)
+ def self.enchant(target, force = false)
+ unless target.instance_variables.include?('@__props')
+ # FIXME: should be thread safe here!
+ target.instance_variable_set('@__meta', Flexob.new)
+ target.instance_variable_set('@__props', SafeArray.new)
- # gmosx: Ruby surprises and amazes me! We are in the Metaclass
- # when defining methods and attributes so @__props is really
- # a class scoped variable that unlike @@__props is not shared
- # through the hierarchy.
-
- target.module_eval %{
- def self.__props
- @__props
- end
+ # gmosx: Ruby surprises and amazes me! We are in the Metaclass
+ # when defining methods and attributes so @__props is really
+ # a class scoped variable that unlike @@__props is not shared
+ # through the hierarchy.
+
+ target.module_eval %{
+ def self.__props
+ @__props
+ end
- def self.properties
- @__props
- end
+ def self.properties
+ @__props
+ end
- def self.__props=(props)
- @__props = props
- end
-
- def self.__meta
- @__meta
- end
+ def self.__props=(props)
+ @__props = props
+ end
+
+ def self.__meta
+ @__meta
+ end
- def self.__meta=(meta)
- @__meta = meta
- end
-
- def self.metadata
- @__meta
- end
- }
+ def self.__meta=(meta)
+ @__meta = meta
+ end
+
+ def self.metadata
+ @__meta
+ end
+ }
- if target.is_a?(Class)
+ if target.is_a?(Class)
- # Add some extra code to append features to
- # subclasses.
+ # Add some extra code to append features to
+ # subclasses.
- target.module_eval %{
- def self.inherited(child)
- Glue::PropertyUtils.enchant(child)
- Glue::PropertyUtils.copy_props(self, child)
- # gmosx: We have to define @@__props first to avoid
- # reusing the hash from the module. super must stay
- # at the end.
- super
- end
- }
+ target.module_eval %{
+ def self.inherited(child)
+ Glue::PropertyUtils.enchant(child)
+ Glue::PropertyUtils.copy_props(self, child)
+ # gmosx: We have to define @@__props first to avoid
+ # reusing the hash from the module. super must stay
+ # at the end.
+ super
+ end
+ }
- else
+ else
- # Add some extra code for modules to append
- # their features to classes that include it.
-
- target.module_eval %{
- def self.append_features(base)
- # gmosx: We have to define @@__props first to avoid
- # reusing the hash from the module. super must stay
- # at the end.
- Glue::PropertyUtils.copy_features(self, base)
- super
- end
- }
+ # Add some extra code for modules to append
+ # their features to classes that include it.
+
+ target.module_eval %{
+ def self.append_features(base)
+ # gmosx: We have to define @@__props first to avoid
+ # reusing the hash from the module. super must stay
+ # at the end.
+ Glue::PropertyUtils.copy_features(self, base)
+ super
+ end
+ }
- end
- end
- end
+ end
+ end
+ end
- # Copy properties from src (Module or Class) to dest.
-
- def self.copy_props(src, dest)
- src.__props.each do |p|
- add_prop(dest, p)
- end
-
- # copy the metadata.
- src.__meta.each do |k, val|
- if val.is_a?(TrueClass)
- dest.__meta[k] = val
- else
- dest.__meta[k] = val.dup
- end
- # val.each { |v| dest.meta(k, v) } if val
- end
- end
+ # Copy properties from src (Module or Class) to dest.
+
+ def self.copy_props(src, dest)
+ src.__props.each do |p|
+ add_prop(dest, p)
+ end
+
+ # copy the metadata.
+ src.__meta.each do |k, val|
+ if val.is_a?(TrueClass)
+ dest.__meta[k] = val
+ else
+ dest.__meta[k] = val.dup
+ end
+ # val.each { |v| dest.meta(k, v) } if val
+ end
+ end
- # Add the property to the target (Class or Module)
-
- def self.add_prop(target, prop)
- if idx = target.__props.index(prop)
- # override in case of duplicates. Keep the order of the props.
- target.__props[idx] = prop
- else
- target.__props << prop
- end
+ # Add the property to the target (Class or Module)
+
+ def self.add_prop(target, prop)
+ if idx = target.__props.index(prop)
+ # override in case of duplicates. Keep the order of the props.
+ target.__props[idx] = prop
+ else
+ target.__props << prop
+ end
- # Store the property in the :props_and_relations
- # metadata array.
+ # Store the property in the :props_and_relations
+ # metadata array.
- target.meta :props_and_relations, prop
-
- # Precompile the property read/write methods
+ target.meta :props_and_relations, prop
+
+ # Precompile the property read/write methods
- s, klass = prop.symbol, prop.klass
+ s, klass = prop.symbol, prop.klass
- if prop.meta[:reader]
- target.module_eval %{
- def #{s}
- return @#{s}
- end
- }
- end
-
- # gmosx: __force_xxx reuses xxx= to allow for easier
- # overrides.
+ if prop.meta[:reader]
+ target.module_eval %{
+ def #{s}
+ return @#{s}
+ end
+ }
+ end
+
+ # gmosx: __force_xxx reuses xxx= to allow for easier
+ # overrides.
- if prop.meta[:writer]
- target.module_eval %{
- #{prop_setter(prop)}
-
- def __force_#{s}(val)
- self.#{s}=(} + case klass.name
- when Fixnum.name
- "val.to_i()"
- when String.name
- "val.to_s()"
- when Float.name
- "val.to_f()"
- when Time.name
- "Time.parse(val.to_s())"
- when TrueClass.name, FalseClass.name
- "val.to_i() > 0"
- else
- "val"
- end + %{)
- end
- }
- end
- end
+ if prop.meta[:writer]
+ target.module_eval %{
+ #{prop_setter(prop)}
+
+ def __force_#{s}(val)
+ self.#{s}=(} + case klass.name
+ when Fixnum.name
+ "val.to_i()"
+ when String.name
+ "val.to_s()"
+ when Float.name
+ "val.to_f()"
+ when Time.name
+ "Time.parse(val.to_s())"
+ when TrueClass.name, FalseClass.name
+ "val.to_i() > 0"
+ else
+ "val"
+ end + %{)
+ end
+ }
+ end
+ end
- # Generates the property setter code. Can be overriden
- # to support extra functionality (example: markup)
-
- def self.prop_setter(prop)
- s = prop.symbol
+ # Generates the property setter code. Can be overriden
+ # to support extra functionality (example: markup)
+
+ def self.prop_setter(prop)
+ s = prop.symbol
- code = %{
- def #{s}=(val)
- }
-
- if Glue::Property.type_checking
- code << %{
- unless #{prop.klass} == val.class
- raise "Invalid type, expected '#{prop.klass}', is '\#\{val.class\}'."
- end
- }
- end
+ code = %{
+ def #{s}=(val)
+ }
+
+ if Glue::Property.type_checking
+ code << %{
+ unless #{prop.klass} == val.class
+ raise "Invalid type, expected '#{prop.klass}', is '\#\{val.class\}'."
+ end
+ }
+ end
- code << %{
- @#{s} = val
- end
- }
+ code << %{
+ @#{s} = val
+ end
+ }
- return code
- end
+ return code
+ end
- # Get the property metadata for the given symbol.
+ # Get the property metadata for the given symbol.
- def self.get_prop(klass, sym)
- return klass.__props.find { |p| p.symbol == sym }
- end
+ def self.get_prop(klass, sym)
+ return klass.__props.find { |p| p.symbol == sym }
+ end
- # Include meta-language mixins
-
- def self.include_meta_mixins(target)
- target.module_eval %{ include Glue::Validation } if defined?(Glue::Validation)
- # gmosx: TODO, make Og::MetaLanguage equivalent to Validation.
- # target.module_eval %{ extend Og::MetaLanguage } if defined?(Og::MetaLanguage)
- target.module_eval %{ include Glue::Aspects } if defined?(Glue::Aspects)
- target.send(:include, Og::EntityMixin) if defined?(Og::EntityMixin)
- end
+ # Include meta-language mixins
+
+ def self.include_meta_mixins(target)
+ target.module_eval %{ include Glue::Validation } if defined?(Glue::Validation)
+ # gmosx: TODO, make Og::MetaLanguage equivalent to Validation.
+ # target.module_eval %{ extend Og::MetaLanguage } if defined?(Og::MetaLanguage)
+ target.module_eval %{ include Glue::Aspects } if defined?(Glue::Aspects)
+ target.send(:include, Og::EntityMixin) if defined?(Og::EntityMixin)
+ end
- def self.copy_features(this, other)
- Glue::PropertyUtils.enchant(other)
- Glue::PropertyUtils.copy_props(this, other)
- Glue::PropertyUtils.include_meta_mixins(other)
- end
+ def self.copy_features(this, other)
+ Glue::PropertyUtils.enchant(other)
+ Glue::PropertyUtils.copy_props(this, other)
+ Glue::PropertyUtils.include_meta_mixins(other)
+ end
- # Resolves the parameters passed to the propxxx macros
- # to generate the meta, klass and symbols variables. This
- # way the common functionality is factored out.
- #
- # [+params+]
- # The params to resolve.
- # [+one_symbol+]
- # If true, only resolves one symbol (used in prop).
+ # Resolves the parameters passed to the propxxx macros
+ # to generate the meta, klass and symbols variables. This
+ # way the common functionality is factored out.
+ #
+ # [+params+]
+ # The params to resolve.
+ # [+one_symbol+]
+ # If true, only resolves one symbol (used in prop).
- def self.resolve_prop_params(*params)
- meta = {}
- klass = Object
- symbols = []
+ def self.resolve_prop_params(*params)
+ meta = {}
+ klass = Object
+ symbols = []
- for param in params.flatten
- if param.is_a?(Class)
- klass = param
- elsif param.is_a?(Symbol)
- symbols << param
- elsif param.is_a?(TrueClass) or param.is_a?(TrueClass)
- writer = param
- elsif param.is_a?(Hash)
- # the meta hash.
- meta.update(param) { |k, a, b| [a,b].join(' ') }
- else
- raise 'Error when defining property!'
- end
- end
+ for param in params.flatten
+ if param.is_a?(Class)
+ klass = param
+ elsif param.is_a?(Symbol)
+ symbols << param
+ elsif param.is_a?(TrueClass) or param.is_a?(TrueClass)
+ writer = param
+ elsif param.is_a?(Hash)
+ # the meta hash.
+ meta.update(param) { |k, a, b| [a,b].join(' ') }
+ else
+ raise 'Error when defining property!'
+ end
+ end
- raise 'No symbols provided!' if symbols.empty?
+ raise 'No symbols provided!' if symbols.empty?
- return meta, klass, symbols
- end
+ return meta, klass, symbols
+ end
end
end
class Module
- # Define a property (== typed attribute)
- # This works like Ruby's standard attr method, ie creates
- # only one property.
- #
- # Use the prop_reader, prop_writer, prop_accessor methods
- # for multiple properties.
- #
- # === Examples
- #
- # prop String, :name, :sql => "char(32), :sql_index => "name(32)"
- # --> creates only writer.
- # prop Fixnum, :oid, writer = true, :sql => "integer PRIMARY KEY"
- # --> creates reader and writer.
-
- def prop(*params)
- meta, klass, symbols = Glue::PropertyUtils.resolve_prop_params(params)
- symbol = symbols.first
-
- Glue::PropertyUtils.enchant(self)
+ # Define a property (== typed attribute)
+ # This works like Ruby's standard attr method, ie creates
+ # only one property.
+ #
+ # Use the prop_reader, prop_writer, prop_accessor methods
+ # for multiple properties.
+ #
+ # === Examples
+ #
+ # prop String, :name, :sql => "char(32), :sql_index => "name(32)"
+ # --> creates only writer.
+ # prop Fixnum, :oid, writer = true, :sql => "integer PRIMARY KEY"
+ # --> creates reader and writer.
+
+ def prop(*params)
+ meta, klass, symbols = Glue::PropertyUtils.resolve_prop_params(params)
+ symbol = symbols.first
+
+ Glue::PropertyUtils.enchant(self)
- property = Glue::Property.new(symbol, klass, meta)
-
- reader = meta[:reader] || true
- writer = writer || meta[:writer] || false
-
- meta[:reader] = true if meta[:reader].nil?
- if defined?(writer)
- meta[:writer] = writer
- else
- meta[:writer] = true if meta[:writer].nil?
- end
+ property = Glue::Property.new(symbol, klass, meta)
+
+ reader = meta[:reader] || true
+ writer = writer || meta[:writer] || false
+
+ meta[:reader] = true if meta[:reader].nil?
+ if defined?(writer)
+ meta[:writer] = writer
+ else
+ meta[:writer] = true if meta[:writer].nil?
+ end
- Glue::PropertyUtils.add_prop(self, property)
+ Glue::PropertyUtils.add_prop(self, property)
- # gmosx: should be placed AFTER enchant!
-
- Glue::PropertyUtils.include_meta_mixins(self)
- end
+ # gmosx: should be placed AFTER enchant!
+
+ Glue::PropertyUtils.include_meta_mixins(self)
+ end
- # Helper method. Accepts a collection of symbols and generates
- # properties. Only generates reader.
- #
- # Example:
- # prop_reader String, :name, :title, :body, :sql => "char(32)"
-
- def prop_reader(*params)
- meta, klass, symbols = Glue::PropertyUtils.resolve_prop_params(params)
-
- meta[:reader] = true
- meta[:writer] = false
-
- for symbol in symbols
- prop(klass, symbol, meta)
- end
- end
+ # Helper method. Accepts a collection of symbols and generates
+ # properties. Only generates reader.
+ #
+ # Example:
+ # prop_reader String, :name, :title, :body, :sql => "char(32)"
+
+ def prop_reader(*params)
+ meta, klass, symbols = Glue::PropertyUtils.resolve_prop_params(params)
+
+ meta[:reader] = true
+ meta[:writer] = false
+
+ for symbol in symbols
+ prop(klass, symbol, meta)
+ end
+ end
- # Helper method. Accepts a collection of symbols and generates
- # properties. Only generates writer.
- #
- # Example:
- # prop_writer String, :name, :title, :body, :sql => "char(32)"
-
- def prop_writer(*params)
- meta, klass, symbols = Glue::PropertyUtils.resolve_prop_params(params)
+ # Helper method. Accepts a collection of symbols and generates
+ # properties. Only generates writer.
+ #
+ # Example:
+ # prop_writer String, :name, :title, :body, :sql => "char(32)"
+
+ def prop_writer(*params)
+ meta, klass, symbols = Glue::PropertyUtils.resolve_prop_params(params)
- meta[:reader] = false
- meta[:writer] = true
-
- for symbol in symbols
- prop(klass, symbol, meta)
- end
- end
+ meta[:reader] = false
+ meta[:writer] = true
+
+ for symbol in symbols
+ prop(klass, symbol, meta)
+ end
+ end
- # Helper method. Accepts a collection of symbols and generates
- # properties. Generates reader and writer.
- #
- # Example:
- # prop_accessor String, :name, :title, :body, :sql => "char(32)"
-
- def prop_accessor(*params)
- meta, klass, symbols = Glue::PropertyUtils.resolve_prop_params(params)
+ # Helper method. Accepts a collection of symbols and generates
+ # properties. Generates reader and writer.
+ #
+ # Example:
+ # prop_accessor String, :name, :title, :body, :sql => "char(32)"
+
+ def prop_accessor(*params)
+ meta, klass, symbols = Glue::PropertyUtils.resolve_prop_params(params)
- meta[:reader] = true
- meta[:writer] = true
-
- for symbol in symbols
- prop(klass, symbol, meta)
- end
- end
- alias_method :property, :prop_accessor
-
- # Attach metadata.
- # Guard against duplicates, no need to keep order.
- # This method uses closures :)
- #--
- # gmosx: crappy implementation, recode.
- #++
-
- def meta(key, *val)
- Glue::PropertyUtils.enchant(self)
+ meta[:reader] = true
+ meta[:writer] = true
+
+ for symbol in symbols
+ prop(klass, symbol, meta)
+ end
+ end
+ alias_method :property, :prop_accessor
+
+ # Attach metadata.
+ # Guard against duplicates, no need to keep order.
+ # This method uses closures :)
+ #--
+ # gmosx: crappy implementation, recode.
+ #++
+
+ def meta(key, *val)
+ Glue::PropertyUtils.enchant(self)
- if val.empty?
- self.module_eval %{
- @__meta[key] = true
- }
- else
- val = val.first if val.size == 1
-
- self.module_eval %{
- @__meta[key] ||= []
- @__meta[key].delete_if { |v| val == v }
- @__meta[key] << val
- }
- end
- end
+ if val.empty?
+ self.module_eval %{
+ @__meta[key] = true
+ }
+ else
+ val = val.first if val.size == 1
+
+ self.module_eval %{
+ @__meta[key] ||= []
+ @__meta[key].delete_if { |v| val == v }
+ @__meta[key] << val
+ }
+ end
+ end
end
# * George Moschovitis <gm@navel.gr>