propel_api 0.2.1 → 0.3.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 (22) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +58 -0
  3. data/README.md +239 -4
  4. data/lib/generators/propel_api/core/named_base.rb +92 -1
  5. data/lib/generators/propel_api/core/relationship_inferrer.rb +4 -11
  6. data/lib/generators/propel_api/install/install_generator.rb +2 -2
  7. data/lib/generators/propel_api/resource/resource_generator.rb +205 -63
  8. data/lib/generators/propel_api/templates/config/propel_api.rb.tt +26 -1
  9. data/lib/generators/propel_api/templates/controllers/api_controller_graphiti.rb +2 -2
  10. data/lib/generators/propel_api/templates/controllers/api_controller_propel_facets.rb +20 -10
  11. data/lib/generators/propel_api/templates/controllers/example_controller.rb.tt +2 -2
  12. data/lib/generators/propel_api/templates/scaffold/facet_controller_template.rb.tt +34 -9
  13. data/lib/generators/propel_api/templates/scaffold/facet_model_template.rb.tt +17 -8
  14. data/lib/generators/propel_api/templates/scaffold/graphiti_controller_template.rb.tt +1 -1
  15. data/lib/generators/propel_api/templates/scaffold/graphiti_resource_template.rb.tt +2 -2
  16. data/lib/generators/propel_api/templates/seeds/seeds_template.rb.tt +62 -14
  17. data/lib/generators/propel_api/templates/tests/controller_test_template.rb.tt +34 -6
  18. data/lib/generators/propel_api/templates/tests/fixtures_template.yml.tt +58 -15
  19. data/lib/generators/propel_api/templates/tests/integration_test_template.rb.tt +108 -36
  20. data/lib/generators/propel_api/templates/tests/model_test_template.rb.tt +20 -0
  21. data/lib/propel_api.rb +1 -1
  22. metadata +22 -2
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- <% unless options[:skip_tenancy] -%>
3
+ <% if has_organization_reference? || has_agency_reference? -%>
4
4
  require_relative '../concerns/tenancy'
5
5
  <% end -%>
6
6
 
@@ -20,7 +20,7 @@ require_relative '../concerns/tenancy'
20
20
  # @see https://www.graphiti.dev/guides/concepts/resources
21
21
  #
22
22
  class <%= class_name %>Resource < ApplicationResource
23
- <% unless options[:skip_tenancy] -%>
23
+ <% if has_organization_reference? || has_agency_reference? -%>
24
24
  include Tenancy
25
25
  <% end -%>
26
26
 
@@ -13,7 +13,7 @@ puts "📋 Loading fixture data for consistent test/dev experience..."
13
13
 
14
14
  # Only load fixtures if not already in development environment with existing data
15
15
  fixture_count = 0
16
- <% unless options[:skip_tenancy] -%>
16
+ <% if has_organization_reference? || has_agency_reference? -%>
17
17
  if Rails.env.development? && (Organization.count > 0 || User.count > 0)
18
18
  <% else -%>
19
19
  <% if attributes.any? { |attr| attr.type == :references && attr.name.to_s.match?(/user/) } -%>
@@ -31,8 +31,10 @@ else
31
31
  begin
32
32
  # Load all necessary fixtures together to handle dependencies
33
33
  fixture_files = ['<%= table_name %>']
34
- <% unless options[:skip_tenancy] -%>
34
+ <% if has_organization_reference? -%>
35
35
  fixture_files << 'organizations'
36
+ <% end -%>
37
+ <% if has_agency_reference? -%>
36
38
  fixture_files << 'agencies'
37
39
  <% end -%>
38
40
  <% if attributes.any? { |attr| attr.type == :references && attr.name.to_s.match?(/user/) } -%>
@@ -67,8 +69,10 @@ end
67
69
  puts "🎨 Generating additional realistic <%= table_name %> with Faker..."
68
70
 
69
71
  # Use existing organizations, agencies, and users (from fixtures + any additional ones)
