require 'spiderfw/model/type' # The class representing a BaseModel element. module Spider; module Model class Element attr_reader :name, :attributes def initialize(name, type, attributes={}) @name = name @type = type @attributes = attributes end # The element type, as per the second argument passed to the Model::BaseModel.element method. # This may be different from #model. def type @type end # The actual model used to represent the association. Will return the #association_type or the #type. def model return nil unless model? return association_type || type end # True if the element model is a junction (for a many to many relationship). def junction? @attributes[:junction] end # The model used for the association. The BaseModel will automatically create a junction model for # many to many relationships, unless one is supplied with the :through attribute. This will be set # for n <-> n relationships, and will be nil otherwise. def association_type @association_type ||= @attributes[:association_type] end # True if the element defines a 1|n -> n association. def multiple? @attributes[:multiple] end # True if the element must have a value. def required? @attributes[:required] end # True if no two model instances can have the same value for the element. def unique? @attributes[:unique] end # True if the element defines an association to another model. def model? return @is_model if @is_model != nil @is_model = (type < Spider::Model::BaseModel || association_type) end # True if the element is integrated from another one. (See also BaseModel#integrate). # Example: # class Address < BaseModel # element :street, String # element :area_code, String # end # class Person < BaseModel # element :name, String # element :address, Address # integrate :address # end # Person.elements[:street].integrated? => true # Person.elements[:street].integrated_from => :address # Person.elements[:street].integrated_from_element => :street def integrated? return @is_integrated if @is_integrated != nil @is_integrated = @attributes[:integrated_from] end # If the element is integrated, the element from which it is taken. See also #integrated?. def integrated_from @attributes[:integrated_from] end # If the element is integrated, the element corresponding to this in the model corresponding to # the #integrated_from element. See also #integrated?. def integrated_from_element @attributes[:integrated_from_element] end # True if the element is a primary key. def primary_key? @primary_key ||= @attributes[:primary_key] end # True if the element is read only. def read_only? @attributes[:read_only] end # The reverse element in the relationship to another model. def reverse @attributes[:reverse] end # True if the element has a reverse, and it is not multiple (i.e., the relationship is 1 <-> 1|n) def has_single_reverse? return true if @attributes[:reverse] && !model.elements[@attributes[:reverse]].multiple? end # True if the element model is an InlineModel def inline? @attributes[:inline] end # True if the element type has been extended by passing a block to Model::BaseModel.element def extended? @attributes[:extended] end # True if the :hidden attribute is set. def hidden? @attributes[:hidden] end # True if only the defining BaseModel holds references to the associated def owned? @attributes[:owned] end # True if the element is generated on save by the model or the mapper. def autogenerated? return (@attributes[:auto] || @attributes[:autoincrement]) end # Named association. def association @attributes[:association] end # Label. Will use the :label attribute, or return the name split by '_' with each word capitalized. def label return @attributes[:label] ? _(@attributes[:label]) : Inflector.underscore_to_upcasefirst(@name.to_s) end def to_s return "Element '#{@name.to_s}'" end # Storage for the element's #model. def storage return nil unless model? return self.mapper.storage end # Mapper for the element's #model. def mapper return nil unless model? return self.model.mapper end # The element's Condition, if any. If a condition is set with the :condition attribute, # the association to the element's model will be filtered by it. def condition cond = attributes[:condition] cond = Condition.new(cond) if (cond && !cond.is_a?(Condition)) return cond end # Lazy attribute. (See #lazy_groups). def lazy attributes[:lazy] end # True if lazy attribute is set. (See #lazy_groups). def lazy? attributes[:lazy] end # Returns the lazy groups this elements is in, as set by BaseModel#element with the :lazy attributes. # Lazy groups are used by the mapper to determine which elements to autoload: # when an element in a lazy group is accessed, all the elements in the same group(s) will be loaded. def lazy_groups return nil unless attributes[:lazy] && attributes[:lazy] != true return attributes[:lazy].is_a?(Array) ? attributes[:lazy] : [attributes[:lazy]] end def clone self.class.new(@name, @type, @attributes.clone) end # def queryset # return nil unless model? # set_model = @attributes[:queryset_model] ? @attributes[:queryset_model] : type # set = QuerySet.new(set_model) # set.query.condition = @attributes[:condition] if @attributes[:condition] # if (@attributes[:request]) # set.query.request = @attributes[:request] # else # set_model.elements.each do |name, el| # set.query.request[name] = true unless el.model? # end # end # return set # end # # Clones the current model, detaching it from the original class and allowing to modify # # it (adding other elements) # def extend_model # return if @extended_model # @original_model = @type # class_name = @type.name # @type = Class.new(BaseModel) # params = {} # if (@attributes[:association] == :multiple_choice) # params[:hide_elements] = true # params[:hide_integrated] = false # else # params[:hide_integrated] = true # end # @type.extend_model(@original_model, params) # if (@attributes[:model_name]) # new_name = @original_model.parent_module.name.to_s+'::'+@attributes[:model_name].to_s # else # new_name = @original_model.name+'.'+@name.to_s # end # @type.instance_variable_set(:"@name", new_name) # proxied_type = @original_model # @type.instance_eval do # def name # @name # end # # @proxied_type = proxied_type # # def storage # # # it has only added elements, they will be merged in by the element owner # # require 'spiderfw/model/storage/null_storage' # # return Spider::Model::Storage::NullStorage.new # # end # def mapper # require 'spiderfw/model/mappers/proxy_mapper' # return @mapper ||= Spider::Model::Mappers::ProxyMapper.new(self, @proxied_type) # end # end # @extended_model = true # end end end; end