Module: NestedSelect::Preloader::Association

Defined in:
lib/nested_select/preloader/association.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#nested_select_valuesObject (readonly)

Returns the value of attribute nested_select_values.



6
7
8
# File 'lib/nested_select/preloader/association.rb', line 6

def nested_select_values
  @nested_select_values
end

Instance Method Details

#apply_nested_select_values(partial_select_values) ⇒ Object



12
13
14
15
# File 'lib/nested_select/preloader/association.rb', line 12

def apply_nested_select_values(partial_select_values)
  @nested_select_values = [*partial_select_values]
  ensure_nesting_selection_integrity!(association_nested_select_values)
end

#association_nested_select_valuesObject



22
23
24
25
26
27
# File 'lib/nested_select/preloader/association.rb', line 22

def association_nested_select_values
  this_association_select_values = nested_select_values&.grep_v(Hash)&.map { _1.try(:to_s) }
  return if this_association_select_values.blank?

  [*this_association_select_values, *reflection_relation_keys_attributes].uniq
end

#build_scopeObject



8
9
10
# File 'lib/nested_select/preloader/association.rb', line 8

def build_scope
  association_nested_select_values.blank? ? super : super.select(association_nested_select_values)
end

#ensure_nesting_selection_integrity!(nested_select_final_values) ⇒ Object

ensure that different preloading branches will match nested selected attributes

Raises:

  • (ActiveModel::MissingAttributeError)


30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/nested_select/preloader/association.rb', line 30

def ensure_nesting_selection_integrity!(nested_select_final_values)
  single_owner = owners.first
  # do nothing unless not yet loaded
  return unless single_owner.association(reflection.name).loaded?

  single_reflection_record = single_owner.send(reflection.name)
  return if single_reflection_record.blank?

  single_reflection_record = single_reflection_record.first if single_reflection_record.is_a?(Enumerable)

  attributes_loaded = single_reflection_record.attributes.keys.map(&:to_s)
  current_selection = nested_select_final_values.grep_v(Hash).map(&:to_s)

  basic_attributes_matched = (attributes_loaded & reflection.klass.column_names).tally ==
    (current_selection & reflection.klass.column_names).tally

  # this is not a 100% safe verification, but it will match cases with custom attributes selection for example:
  # "(SELECT COUNT(*) FROM images) as IMG_count" =~ /img_count/
  custom_attributes_matched = (attributes_loaded - reflection.klass.column_names).all? do |loaded_attr|
    (current_selection - reflection.klass.column_names).any? do |upcoming_custom_attr|
      upcoming_custom_attr =~ /#{loaded_attr}/i
    end
  end

  raise ActiveModel::MissingAttributeError, <<~ERR if !basic_attributes_matched || !custom_attributes_matched
    Reflection '#{reflection.name}' already loaded with a different set of basic attributes.
    expected: #{current_selection}, already loaded with: #{attributes_loaded}
    Hint: ensure that you are using same set of attributes for entrance of same relation
          on nesting selection tree including reverse through relations
  ERR
end

#reflection_relation_keys_attributesObject



17
18
19
20
# File 'lib/nested_select/preloader/association.rb', line 17

def reflection_relation_keys_attributes
  foreign_key = reflection.foreign_key unless reflection.is_a?(ActiveRecord::Reflection::BelongsToReflection)
  [*foreign_key, *reflection.klass.primary_key].map(&:to_s)
end