Class: PropelApi::ResourceGenerator

Inherits:
NamedBase
  • Object
show all
Defined in:
lib/generators/propel_api/resource/resource_generator.rb

Instance Method Summary collapse

Methods included from ConfigurationMethods

included

Constructor Details

#initialize(args = [], local_options = {}, config = {}) ⇒ ResourceGenerator

Returns a new instance of ResourceGenerator.



47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/generators/propel_api/resource/resource_generator.rb', line 47

def initialize(args = [], local_options = {}, config = {})
  # Process attributes to handle our custom polymorphic syntax
  processed_args = args.dup
  if processed_args.length > 1
    # Convert user-friendly polymorphic syntax to Rails format
    processed_args[1..-1] = processed_args[1..-1].map do |attr|
      convert_polymorphic_syntax(attr)
    end
  end
  
  super(processed_args, local_options, config)
end

Instance Method Details

#add_routesObject



146
147
148
# File 'lib/generators/propel_api/resource/resource_generator.rb', line 146

def add_routes
  create_propel_routes
end

#create_controllerObject



142
143
144
# File 'lib/generators/propel_api/resource/resource_generator.rb', line 142

def create_controller
  create_propel_controller_template
end

#create_controller_testObject



154
155
156
# File 'lib/generators/propel_api/resource/resource_generator.rb', line 154

def create_controller_test
  create_propel_controller_test unless behavior == :revoke && options[:skip_tests]
end

#create_fixturesObject



162
163
164
# File 'lib/generators/propel_api/resource/resource_generator.rb', line 162

def create_fixtures
  create_propel_fixtures unless behavior == :revoke && options[:skip_tests]
end

#create_integration_testObject



158
159
160
# File 'lib/generators/propel_api/resource/resource_generator.rb', line 158

def create_integration_test
  create_propel_integration_test unless behavior == :revoke && options[:skip_tests]
end

#create_migrationObject



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/generators/propel_api/resource/resource_generator.rb', line 79

def create_migration
  initialize_propel_api_settings
  
  if behavior == :revoke
    # Check for critical dependencies before destroying
    check_for_critical_dependencies
    
    # Generate proper removal migration following Rails conventions
    generate_removal_migration
  else
    # Check for tenancy and warn if missing (unless warnings are disabled)
    check_tenancy_attributes_and_warn unless options[:skip_tenancy]
    
    # Build migration arguments from processed attributes
    # This ensures our custom polymorphic syntax is converted to Rails format
    migration_args = attributes.map do |attr|
      if attr.type == :references && attr.respond_to?(:polymorphic?) && attr.polymorphic?
        "#{attr.name}:references{polymorphic}"
      else
        "#{attr.name}:#{attr.type}"
      end
    end
    
    generate :migration, "create_#{table_name}", *migration_args
    # Post-process migration to make non-organization references nullable
    update_migration_constraints
  end
end

#create_modelObject



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/generators/propel_api/resource/resource_generator.rb', line 108

def create_model
  initialize_propel_api_settings
  
  # Initialize relationship inferrer for templates
  @relationship_inferrer = RelationshipInferrer.new(class_name, attributes, { 
    skip_associations: options[:skip_associations]
  })
  
  # Validate attributes if not using --all_attributes and model already exists
  if File.exist?(File.join(destination_root, "app/models/#{file_name}.rb"))
    validate_attributes_exist
  end
  
  # Note: Relationship cleanup is now handled by the comprehensive dependency auto-fix system
  # No need for separate inverse relationship logic here
  
  # Direct template call - Rails can reverse this automatically (unless skipped)!
  unless behavior == :revoke && options[:skip_model]
    case @adapter
    when 'propel_facets'
      template "scaffold/facet_model_template.rb.tt", "app/models/#{file_name}.rb"
    when 'graphiti'
      template "scaffold/graphiti_model_template.rb.tt", "app/models/#{file_name}.rb"
    else
      raise "Unknown adapter: #{@adapter}. Run 'rails generate propel_api:install' first."
    end
  end

  # Apply inverse relationships AFTER template creation
  if behavior != :revoke
    apply_inverse_relationships
  end
end

#create_model_testObject



150
151
152
# File 'lib/generators/propel_api/resource/resource_generator.rb', line 150

def create_model_test
  create_propel_model_test unless behavior == :revoke && options[:skip_tests]
end

#create_seedsObject



166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
# File 'lib/generators/propel_api/resource/resource_generator.rb', line 166

