module Lolita module Configuration # Lolita::Configuration::Field is class that allow to configure fields. # To change behaviour of field you can use these attributes # * name - field name, used to set or get value from related ORM object # * type - can change the way field is shown and how data is formated # * field_set - define field set that field belongs to. See Lolita::Configuration::FieldSet # * nested_in - define field for different Lolita::DBI instance, than given. This is used # to create nested fields in one form for related models. Like user and profile, where in user # form there are fields from profile that can be manipulated when user is changed or created. # * optinos - specific options for different type of fields, see Lolita::Configuration::FieldExtensions for details # * html_options - used to change field HTML output,like class or style etc. # # To define field in ORM class through lolita configuration block # ====Example # lolita do # tab do # field :email # field :user_id, :type=>"string" # field :body do # title "Full text" # html_options :class=>"full_text" # end # end # end class Field extend Lolita::Configuration::Factory @@default_type="string" lolita_accessor :name,:title,:type,:field_set,:nested_for,:options, :html_options,:record,:association attr_reader :dbi,:nested_in def initialize dbi, *args, &block @dbi=dbi begin self.set_attributes(*args) self.instance_eval(&block) if block_given? set_default_values validate rescue Exception=>e unless self.class.temp_object? raise e end end end def value value=nil, &block self.send(:value=,value,&block) if value || block_given? unless @value self.record_value else if @value.is_a?(Proc) @value.call(self) else @value end end end def value=(value=nil,&block) if block_given? @value=block else @value=value end end def name=(value) @name=value.to_sym end def type_name self.type.to_s.downcase end def nested_in=(dbi) unless self.dbi.associations_class_names.include?(dbi.klass.to_s) raise Lolita::ReferenceError, "There is no association between #{self.dbi.klass} and #{dbi.klass}" end raise ArgumentError, "Field can be nested only in Lolita::DBI::Base object." unless dbi.is_a?(Lolita::DBI::Base) @nested_in=dbi end def nested? !self.nested_in.nil? end def nested_in?(dbi_or_class) if dbi_or_class.is_a?(Lolita::DBI::Base) self.nested_in && self.nested_in.klass==dbi_or_class.klass else self.nested_in && self.nested_in.klass==dbi_or_class end end def set_attributes(*args) if args attributes=args.extract_options! self.name=args.first if args.first self.type=args[1] if args[1] attributes.each{|attr,value| self.send(:"#{attr}=",value) } end end # TODO is this useable def record_value #TODO test if self.record self.record.send(self.name.to_sym) else nil end end private def set_default_values set_association set_type self.title||=self.name.to_s.capitalize self.options||={} end def set_type @type=@type.to_s.downcase if @type if @association @type="collection" elsif @type.nil? || @type.to_s=="object" @type=@@default_type end end # Need here because this don't know how to recognize association. # TODO maybe need move to adapter, and it is converted there def set_association #TODO test assoc_name=@name.to_s.gsub(/_id$/,"") @association||=@dbi.reflect_on_association(assoc_name.to_sym) || @dbi.reflect_on_association(assoc_name.pluralize.to_sym) end def validate unless self.name raise Lolita::FieldNameError, "Field must have name." end #FIXME need this validation # if !@value && !@dbi.klass.instance_methods.include?(self.name.to_s) # raise Lolita::FieldNameError, "#{@dbi.klass} must respond to #{self.name} method." # end end end end end