70
- <% unless options[:skip_tenancy] -%>
72
+ <% if has_organization_reference? -%>
71
73
  organizations = Organization.all
74
+ <% end -%>
75
+ <% if has_agency_reference? -%>
72
76
  agencies = Agency.all
73
77
 
74
78
  # If we still don't have enough orgs, create some with Faker
@@ -102,7 +106,7 @@ users = User.all
102
106
 
103
107
  if users.count < 6
104
108
  puts "➕ Creating additional users..."
105
- <% unless options[:skip_tenancy] -%>
109
+ <% if has_organization_reference? -%>
106
110
  organizations.each do |org|
107
111
  next if users.where(organization: org).count >= 2
108
112
 
@@ -145,12 +149,14 @@ end
145
149
 
146
150
  # Generate varied, realistic data based on the model attributes
147
151
  30.times do |i|
148
- <% unless options[:skip_tenancy] -%>
152
+ <% if has_organization_reference? -%>
149
153
  organization = organizations.sample
154
+ <% end -%>
155
+ <% if has_agency_reference? -%>
150
156
  agency = agencies.where(organization: organization).sample || agencies.sample
151
157
  <% end -%>
152
158
  <% if attributes.any? { |attr| attr.type == :references && attr.name.to_s.match?(/user/) } -%>
153
- <% unless options[:skip_tenancy] -%>
159
+ <% if has_organization_reference? -%>
154
160
  user = users.where(organization: organization).sample || users.sample
155
161
  <% else -%>
156
162
  user = users.sample
@@ -159,16 +165,32 @@ end
159
165
 
160
166
  <%= singular_table_name %>_attributes = {}
161
167
 
162
- <% unless options[:skip_tenancy] -%>
163
- # Multi-tenancy: Always assign organization (required) and agency (optional)
168
+ <% if has_organization_reference? -%>
169
+ # Multi-tenancy: Assign organization
164
170
  <%= singular_table_name %>_attributes[:organization] = organization
171
+ <% end -%>
172
+ <% if has_agency_reference? -%>
173
+ # Multi-tenancy: Assign agency
165
174
  <%= singular_table_name %>_attributes[:agency] = [agency, nil].sample # 50% chance of having agency
175
+ <% end -%>
176
+ <% if has_organization_reference? || has_agency_reference? -%>
166
177
 
167
178
  <% end -%>
179
+ # Handle polymorphic associations
180
+ <% polymorphic_associations.each do |poly_assoc| -%>
181
+ # Assign polymorphic association: <%= poly_assoc[:field_name] %>
182
+ available_parents = <%= poly_assoc[:parent_types].map { |type| "#{type}.limit(5).to_a" }.join(' + ') %>
183
+ if available_parents.any?
184
+ selected_parent = available_parents.sample
185
+ <%= singular_table_name %>_attributes[:<%= poly_assoc[:id_field] %>] = selected_parent.id
186
+ <%= singular_table_name %>_attributes[:<%= poly_assoc[:type_field] %>] = selected_parent.class.name
187
+ end
188
+ <% end -%>
189
+
168
190
  # Assign additional reference attributes dynamically
169
- <% attributes.select { |attr| attr.type == :references }.each do |reference| -%>
191
+ <% attributes.select { |attr| attr.type == :references && !(attr.respond_to?(:polymorphic?) && attr.polymorphic?) }.each do |reference| -%>
170
192
  <% # Skip organization and agency if tenancy is included (they're already assigned above) -%>
171
- <% unless !options[:skip_tenancy] && (reference.name == 'organization' || reference.name == 'agency') -%>
193
+ <% unless (has_organization_reference? && reference.name == 'organization') || (has_agency_reference? && reference.name == 'agency') -%>
172
194
  <% if reference.name.to_s.match?(/user/) -%>
173
195
  <%= singular_table_name %>_attributes[:<%= reference.name %>] = user
