elaine_crud 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/LICENSE +21 -0
  4. data/README.md +225 -0
  5. data/Rakefile +9 -0
  6. data/TODO.md +496 -0
  7. data/app/controllers/elaine_crud/base_controller.rb +228 -0
  8. data/app/helpers/elaine_crud/base_helper.rb +787 -0
  9. data/app/helpers/elaine_crud/search_helper.rb +132 -0
  10. data/app/javascript/controllers/dropdown_controller.js +18 -0
  11. data/app/views/elaine_crud/base/_edit_row.html.erb +60 -0
  12. data/app/views/elaine_crud/base/_export_button.html.erb +88 -0
  13. data/app/views/elaine_crud/base/_foreign_key_select_refresh.html.erb +52 -0
  14. data/app/views/elaine_crud/base/_form.html.erb +45 -0
  15. data/app/views/elaine_crud/base/_form_fields.html.erb +45 -0
  16. data/app/views/elaine_crud/base/_index_table.html.erb +58 -0
  17. data/app/views/elaine_crud/base/_modal.html.erb +71 -0
  18. data/app/views/elaine_crud/base/_pagination.html.erb +110 -0
  19. data/app/views/elaine_crud/base/_per_page_selector.html.erb +30 -0
  20. data/app/views/elaine_crud/base/_search_bar.html.erb +75 -0
  21. data/app/views/elaine_crud/base/_show_details.html.erb +29 -0
  22. data/app/views/elaine_crud/base/_view_row.html.erb +96 -0
  23. data/app/views/elaine_crud/base/edit.html.erb +51 -0
  24. data/app/views/elaine_crud/base/index.html.erb +74 -0
  25. data/app/views/elaine_crud/base/new.html.erb +12 -0
  26. data/app/views/elaine_crud/base/new_modal.html.erb +37 -0
  27. data/app/views/elaine_crud/base/not_found.html.erb +49 -0
  28. data/app/views/elaine_crud/base/show.html.erb +32 -0
  29. data/docs/ARCHITECTURE.md +410 -0
  30. data/docs/CSS_GRID_LAYOUT.md +126 -0
  31. data/docs/DEMO.md +693 -0
  32. data/docs/DSL_EXAMPLES.md +313 -0
  33. data/docs/FOREIGN_KEY_EXAMPLE.rb +100 -0
  34. data/docs/FOREIGN_KEY_SUPPORT.md +197 -0
  35. data/docs/HAS_MANY_IMPLEMENTATION.md +154 -0
  36. data/docs/LAYOUT_EXAMPLES.md +301 -0
  37. data/docs/TROUBLESHOOTING.md +170 -0
  38. data/elaine_crud.gemspec +46 -0
  39. data/lib/elaine_crud/dsl_methods.rb +348 -0
  40. data/lib/elaine_crud/engine.rb +37 -0
  41. data/lib/elaine_crud/export_handling.rb +164 -0
  42. data/lib/elaine_crud/field_configuration.rb +422 -0
  43. data/lib/elaine_crud/field_configuration_methods.rb +152 -0
  44. data/lib/elaine_crud/layout_calculation.rb +55 -0
  45. data/lib/elaine_crud/parameter_handling.rb +48 -0
  46. data/lib/elaine_crud/record_fetching.rb +150 -0
  47. data/lib/elaine_crud/relationship_handling.rb +220 -0
  48. data/lib/elaine_crud/routing.rb +33 -0
  49. data/lib/elaine_crud/search_and_filtering.rb +285 -0
  50. data/lib/elaine_crud/sorting_concern.rb +65 -0
  51. data/lib/elaine_crud/version.rb +5 -0
  52. data/lib/elaine_crud.rb +25 -0
  53. data/lib/tasks/demo.rake +111 -0
  54. data/lib/tasks/spec.rake +26 -0
  55. metadata +264 -0
