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 module Field class Base include Lolita::Builder @@default_type = :string lolita_accessor :name,:title,:field_set, :nested_form,:nested_for,:options, :html_options attr_reader :dbi,:nested_in attr_accessor :dbi_field def initialize dbi,name,*args, &block @dbi=dbi self.name = name options = args ? args.extract_options! : {} type = args[0] self.type = type || @@default_type self.set_attributes(options) if block_given? self.instance_eval(&block) end set_default_values validate end def title(value=nil) @title=value if value @title||=@dbi.klass.human_attribute_name(@name) @title end def type value=nil @type=value.to_s.underscore if value @type end def type=(value) @type=value ? value.to_s.underscore : nil end def name=(value) @name= value ? value.to_sym : nil 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 if !dbi.is_a?(Lolita::DBI::Base) && !dbi.class.to_s.match(/Lolita::Adapter/) raise ArgumentError, "Field can be nested only in Lolita::DBI::Base object." end @nested_in=dbi end def nested? !self.nested_in.nil? end def nested_in?(dbi_or_class) if dbi_or_class.respond_to?(:klass) 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(attributes) excluded_keys = [:name,:type] attributes.each{|attr,value| unless excluded_keys.include?(attr.to_sym) self.send(:"#{attr}=",value) end } end def find_dbi_field @dbi_field ||= self.dbi.fields.detect{|field| field.name.to_s == @name.to_s || (field.association && field.association.name.to_s == @name.to_s) } end private def builder_local_variable_name :field end def set_default_values self.options||={} self.html_options ||= {} @html_options[:class] = if @html_options[:class] "#{@html_options[:class]} #{self.type}" else self.type.to_s end end def validate unless self.name raise Lolita::FieldNameError, "Field must have name." end end end end end end