Class: PropelAuthentication::InstallGenerator

Inherits:
Rails::Generators::Base
  • Object
show all
Includes:
ConfigurationMethods, Rails::Generators::Migration
Defined in:
lib/generators/propel_authentication/install_generator.rb

Class Method Summary collapse

Instance Method Summary collapse

Methods included from ConfigurationMethods

included

Class Method Details

.next_migration_number(dir) ⇒ Object



30
31
32
# File 'lib/generators/propel_authentication/install_generator.rb', line 30

def self.next_migration_number(dir)
  ActiveRecord::Generators::Base.next_migration_number(dir)
end

Instance Method Details

#add_auth_routes_simpleObject



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
# File 'lib/generators/propel_authentication/install_generator.rb', line 188

def add_auth_routes_simple
   routes_content = File.read("config/routes.rb")
  
  # Check if auth routes already exist - be specific
  if routes_content.include?("post 'login', to: 'tokens#create'")
    say "Authentication routes already exist, skipping", :yellow
    return
  end
  
  auth_routes = [
    "post 'signup', to: 'signup#create', as: :signup",
    "post 'login', to: 'tokens#create', as: :login",
    "get 'me', to: 'tokens#show', as: :me", 
    "delete 'logout', to: 'tokens#destroy', as: :logout",
    "post 'unlock', to: 'tokens#unlock', as: :unlock",
    "post 'reset', to: 'passwords#create', as: :reset",
    "get 'reset', to: 'passwords#show', as: :verify_reset",
    "patch 'reset', to: 'passwords#update', as: :update_reset"
  ]
  
  # Try to insert into existing namespace, otherwise create new
  if @auth_namespace.present? && @auth_version.present?
    # Look for api/v1 pattern  
    pattern = /namespace\s+:#{@auth_namespace}\s+do\s*\n\s*namespace\s+:#{@auth_version}\s+do\s*\n/
    if routes_content.match?(pattern)
      # Insert into existing namespace with proper indentation
      gsub_file "config/routes.rb", pattern do |match|
        routes_to_add = auth_routes.map { |r| "      #{r}" }.join("\n") + "\n"
        match + routes_to_add
      end
      say "Added authentication routes to existing #{@auth_namespace}/#{@auth_version} namespace", :green
    else
      # Create new namespace
      route_block = "namespace :#{@auth_namespace} do\n  namespace :#{@auth_version} do\n"
      auth_routes.each { |r| route_block += "    #{r}\n" }
      route_block += "  end\nend"
      route route_block
      say "Created new #{@auth_namespace}/#{@auth_version} namespace with authentication routes", :green
    end
  else
    # Fallback - just add routes
    route auth_routes.join("\n")
    say "Added authentication routes at root level", :green
  end
end

#add_authentication_gemsObject



141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/generators/propel_authentication/install_generator.rb', line 141

def add_authentication_gems
  add_gem_if_missing('bcrypt', '~> 3.1.20')
  add_gem_if_missing('jwt', '~> 3.1.2')
  add_gem_if_missing('noticed', '~> 2.8.1')
  add_gem_if_missing('letter_opener', '~> 1.10', group: :development)
  
  say "šŸ“¦ Installing gems...", :blue
  run "bundle install"
  
  # Require bcrypt so it's available for fixture templates
  begin
    require 'bcrypt'
  rescue LoadError
    say "āš ļø  BCrypt not yet available, fixtures may need manual adjustment", :yellow
  end
end

#add_authentication_routesObject



173
174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/generators/propel_authentication/install_generator.rb', line 173

def add_authentication_routes
  routes_file = File.join(destination_root, "config/routes.rb")
  return unless File.exist?(routes_file)
  
  routes_content = File.read(routes_file)
  
  # Check if authentication routes already exist
  if authentication_routes_exist?(routes_content)
    say "Authentication routes already exist, skipping route generation", :yellow
    return
  end
  
  add_auth_routes_simple
end

#copy_authentication_concernsObject



91
92
93
94
95
96
97
98
# File 'lib/generators/propel_authentication/install_generator.rb', line 91

def copy_authentication_concerns
  copy_file "authenticatable.rb", "app/models/concerns/authenticatable.rb"
  copy_file "concerns/lockable.rb", "app/models/concerns/lockable.rb"
  copy_file "concerns/recoverable.rb", "app/models/concerns/recoverable.rb"
  copy_file "concerns/confirmable.rb", "app/models/concerns/confirmable.rb"
  copy_file "concerns/propel_authentication_concern.rb", "app/controllers/concerns/propel_authentication_concern.rb"
  copy_file "concerns/rack_session_disable.rb", "app/controllers/concerns/rack_session_disable.rb"
