app/models/effective/effective_datatable/resource.rb in effective_datatables-3.6.3 vs app/models/effective/effective_datatable/resource.rb in effective_datatables-3.7.0

- old
+ new

@@ -6,13 +6,17 @@ def admin_namespace? controller_namespace == 'admin' end def controller_namespace - @attributes[:_n] + @attributes[:namespace] end + def association_macros + [:belongs_to, :belongs_to_polymorphic, :has_many, :has_and_belongs_to_many, :has_one] + end + private def load_effective_resource! @effective_resource = if active_record_collection? Effective::Resource.new(collection_class, namespace: controller_namespace) @@ -21,103 +25,133 @@ # This looks at all the columns and figures out the as: def load_resource! load_effective_resource! - if active_record_collection? - columns.each do |name, opts| + load_active_record_collection! + load_active_record_array_collection! + load_array_collection! - # col 'comments.title' - if name.kind_of?(String) && name.include?('.') - raise "invalid datatables column '#{name}'. the joined syntax only supports one dot." if name.scan(/\./).count > 1 + load_resource_columns! + load_resource_belongs_tos! + load_resource_search! + end - (associated, field) = name.split('.').first(2) + def load_effective_resource! + @effective_resource = if active_record_collection? + Effective::Resource.new(collection_class, namespace: controller_namespace) + end + end - unless resource.macros.include?(resource.sql_type(associated)) - raise "invalid datatables column '#{name}'. unable to find '#{name.split('.').first}' association on '#{resource}'." - end + def load_active_record_collection! + return unless active_record_collection? - joins_values = (collection.joins_values + collection.left_outer_joins_values) + columns.each do |name, opts| + # col 'comments.title' + if name.kind_of?(String) && name.include?('.') + raise "invalid datatables column '#{name}'. the joined syntax only supports one dot." if name.scan(/\./).count > 1 - unless joins_values.include?(associated.to_sym) - raise "your datatables collection must .joins(:#{associated}) or .left_outer_joins(:#{associated}) to work with the joined syntax" - end + (associated, field) = name.split('.').first(2) - opts[:resource] = Effective::Resource.new(resource.associated(associated), namespace: controller_namespace) + unless association_macros.include?(effective_resource.sql_type(associated)) + raise "invalid datatables column '#{name}'. unable to find '#{name.split('.').first}' association on '#{effective_resource}'." + end - if opts[:resource].column(field) - opts[:as] ||= opts[:resource].sql_type(field) - opts[:as] = :integer if opts[:resource].sql_type(field) == :belongs_to && field.end_with?('_id') - opts[:sql_column] = opts[:resource].sql_column(field) if opts[:sql_column].nil? + joins_values = (collection.joins_values + collection.left_outer_joins_values) - opts[:resource].sort_column = field - opts[:resource].search_columns = field - end + unless joins_values.include?(associated.to_sym) + raise "your datatables collection must .joins(:#{associated}) or .left_outer_joins(:#{associated}) to work with the joined syntax" + end - opts[:resource_field] = field + opts[:resource] = Effective::Resource.new(effective_resource.associated(associated), namespace: controller_namespace) - next + if opts[:resource].column(field) + opts[:as] ||= opts[:resource].sql_type(field) + opts[:as] = :integer if opts[:resource].sql_type(field) == :belongs_to && field.end_with?('_id') + opts[:sql_column] = opts[:resource].sql_column(field) if opts[:sql_column].nil? + + opts[:resource].sort_column = field + opts[:resource].search_columns = field end - # Regular fields - opts[:as] ||= resource.sql_type(name) - opts[:sql_column] = resource.sql_column(name) if opts[:sql_column].nil? + opts[:resource_field] = field - case opts[:as] - when *resource.macros - opts[:resource] ||= Effective::Resource.new(resource.associated(name), namespace: controller_namespace) + next + end + + # Regular fields + opts[:as] ||= effective_resource.sql_type(name) + opts[:sql_column] = effective_resource.sql_column(name) if opts[:sql_column].nil? + + case opts[:as] + when *association_macros + opts[:resource] ||= Effective::Resource.new(effective_resource.associated(name), namespace: controller_namespace) + opts[:sql_column] = name if opts[:sql_column].nil? + when Class + if opts[:as].ancestors.include?(ActiveRecord::Base) + opts[:resource] = Effective::Resource.new(opts[:as], namespace: controller_namespace) + opts[:as] = :resource opts[:sql_column] = name if opts[:sql_column].nil? - when Class - if opts[:as].ancestors.include?(ActiveRecord::Base) - opts[:resource] = Effective::Resource.new(opts[:as], namespace: controller_namespace) - opts[:as] = :resource - opts[:sql_column] = name if opts[:sql_column].nil? - end - when :effective_addresses - opts[:resource] = Effective::Resource.new(resource.associated(name), namespace: controller_namespace) - opts[:sql_column] = :effective_addresses - when :effective_roles - opts[:sql_column] = :effective_roles - when :string # This is the fallback - # Anything that doesn't belong to the model or the sql table, we assume is a SELECT SUM|AVG|RANK() as fancy - opts[:sql_as_column] = true if (resource.table && resource.column(name).blank?) end + when :effective_addresses + opts[:resource] = Effective::Resource.new(effective_resource.associated(name), namespace: controller_namespace) + opts[:sql_column] = :effective_addresses + when :effective_roles + opts[:sql_column] = :effective_roles + when :string # This is the fallback + # Anything that doesn't belong to the model or the sql table, we assume is a SELECT SUM|AVG|RANK() as fancy + opts[:sql_as_column] = true if (effective_resource.table && effective_resource.column(name).blank?) + end - if opts[:sql_column].present? && AGGREGATE_SQL_FUNCTIONS.any? { |str| opts[:sql_column].to_s.start_with?(str) } - opts[:sql_as_column] = true - end + if opts[:sql_column].present? && AGGREGATE_SQL_FUNCTIONS.any? { |str| opts[:sql_column].to_s.start_with?(str) } + opts[:sql_as_column] = true end end + end - if array_collection? - row = collection.first + def load_active_record_array_collection! + return unless active_record_array_collection? + end - columns.each do |name, opts| - if opts[:as].kind_of?(Class) && opts[:as].ancestors.include?(ActiveRecord::Base) - opts[:resource] = Effective::Resource.new(opts[:as], namespace: controller_namespace) + def load_array_collection! + return unless array_collection? + + row = collection.first + + columns.each do |name, opts| + if opts[:as].kind_of?(Class) && opts[:as].ancestors.include?(ActiveRecord::Base) + opts[:resource] = Effective::Resource.new(opts[:as], namespace: controller_namespace) + opts[:as] = :resource + elsif opts[:as] == nil && row.present? + if (value = Array(row[opts[:index]]).first).kind_of?(ActiveRecord::Base) + opts[:resource] = Effective::Resource.new(value, namespace: controller_namespace) opts[:as] = :resource - elsif opts[:as] == nil - if (value = Array(row[opts[:index]]).first).kind_of?(ActiveRecord::Base) - opts[:resource] = Effective::Resource.new(value, namespace: controller_namespace) - opts[:as] = :resource - end end end end + end + def load_resource_columns! columns.each do |name, opts| opts[:as] ||= :string opts[:as] = :email if (opts[:as] == :string && name.to_s.end_with?('email')) + if opts[:action] + opts[:resource] ||= effective_resource + end + if opts[:resource] && !opts[:resource_field] && opts[:as] != :effective_addresses opts[:partial] ||= '/effective/datatables/resource_column' end - opts[:col_class] = "col-#{opts[:as]} col-#{name.to_s.parameterize} #{opts[:col_class]}".strip + opts[:col_class] = [ + "col-#{opts[:as]}", + "col-#{name.to_s.parameterize}", + ('colvis-default' if opts[:visible]), + opts[:col_class].presence + ].compact.join(' ') end - - load_resource_search! end def load_resource_search! columns.each do |name, opts| @@ -125,54 +159,75 @@ when false opts[:search] = { as: :null }; next when Symbol opts[:search] = { as: opts[:search] } when Array, ActiveRecord::Relation - opts[:search] = { collection: opts[:search] } + opts[:search] = { as: :select, collection: opts[:search] } when Hash # Nothing else raise "column #{name} unexpected search value" end search = opts[:search] + # Parameterize collection if search[:collection].kind_of?(ActiveRecord::Relation) search[:collection] = search[:collection].map { |obj| [obj.to_s, obj.to_param] } elsif search[:collection].kind_of?(Array) && search[:collection].first.kind_of?(ActiveRecord::Base) search[:collection] = search[:collection].map { |obj| [obj.to_s, obj.to_param] } - elsif search[:collection].kind_of?(Array) - search[:collection].each { |obj| obj[1] = 'nil' if obj[1] == nil } - elsif search[:collection].kind_of?(Hash) - search[:collection].each { |k, v| search[:collection][k] = 'nil' if v == nil } end + search[:as] ||= :select if search.key?(:collection) + search[:fuzzy] ||= true unless search.key?(:fuzzy) search[:value] ||= search.delete(:selected) if search.key?(:selected) - search[:as] ||= :select if (search.key?(:collection) && opts[:as] != :belongs_to_polymorphic) + # Merge with defaults + search_resource = [opts[:resource], effective_resource, fallback_effective_resource].compact + search_resource = search_resource.find { |res| res.klass.present? } || search_resource.first - search[:fuzzy] = true unless search.key?(:fuzzy) - if array_collection? && opts[:resource].present? - search.reverse_merge!(resource.search_form_field(name, collection.first[opts[:index]])) + search.reverse_merge!(search_resource.search_form_field(name, collection.first[opts[:index]])) elsif search[:as] != :string - search.reverse_merge!(resource.search_form_field(name, opts[:as])) + search.reverse_merge!(search_resource.search_form_field(name, opts[:as])) end + + # Assign default include_null + if search[:as] == :select && !search.key?(:include_null) + search[:include_null] = true + end end end - def apply_belongs_to_attributes! + def load_resource_belongs_tos! return unless active_record_collection? changed = attributes.select do |attribute, value| attribute = attribute.to_s next unless attribute.ends_with?('_id') associated = attribute.gsub(/_id\z/, '').to_sym # Replace last _id - next unless columns[associated] && columns[associated][:as] == :belongs_to - @_collection = @_collection.where(attribute => value) - columns.delete(associated) + next unless columns[associated] + + if columns[associated][:as] == :belongs_to + if @_collection_apply_belongs_to && !@_collection.where_values_hash.include?(attribute) + @_collection = @_collection.where(attribute => value) + end + + columns.delete(associated) + elsif columns[associated][:as] == :belongs_to_polymorphic + associated_type = attributes["#{associated}_type".to_sym] || raise("Expected #{associated}_type attribute to be present when #{associated}_id is present on a polymorphic belongs to") + + if @_collection_apply_belongs_to + if !@_collection.where_values_hash.include?(attribute) && !@_collection.where_values_hash.include?("#{associated}_type") + @_collection = @_collection.where(attribute => value).where("#{associated}_type" => associated_type) + end + end + + columns.delete(associated) + end + end.present? load_columns! if changed end