module Lolita module Configuration module Field # Fields with Array is used to # * create select for belongs_to association or # * has_and_belongs_to_many field for selecting all associated objects or # * for polymorphic belongs_to associations # ==Polymorphic builder (:polymorphic) # Create two select boxes one of all related classes and second with related records for that class. # Related classes can have polymorphic_select_name instance method, that is used to populate second # select with visible values by default it calles text_method. It will fallback first content column. Class should respond to one # of these. class Array < Lolita::Configuration::Field::Base lolita_accessor :conditions,:text_method,:value_method,:find_options,:association,:include_blank lolita_accessor :related_classes def initialize dbi,name,*args, &block @include_blank=true super self.find_dbi_field unless self.dbi_field @association ||= self.dbi_field ? self.dbi_field.association : detect_association self.builder = detect_builder unless @builder self.name = recognize_real_name end def options_for_select=(value=nil) @options_for_select=value end def options_for_select value=nil, &block @options_for_select=value || block if value || block_given? @options_for_select end # Collect values for array type field. # Uses text_method for content. By default it search for # first _String_ type field in DB. Uses value_method for value, # by default it it is id. Use conditions or # find_options for advanced search. When find_options # is used, than conditions is ignored. def association_values(record = nil) #TODO test @association_values=if options_for_select options_for_select elsif @association && @association.polymorphic? polymorphic_association_values(record) elsif @association klass=@association.klass options=@find_options || {} options[:conditions]||=@conditions options_array(klass.find(:all,options)) else [] end @association_values end # Collect values for polymorphic association, you may pass # * :klass - class that's records are used # * :record - record class that has polymorphic association. It is used to call to detect related object class. def polymorphic_association_values(options={}) options ||= {} options[:klass] ||= options[:record] && options[:record].send(self.name) ? options[:record].send(self.name).class : nil if options[:klass] options_array(options[:klass].all) else [] end end def options_array(collection) klass = collection.last ? collection.last.class : nil collection.map{|r| [r.send(current_text_method(klass)),r.send(current_value_method)] } end def current_text_method(klass) @text_method || default_text_method(klass) end def current_value_method @value_method || :id end # used in views for shorter accessing to values def view_values(view) record = view.send(:tab_form).object values = association_values(record) if values.respond_to?(:call) values.call(view) else association_values(record) end end def detect_builder if @association if @association.polymorphic? "polymorphic" elsif @association.macro == :many_to_many "habtm" else "select" end else "select" end end def detect_association unless @association dbi.associations[self.name.to_sym] else @association end end def polymorphic_classes if @related_classes @related_classes.map do |klass| [klass.constantize.model_name.human, klass.to_s] end end end def recognize_real_name if @association && !@association.polymorphic? && @association.macro == :one self.name = @association.key else @name end end private def default_text_method(klass) assoc_dbi=Lolita::DBI::Base.create(klass) rescue nil if assoc_dbi field=assoc_dbi.fields.detect{|f| f.type.to_s=="string"} if field field.name else raise Lolita::FieldTypeError, %^ Can't find any content field in #{assoc_dbi.klass}. Use text_method in #{klass} to set one. ^ end else warn("Not a ORM class (#{klass.inspect})") end end end end end end