end

#copy_authentication_migrations_and_fixturesObject



234
235
236
237
238
239
240
241
242
243
244
245
# File 'lib/generators/propel_authentication/install_generator.rb', line 234

def copy_authentication_migrations_and_fixtures
  # Organizations must be created first if required (users depend on organizations)
  copy_organizations_migration_and_fixtures if organization_required?
  # Agencies must be created before agents and users (if agency auth is enabled)
  copy_agencies_migration_and_fixtures if agency_required?
  # Users can now be created (organizations and agencies exist)
  copy_users_migration_and_fixtures
  # Agents depend on users and agencies
  copy_agents_migration_and_fixtures if agency_required?
  # Invitations depend on users and organizations
  copy_invitations_migration_and_fixtures
end

#copy_authentication_modelsObject



76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/generators/propel_authentication/install_generator.rb', line 76

def copy_authentication_models
  # Detect rendering engine for conditional facet generation
  @rendering_engine = detect_rendering_engine
  # Organizations must be created first if required (users depend on organizations)
  copy_organizations_model if organization_required?
  # Agencies must be created before agents and users (if agency auth is enabled)
  copy_agencies_model if agency_required?
  # Users can now be created (organizations and agencies exist)
  copy_users_model
  # Agents depend on users and agencies
  copy_agents_model if agency_required?
  # Invitations depend on users and organizations
  copy_invitations_model
end

#copy_authentication_testsObject



123
124
125
126
127
128
129
# File 'lib/generators/propel_authentication/install_generator.rb', line 123

def copy_authentication_tests
  template "user_test.rb.tt", "test/models/user_test.rb"
  template "test/controllers/auth/tokens_controller_test.rb.tt", "test/controllers/auth/tokens_controller_test.rb"
  template "test/controllers/auth/signup_controller_test.rb.tt", "test/controllers/auth/signup_controller_test.rb"
  template "test/mailers/auth_mailer_test.rb.tt", "test/mailers/auth_mailer_test.rb"
  copy_file "test/mailers/previews/auth_mailer_preview.rb", "test/mailers/previews/auth_mailer_preview.rb"
end

#copy_concern_testsObject



131
132
133
134
135
136
137
138
# File 'lib/generators/propel_authentication/install_generator.rb', line 131

def copy_concern_tests
  template "test/concerns/propel_authentication_test.rb.tt", "test/concerns/propel_authentication_test.rb"
  template "test/concerns/lockable_test.rb.tt", "test/concerns/lockable_test.rb"
  template "test/concerns/recoverable_test.rb.tt", "test/concerns/recoverable_test.rb"
  template "test/concerns/confirmable_test.rb.tt", "test/concerns/confirmable_test.rb"
  template "test/controllers/auth/lockable_integration_test.rb.tt", "test/controllers/auth/lockable_integration_test.rb"
  template "test/controllers/auth/password_reset_integration_test.rb.tt", "test/controllers/auth/password_reset_integration_test.rb"
end

#copy_database_seedsObject



247
248
249
250
251
252
253
254
255
256
257
258
# File 'lib/generators/propel_authentication/install_generator.rb', line 247

def copy_database_seeds
  template "db/propel_seeds.rb.tt", "db/propel_seeds.rb"
  
  # Add require_relative to main seeds.rb file so propel_seeds is loaded during rails db:seed
  seeds_file = File.join(destination_root, "db/seeds.rb")
  if File.exist?(seeds_file)
    seeds_content = File.read(seeds_file)
    unless seeds_content.include?("require_relative 'propel_seeds'")
      append_to_file "db/seeds.rb", "\n# Load Propel Authentication seed data\nrequire_relative 'propel_seeds'\n"
    end
  end
end

#copy_documentationObject



119
120
121
# File 'lib/generators/propel_authentication/install_generator.rb', line 119

def copy_documentation
  copy_file "doc/signup_flow.md", "doc/signup_flow.md"
end

#copy_email_infrastructureObject



107
108
109
110
111
112
113
114
115
116
117
# File 'lib/generators/propel_authentication/install_generator.rb', line 107

