Module: ElaineCrud::RelationshipHandling

Extended by:
ActiveSupport::Concern
Included in:
BaseController
Defined in:
lib/elaine_crud/relationship_handling.rb

Overview

Methods for handling ActiveRecord relationships (belongs_to, has_many) Provides eager loading, filtering, and parent context management

Instance Method Summary collapse

Instance Method Details

#apply_has_many_filtering(records) ⇒ ActiveRecord::Relation

Apply filtering based on parent relationship parameters

Parameters:

  • records (ActiveRecord::Relation)

    The base query

Returns:

  • (ActiveRecord::Relation)

    Filtered query



118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/elaine_crud/relationship_handling.rb', line 118

def apply_has_many_filtering(records)
  parent_filters = detect_parent_filters

  parent_filters.each do |parent_field, parent_id|
    next unless parent_id.present?

    # Validate that this is a legitimate foreign key
    if valid_parent_filter?(parent_field)
      records = records.where(parent_field => parent_id)

      # Set instance variables for UI context
      set_parent_context(parent_field, parent_id)
    end
  end

  records
end

#detect_parent_filtersHash

Detect parent filter parameters from URL

Returns:

  • (Hash)

    Hash of foreign_key => parent_id



138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/elaine_crud/relationship_handling.rb', line 138

def detect_parent_filters
  parent_filters = {}

  # Look for foreign key parameters in the URL
  crud_model.reflections.each do |name, reflection|
    next unless reflection.is_a?(ActiveRecord::Reflection::BelongsToReflection)

    foreign_key = reflection.foreign_key.to_sym
    param_value = params[foreign_key]

    if param_value.present?
      parent_filters[foreign_key] = param_value
    end
  end

  parent_filters
end

#find_reflection_by_foreign_key(foreign_key) ⇒ ActiveRecord::Reflection::BelongsToReflection?

Find reflection by foreign key name

Parameters:

  • foreign_key (Symbol)

    The foreign key field

Returns:

  • (ActiveRecord::Reflection::BelongsToReflection, nil)

    The reflection or nil



191
192
193
194
195
196
# File 'lib/elaine_crud/relationship_handling.rb', line 191

def find_reflection_by_foreign_key(foreign_key)
  crud_model.reflections.values.find do |reflection|
    reflection.is_a?(ActiveRecord::Reflection::BelongsToReflection) &&
    reflection.foreign_key.to_sym == foreign_key.to_sym
  end
end

#get_all_relationship_includesArray<Symbol>

Get list of associations to include for avoiding N+1 queries

Returns:

  • (Array<Symbol>)

    List of association names to include



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/elaine_crud/relationship_handling.rb', line 95

def get_all_relationship_includes
  return [] unless crud_model

  includes = []

  # Include belongs_to relationships (existing logic)
  includes += get_belongs_to_includes

  # Include has_many relationships that are displayed
  includes += get_has_many_includes

  # Include has_one relationships that are displayed
  includes += get_has_one_includes

  # Include has_and_belongs_to_many relationships that are displayed
  includes += get_habtm_includes

  includes.uniq
end

#get_belongs_to_includesArray<Symbol>

Get list of belongs_to associations to include for avoiding N+1 queries

Returns:

  • (Array<Symbol>)

    List of association names to include



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/elaine_crud/relationship_handling.rb', line 16

def get_belongs_to_includes
  return [] unless crud_model

  includes = []

  # Get all belongs_to relationships that are displayed
  crud_model.reflections.each do |name, reflection|
    next unless reflection.is_a?(ActiveRecord::Reflection::BelongsToReflection)

    foreign_key = reflection.foreign_key.to_sym

    # Include if this foreign key is in the displayed columns or configured
    if determine_columns.include?(foreign_key.to_s) || field_configured?(foreign_key)
      includes << name.to_sym
    end
  end

  includes
end

#get_habtm_includesArray<Symbol>

Get has_and_belongs_to_many relationships that need to be included

