propel_api 0.3.1.6 → 0.3.2
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.
@@ -80,47 +80,11 @@ module PropelApi
|
|
80
80
|
initialize_propel_api_settings
|
81
81
|
|
82
82
|
if behavior == :revoke
|
83
|
-
#
|
84
|
-
|
83
|
+
# Check for critical dependencies before destroying
|
84
|
+
check_for_critical_dependencies
|
85
85
|
|
86
|
-
|
87
|
-
|
88
|
-
migration_version = File.basename(migration_file).split('_').first
|
89
|
-
|
90
|
-
# Check if migration was executed
|
91
|
-
if migration_was_executed?(migration_version)
|
92
|
-
say "\n" + "="*80, :red
|
93
|
-
say "⚠️ MIGRATION ALREADY EXECUTED!", :red
|
94
|
-
say "="*80, :red
|
95
|
-
say "\n📋 Migration #{migration_version} (create_#{table_name}) has been executed.", :yellow
|
96
|
-
say "\n🚨 Rails does not automatically rollback migrations for safety reasons.", :yellow
|
97
|
-
say "\n✅ To safely destroy this resource:", :green
|
98
|
-
say " 1. First rollback: rails db:rollback", :green
|
99
|
-
say " 2. Then destroy: rails destroy propel_api #{class_name}", :green
|
100
|
-
say "\n💡 Alternative approaches:", :cyan
|
101
|
-
say " • Check migration status: rails db:migrate:status"
|
102
|
-
say " • Rollback specific: rails db:migrate:down VERSION=#{migration_version}"
|
103
|
-
say "\n" + "="*80, :red
|
104
|
-
raise Thor::Error, "Please rollback the migration first, then re-run destroy command."
|
105
|
-
else
|
106
|
-
# Migration not executed, check if safe to delete
|
107
|
-
if safe_to_delete_migration?(migration_version)
|
108
|
-
say "Removing migration: #{File.basename(migration_file)}", :red
|
109
|
-
File.delete(migration_file)
|
110
|
-
else
|
111
|
-
say "\n" + "="*80, :yellow
|
112
|
-
say "⚠️ MIGRATION CLEANUP REQUIRED", :yellow
|
113
|
-
say "="*80, :yellow
|
114
|
-
say "\n📋 Found unexecuted migration: #{File.basename(migration_file)}", :cyan
|
115
|
-
say "\n🚨 Cannot auto-delete due to intervening migrations.", :yellow
|
116
|
-
say "💡 Please manually remove when safe:", :green
|
117
|
-
say " rm #{migration_file}"
|
118
|
-
say "\n" + "="*80, :yellow
|
119
|
-
end
|
120
|
-
end
|
121
|
-
else
|
122
|
-
say "No migration file found for #{table_name}", :yellow
|
123
|
-
end
|
86
|
+
# Generate proper removal migration following Rails conventions
|
87
|
+
generate_removal_migration
|
124
88
|
else
|
125
89
|
# Check for tenancy and warn if missing (unless warnings are disabled)
|
126
90
|
check_tenancy_attributes_and_warn unless options[:skip_tenancy]
|
@@ -154,32 +118,49 @@ module PropelApi
|
|
154
118
|
validate_attributes_exist
|
155
119
|
end
|
156
120
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
121
|
+
# Note: Relationship cleanup is now handled by the comprehensive dependency auto-fix system
|
122
|
+
# No need for separate inverse relationship logic here
|
123
|
+
|
124
|
+
# Direct template call - Rails can reverse this automatically (unless skipped)!
|
125
|
+
unless behavior == :revoke && options[:skip_model]
|
126
|
+
case @adapter
|
127
|
+
when 'propel_facets'
|
128
|
+
template "scaffold/facet_model_template.rb.tt", "app/models/#{file_name}.rb"
|
129
|
+
when 'graphiti'
|
130
|
+
template "scaffold/graphiti_model_template.rb.tt", "app/models/#{file_name}.rb"
|
131
|
+
else
|
132
|
+
raise "Unknown adapter: #{@adapter}. Run 'rails generate propel_api:install' first."
|
133
|
+
end
|
164
134
|
end
|
165
135
|
|
166
|
-
# Apply inverse relationships
|
167
|
-
|
136
|
+
# Apply inverse relationships AFTER template creation
|
137
|
+
if behavior != :revoke
|
138
|
+
apply_inverse_relationships
|
139
|
+
end
|
168
140
|
end
|
169
141
|
|
170
142
|
def create_controller
|
171
|
-
|
172
|
-
create_propel_controller
|
143
|
+
create_propel_controller_template
|
173
144
|
end
|
174
145
|
|
175
|
-
def
|
176
|
-
# Use shared method from Base class
|
146
|
+
def add_routes
|
177
147
|
create_propel_routes
|
178
148
|
end
|
179
149
|
|
180
|
-
def
|
181
|
-
|
182
|
-
|
150
|
+
def create_model_test
|
151
|
+
create_propel_model_test unless behavior == :revoke && options[:skip_tests]
|
152
|
+
end
|
153
|
+
|
154
|
+
def create_controller_test
|
155
|
+
create_propel_controller_test unless behavior == :revoke && options[:skip_tests]
|
156
|
+
end
|
157
|
+
|
158
|
+
def create_integration_test
|
159
|
+
create_propel_integration_test unless behavior == :revoke && options[:skip_tests]
|
160
|
+
end
|
161
|
+
|
162
|
+
def create_fixtures
|
163
|
+
create_propel_fixtures unless behavior == :revoke && options[:skip_tests]
|
183
164
|
end
|
184
165
|
|
185
166
|
def create_seeds
|
@@ -288,13 +269,20 @@ module PropelApi
|
|
288
269
|
next_steps << "Customize facets in: app/models/#{file_name}.rb"
|
289
270
|
end
|
290
271
|
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
272
|
+
if behavior == :revoke
|
273
|
+
# Use shared destroy completion message from named_base
|
274
|
+
show_destroy_completion_message
|
275
|
+
else
|
276
|
+
show_propel_completion_message(
|
277
|
+
resource_type: "Resource",
|
278
|
+
generated_files: generated_files,
|
279
|
+
next_steps: next_steps
|
280
|
+
)
|
281
|
+
end
|
296
282
|
end
|
297
283
|
|
284
|
+
|
285
|
+
|
298
286
|
private
|
299
287
|
|
300
288
|
def relationship_inferrer
|
@@ -419,6 +407,130 @@ module PropelApi
|
|
419
407
|
end
|
420
408
|
end
|
421
409
|
|
410
|
+
def remove_inverse_relationships
|
411
|
+
# Use Rails model reflection - much cleaner approach!
|
412
|
+
begin
|
413
|
+
model_class = class_name.constantize
|
414
|
+
|
415
|
+
# Get all belongs_to associations using Rails reflection
|
416
|
+
belongs_to_associations = model_class.reflect_on_all_associations(:belongs_to)
|
417
|
+
|
418
|
+
belongs_to_associations.each do |association|
|
419
|
+
# Skip polymorphic associations (they don't get inverse relationships)
|
420
|
+
next if association.polymorphic?
|
421
|
+
|
422
|
+
parent_class_name = association.class_name
|
423
|
+
parent_model_file = "app/models/#{parent_class_name.underscore}.rb"
|
424
|
+
full_path = File.join(destination_root, parent_model_file)
|
425
|
+
|
426
|
+
# Skip if parent model doesn't exist
|
427
|
+
next unless File.exist?(full_path)
|
428
|
+
|
429
|
+
# The inverse relationship we need to remove
|
430
|
+
inverse_relationship = "has_many :#{table_name}, dependent: :destroy"
|
431
|
+
|
432
|
+
begin
|
433
|
+
remove_specific_relationship(full_path, inverse_relationship, parent_class_name)
|
434
|
+
rescue => e
|
435
|
+
say "Warning: Could not remove relationship from #{parent_model_file}: #{e.message}", :yellow
|
436
|
+
end
|
437
|
+
end
|
438
|
+
|
439
|
+
rescue NameError => e
|
440
|
+
# Fallback: Parse attributes from migration file if model class doesn't exist
|
441
|
+
migration_attributes = get_attributes_from_migration
|
442
|
+
belongs_to_attributes = migration_attributes.select { |attr| attr[:type] == :references }
|
443
|
+
|
444
|
+
belongs_to_attributes.each do |attr|
|
445
|
+
# Skip polymorphic references
|
446
|
+
next if attr[:polymorphic]
|
447
|
+
|
448
|
+
parent_class_name = attr[:name].classify
|
449
|
+
parent_model_file = "app/models/#{parent_class_name.underscore}.rb"
|
450
|
+
full_path = File.join(destination_root, parent_model_file)
|
451
|
+
|
452
|
+
# Skip if parent model doesn't exist
|
453
|
+
next unless File.exist?(full_path)
|
454
|
+
|
455
|
+
inverse_relationship = "has_many :#{table_name}, dependent: :destroy"
|
456
|
+
|
457
|
+
begin
|
458
|
+
remove_specific_relationship(full_path, inverse_relationship, parent_class_name)
|
459
|
+
rescue => e
|
460
|
+
say "Warning: Could not remove relationship from #{parent_model_file}: #{e.message}", :yellow
|
461
|
+
end
|
462
|
+
end
|
463
|
+
end
|
464
|
+
end
|
465
|
+
|
466
|
+
def get_attributes_from_migration
|
467
|
+
# Find the migration file (using same pattern as create_migration)
|
468
|
+
migration_files = Dir[File.join(destination_root, "db/migrate/*_create_#{table_name}.rb")]
|
469
|
+
return [] unless migration_files.any?
|
470
|
+
|
471
|
+
migration_file = migration_files.first
|
472
|
+
migration_content = File.read(migration_file)
|
473
|
+
|
474
|
+
# Parse t.references and t.string, t.text, etc. lines
|
475
|
+
parsed_attributes = []
|
476
|
+
|
477
|
+
# Match references with polymorphic option
|
478
|
+
migration_content.scan(/t\.references\s+:(\w+).*?(polymorphic:\s*true)?/) do |name, polymorphic|
|
479
|
+
parsed_attributes << {
|
480
|
+
name: name,
|
481
|
+
type: :references,
|
482
|
+
polymorphic: !polymorphic.nil?
|
483
|
+
}
|
484
|
+
end
|
485
|
+
|
486
|
+
# Match other attribute types
|
487
|
+
%w[string text integer decimal boolean datetime date json jsonb].each do |type|
|
488
|
+
migration_content.scan(/t\.#{type}\s+:(\w+)/) do |name|
|
489
|
+
parsed_attributes << {
|
490
|
+
name: name.first,
|
491
|
+
type: type.to_sym,
|
492
|
+
polymorphic: false
|
493
|
+
}
|
494
|
+
end
|
495
|
+
end
|
496
|
+
|
497
|
+
parsed_attributes
|
498
|
+
end
|
499
|
+
|
500
|
+
def remove_specific_relationship(model_file_path, relationship, parent_class_name)
|
501
|
+
content = File.read(model_file_path)
|
502
|
+
original_content = content.dup
|
503
|
+
|
504
|
+
# Remove the relationship line if it exists
|
505
|
+
relationship_pattern = /^\s*#{Regexp.escape(relationship)}\s*\n/
|
506
|
+
if content.match?(relationship_pattern)
|
507
|
+
content = content.gsub(relationship_pattern, '')
|
508
|
+
File.write(model_file_path, content)
|
509
|
+
say " ❌ Removed from #{parent_class_name}: #{relationship}", :red
|
510
|
+
else
|
511
|
+
say " ⚠️ Relationship not found in #{parent_class_name}: #{relationship}", :yellow
|
512
|
+
end
|
513
|
+
end
|
514
|
+
|
515
|
+
def remove_from_parent_model(model_file_path, relationships, parent_class_name)
|
516
|
+
content = File.read(model_file_path)
|
517
|
+
original_content = content.dup
|
518
|
+
|
519
|
+
relationships.each do |relationship|
|
520
|
+
# Remove the relationship line if it exists
|
521
|
+
relationship_pattern = /^\s*#{Regexp.escape(relationship)}\s*\n/
|
522
|
+
if content.match?(relationship_pattern)
|
523
|
+
content = content.gsub(relationship_pattern, '')
|
524
|
+
say " ❌ Removed from #{parent_class_name}: #{relationship}", :red
|
525
|
+
end
|
526
|
+
end
|
527
|
+
|
528
|
+
# Only write if content changed
|
529
|
+
if content != original_content
|
530
|
+
File.write(model_file_path, content)
|
531
|
+
end
|
532
|
+
end
|
533
|
+
|
422
534
|
def update_parent_model(model_file_path, relationships, parent_class_name)
|
423
535
|
content = File.read(model_file_path)
|
424
536
|
original_content = content.dup
|
data/lib/propel_api.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: propel_api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryan Martin, Rafael Pivato, Chi Putera
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-09-
|
11
|
+
date: 2025-09-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|