174
196
  <% else -%>
@@ -312,9 +334,11 @@ puts "\n✅ Created #{<%= class_name %>.count} <%= table_name %>"
312
334
  puts "🎯 Creating specific test scenarios..."
313
335
 
314
336
  # Create some <%= table_name %> with edge case data
315
- <% unless options[:skip_tenancy] -%>
337
+ <% if has_organization_reference? -%>
316
338
  organizations.each do |org|
339
+ <% if has_agency_reference? -%>
317
340
  agency = agencies.where(organization: org).sample
341
+ <% end -%>
318
342
  <% if attributes.any? { |attr| attr.type == :references && attr.name.to_s.match?(/user/) } -%>
319
343
  user = users.where(organization: org).sample
320
344
  <% end -%>
@@ -326,8 +350,19 @@ organizations.each do |org|
326
350
  minimal_<%= singular_table_name %>[:organization] = org
327
351
  minimal_<%= singular_table_name %>[:agency] = agency
328
352
 
353
+ # Handle polymorphic associations
354
+ <% polymorphic_associations.each do |poly_assoc| -%>
355
+ # Assign polymorphic association: <%= poly_assoc[:field_name] %>
356
+ available_parents = <%= poly_assoc[:parent_types].map { |type| "#{type}.limit(5).to_a" }.join(' + ') %>
357
+ if available_parents.any?
358
+ selected_parent = available_parents.sample
359
+ minimal_<%= singular_table_name %>[:<%= poly_assoc[:id_field] %>] = selected_parent.id
360
+ minimal_<%= singular_table_name %>[:<%= poly_assoc[:type_field] %>] = selected_parent.class.name
361
+ end
362
+ <% end -%>
363
+
329
364
  # Assign additional reference attributes dynamically
330
- <% attributes.select { |attr| attr.type == :references }.each do |reference| -%>
365
+ <% attributes.select { |attr| attr.type == :references && !(attr.respond_to?(:polymorphic?) && attr.polymorphic?) }.each do |reference| -%>
331
366
  <% # Skip organization and agency if tenancy is included (they're already assigned above) -%>
332
367
  <% unless reference.name == 'organization' || reference.name == 'agency' -%>
333
368
  <% if reference.name.to_s.match?(/user/) -%>
@@ -393,8 +428,19 @@ end
393
428
  # Create a minimal <%= singular_table_name %> (only required fields)
394
429
  minimal_<%= singular_table_name %> = {}
395
430
 
431
+ # Handle polymorphic associations
432
+ <% polymorphic_associations.each do |poly_assoc| -%>
433
+ # Assign polymorphic association: <%= poly_assoc[:field_name] %>
434
+ available_parents = <%= poly_assoc[:parent_types].map { |type| "#{type}.limit(5).to_a" }.join(' + ') %>
435
+ if available_parents.any?
436
+ selected_parent = available_parents.sample
437
+ minimal_<%= singular_table_name %>[:<%= poly_assoc[:id_field] %>] = selected_parent.id
438
+ minimal_<%= singular_table_name %>[:<%= poly_assoc[:type_field] %>] = selected_parent.class.name
439
+ end
440
+ <% end -%>
441
+
396
442
  # Assign reference attributes dynamically
397
- <% attributes.select { |attr| attr.type == :references }.each do |reference| -%>
443
+ <% attributes.select { |attr| attr.type == :references && !(attr.respond_to?(:polymorphic?) && attr.polymorphic?) }.each do |reference| -%>
398
444
  <% if reference.name.to_s.match?(/user/) -%>
399
445
  minimal_<%= singular_table_name %>[:<%= reference.name %>] = users.sample if users.any?
400
446
  <% else -%>
@@ -466,8 +512,10 @@ puts "📊 Summary:"
466
512
  puts " • Total <%= table_name %>: #{total_<%= table_name %>}"
467
513
  puts " • └── Fixture <%= table_name %>: #{fixture_<%= table_name %>} (same as test data)"