Returns:

  • (Array<Symbol>)

    List of association names to include



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/elaine_crud/relationship_handling.rb', line 76

def get_habtm_includes
  return [] unless crud_model

  includes = []

  crud_model.reflections.each do |name, reflection|
    next unless reflection.is_a?(ActiveRecord::Reflection::HasAndBelongsToManyReflection)

    # Include if this relationship is displayed in columns or configured
    if determine_columns.include?(name) || field_configured?(name.to_sym)
      includes << name.to_sym
    end
  end

  includes
end

#get_has_many_includesArray<Symbol>

Get has_many relationships that need to be included

Returns:

  • (Array<Symbol>)

    List of association names to include



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/elaine_crud/relationship_handling.rb', line 38

def get_has_many_includes
  return [] unless crud_model

  includes = []

  crud_model.reflections.each do |name, reflection|
    next unless reflection.is_a?(ActiveRecord::Reflection::HasManyReflection)

    # Include if this relationship is displayed in columns or configured
    if determine_columns.include?(name) || field_configured?(name.to_sym)
      includes << name.to_sym
    end
  end

  includes
end

#get_has_one_includesArray<Symbol>

Get has_one relationships that need to be included

Returns:

  • (Array<Symbol>)

    List of association names to include



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/elaine_crud/relationship_handling.rb', line 57

def get_has_one_includes
  return [] unless crud_model

  includes = []

  crud_model.reflections.each do |name, reflection|
    next unless reflection.is_a?(ActiveRecord::Reflection::HasOneReflection)

    # Include if this relationship is displayed in columns or configured
    if determine_columns.include?(name) || field_configured?(name.to_sym)
      includes << name.to_sym
    end
  end

  includes
end

#populate_parent_relationships(record) ⇒ Object

Populate parent relationships from URL parameters

Parameters:

  • record (ActiveRecord::Base)

    The record to populate



200
201
202
203
204
205
206
# File 'lib/elaine_crud/relationship_handling.rb', line 200

def populate_parent_relationships(record)
  detect_parent_filters.each do |foreign_key, parent_id|
    if valid_parent_filter?(foreign_key) && record.public_send(foreign_key).blank?
      record.public_send("#{foreign_key}=", parent_id)
    end
  end
end

#redirect_after_create_pathString

Determine redirect path after create based on context

Returns:

  • (String)

    The redirect path



210
211
212
213
214
215
216
217
218
# File 'lib/elaine_crud/relationship_handling.rb', line 210

def redirect_after_create_path
  if @parent_context
    # Redirect back to filtered index view
    polymorphic_path([crud_model], @parent_context[:foreign_key] => @parent_context[:record].id)
  else
    # Standard redirect to show page
    polymorphic_path(@record)
  end
end

#set_parent_context(parent_field, parent_id) ⇒ Object

Set context for UI display

Parameters:

  • parent_field (Symbol)

    The foreign key field

  • parent_id (Integer)

    The parent record ID



167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/elaine_crud/relationship_handling.rb', line 167

def set_parent_context(parent_field, parent_id)
  reflection = find_reflection_by_foreign_key(parent_field)
  return unless reflection

  begin
    parent_record = reflection.klass.find(parent_id)
    @parent_context = {
      record: parent_record,
      relationship_name: reflection.name,
      foreign_key: parent_field,
      model_class: reflection.klass
    }
  rescue ActiveRecord::RecordNotFound
    @parent_context = {
      error: "#{reflection.klass.name} with ID #{parent_id} not found",
      foreign_key: parent_field,
      model_class: reflection.klass
    }
  end
end

#valid_parent_filter?(field_name) ⇒ Boolean

Validate that the parent filter is legitimate

Parameters:

  • field_name (Symbol)

    The field name to validate

Returns:

  • (Boolean)

    True if valid foreign key column



159
160
161
162
# File 'lib/elaine_crud/relationship_handling.rb', line 159

def valid_parent_filter?(field_name)
  crud_model.column_names.include?(field_name.to_s) &&
  field_name.to_s.end_with?('_id')
end