propel_authentication 0.1.1

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 (102) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +290 -0
  4. data/Rakefile +12 -0
  5. data/lib/generators/propel_auth/install_generator.rb +486 -0
  6. data/lib/generators/propel_auth/pack_generator.rb +277 -0
  7. data/lib/generators/propel_auth/templates/agency.rb +7 -0
  8. data/lib/generators/propel_auth/templates/agent.rb +7 -0
  9. data/lib/generators/propel_auth/templates/auth/base_passwords_controller.rb.tt +99 -0
  10. data/lib/generators/propel_auth/templates/auth/base_tokens_controller.rb.tt +90 -0
  11. data/lib/generators/propel_auth/templates/auth/passwords_controller.rb.tt +126 -0
  12. data/lib/generators/propel_auth/templates/auth_mailer.rb +180 -0
  13. data/lib/generators/propel_auth/templates/authenticatable.rb +38 -0
  14. data/lib/generators/propel_auth/templates/concerns/confirmable.rb +145 -0
  15. data/lib/generators/propel_auth/templates/concerns/lockable.rb +123 -0
  16. data/lib/generators/propel_auth/templates/concerns/propel_authentication.rb +44 -0
  17. data/lib/generators/propel_auth/templates/concerns/rack_session_disable.rb +19 -0
  18. data/lib/generators/propel_auth/templates/concerns/recoverable.rb +124 -0
  19. data/lib/generators/propel_auth/templates/config/environments/development_email.rb +43 -0
  20. data/lib/generators/propel_auth/templates/db/migrate/create_agencies.rb +20 -0
  21. data/lib/generators/propel_auth/templates/db/migrate/create_agents.rb +11 -0
  22. data/lib/generators/propel_auth/templates/db/migrate/create_invitations.rb +28 -0
  23. data/lib/generators/propel_auth/templates/db/migrate/create_organizations.rb +18 -0
  24. data/lib/generators/propel_auth/templates/db/migrate/create_users.rb +43 -0
  25. data/lib/generators/propel_auth/templates/db/seeds.rb +29 -0
  26. data/lib/generators/propel_auth/templates/invitation.rb +133 -0
  27. data/lib/generators/propel_auth/templates/lib/propel_auth.rb +84 -0
  28. data/lib/generators/propel_auth/templates/organization.rb +7 -0
  29. data/lib/generators/propel_auth/templates/propel_auth.rb +132 -0
  30. data/lib/generators/propel_auth/templates/services/auth_notification_service.rb +89 -0
  31. data/lib/generators/propel_auth/templates/test/concerns/confirmable_test.rb.tt +247 -0
  32. data/lib/generators/propel_auth/templates/test/concerns/lockable_test.rb.tt +282 -0
  33. data/lib/generators/propel_auth/templates/test/concerns/propel_authentication_test.rb.tt +75 -0
  34. data/lib/generators/propel_auth/templates/test/concerns/recoverable_test.rb.tt +327 -0
  35. data/lib/generators/propel_auth/templates/test/controllers/auth/lockable_integration_test.rb.tt +196 -0
  36. data/lib/generators/propel_auth/templates/test/controllers/auth/password_reset_integration_test.rb.tt +471 -0
  37. data/lib/generators/propel_auth/templates/test/controllers/auth/tokens_controller_test.rb.tt +265 -0
  38. data/lib/generators/propel_auth/templates/test/mailers/auth_mailer_test.rb.tt +216 -0
  39. data/lib/generators/propel_auth/templates/test/mailers/previews/auth_mailer_preview.rb +161 -0
  40. data/lib/generators/propel_auth/templates/tokens_controller.rb.tt +96 -0
  41. data/lib/generators/propel_auth/templates/user.rb +21 -0
  42. data/lib/generators/propel_auth/templates/user_test.rb.tt +81 -0
  43. data/lib/generators/propel_auth/templates/views/auth_mailer/account_unlock.html.erb +213 -0
  44. data/lib/generators/propel_auth/templates/views/auth_mailer/account_unlock.text.erb +56 -0
  45. data/lib/generators/propel_auth/templates/views/auth_mailer/email_confirmation.html.erb +213 -0
  46. data/lib/generators/propel_auth/templates/views/auth_mailer/email_confirmation.text.erb +32 -0
  47. data/lib/generators/propel_auth/templates/views/auth_mailer/password_reset.html.erb +166 -0
  48. data/lib/generators/propel_auth/templates/views/auth_mailer/password_reset.text.erb +32 -0
  49. data/lib/generators/propel_auth/templates/views/auth_mailer/user_invitation.html.erb +194 -0
  50. data/lib/generators/propel_auth/templates/views/auth_mailer/user_invitation.text.erb +51 -0
  51. data/lib/generators/propel_auth/test/dummy/Dockerfile +72 -0
  52. data/lib/generators/propel_auth/test/dummy/Gemfile +63 -0
  53. data/lib/generators/propel_auth/test/dummy/Gemfile.lock +394 -0
  54. data/lib/generators/propel_auth/test/dummy/README.md +24 -0
  55. data/lib/generators/propel_auth/test/dummy/Rakefile +6 -0
  56. data/lib/generators/propel_auth/test/dummy/app/assets/stylesheets/application.css +10 -0
  57. data/lib/generators/propel_auth/test/dummy/app/controllers/application_controller.rb +4 -0
  58. data/lib/generators/propel_auth/test/dummy/app/helpers/application_helper.rb +2 -0
  59. data/lib/generators/propel_auth/test/dummy/app/jobs/application_job.rb +7 -0
  60. data/lib/generators/propel_auth/test/dummy/app/mailers/application_mailer.rb +4 -0
  61. data/lib/generators/propel_auth/test/dummy/app/models/application_record.rb +3 -0
  62. data/lib/generators/propel_auth/test/dummy/app/views/layouts/application.html.erb +27 -0
  63. data/lib/generators/propel_auth/test/dummy/app/views/layouts/mailer.html.erb +13 -0
  64. data/lib/generators/propel_auth/test/dummy/app/views/layouts/mailer.text.erb +1 -0
  65. data/lib/generators/propel_auth/test/dummy/app/views/pwa/manifest.json.erb +22 -0
  66. data/lib/generators/propel_auth/test/dummy/app/views/pwa/service-worker.js +26 -0
  67. data/lib/generators/propel_auth/test/dummy/bin/brakeman +7 -0
  68. data/lib/generators/propel_auth/test/dummy/bin/dev +2 -0
  69. data/lib/generators/propel_auth/test/dummy/bin/docker-entrypoint +14 -0
  70. data/lib/generators/propel_auth/test/dummy/bin/rails +4 -0
  71. data/lib/generators/propel_auth/test/dummy/bin/rake +4 -0
  72. data/lib/generators/propel_auth/test/dummy/bin/rubocop +8 -0
  73. data/lib/generators/propel_auth/test/dummy/bin/setup +34 -0
  74. data/lib/generators/propel_auth/test/dummy/bin/thrust +5 -0
  75. data/lib/generators/propel_auth/test/dummy/config/application.rb +42 -0
  76. data/lib/generators/propel_auth/test/dummy/config/boot.rb +4 -0
  77. data/lib/generators/propel_auth/test/dummy/config/cable.yml +10 -0
  78. data/lib/generators/propel_auth/test/dummy/config/credentials.yml.enc +1 -0
  79. data/lib/generators/propel_auth/test/dummy/config/database.yml +41 -0
  80. data/lib/generators/propel_auth/test/dummy/config/environment.rb +5 -0
  81. data/lib/generators/propel_auth/test/dummy/config/environments/development.rb +72 -0
  82. data/lib/generators/propel_auth/test/dummy/config/environments/production.rb +89 -0
  83. data/lib/generators/propel_auth/test/dummy/config/environments/test.rb +53 -0
  84. data/lib/generators/propel_auth/test/dummy/config/initializers/assets.rb +10 -0
  85. data/lib/generators/propel_auth/test/dummy/config/initializers/content_security_policy.rb +25 -0
  86. data/lib/generators/propel_auth/test/dummy/config/initializers/filter_parameter_logging.rb +8 -0
  87. data/lib/generators/propel_auth/test/dummy/config/initializers/inflections.rb +16 -0
  88. data/lib/generators/propel_auth/test/dummy/config/locales/en.yml +31 -0
  89. data/lib/generators/propel_auth/test/dummy/config/master.key +1 -0
  90. data/lib/generators/propel_auth/test/dummy/config/puma.rb +41 -0
  91. data/lib/generators/propel_auth/test/dummy/config/routes.rb +2 -0
  92. data/lib/generators/propel_auth/test/dummy/config/storage.yml +34 -0
  93. data/lib/generators/propel_auth/test/dummy/config.ru +6 -0
  94. data/lib/generators/propel_auth/test/dummy/db/schema.rb +14 -0
  95. data/lib/generators/propel_auth/test/generators/authentication/controllers/tokens_controller_test.rb +230 -0
  96. data/lib/generators/propel_auth/test/generators/authentication/install_generator_test.rb +490 -0
  97. data/lib/generators/propel_auth/test/generators/authentication/uninstall_generator_test.rb +408 -0
  98. data/lib/generators/propel_auth/test/integration/generator_integration_test.rb +158 -0
  99. data/lib/generators/propel_auth/test/integration/multi_version_generator_test.rb +125 -0
  100. data/lib/generators/propel_auth/unpack_generator.rb +345 -0
  101. data/lib/propel_auth.rb +3 -0
  102. metadata +195 -0