468
514
  puts " • └── Faker <%= table_name %>: #{faker_<%= table_name %>} (realistic dev data)"
469
- <% unless options[:skip_tenancy] -%>
515
+ <% if has_organization_reference? -%>
470
516
  puts " • Organizations: #{Organization.count}"
517
+ <% end -%>
518
+ <% if has_agency_reference? -%>
471
519
  puts " • Agencies: #{Agency.count}"
472
520
  puts " • <%= table_name %> per organization: #{(total_<%= table_name %>.to_f / Organization.count).round(1)} avg"
473
521
  <% end -%>
@@ -8,6 +8,16 @@ class <%= controller_class_name_with_namespace %>ControllerTest < ActionDispatch
8
8
  @organization = organizations(:acme_org)
9
9
  @user = users(:john_user)
10
10
  @agency = agencies(:marketing_agency)
11
+ <% polymorphic_associations.each do |assoc| -%>
12
+ <% assoc[:parent_types].each_with_index do |parent_type, index| -%>
13
+ <% if index == 0 -%>
14
+ @<%= parent_type.underscore %> = <%= parent_type.underscore.pluralize %>(<%= parent_type.underscore == 'agency' ? ':marketing_agency' : ':one' %>)
15
+ @<%= assoc[:field_name] %> = @<%= parent_type.underscore %> # Default polymorphic parent for tests
16
+ <% else -%>
17
+ @<%= parent_type.underscore %> = <%= parent_type.underscore.pluralize %>(<%= index == 1 ? ':marketing_agency' : ':one' %>)
18
+ <% end -%>
19
+ <% end -%>
20
+ <% end -%>
11
21
  <% if singular_table_name == 'organization' -%>
12
22
  @<%= singular_table_name %> = <%= table_name %>(:acme_org)
13
23
  <% elsif singular_table_name == 'user' -%>
@@ -21,7 +31,7 @@ class <%= controller_class_name_with_namespace %>ControllerTest < ActionDispatch
21
31
  @auth_headers = { 'Authorization' => "Bearer #{@token}" }
22
32
 
23
33
  # Ensure test <%= singular_table_name %> belongs to test user's organization
24
- <% unless singular_table_name == 'organization' -%>
34
+ <% if has_organization_reference? -%>
25
35
  @<%= singular_table_name %>.update!(organization: @organization)
26
36
  <% end -%>
27
37
  end
@@ -98,6 +108,7 @@ class <%= controller_class_name_with_namespace %>ControllerTest < ActionDispatch
98
108
  assert_includes pagination.keys, 'prev_page'
99
109
  end
100
110
 
111
+ <% if has_organization_reference? -%>
101
112
  test "should only show records for user's organization" do
102
113
  # Create record for different organization
103
114
  other_org = Organization.create!(name: "Other Organization")
@@ -148,6 +159,7 @@ class <%= controller_class_name_with_namespace %>ControllerTest < ActionDispatch
148
159
  # Should include own organization's record
149
160
  assert_includes <%= table_name %>_ids, @<%= singular_table_name %>.id
150
161
  end
162
+ <% end -%>
151
163
 
152
164
  # === SHOW TESTS ===
153
165
 
@@ -167,6 +179,7 @@ class <%= controller_class_name_with_namespace %>ControllerTest < ActionDispatch
167
179
  assert_response :not_found
168
180
  end
169
181
 
182
+ <% if has_organization_reference? -%>
170
183
  test "should not show <%= singular_table_name %> from different organization" do
171
184
  other_org = Organization.create!(name: "Other Organization")