def create_seeds
  if behavior == :revoke
    # Remove seeds file when destroying
    seeds_file_path = "db/seeds/#{table_name}_seeds.rb"
    full_seeds_path = File.join(destination_root, seeds_file_path)
    if File.exist?(full_seeds_path)
      File.delete(full_seeds_path)
      say "Removed seeds file: #{seeds_file_path}", :red
    end
    
    # Remove require from main seeds file
    seeds_file = File.join(destination_root, "db/seeds.rb")
    seeds_require = "require_relative 'seeds/#{table_name}_seeds'"
    
    if File.exist?(seeds_file)
      seeds_content = File.read(seeds_file)
      if seeds_content.include?(seeds_require)
        updated_content = seeds_content.dup
        
        # Try multiple patterns to remove the require and any associated comment
        patterns_to_try = [
          # Pattern 1: Comment above the require
          /\n# #{class_name} seeds\n#{Regexp.escape(seeds_require)}\n?/,
          # Pattern 2: Comment with different casing
          /\n# #{class_name.downcase} seeds\n#{Regexp.escape(seeds_require)}\n?/,
          # Pattern 3: Just the require line with newlines
          /\n#{Regexp.escape(seeds_require)}\n/,
          # Pattern 4: Just the require line at start of line
          /^#{Regexp.escape(seeds_require)}\n/,
          # Pattern 5: Just the require line without trailing newline
          /#{Regexp.escape(seeds_require)}/
        ]
        
        removed = false
        patterns_to_try.each do |pattern|
          if updated_content.match?(pattern)
            updated_content = updated_content.gsub(pattern, '')
            removed = true
            break
          end
        end
        
        if removed
          # Clean up any double newlines that might result
          updated_content = updated_content.gsub(/\n\n+/, "\n\n")
          # Remove leading/trailing whitespace
          updated_content = updated_content.strip + "\n" if updated_content.strip.present?
          
        File.write(seeds_file, updated_content)
          say "Removed require statement from db/seeds.rb", :red
        else
          say "Could not automatically remove require statement from db/seeds.rb", :yellow
        end
      end
    end
  else
    initialize_propel_api_settings
    
    # Generate seeds file
    template "seeds/seeds_template.rb.tt", "db/seeds/#{table_name}_seeds.rb"
    
    # Add require to main seeds file
    seeds_file = File.join(destination_root, "db/seeds.rb")
    seeds_require = "require_relative 'seeds/#{table_name}_seeds'"
    
    if File.exist?(seeds_file)
      seeds_content = File.read(seeds_file)
      unless seeds_content.include?(seeds_require)
        append_to_file "db/seeds.rb", "\n# #{class_name} seeds\n#{seeds_require}\n"
      end
    else
      create_file "db/seeds.rb", "#{seeds_require}\n"
    end
  end
end

#show_completion_messageObject



242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
# File 'lib/generators/propel_api/resource/resource_generator.rb', line 242

def show_completion_message
  # Use shared completion message framework from Base class
  generated_files = [
    "๐Ÿ“„ Model: app/models/#{file_name}.rb",
    "๐ŸŽฎ Controller: app/controllers/#{controller_path}/#{controller_file_name}.rb",
    "๐Ÿ—„๏ธ  Migration: db/migrate/*_create_#{table_name}.rb",
    "๐ŸŒฑ Seeds: db/seeds/#{table_name}_seeds.rb",
    "๐Ÿงช Model Tests: test/models/#{file_name}_test.rb",
    "๐Ÿงช Controller Tests: test/controllers/#{controller_path}/#{controller_file_name}_test.rb",
    "๐Ÿงช Integration Tests: test/integration/#{file_name}_api_test.rb",
    "๐Ÿ“‹ Test Fixtures: test/fixtures/#{table_name}.yml"
  ]
  
  # Add Graphiti resource if using graphiti adapter
  if @adapter == 'graphiti'
    generated_files << "๐Ÿ“Š Graphiti Resource: app/resources/#{file_name}_resource.rb"
  end
  
  next_steps = [
    "Run migration: rails db:migrate",
    "Generate test data: rails db:seed",
    "Run tests: rails test test/models/#{file_name}_test.rb",
    "Run all tests: rails test",
    "Test your API: GET #{api_route_path}"
  ]
  
  if @adapter == 'propel_facets'
    next_steps << "Customize facets in: app/models/#{file_name}.rb"
  end
  
  if behavior == :revoke
    # Use shared destroy completion message from named_base
    show_destroy_completion_message
  else
    show_propel_completion_message(
      resource_type: "Resource",
      generated_files: generated_files,
      next_steps: next_steps
    )
  end
end