def copy_email_infrastructure
  copy_file "auth_mailer.rb", "app/mailers/auth_mailer.rb"
  copy_file "services/auth_notification_service.rb", "app/services/auth_notification_service.rb"
  copy_file "config/environments/development_email.rb", "config/environments/development_email.rb"
  
  # Email view templates
  %w[password_reset account_unlock user_invitation email_confirmation].each do |template|
    copy_file "views/auth_mailer/#{template}.html.erb", "app/views/auth_mailer/#{template}.html.erb"
    copy_file "views/auth_mailer/#{template}.text.erb", "app/views/auth_mailer/#{template}.text.erb"
  end
end

#copy_jwt_controllersObject



100
101
102
103
104
105
# File 'lib/generators/propel_authentication/install_generator.rb', line 100

def copy_jwt_controllers
  # Generate direct controllers with dynamic file paths (no inheritance)
  template "auth/tokens_controller.rb.tt", controller_file_path('tokens')
  template "auth/passwords_controller.rb.tt", controller_file_path('passwords')
  template "auth/signup_controller.rb.tt", controller_file_path('signup')
end

#copy_jwt_initializerObject



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/generators/propel_authentication/install_generator.rb', line 60

def copy_jwt_initializer
  determine_configuration
  
  if behavior == :revoke
    remove_file "config/initializers/propel_authentication.rb"
    say "Removed PropelAuthentication configuration", :red
  else
    # Convert to ERB template to support configuration
    template "propel_authentication.rb.tt", "config/initializers/propel_authentication.rb"
    say "Created PropelAuthentication configuration with namespace: #{namespace_display}, version: #{version_display}", :green
    
    # Load the configuration immediately so templates can access it
    load_propel_authentication_configuration
  end
end

#extract_generator_for_customizationObject



260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
# File 'lib/generators/propel_authentication/install_generator.rb', line 260

def extract_generator_for_customization
  generator_path = "lib/generators/propel_authentication"
  
  if File.exist?(generator_path)
    say ""
    say "šŸ“¦ Generator logic already extracted at #{generator_path}/", :blue
    say "šŸ’” Skipping extraction to preserve your customizations", :cyan
  else
    say ""
    say "šŸ“¦ Extracting generator logic for full customization...", :blue
    
    # Automatically run the unpack generator to extract generator logic
    invoke PropelAuthentication::UnpackGenerator, [], { force: false }
    
    say ""
          say "āœ… Generator logic extracted to lib/generators/propel_authentication/", :green
  say "šŸ’” Your application is now completely standalone - no gem dependency needed!", :cyan
  say "šŸ—‘ļø  You can now remove 'propel_auth' from your Gemfile", :yellow
          say "šŸ“¦ All PropelAuthentication runtime code is now in config/initializers/propel_authentication.rb", :blue
  end
end

#install_noticed_gemObject



158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/generators/propel_authentication/install_generator.rb', line 158

def install_noticed_gem
  say ""
  say "šŸ“¦ Setting up Noticed gem for email notifications...", :green
  
  begin
    # Run the noticed install generator
    rails_command "noticed:install:migrations", capture: true
    say "āœ… Noticed gem configured successfully", :green
  rescue => e
    say "āš ļø  Warning: Could not run noticed:install:migrations automatically", :yellow
    say "   Please run manually: rails generate noticed:install", :yellow
    say "   Error: #{e.message}", :red if options[:verbose]
  end
end

#show_setup_complete_messageObject



282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
# File 'lib/generators/propel_authentication/install_generator.rb', line 282

def show_setup_complete_message
  say "\nšŸš€ JWT Authentication System Generated Successfully!", :green
  
  say "\nšŸ“¦ System Status: Completely standalone - no gem dependency!", :green
  say "\nNext steps:", :yellow
  say "• Run: rails db:migrate", :blue
  say "• Run: rails test", :blue
  say "• Optional: Remove 'propel_auth' from Gemfile (system is fully extracted)", :cyan
  
  if @auth_namespace.present? || @auth_version.present? || @auth_scope.present?
    say "• Routes: #{auth_route_prefix}/*", :blue
    say "• Controllers: #{controller_namespace('tokens')}*", :blue
  else
    say "• Routes: /login, /logout, /me", :blue
    say "• Controllers: TokensController, PasswordsController, etc.", :blue
  end
  
  say "\nšŸŽØ Customization:", :bold
  say "• Generator logic: lib/generators/propel_authentication/install_generator.rb", :blue
  say "• Templates: lib/generators/propel_authentication/templates/", :blue
  say "• Modify any part of the system - it's all yours now!", :cyan
  
  say "\nšŸ—‘ļø  To uninstall: rails destroy propel_authentication:install", :yellow
end