172
185
  other_<%= singular_table_name %> = <%= class_name %>.create!(
@@ -207,6 +220,7 @@ class <%= controller_class_name_with_namespace %>ControllerTest < ActionDispatch
207
220
  get <%= api_singular_route_helper %>_url(other_<%= singular_table_name %>), headers: @auth_headers
208
221
  assert_response :not_found
209
222
  end
223
+ <% end -%>
210
224
 
211
225
  # === CREATE TESTS ===
212
226
 
@@ -242,7 +256,7 @@ class <%= controller_class_name_with_namespace %>ControllerTest < ActionDispatch
242
256
  created_<%= singular_table_name %> = response_body['data']
243
257
  assert_not_nil created_<%= singular_table_name %>, "Response should contain <%= singular_table_name %> data"
244
258
 
245
- <% if class_name != 'Organization' -%>
259
+ <% if has_organization_reference? -%>
246
260
  # Verify organization reference (handle both nested and flat formats)
247
261
  if created_<%= singular_table_name %>['organization']
248
262
  assert_equal @organization.id, created_<%= singular_table_name %>['organization']['id']
@@ -284,7 +298,7 @@ class <%= controller_class_name_with_namespace %>ControllerTest < ActionDispatch
284
298
  headers: @auth_headers
285
299
  end
286
300
 
287
- assert_response :unprocessable_entity
301
+ assert_response :unprocessable_content
288
302
 
289
303
  response_body = JSON.parse(response.body)
290
304
  assert_includes response_body.keys, 'errors'
@@ -317,7 +331,9 @@ class <%= controller_class_name_with_namespace %>ControllerTest < ActionDispatch
317
331
  -%>
318
332
 
319
333
  params = valid_<%= singular_table_name %>_params
334
+ <% if has_organization_reference? -%>
320
335
  params.delete(:organization_id) # Remove organization_id to test behavior
336
+ <% end -%>
321
337
  <% if has_user_id -%>
322
338
  params.delete(:user_id) # Remove user_id to test behavior
323
339
  <% end -%>
@@ -333,15 +349,17 @@ class <%= controller_class_name_with_namespace %>ControllerTest < ActionDispatch
333
349
  headers: @auth_headers
334
350
  end
335
351
 
336
- assert_response :unprocessable_entity
352
+ assert_response :unprocessable_content
337
353
 
338
354
  # Should return validation errors for missing tenancy
339
355
  error_response = JSON.parse(response.body)
340
356
 
357
+ <% if has_organization_reference? -%>
341
358
  if require_org_id
342
359
  assert_includes error_response['errors'].keys, 'organization_id',
343
360
  "Should require organization_id when require_organization_id = true"
344
361
  end
362
+ <% end -%>
345
363
  <% if has_user_id -%>
346
364
  if require_user_id
347
365
  assert_includes error_response['errors'].keys, 'user_id',
@@ -367,7 +385,7 @@ class <%= controller_class_name_with_namespace %>ControllerTest < ActionDispatch
367
385
  created_response = JSON.parse(response.body)
368
386
  created_<%= singular_table_name %> = created_response['data']
369
387
 
370
- <% unless class_name == 'Organization' -%>
388
+ <% if has_organization_reference? -%>
371
389
  # Verify organization_id was auto-assigned
372
390
  if created_<%= singular_table_name %>['organization']
373
391
  assert_equal @user.organization.id, created_<%= singular_table_name %>['organization']['id'],
@@ -423,12 +441,13 @@ class <%= controller_class_name_with_namespace %>ControllerTest < ActionDispatch
423
441
  params: { data: invalid_<%= singular_table_name %>_params },
424
442
  headers: @auth_headers
425
443
 
426
- assert_response :unprocessable_entity
444
+ assert_response :unprocessable_content
427
445
 
428
446
  response_body = JSON.parse(response.body)
429
447
  assert_includes response_body.keys, 'errors'
430
448
  end
431
449
 
450
+ <% if has_organization_reference? -%>
432
451
  test "should not update <%= singular_table_name %> from different organization" do
433
452
  other_org = Organization.create!(name: "Other Organization")
434
453
  other_<%= singular_table_name %> = <%= class_name %>.create!(
@@ -483,6 +502,9 @@ class <%= controller_class_name_with_namespace %>ControllerTest < ActionDispatch
483
502
  assert_response :no_content
484
503
  end
485
504
 
505
+ <% end -%>
506
+
507
+ <% if has_organization_reference? -%>
486
508
  test "should not destroy <%= singular_table_name %> from different organization" do
487
509
  other_org = Organization.create!(name: "Other Organization")
488
510
  other_<%= singular_table_name %> = <%= class_name %>.create!(
@@ -526,6 +548,7 @@ class <%= controller_class_name_with_namespace %>ControllerTest < ActionDispatch
526
548
 
527
549
  assert_response :not_found
528
550
  end
551
+ <% end -%>
529
552
 
530
553
  # === PARAMETER HANDLING TESTS ===
531
554
 
@@ -664,7 +687,12 @@ class <%= controller_class_name_with_namespace %>ControllerTest < ActionDispatch
664
687
 
665
688
  <% attributes.each_with_index do |attribute, index| -%>
666
689
  <% if attribute.type == :references -%>
690
+ <% if attribute.respond_to?(:polymorphic?) && attribute.polymorphic? -%>
691
+ <%= attribute.name %>_id: @<%= attribute.name %>.id,
692
+ <%= attribute.name %>_type: @<%= attribute.name %>.class.name<%= ',' if index < attributes.length - 1 %>
693
+ <% else -%>
667
694
  <%= attribute.name %>_id: @<%= attribute.name %>.id<%= ',' if index < attributes.length - 1 %>
695
+ <% end -%>
668
696
  <% elsif attribute.type == :string && !reference_names.include?(attribute.name) -%>
669
697
  <% if attribute.name.to_s.match?(/email/) -%>
670
698
  <%= attribute.name %>: "test@example.com"<%= ',' if index < attributes.length - 1 %>
@@ -1,8 +1,29 @@
1
1
  # Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
2
2
 
3
3
  one:
4
+ <% # Handle polymorphic associations first -%>
5
+ <% polymorphic_associations.each do |poly_assoc| -%>
6
+ <% parent_info = polymorphic_parent_for_fixture(poly_assoc[:field_name], 0) -%>
7
+ <%= poly_assoc[:field_name] %>: <%= parent_info[:fixture_name] %> (<%= parent_info[:parent_type] %>)
8
+ <% end -%>
9
+ <% # Handle regular references (excluding polymorphic ones) -%>
10
+ <% polymorphic_field_names = polymorphic_associations.map { |assoc| assoc[:field_name].to_s } -%>
4
11
  <% attributes.each do |attribute| -%>
5
- <% if attribute.type == :references -%>
12
+ <% # Skip polymorphic references - check both Rails detection and our field names -%>
13
+ <% is_polymorphic = (attribute.respond_to?(:polymorphic?) && attribute.polymorphic?) || polymorphic_field_names.include?(attribute.name.to_s) -%>
14
+ <% if attribute.type == :references && !is_polymorphic -%>
15
+ <% if attribute.name == 'organization' -%>
16
+ <%= attribute.name %>: acme_org
17
+ <% elsif attribute.name == 'user' -%>
18
+ <%= attribute.name %>: john_user
19
+ <% elsif attribute.name == 'agency' -%>
20
+ <%= attribute.name %>: marketing_agency
21
+ <% else -%>
22
+ <%= attribute.name %>: one
23
+ <% end -%>
24
+ <% elsif attribute.type == :references && is_polymorphic -%>
25
+ <% # Polymorphic references are handled above, skip them here -%>
26
+ <% elsif attribute.type == :string -%>
6
27
  <% if attribute.name == 'organization' -%>
7
28
  <%= attribute.name %>: acme_org
8
29
  <% elsif attribute.name == 'user' -%>
@@ -80,11 +101,11 @@ one:
80
101
  <%= attribute.name %>: "2024-06-15 10:30:00"
81
102
  <% elsif attribute.type == :time -%>
82
103
  <%= attribute.name %>: "10:30:00"
83
- <% elsif attribute.type == :json || attribute.name.to_s.match?(/^(meta|settings)$/) -%>
84
- <% if attribute.name == 'meta' -%>
85
- <%= attribute.name %>: { fixture_meta: "test_one", environment: "test", record_type: "<%= singular_table_name %>" }
104
+ <% elsif attribute.type == :json || attribute.name.to_s.match?(/^(metadata|settings)$/) -%>
105
+ <% if attribute.name == 'metadata' -%>
106
+ <%= attribute.name %>: { resource_type: "<%= singular_table_name %>", category: "test", tags: ["fixture", "one"], attributes: { priority: "high", status_info: { verified: true, active: true } } }
86
107
  <% elsif attribute.name == 'settings' -%>
87
- <%= attribute.name %>: { dark_mode: false, money_format: "usd", language: "en", test_mode: true }
108
+ <%= attribute.name %>: { ui_preferences: { theme: "light", language: "en", notifications: { email: true, sms: false, push: true } }, feature_flags: { beta_features: false, analytics: true } }
88
109
  <% else -%>
89
110
  <%= attribute.name %>: { test_data: "fixture_one" }
90
111
  <% end -%>
@@ -94,8 +115,17 @@ one:
94
115
  <% end -%>
95
116
 
96
117
  two:
118
+ <% # Handle polymorphic associations first -%>
119
+ <% polymorphic_associations.each do |poly_assoc| -%>
120
+ <% parent_info = polymorphic_parent_for_fixture(poly_assoc[:field_name], 1) -%>
121
+ <%= poly_assoc[:field_name] %>: <%= parent_info[:fixture_name] %> (<%= parent_info[:parent_type] %>)
122
+ <% end -%>
123
+ <% # Handle regular references (excluding polymorphic ones) -%>
124
+ <% polymorphic_field_names = polymorphic_associations.map { |assoc| assoc[:field_name].to_s } -%>
97
125
  <% attributes.each do |attribute| -%>
98
- <% if attribute.type == :references -%>
126
+ <% # Skip polymorphic references - check both Rails detection and our field names -%>
127
+ <% is_polymorphic = (attribute.respond_to?(:polymorphic?) && attribute.polymorphic?) || polymorphic_field_names.include?(attribute.name.to_s) -%>
128
+ <% if attribute.type == :references && !is_polymorphic -%>
99
129
  <% if attribute.name == 'organization' -%>
100
130
  <%= attribute.name %>: tech_startup
101
131
  <% elsif attribute.name == 'user' -%>
@@ -105,6 +135,8 @@ two:
105
135
  <% else -%>
106
136
  <%= attribute.name %>: one
107
137
  <% end -%>
138
+ <% elsif attribute.type == :references && is_polymorphic -%>
139
+ <% # Polymorphic references are handled above, skip them here -%>
108
140
  <% elsif attribute.type == :string -%>
109
141
  <% if attribute.name.to_s.match?(/\A(email|email_address)\z/i) -%>
110
142
  <%= attribute.name %>: test2@example.com
@@ -173,11 +205,11 @@ two:
173
205
  <%= attribute.name %>: "2023-12-25 15:45:00"
174
206
  <% elsif attribute.type == :time -%>
175
207
  <%= attribute.name %>: "15:45:00"
176
- <% elsif attribute.type == :json || attribute.name.to_s.match?(/^(meta|settings)$/) -%>
177
- <% if attribute.name == 'meta' -%>
178
- <%= attribute.name %>: { fixture_meta: "test_two", environment: "test", record_type: "<%= singular_table_name %>" }
208
+ <% elsif attribute.type == :json || attribute.name.to_s.match?(/^(metadata|settings)$/) -%>
209
+ <% if attribute.name == 'metadata' -%>
210
+ <%= attribute.name %>: { resource_type: "<%= singular_table_name %>", category: "premium", tags: ["fixture", "two"], attributes: { priority: "medium", status_info: { verified: false, active: true } } }
179
211
  <% elsif attribute.name == 'settings' -%>
180
- <%= attribute.name %>: { dark_mode: true, money_format: "eur", language: "fr", test_mode: true }
212
+ <%= attribute.name %>: { ui_preferences: { theme: "dark", language: "fr", notifications: { email: true, sms: true, push: false } }, feature_flags: { beta_features: true, analytics: false } }
181
213
  <% else -%>
182
214
  <%= attribute.name %>: { test_data: "fixture_two" }
183
215
  <% end -%>
@@ -187,8 +219,17 @@ two:
187
219
  <% end -%>
188
220
 
189
221
  three:
222
+ <% # Handle polymorphic associations first -%>
223
+ <% polymorphic_associations.each do |poly_assoc| -%>
224
+ <% parent_info = polymorphic_parent_for_fixture(poly_assoc[:field_name], 2) -%>
225
+ <%= poly_assoc[:field_name] %>: <%= parent_info[:fixture_name] %> (<%= parent_info[:parent_type] %>)
226
+ <% end -%>
227
+ <% # Handle regular references (excluding polymorphic ones) -%>
228
+ <% polymorphic_field_names = polymorphic_associations.map { |assoc| assoc[:field_name].to_s } -%>
190
229
  <% attributes.each do |attribute| -%>
191
- <% if attribute.type == :references -%>
230
+ <% # Skip polymorphic references - check both Rails detection and our field names -%>
231
+ <% is_polymorphic = (attribute.respond_to?(:polymorphic?) && attribute.polymorphic?) || polymorphic_field_names.include?(attribute.name.to_s) -%>
232
+ <% if attribute.type == :references && !is_polymorphic -%>
192
233
  <% if attribute.name == 'organization' -%>
193
234
  <%= attribute.name %>: acme_org
194
235
  <% elsif attribute.name == 'user' -%>
@@ -198,6 +239,8 @@ three:
198
239
  <% else -%>
199
240
  <%= attribute.name %>: one
200
241
  <% end -%>
242
+ <% elsif attribute.type == :references && is_polymorphic -%>
243
+ <% # Polymorphic references are handled above, skip them here -%>
201
244
  <% elsif attribute.type == :string -%>
202
245
  <% if attribute.name.to_s.match?(/\A(email|email_address)\z/i) -%>
203
246
  <%= attribute.name %>: test3@example.com
@@ -266,11 +309,11 @@ three:
266
309
  <%= attribute.name %>: "2025-03-10 08:15:00"
267
310
  <% elsif attribute.type == :time -%>
268
311
  <%= attribute.name %>: "08:15:00"
269
- <% elsif attribute.type == :json || attribute.name.to_s.match?(/^(meta|settings)$/) -%>
270
- <% if attribute.name == 'meta' -%>
271
- <%= attribute.name %>: { fixture_meta: "test_three", environment: "test", record_type: "<%= singular_table_name %>" }
312
+ <% elsif attribute.type == :json || attribute.name.to_s.match?(/^(metadata|settings)$/) -%>
313
+ <% if attribute.name == 'metadata' -%>
314
+ <%= attribute.name %>: { resource_type: "<%= singular_table_name %>", category: "basic", tags: ["fixture", "three"], attributes: { priority: "low", status_info: { verified: true, active: false } } }
272
315
  <% elsif attribute.name == 'settings' -%>
273
- <%= attribute.name %>: { dark_mode: true, money_format: "gbp", language: "es", test_mode: true }
316
+ <%= attribute.name %>: { ui_preferences: { theme: "auto", language: "es", notifications: { email: false, sms: true, push: true } }, feature_flags: { beta_features: false, analytics: true } }
274
317
  <% else -%>
275
318
  <%= attribute.name %>: { test_data: "fixture_three" }
276
319
  <% end -%>