lib/lolita/configuration/field/array.rb in lolita-3.1.17 vs lib/lolita/configuration/field/array.rb in lolita-3.1.18
- old
+ new
@@ -1,85 +1,170 @@
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 <em>polymorphic_select_name</em> instance method, that is used to populate second
+ # select with visible values by default it calles <em>text_method</em>. 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_type,:include_blank
+ lolita_accessor :conditions,:text_method,:value_method,:find_options,:association,:include_blank
+ lolita_accessor :related_classes
- def initialize *args,&block
- @type="array"
- self.builder="select"
+ def initialize dbi,name,*args, &block
@include_blank=true
super
- set_association_type
+ 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
+ # For details see Lolita::Configuration::Search
+ def search *args, &block
+ if (args && args.any?) || block_given?
+ @search = create_search(*args,&block)
+ end
+ @search
end
- def options_for_select value=nil, &block
- @options_for_select=value || block if value || block_given?
- @options_for_select
+ def create_search *args, &block
+ Lolita::Configuration::Search.new(Lolita::DBI::Base.create(@association.klass),*args,&block)
end
+ def values=(value=nil)
+ @values=value
+ end
+
+ def values value=nil, &block
+ @values=value || block if value || block_given?
+ @values
+ end
+
# Collect values for array type field.
# Uses <code>text_method</code> for content. By default it search for
# first _String_ type field in DB. Uses <code>value_method</code> for value,
# by default it it is <code>id</code>. Use <code>conditions</code> or
# <code>find_options</code> for advanced search. When <code>find_options</code>
# is used, than <code>conditions</code> is ignored.
- def association_values() #TODO test
- @association_values=if options_for_select
- options_for_select
+ def association_values(record = nil) #TODO test
+ @association_values=if values
+ values
+ elsif search
+ search.run("")
+ elsif @association && @association.polymorphic?
+ polymorphic_association_values(record)
elsif @association
- klass=@dbi.association_class_name(@association).camelize.constantize
- current_text_method=@text_method || default_text_method(klass)
- current_value_method=@value_method || :id
+ klass=@association.klass
options=@find_options || {}
options[:conditions]||=@conditions
-
- klass.find(:all,options).map{|r|
- [r.send(current_text_method),r.send(current_value_method)]
- }
+ options_array(klass.find(:all,options))
else
[]
end
@association_values
end
+ # Collect values for polymorphic association, you may pass
+ # * <tt>:klass</tt> - class that's records are used
+ # * <tt>:record</tt> - 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)
- values = association_values
+ record = view.send(:tab_form).object
+ values = association_values(record)
if values.respond_to?(:call)
values.call(view)
else
- association_values
+ association_values(record)
end
end
- private
+ def detect_builder
+ if @association
+ if @association.polymorphic?
+ "polymorphic"
+ elsif @association.macro == :many_to_many
+ "autocomplete"
+ else
+ "select"
+ end
+ else
+ "select"
+ end
+ end
- def set_association_type #TODO test
- if @association
- @association_type||=(@dbi.association_macro(@association) || :one)
- else
- @association_type||=:one
+ def detect_association
+ unless @association
+ dbi.associations[self.name.to_sym]
+ else
+ @association
+ end
end
- end
- def default_text_method(klass)
- assoc_dbi=Lolita::DBI::Base.new(klass)
- field=assoc_dbi.fields.detect{|f| f[:type].downcase=="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.
- ^
+ 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
end