@@ -0,0 +1,313 @@
1
+ # ElaineCrud DSL Examples
2
+
3
+ This document shows comprehensive examples of the ElaineCrud field DSL framework that has been implemented. All examples include TODO comments since the actual functionality is not yet implemented.
4
+
5
+ ## Basic Field Configuration
6
+
7
+ ```ruby
8
+ class PeopleController < ElaineCrud::BaseController
9
+ model Person
10
+ permit_params :name, :email, :role, :active, :company_id, :salary
11
+
12
+ # Simple hash-style configuration
13
+ field :name, title: "Full Name", description: "Enter first and last name"
14
+
15
+ # Block-style configuration for complex setups
16
+ field :email do |f|
17
+ f.title "Email Address"
18
+ f.description "Primary contact email"
19
+ f.display_as { |value| mail_to(value) if value.present? }
20
+ f.edit_as { |value| email_field_tag(field_name, value, class: "form-input") }
21
+ end
22
+ end
23
+ ```
24
+
25
+ ## Dropdown Options
26
+
27
+ ```ruby
28
+ class ProductController < ElaineCrud::BaseController
29
+ model Product
30
+ permit_params :name, :status, :category
31
+
32
+ # Array of options
33
+ field :status,
34
+ title: "Product Status",
35
+ options: ["draft", "published", "archived"]
36
+
37
+ # Hash mapping display => value
38
+ field :category,
39
+ title: "Product Category",
40
+ options: {
41
+ "Consumer Electronics" => "electronics",
42
+ "Home & Garden" => "home_garden",
43
+ "Books & Media" => "books"
44
+ }
45
+ end
46
+ ```
47
+
48
+ ## Foreign Key Relationships
49
+
50
+ ```ruby
51
+ class EmployeeController < ElaineCrud::BaseController
52
+ model Employee
53
+ permit_params :name, :email, :company_id, :department_id
54
+
55
+ # Basic foreign key - displays with to_s
56
+ field :company_id do |f|
57
+ f.title "Company"
58
+ f.description "Select the company this employee works for"
59
+ f.foreign_key model: Company
60
+ end
61
+
62
+ # Foreign key with custom display and scoping
63
+ field :department_id do |f|
64
+ f.title "Department"
65
+ f.foreign_key model: Department,
66
+ display: ->(dept) { "#{dept.name} (#{dept.location})" },
67
+ scope: -> { Department.active.order(:name) },
68
+ null_option: "Select a department..."
69
+ end
70
+ end
71
+ ```
72
+
73
+ ## Custom Display and Edit Callbacks
74
+
75
+ ```ruby
76
+ class TransactionController < ElaineCrud::BaseController
77
+ model Transaction
78
+ permit_params :amount, :description, :transaction_type, :processed_at
79
+
80
+ # Method reference for display callback
81
+ field :amount,
82
+ title: "Transaction Amount",
83
+ display_as: :format_currency,
84
+ edit_as: :currency_input
85
+
86
+ # Inline block for display
87
+ field :transaction_type do |f|
88
+ f.title "Type"
89
+ f.display_as { |value|
90
+ case value
91
+ when 'credit' then content_tag(:span, 'Credit', class: 'text-green-600 font-semibold')
92
+ when 'debit' then content_tag(:span, 'Debit', class: 'text-red-600 font-semibold')
93
+ else value.to_s.titleize
94
+ end
95
+ }
96
+ f.options ["credit", "debit", "transfer"]
97
+ end
98
+
99
+ private
100
+
101
+ def format_currency(value, record)
102
+ number_to_currency(value) if value
103
+ end
104
+
105
+ def currency_input(value, record, form)
106
+ form.number_field(:amount, step: 0.01, class: "form-input", placeholder: "0.00")
107
+ end
108
+ end
109
+ ```
110
+
111
+ ## Custom Partial Rendering
112
+
113
+ ```ruby
114
+ class AudienceController < ElaineCrud::BaseController
115
+ model Audience
116
+ permit_params :name, :audience_query, :state
117
+
118
+ # Use a custom partial for complex field rendering
119
+ field :audience_query do |f|
120
+ f.title "Audience Query"
121
+ f.description "Build your audience targeting criteria using the visual query builder"
122
+ f.edit_partial "audience_query_builder"
123
+ end
124
+
125
+ # Hash-style partial configuration
126
+ field :rich_content,
127
+ title: "Rich Content Editor",
128
+ description: "WYSIWYG editor for rich text content",
129
+ edit_partial: "shared/rich_text_editor"
130
+ end
131
+ ```
132
+
133
+ ### Partial Template Example
134
+
135
+ Then create the partial file `app/views/audience_browser/_audience_query_builder.html.erb`:
136
+
137
+ ## Read-Only Fields with Defaults
138
+
139
+ ```ruby
140
+ class OrderController < ElaineCrud::BaseController
141
+ model Order
142
+ permit_params :customer_name, :total_amount, :status, :order_number, :created_by
143
+
144
+ # Read-only with static default
145
+ field :order_number,
146
+ title: "Order Number",
147
+ description: "Automatically generated unique identifier",
148
+ readonly: true,
149
+ default_value: -> { "ORD-#{SecureRandom.alphanumeric(8).upcase}" }
150
+
151
+ # Read-only timestamp with custom formatting
152
+ field :created_at,
153
+ title: "Order Date",
154
+ readonly: true,
155
+ display_as: :format_order_date
156
+
157
+ # Read-only field populated from session/context
158
+ field :created_by,
159
+ title: "Created By",
160
+ readonly: true,
161
+ default_value: -> { current_user&.name }
162
+
163
+ private
164
+
165
+ def format_order_date(value, record)
166
+ value&.strftime("%B %d, %Y at %I:%M %p")
167
+ end
168
+
169
+ def current_user
170
+ # In this app, user info comes from reverse proxy headers
171
+ OpenStruct.new(name: request.headers['X-Forwarded-User'])
172
+ end
173
+ end
174
+ ```
175
+
176
+ ## Complex Real-World Example
177
+
178
+ ```ruby
179
+ class UserController < ElaineCrud::BaseController
180
+ model User
181
+ permit_params :name, :email, :role, :company_id, :department_id, :salary, :active, :hire_date
182
+
183
+ # Basic text field with validation
184
+ field :name do |f|
185
+ f.title "Full Name"
186
+ f.description "Enter the employee's first and last name"
187
+ f.display_as { |value| content_tag(:strong, value) }
188
+ end
189
+
190
+ # Email with custom display and validation
191
+ field :email do |f|
192
+ f.title "Email Address"
193
+ f.description "Primary work email address"
194
+ f.display_as { |value| mail_to(value, value, class: "text-blue-600") }
195
+ f.edit_as { |value, record, form|
196
+ form.email_field(:email, value: value, required: true, class: "form-input")
197
+ }
198
+ end
199
+
200
+ # Dropdown with predefined options
201
+ field :role,
202
+ title: "Job Role",
203
+ description: "Employee's primary role in the organization",
204
+ options: ["Developer", "Designer", "Manager", "Admin", "HR"]
205
+
206
+ # Foreign key to Company with custom display
207
+ field :company_id do |f|
208
+ f.title "Company"
209
+ f.description "Select the company this employee works for"
210
+ f.foreign_key model: Company,
211
+ display: ->(company) { "#{company.name} (#{company.city})" },
212
+ scope: -> { Company.active.includes(:address) },
213
+ null_option: "Select a company..."
214
+ end
215
+
216
+ # Foreign key to Department filtered by company
217
+ field :department_id do |f|
218
+ f.title "Department"
219
+ f.description "Select the department within the company"
220
+ f.foreign_key model: Department,
221
+ display: :name_with_code,
222
+ scope: -> { Department.joins(:company).where(company: company_scope) }
223
+ end
224
+
225
+ # Currency field with formatting
226
+ field :salary do |f|
227
+ f.title "Annual Salary"
228
+ f.description "Base annual salary in USD"
229
+ f.display_as :format_salary
230
+ f.edit_as { |value, record, form|
231
+ form.number_field(:salary, value: value, step: 1000, min: 0,
232
+ class: "form-input", placeholder: "Enter amount")
233
+ }
234
+ end
235
+
236
+ # Boolean with custom display
237
+ field :active,
238
+ title: "Active Employee",
239
+ description: "Is this person currently employed?",
240
+ display_as: ->(value) {
241
+ if value
242
+ content_tag(:span, "✅ Active", class: "text-green-600 font-semibold")
243
+ else
244
+ content_tag(:span, "❌ Inactive", class: "text-red-600 font-semibold")
245
+ end
246
+ }
247
+
248
+ # Date field with custom formatting
249
+ field :hire_date do |f|
250
+ f.title "Hire Date"
251
+ f.description "Employee's first day of work"
252
+ f.display_as { |value| value&.strftime("%B %d, %Y") }
253
+ f.default_value -> { Date.current }
254
+ end
255
+
256
+ private
257
+
258
+ def format_salary(value, record)
259
+ return "Not disclosed" if value.blank?
260
+ number_to_currency(value, precision: 0)
261
+ end
262
+
263
+ def company_scope
264
+ # This would be used to filter departments by selected company
265
+ # Implementation would need to be dynamic based on form state
266
+ Company.all
267
+ end
268
+ end
269
+ ```
270
+
271
+ ## Field Configuration Reference
272
+
273
+ ### Available Options
274
+
275
+ | Option | Type | Description | Example |
276
+ |--------|------|-------------|---------|
277
+ | `title` | String | Human-readable field title | `"Full Name"` |
278
+ | `description` | String | Help text for forms | `"Enter first and last name"` |
279
+ | `readonly` | Boolean | Prevents editing | `true` |
280
+ | `default_value` | Value/Proc | Default for new records | `-> { Date.current }` |
281
+ | `display_as` | Symbol/Proc | Custom display rendering | `:format_currency` |
282
+ | `edit_as` | Symbol/Proc | Custom form field rendering | `{ |v| email_field(...) }` |
283
+ | `edit_partial` | String | Custom partial for field rendering | `"audience_query_builder"` |
284
+ | `options` | Array/Hash | Dropdown options | `["option1", "option2"]` |
285
+ | `foreign_key` | Hash | Foreign key configuration | `{ model: Company, display: :name }` |
286
+
287
+ ### Foreign Key Configuration
288
+
289
+ ```ruby
290
+ field :company_id do |f|
291
+ f.foreign_key model: Company, # Required: target model
292
+ display: :name, # Optional: how to display (Symbol/Proc)
293
+ scope: -> { Company.active }, # Optional: filter records
294
+ null_option: "Select company..." # Optional: placeholder text
295
+ end
296
+ ```
297
+
298
+ ### Callback Signatures
299
+
300
+ ```ruby
301
+ # Display callbacks
302
+ display_as: :method_name # Calls controller.method_name(value, record)
303
+ display_as: { |value, record| ... } # Inline block
304
+
305
+ # Edit callbacks
306
+ edit_as: :method_name # Calls controller.method_name(value, record, form)
307
+ edit_as: { |value, record, form| ... } # Inline block
308
+
309
+ # Default value callbacks
310
+ default_value: -> { Time.current } # Called in controller context
311
+ ```
312
+
313
+ All of these examples are currently placeholders with TODO comments in the actual implementation. Each feature needs to be implemented one by one in the FieldConfiguration and BaseController classes.
@@ -0,0 +1,100 @@
1
+ # Example: Meeting Controller with Foreign Key Support
2
+ #
3
+ # This example shows how ElaineCrud automatically handles foreign key relationships
4
+ # for a Meeting model that belongs_to a MeetingRoom.
5
+ #
6
+ # Models (example structure):
7
+ #
8
+ # class Meeting < ApplicationRecord
9
+ # belongs_to :meeting_room
10
+ #
11
+ # validates :title, presence: true
12
+ # validates :start_time, presence: true
13
+ # end
14
+ #
15
+ # class MeetingRoom < ApplicationRecord
16
+ # has_many :meetings
17
+ #
18
+ # validates :name, presence: true
19
+ # end
20
+
21
+ class MeetingsController < ElaineCrud::BaseController
22
+ # Set the model - this automatically detects belongs_to relationships
23
+ model Meeting
24
+
25
+ # Specify the fields you want to permit for forms
26
+ # Foreign keys (meeting_room_id) are automatically included
27
+ permit_params :title, :description, :start_time, :end_time
28
+
29
+ # Optional: Customize foreign key display and behavior
30
+ field :meeting_room_id do
31
+ title "Meeting Room"
32
+ foreign_key(
33
+ model: MeetingRoom,
34
+ display: :name, # Show the 'name' field from MeetingRoom
35
+ null_option: "Choose a room",
36
+ scope: -> { MeetingRoom.available } # Optional: only show available rooms
37
+ )
38
+ end
39
+
40
+ # Optional: Customize other fields
41
+ field :title do
42
+ title "Meeting Title"
43
+ description "Enter a descriptive title for the meeting"
44
+ end
45
+
46
+ field :start_time do
47
+ title "Start Time"
48
+ description "When the meeting begins"
49
+ end
50
+
51
+ field :end_time do
52
+ title "End Time"
53
+ description "When the meeting ends"
54
+ end
55
+
56
+ # Optional: Hide certain fields from display
57
+ field :description do
58
+ visible false # Won't show in index listing
59
+ end
60
+
61
+ # Optional: Make fields readonly
62
+ field :created_at do
63
+ visible true
64
+ readonly true
65
+ title "Created"
66
+ end
67
+ end
68
+
69
+ # What happens automatically:
70
+ #
71
+ # 1. ElaineCrud detects that Meeting belongs_to :meeting_room
72
+ # 2. It automatically configures meeting_room_id as a foreign key field
73
+ # 3. In the index view, instead of showing "1, 2, 3" for meeting_room_id,
74
+ # it shows the related MeetingRoom's display field (usually name, title, etc.)
75
+ # 4. In edit forms, meeting_room_id becomes a dropdown with all MeetingRoom options
76
+ # 5. The foreign key is automatically included in permitted_params
77
+ # 6. Database queries are optimized with includes() to avoid N+1 queries
78
+ #
79
+ # Manual configuration options:
80
+ #
81
+ # field :meeting_room_id do
82
+ # foreign_key(
83
+ # model: MeetingRoom, # Required: the related model class
84
+ # display: :name, # Field to show (default: auto-detected)
85
+ # scope: -> { MeetingRoom.active }, # Optional: filter available options
86
+ # null_option: "Select room" # Optional: placeholder text
87
+ # )
88
+ # end
89
+ #
90
+ # Alternative display options:
91
+ #
92
+ # field :meeting_room_id do
93
+ # foreign_key(
94
+ # model: MeetingRoom,
95
+ # display: ->(room) { "#{room.name} (#{room.capacity} seats)" } # Proc for complex display
96
+ # )
97
+ # end
98
+
99
+ # Routes (add to your routes.rb):
100
+ # resources :meetings
@@ -0,0 +1,197 @@
1
+ # Foreign Key Support in ElaineCrud
2
+
3
+ ElaineCrud now provides comprehensive automatic support for `belongs_to` relationships in ActiveRecord models. This feature eliminates the need for manual configuration in most cases while still providing flexibility for customization.
4
+
5
+ ## Features
6
+
7
+ ### Automatic Detection
8
+ - Automatically detects all `belongs_to` relationships in your ActiveRecord model
9
+ - Auto-configures foreign key fields without requiring manual setup
10
+ - Includes foreign keys in permitted parameters automatically
11
+ - Optimizes database queries with automatic `includes()` to prevent N+1 queries
12
+
13
+ ### Smart Display Field Detection
14
+ - Automatically determines the best display field for related models
15
+ - Prefers common fields like `:name`, `:title`, `:display_name`, `:full_name`, `:label`, `:description`
16
+ - Falls back to first string/text column if common fields aren't found
17
+ - Uses `:id` as final fallback
18
+
19
+ ### Display Behavior
20
+ - **Index View**: Shows the related record's display field instead of the foreign key ID
21
+ - **Edit Forms**: Renders a dropdown with all available options from the related model, with the current value pre-selected
22
+ - **Error Handling**: Gracefully handles missing records and configuration errors
23
+
24
+ ## Basic Usage
25
+
26
+ ```ruby
27
+ class MeetingsController < ElaineCrud::BaseController
28
+ model Meeting # Automatically detects belongs_to :meeting_room
29
+ permit_params :title, :start_time, :end_time # meeting_room_id included automatically
30
+ end
31
+ ```
32
+
33
+ That's it! ElaineCrud will automatically:
34
+ 1. Detect the `belongs_to :meeting_room` relationship
35
+ 2. Configure `meeting_room_id` as a foreign key field
36
+ 3. Show meeting room names in the index instead of IDs
37
+ 4. Provide a dropdown in forms with all meeting rooms
38
+ 5. Include `meeting_room_id` in permitted parameters
39
+
40
+ ## Customization Options
41
+
42
+ ### Basic Customization
43
+ ```ruby
44
+ field :meeting_room_id do
45
+ title "Conference Room"
46
+ foreign_key(
47
+ model: MeetingRoom,
48
+ display: :name,
49
+ null_option: "Choose a room"
50
+ )
51
+ end
52
+ ```
53
+
54
+ ### Advanced Display Logic
55
+ ```ruby
56
+ field :meeting_room_id do
57
+ foreign_key(
58
+ model: MeetingRoom,
59
+ display: ->(room) { "#{room.name} (#{room.capacity} seats)" }
60
+ )
61
+ end
62
+ ```
63
+
64
+ ### Scoped Options
65
+ ```ruby
66
+ field :meeting_room_id do
67
+ foreign_key(
68
+ model: MeetingRoom,
69
+ display: :name,
70
+ scope: -> { MeetingRoom.available.order(:name) }
71
+ )
72
+ end
73
+ ```
74
+
75
+ ### Custom Method Display
76
+ ```ruby
77
+ # In MeetingRoom model
78
+ class MeetingRoom < ApplicationRecord
79
+ def display_name
80
+ "#{name} - #{building.name}"
81
+ end
82
+ end
83
+
84
+ # In controller
85
+ field :meeting_room_id do
86
+ foreign_key(
87
+ model: MeetingRoom,
88
+ display: :display_name
89
+ )
90
+ end
91
+ ```
92
+
93
+ ## Configuration Options
94
+
95
+ | Option | Type | Description | Default |
96
+ |--------|------|-------------|---------|
97
+ | `model` | Class | The related ActiveRecord model | Auto-detected from belongs_to |
98
+ | `display` | Symbol/Proc | Field or method to display | Auto-detected (name, title, etc.) |
99
+ | `scope` | Proc | Limits available options | `-> { Model.all }` |
100
+ | `null_option` | String | Placeholder text for blank option | "Select [relationship name]" |
101
+
102
+ ## Implementation Details
103
+
104
+ ### Automatic Model Analysis
105
+ When you call `model ModelClass`, ElaineCrud:
106
+ 1. Inspects all `belongs_to` reflections on the model
107
+ 2. Creates `FieldConfiguration` objects for each foreign key
108
+ 3. Determines the best display field for each related model
109
+ 4. Adds foreign keys to the permitted parameters list
110
+
111
+ ### Query Optimization
112
+ The `fetch_records` method automatically includes belongs_to associations to prevent N+1 queries:
113
+ ```ruby
114
+ # Instead of this (N+1 queries):
115
+ meetings.each { |meeting| puts meeting.meeting_room.name }
116
+
117
+ # ElaineCrud automatically does this:
118
+ Meeting.includes(:meeting_room).each { |meeting| puts meeting.meeting_room.name }
119
+ ```
120
+
121
+ ### Display Field Detection Logic
122
+ 1. Check for common display fields: `name`, `title`, `display_name`, `full_name`, `label`, `description`
123
+ 2. If none found, use first string/text column (excluding id, created_at, updated_at)
124
+ 3. Fall back to `:id` if no suitable field found
125
+
126
+ ## Error Handling
127
+
128
+ ElaineCrud handles various error scenarios gracefully:
129
+ - **Missing Related Record**: Shows "Not found (ID: X)" message
130
+ - **Configuration Errors**: Shows error message in development, falls back to ID in production
131
+ - **Missing Display Field**: Falls back to `to_s` method
132
+ - **Database Errors**: Gracefully handles and logs errors
133
+
134
+ ## Migration from Manual Configuration
135
+
136
+ If you previously configured foreign keys manually, ElaineCrud will respect your existing configuration:
137
+ ```ruby
138
+ # Your existing configuration takes precedence
139
+ field :meeting_room_id do
140
+ foreign_key(model: MeetingRoom, display: :custom_name)
141
+ end
142
+ # Auto-detection skips this field since it's already configured
143
+ ```
144
+
145
+ ## Performance Considerations
146
+
147
+ - **Automatic Includes**: Foreign key relationships are automatically included in queries
148
+ - **Lazy Loading**: Related records are only loaded when actually displayed
149
+ - **Caching**: Consider adding Rails caching for frequently accessed dropdown options
150
+
151
+ ## Future Enhancements
152
+
153
+ The current implementation focuses on `belongs_to` relationships. Future versions may include:
154
+ - `has_many` relationship support
155
+ - `has_and_belongs_to_many` relationship support
156
+ - Polymorphic relationship support
157
+ - Search/autocomplete for large datasets
158
+ - Nested attribute support
159
+
160
+ ## Example Models
161
+
162
+ ```ruby
163
+ class Meeting < ApplicationRecord
164
+ belongs_to :meeting_room
165
+ belongs_to :organizer, class_name: 'User'
166
+
167
+ validates :title, presence: true
168
+ end
169
+
170
+ class MeetingRoom < ApplicationRecord
171
+ has_many :meetings
172
+ validates :name, presence: true
173
+ end
174
+
175
+ class User < ApplicationRecord
176
+ has_many :organized_meetings, class_name: 'Meeting', foreign_key: 'organizer_id'
177
+ end
178
+ ```
179
+
180
+ With these models, ElaineCrud automatically handles both `meeting_room_id` and `organizer_id` foreign keys in your `MeetingsController`.
181
+
182
+ ## Troubleshooting
183
+
184
+ ### Foreign Key Not Showing as Dropdown
185
+ - Ensure your model has a `belongs_to` relationship defined
186
+ - Check that the foreign key column exists in the database
187
+ - Verify the related model class is accessible
188
+
189
+ ### Wrong Display Field Being Used
190
+ - Manually configure the `display` option in field configuration
191
+ - Add a custom display method to your model
192
+ - Check model column names match expected patterns
193
+
194
+ ### Performance Issues
195
+ - Add database indexes on foreign key columns
196
+ - Consider using scopes to limit dropdown options
197
+ - Implement caching for frequently accessed dropdowns