@@ -0,0 +1,486 @@
1
+ require 'rails/generators/base'
2
+ require 'rails/generators/active_record'
3
+ require_relative 'unpack_generator'
4
+ ##
5
+ # PropelAuth installer that provides JWT-based authentication for Rails applications
6
+ #
7
+ # This creates a COMPLETELY STANDALONE authentication system with no gem dependencies.
8
+ # Usage:
9
+ # rails generate propel_auth:install # Full standalone system (runtime + generators)
10
+ #
11
+ # This generator:
12
+ # 1. Installs all runtime code (models, controllers, views, tests, etc.)
13
+ # 2. Automatically extracts generator logic to lib/generators/propel_auth/
14
+ # 3. Creates a system that requires NO gem dependencies
15
+ #
16
+ # After installation, you can remove 'propel_auth' from your Gemfile completely.
17
+ # All code is in your application and fully customizable.
18
+ #
19
+ # Like Rails' built-in generators (action_text:install, devise:install),
20
+ # this generator is designed to be run once per application.
21
+ #
22
+ module PropelAuth
23
+ class InstallGenerator < Rails::Generators::Base
24
+ source_root File.expand_path("templates", __dir__)
25
+ include Rails::Generators::Migration
26
+
27
+ def self.next_migration_number(dir)
28
+ ActiveRecord::Generators::Base.next_migration_number(dir)
29
+ end
30
+
31
+ desc "Generate JWT-based authentication system with complete test coverage"
32
+
33
+ class_option :api_version, type: :string, default: nil,
34
+ desc: "Generate API versioned routes with specified version (e.g., --api-version=v2 creates /api/v2/auth/*)"
35
+
36
+ def copy_jwt_initializer
37
+ copy_file "propel_auth.rb", "config/initializers/propel_auth.rb"
38
+ end
39
+
40
+ def copy_propel_auth_runtime
41
+ copy_file "lib/propel_auth.rb", "lib/propel_auth.rb"
42
+ end
43
+
44
+ def copy_authentication_models
45
+ copy_file "user.rb", "app/models/user.rb"
46
+ copy_file "organization.rb", "app/models/organization.rb"
47
+ copy_file "agency.rb", "app/models/agency.rb"
48
+ copy_file "agent.rb", "app/models/agent.rb"
49
+ copy_file "invitation.rb", "app/models/invitation.rb"
50
+ end
51
+
52
+ def copy_authentication_concerns
53
+ copy_file "authenticatable.rb", "app/models/concerns/authenticatable.rb"
54
+ copy_file "concerns/lockable.rb", "app/models/concerns/lockable.rb"
55
+ copy_file "concerns/recoverable.rb", "app/models/concerns/recoverable.rb"
56
+ copy_file "concerns/confirmable.rb", "app/models/concerns/confirmable.rb"
57
+ copy_file "concerns/propel_authentication.rb", "app/controllers/concerns/propel_authentication.rb"
58
+ copy_file "concerns/rack_session_disable.rb", "app/controllers/concerns/rack_session_disable.rb"
59
+ end
60
+
61
+ def copy_jwt_controllers
62
+ if api_versioned?
63
+ template "auth/base_tokens_controller.rb.tt", "app/controllers/api/auth/base_tokens_controller.rb"
64
+ template "auth/base_passwords_controller.rb.tt", "app/controllers/api/auth/base_passwords_controller.rb"
65
+ create_versioned_controller(api_version, 'tokens', controller_directory)
66
+ create_versioned_controller(api_version, 'passwords', controller_directory)
67
+ else
68
+ template "tokens_controller.rb.tt", "#{controller_directory}/tokens_controller.rb"
69
+ template "auth/passwords_controller.rb.tt", "#{controller_directory}/passwords_controller.rb"
70
+ end
71
+ end
72
+
73
+ def copy_email_infrastructure
74
+ copy_file "auth_mailer.rb", "app/mailers/auth_mailer.rb"
75
+ copy_file "services/auth_notification_service.rb", "app/services/auth_notification_service.rb"
76
+ copy_file "config/environments/development_email.rb", "config/environments/development_email.rb"
77
+
78
+ # Email view templates
79
+ %w[password_reset account_unlock user_invitation email_confirmation].each do |template|
80
+ copy_file "views/auth_mailer/#{template}.html.erb", "app/views/auth_mailer/#{template}.html.erb"
81
+ copy_file "views/auth_mailer/#{template}.text.erb", "app/views/auth_mailer/#{template}.text.erb"
82
+ end
83
+ end
84
+
85
+ def copy_authentication_tests
86
+ template "user_test.rb.tt", "test/models/user_test.rb"
87
+ template "test/controllers/auth/tokens_controller_test.rb.tt", "test/controllers/auth/tokens_controller_test.rb"
88
+ template "test/mailers/auth_mailer_test.rb.tt", "test/mailers/auth_mailer_test.rb"
89
+ copy_file "test/mailers/previews/auth_mailer_preview.rb", "test/mailers/previews/auth_mailer_preview.rb"
90
+ end
91
+
92
+ def copy_concern_tests
93
+ template "test/concerns/propel_authentication_test.rb.tt", "test/concerns/propel_authentication_test.rb"
94
+ template "test/concerns/lockable_test.rb.tt", "test/concerns/lockable_test.rb"
95
+ template "test/concerns/recoverable_test.rb.tt", "test/concerns/recoverable_test.rb"
96
+ template "test/concerns/confirmable_test.rb.tt", "test/concerns/confirmable_test.rb"
97
+ template "test/controllers/auth/lockable_integration_test.rb.tt", "test/controllers/auth/lockable_integration_test.rb"
98
+ template "test/controllers/auth/password_reset_integration_test.rb.tt", "test/controllers/auth/password_reset_integration_test.rb"
99
+ end
100
+
101
+ def copy_test_fixtures
102
+ create_file "test/fixtures/organizations.yml", organizations_fixture
103
+ create_file "test/fixtures/users.yml", users_fixture
104
+ create_file "test/fixtures/agencies.yml", agencies_fixture
105
+ create_file "test/fixtures/agents.yml", agents_fixture
106
+ create_file "test/fixtures/invitations.yml", invitations_fixture
107
+ end
108
+
109
+ def add_authentication_gems
110
+ add_gem_if_missing('bcrypt', '~> 3.1.20')
111
+ add_gem_if_missing('jwt', '~> 2.7')
112
+ add_gem_if_missing('letter_opener', '~> 1.8', group: :development)
113
+ end
114
+
115
+ def add_authentication_routes
116
+ route generate_routes_content
117
+ end
118
+
119
+ def copy_authentication_migrations
120
+ %w[organizations users agencies agents invitations].each do |table|
121
+ unless migration_exists?("create_#{table}")
122
+ migration_template "db/migrate/create_#{table}.rb", "db/migrate/create_#{table}.rb"
123
+ end
124
+ end
125
+ end
126
+
127
+ def copy_database_seeds
128
+ copy_file "db/seeds.rb", "db/seeds.rb"
129
+ end
130
+
131
+ def extract_generator_for_customization
132
+ generator_path = "lib/generators/propel_auth"
133
+
134
+ if File.exist?(generator_path)
135
+ say ""
136
+ say "📦 Generator logic already extracted at #{generator_path}/", :blue
137
+ say "💡 Skipping extraction to preserve your customizations", :cyan
138
+ else
139
+ say ""
140
+ say "📦 Extracting generator logic for full customization...", :blue
141
+
142
+ # Automatically run the unpack generator to extract generator logic
143
+ invoke PropelAuth::UnpackGenerator, [], { force: false }
144
+
145
+ say ""
146
+ say "✅ Generator logic extracted to lib/generators/propel_auth/", :green
147
+ say "💡 Your application is now completely standalone - no gem dependency needed for generators!", :cyan
148
+ say "🗑️ You can now remove 'propel_auth' from your Gemfile", :yellow
149
+ end
150
+ end
151
+
152
+ def show_setup_complete_message
153
+ say "\n🚀 JWT Authentication System Generated Successfully!", :green
154
+
155
+ say "\n📦 System Status: Completely standalone - no gem dependency!", :green
156
+ say "\nNext steps:", :yellow
157
+ say "• Run: rails db:migrate", :blue
158
+ say "• Run: rails test", :blue
159
+ say "• Optional: Remove 'propel_auth' from Gemfile (system is fully extracted)", :cyan
160
+
161
+ if api_versioned?
162
+ say "• Routes: /#{api_namespace}/auth/*", :blue
163
+ say "• Controllers: #{controller_namespace}::*", :blue
164
+ else
165
+ say "• Routes: /auth/*", :blue
166
+ say "• Controllers: Auth::*", :blue
167
+ end
168
+
169
+ say "\n🎨 Customization:", :bold
170
+ say "• Generator logic: lib/generators/propel_auth/install_generator.rb", :blue
171
+ say "• Templates: lib/generators/propel_auth/templates/", :blue
172
+ say "• Modify any part of the system - it's all yours now!", :cyan
173
+
174
+ say "\n🗑️ To uninstall: rails destroy propel_auth:install", :yellow
175
+ end
176
+
177
+ private
178
+
179
+ # Helper methods for API versioning
180
+ def api_versioned?
181
+ options[:api_version].present?
182
+ end
183
+
184
+ def api_version
185
+ options[:api_version] || 'v1'
186
+ end
187
+
188
+ def api_namespace
189
+ "api/#{api_version}"
190
+ end
191
+
192
+ def api_namespace_parts
193
+ api_namespace.split('/')
194
+ end
195
+
196
+ def controller_namespace
197
+ if api_versioned?
198
+ api_namespace_parts.map(&:camelize).join('::') + '::Auth'
199
+ else
200
+ 'Auth'
201
+ end
202
+ end
203
+
204
+ def controller_directory
205
+ if api_versioned?
206
+ "app/controllers/#{api_namespace}/auth"
207
+ else
208
+ "app/controllers/auth"
209
+ end
210
+ end
211
+
212
+ def api_only_app?
213
+ # Check if this is a Rails API-only application
214
+ Rails.application.config.respond_to?(:api_only) && Rails.application.config.api_only
215
+ end
216
+
217
+ # Path helper methods for tests
218
+ def login_path_helper
219
+ if api_versioned?
220
+ "'/#{api_namespace}/auth/login'"
221
+ else
222
+ "'/auth/login'"
223
+ end
224
+ end
225
+
226
+ def me_path_helper
227
+ if api_versioned?
228
+ "'/#{api_namespace}/auth/me'"
229
+ else
230
+ "'/auth/me'"
231
+ end
232
+ end
233
+
234
+ def logout_path_helper
235
+ if api_versioned?
236
+ "'/#{api_namespace}/auth/logout'"
237
+ else
238
+ "'/auth/logout'"
239
+ end
240
+ end
241
+
242
+ def unlock_path_helper
243
+ if api_versioned?
244
+ "'/#{api_namespace}/auth/unlock'"
245
+ else
246
+ "'/auth/unlock'"
247
+ end
248
+ end
249
+
250
+ def password_reset_path_helper
251
+ if api_versioned?
252
+ "'/#{api_namespace}/auth/reset'"
253
+ else
254
+ "'/auth/reset'"
255
+ end
256
+ end
257
+
258
+ def reset_path_helper
259
+ password_reset_path_helper
260
+ end
261
+
262
+ def add_gem_if_missing(gem_name, version_requirement = nil, options = {})
263
+ gemfile_path = File.join(destination_root, 'Gemfile')
264
+ return unless File.exist?(gemfile_path)
265
+
266
+ gemfile_content = File.read(gemfile_path)
267
+
268
+ # Check if gem is already present (handles various quote styles and spacing)
269
+ gem_pattern = /gem\s+['"]#{Regexp.escape(gem_name)}['"](?:\s*,\s*['"][^'"]*['"])?/
270
+
271
+ if gemfile_content.match?(gem_pattern)
272
+ say_status :exists, "gem '#{gem_name}' already in Gemfile", :blue
273
+ return
274
+ end
275
+
276
+ # Add the gem using Rails' built-in method
277
+ if version_requirement && options.any?
278
+ gem gem_name, version_requirement, options
279
+ elsif version_requirement
280
+ gem gem_name, version_requirement
281
+ else
282
+ gem gem_name
283
+ end
284
+
285
+ say_status :gemfile, "gem '#{gem_name}' #{version_requirement}", :green
286
+ end
287
+
288
+ def migration_exists?(migration_name)
289
+ Dir.glob("#{destination_root}/db/migrate/*_#{migration_name}.rb").any?
290
+ end
291
+
292
+ def generate_routes_content
293
+ if api_versioned?
294
+ route_lines = ["# JWT Authentication routes for #{api_namespace}"]
295
+
296
+ # Build nested namespace opening
297
+ api_namespace_parts.each_with_index do |part, index|
298
+ indent = " " * index
299
+ route_lines << "#{indent}namespace :#{part} do"
300
+ end
301
+
302
+ # Add auth namespace and routes
303
+ auth_indent = " " * api_namespace_parts.length
304
+ route_lines << "#{auth_indent}namespace :auth do"
305
+ route_lines << "#{auth_indent} post 'login', to: 'tokens#create'"
306
+ route_lines << "#{auth_indent} get 'me', to: 'tokens#me'"
307
+ route_lines << "#{auth_indent} delete 'logout', to: 'tokens#destroy'"
308
+ route_lines << "#{auth_indent} post 'unlock', to: 'tokens#unlock'"
309
+ route_lines << "#{auth_indent} post 'reset', to: 'passwords#create'"
310
+ route_lines << "#{auth_indent} get 'reset', to: 'passwords#show'"
311
+ route_lines << "#{auth_indent} patch 'reset', to: 'passwords#update'"
312
+ route_lines << "#{auth_indent}end"
313
+
314
+ # Build nested namespace closing
315
+ api_namespace_parts.length.times do |index|
316
+ indent = " " * (api_namespace_parts.length - 1 - index)
317
+ route_lines << "#{indent}end"
318
+ end
319
+
320
+ route_lines.join("\n")
321
+ else
322
+ <<~ROUTES
323
+ # JWT Authentication routes
324
+ namespace :auth do
325
+ post 'login', to: 'tokens#create'
326
+ get 'me', to: 'tokens#me'
327
+ delete 'logout', to: 'tokens#destroy'
328
+ post 'unlock', to: 'tokens#unlock'
329
+ post 'reset', to: 'passwords#create'
330
+ get 'reset', to: 'passwords#show'
331
+ patch 'reset', to: 'passwords#update'
332
+ end
333
+ ROUTES
334
+ end
335
+ end
336
+
337
+ def create_versioned_controller(version, controller_type, directory)
338
+ case controller_type
339
+ when 'tokens'
340
+ controller_content = <<~RUBY
341
+ # Generated versioned controller for API #{version}
342
+ class Api::#{version.camelize}::Auth::TokensController < Api::Auth::BaseTokensController
343
+ end
344
+ RUBY
345
+ when 'passwords'
346
+ controller_content = <<~RUBY
347
+ # Generated versioned controller for API #{version}
348
+ class Api::#{version.camelize}::Auth::PasswordsController < Api::Auth::BasePasswordsController
349
+ end
350
+ RUBY
351
+ end
352
+
353
+ create_file "#{directory}/#{controller_type}_controller.rb", controller_content
354
+ end
355
+
356
+ # Fixture content methods
357
+ def organizations_fixture
358
+ <<~FIXTURE
359
+ acme_org:
360
+ name: "Acme Corporation"
361
+ website: "https://acme-corp.com"
362
+ time_zone: "UTC"
363
+ meta: {}
364
+ settings: {}
365
+ created_at: <%= 30.days.ago %>
366
+ updated_at: <%= 1.day.ago %>
367
+
368
+ tech_startup:
369
+ name: "Tech Startup Inc"
370
+ website: "https://techstartup.io"
371
+ time_zone: "America/New_York"
372
+ meta: {}
373
+ settings: {}
374
+ created_at: <%= 15.days.ago %>
375
+ updated_at: <%= 2.days.ago %>
376
+ FIXTURE
377
+ end
378
+
379
+ def users_fixture
380
+ <<~FIXTURE
381
+ john_user:
382
+ email_address: "john@example.com"
383
+ username: "john_doe"
384
+ first_name: "John"
385
+ last_name: "Doe"
386
+ password_digest: "<%= BCrypt::Password.create('password123') %>"
387
+ organization: acme_org
388
+ status: "active"
389
+ confirmed_at: null
390
+ confirmation_token: "<%= SecureRandom.hex(32) %>"
391
+ confirmation_sent_at: <%= 1.hour.ago %>
392
+ created_at: <%= 7.days.ago %>
393
+ updated_at: <%= 1.day.ago %>
394
+
395
+ jane_user:
396
+ email_address: "jane@example.com"
397
+ username: "jane_smith"
398
+ first_name: "Jane"
399
+ last_name: "Smith"
400
+ password_digest: "<%= BCrypt::Password.create('password123') %>"
401
+ organization: tech_startup
402
+ status: "active"
403
+ confirmed_at: null
404
+ failed_login_attempts: 0
405
+ locked_at: null
406
+ created_at: <%= 5.days.ago %>
407
+ updated_at: <%= 1.day.ago %>
408
+
409
+ confirmed_user:
410
+ email_address: "confirmed@example.com"
411
+ username: "confirmed_user"
412
+ first_name: "Confirmed"
413
+ last_name: "User"
414
+ password_digest: "<%= BCrypt::Password.create('password123') %>"
415
+ organization: acme_org
416
+ status: "active"
417
+ confirmed_at: <%= 7.days.ago %>
418
+ created_at: <%= 14.days.ago %>
419
+ updated_at: <%= 1.day.ago %>
420
+
421
+ locked_user:
422
+ email_address: "locked@example.com"
423
+ username: "locked_user"
424
+ first_name: "Locked"
425
+ last_name: "User"
426
+ password_digest: "<%= BCrypt::Password.create('password123') %>"
427
+ organization: acme_org
428
+ status: "active"
429
+ confirmed_at: <%= 10.days.ago %>
430
+ failed_login_attempts: 5
431
+ locked_at: <%= 1.hour.ago %>
432
+ created_at: <%= 20.days.ago %>
433
+ updated_at: <%= 1.hour.ago %>
434
+
435
+ expired_confirmation_user:
436
+ email_address: "expired@example.com"
437
+ username: "expired_user"
438
+ first_name: "Expired"
439
+ last_name: "User"
440
+ password_digest: "<%= BCrypt::Password.create('password123') %>"
441
+ organization: tech_startup
442
+ status: "active"
443
+ confirmed_at: null
444
+ confirmation_token: "<%= SecureRandom.hex(32) %>"
445
+ confirmation_sent_at: <%= 25.hours.ago %>
446
+ created_at: <%= 30.days.ago %>
447
+ updated_at: <%= 25.hours.ago %>
448
+ FIXTURE
449
+ end
450
+
451
+ def agencies_fixture
452
+ <<~FIXTURE
453
+ marketing_agency:
454
+ name: "Marketing Solutions"
455
+ organization: acme_org
456
+ created_at: <%= 10.days.ago %>
457
+ updated_at: <%= 2.days.ago %>
458
+ FIXTURE
459
+ end
460
+
461
+ def agents_fixture
462
+ <<~FIXTURE
463
+ marketing_agent:
464
+ user: john_user
465
+ agency: marketing_agency
466
+ role: "manager"
467
+ created_at: <%= 8.days.ago %>
468
+ updated_at: <%= 1.day.ago %>
469
+ FIXTURE
470
+ end
471
+
472
+ def invitations_fixture
473
+ <<~FIXTURE
474
+ pending_invitation:
475
+ email_address: "newuser@example.com"
476
+ first_name: "New"
477
+ last_name: "User"
478
+ organization: acme_org
479
+ inviter: john_user
480
+ status: "pending"
481
+ created_at: <%= 3.days.ago %>
482
+ updated_at: <%= 3.days.ago %>
483
+ FIXTURE
484
+ end
485
+ end
486
+ end