inline_forms 7.10.2 → 7.11.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 (40) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +11 -0
  3. data/Rakefile +18 -1
  4. data/docs/prompt/test-the-example-app.md +3 -3
  5. data/inline_forms.gemspec +6 -21
  6. data/lib/inline_forms/gem_files.rb +39 -0
  7. data/lib/inline_forms/version.rb +1 -1
  8. metadata +10 -76
  9. data/bin/inline_forms +0 -141
  10. data/bin/inline_forms_app_template.rb +0 -22
  11. data/bin/inline_forms_installer_core.rb +0 -886
  12. data/lib/generators/templates/capistrano/Capfile +0 -39
  13. data/lib/generators/templates/capistrano/deploy.rb +0 -59
  14. data/lib/generators/templates/capistrano/production.rb +0 -7
  15. data/lib/generators/templates/unicorn/production.rb +0 -39
  16. data/lib/installer_templates/dartsass/devise_main.scss +0 -2
  17. data/lib/installer_templates/dartsass/inline_forms_dartsass_builds.rb +0 -14
  18. data/lib/installer_templates/dartsass/inline_forms_main.scss +0 -2
  19. data/lib/installer_templates/example_app_tests/test/example_app/example_integration_test_case.rb +0 -36
  20. data/lib/installer_templates/example_app_tests/test/integration/example_app_apartment_field_turbo_test.rb +0 -73
  21. data/lib/installer_templates/example_app_tests/test/integration/example_app_apartment_name_list_test.rb +0 -73
  22. data/lib/installer_templates/example_app_tests/test/integration/example_app_apartment_name_required_test.rb +0 -21
  23. data/lib/installer_templates/example_app_tests/test/integration/example_app_apartment_opening_date_test.rb +0 -49
  24. data/lib/installer_templates/example_app_tests/test/integration/example_app_apartment_photos_pagination_test.rb +0 -440
  25. data/lib/installer_templates/example_app_tests/test/integration/example_app_apartment_row_turbo_test.rb +0 -103
  26. data/lib/installer_templates/example_app_tests/test/integration/example_app_apartment_top_level_new_test.rb +0 -70
  27. data/lib/installer_templates/example_app_tests/test/integration/example_app_apartment_top_level_pagination_test.rb +0 -40
  28. data/lib/installer_templates/example_app_tests/test/integration/example_app_apartment_versions_turbo_test.rb +0 -120
  29. data/lib/installer_templates/example_app_tests/test/integration/example_app_guest_access_test.rb +0 -21
  30. data/lib/installer_templates/example_app_tests/test/integration/example_app_photo_revert_test.rb +0 -94
  31. data/lib/installer_templates/example_app_tests/test/integration/example_app_photos_test.rb +0 -22
  32. data/lib/installer_templates/example_app_tests/test/integration/example_app_routing_test.rb +0 -15
  33. data/lib/installer_templates/example_app_tests/test/integration/example_app_turbo_layout_test.rb +0 -25
  34. data/lib/installer_templates/example_app_tests/test/integration/example_app_validation_hints_test.rb +0 -40
  35. data/lib/installer_templates/example_app_tests/test/models/example_app_apartment_name_validation_test.rb +0 -16
  36. data/lib/installer_templates/example_app_tests/test/models/example_app_apartment_photo_test.rb +0 -26
  37. data/lib/installer_templates/example_app_tests/test/models/example_app_paper_trail_changeset_test.rb +0 -78
  38. data/lib/installer_templates/example_app_tests/test/models/example_app_plain_text_rich_text_edge_cases_test.rb +0 -46
  39. data/lib/installer_templates/example_app_views/apartments/name_list.html.erb +0 -26
  40. data/lib/installer_templates/example_app_views/inline_forms/_header.html.erb +0 -45
@@ -1,886 +0,0 @@
1
- GENERATOR_PATH = File.dirname(File.expand_path(__FILE__)) + '/../'
2
-
3
- # Rails 7 dropped --skip-gemfile, so `rails new` always writes its own Gemfile.
4
- # Remove it so our `create_file` below does not prompt for overwrite.
5
- remove_file 'Gemfile' if File.exist?('Gemfile')
6
- create_file 'Gemfile', "# created by inline_forms #{ENV['inline_forms_version']} on #{Date.today}\n"
7
-
8
- # `rails new` is invoked with whatever the system `rails` binary points at
9
- # (often Rails 8.x once it lands in the global gemset), so the generated
10
- # `config/application.rb` may carry Rails 7.1+/8.0 idioms (`load_defaults
11
- # 8.0`, `config.autoload_lib(...)`). The Gemfile we write below pins
12
- # `rails ~> 7.0.0`, so Rails 7.0 must be able to interpret application.rb;
13
- # otherwise the first `bundle exec rails …` aborts with `Unknown version
14
- # "8.0"` or `NoMethodError: undefined method 'autoload_lib'`.
15
- if File.exist?('config/application.rb')
16
- gsub_file 'config/application.rb',
17
- /config\.load_defaults\s+\d+\.\d+/,
18
- 'config.load_defaults 7.1'
19
- # Strip Rails 7.1+ `config.autoload_lib(ignore: ...)` (and any surrounding
20
- # explanatory comment block). Not supported on Rails 7.0.
21
- gsub_file 'config/application.rb',
22
- /^\s*#[^\n]*\n(\s*#[^\n]*\n)*\s*config\.autoload_lib\([^)]*\)\s*\n/,
23
- ""
24
- gsub_file 'config/application.rb',
25
- /^\s*config\.autoload_lib\([^)]*\)\s*\n/,
26
- ""
27
- end
28
-
29
- add_source 'https://rubygems.org'
30
-
31
- gem 'cancancan'
32
- gem 'carrierwave', '~> 3.1'
33
- gem 'devise', '~> 5.0'
34
- gem 'devise-i18n', '~> 1.16'
35
- gem 'autoprefixer-rails'
36
- # foundation-rails 6.7+ uses Dart Sass (`sass:math`); sass-rails/sassc removed.
37
- # Visually tuned against foundation-rails ~> 6.6.2; current pin ~> 6.9 (6.9.0.x).
38
- gem 'foundation-rails', '~> 6.9'
39
- gem 'i18n-active_record', :git => 'https://github.com/acesuares/i18n-active_record.git'
40
- generator_repo = File.expand_path(GENERATOR_PATH)
41
- # Always use the generator source path (repo checkout OR installed gem dir).
42
- # This guarantees the generated app uses the exact inline_forms code that
43
- # launched `inline_forms create`, including unreleased local builds.
44
- gem 'inline_forms', path: generator_repo
45
- gem 'jquery-rails'
46
- gem 'jquery-timepicker-rails'
47
- # jQuery UI JavaScript (`//= require jquery.ui.all` in inline_forms.js). SCSS + PNGs
48
- # are vendored in the inline_forms engine (Dart Sass cannot evaluate sass-rails
49
- # `image-path()`). Pin matches former jquery-ui-sass-rails 4.0.3.x stack.
50
- gem 'jquery-ui-rails', '4.0.3'
51
- # Foundation Icons SCSS + fonts are vendored in the inline_forms engine (Dart Sass;
52
- # foundation-icons-sass-rails depended on sass-rails).
53
- gem 'mini_magick'
54
- gem 'mysql2'
55
- gem 'paper_trail', '~> 16.0'
56
- gem 'rails-i18n', '~> 7.0'
57
- gem 'rails-jquery-autocomplete'
58
- gem 'rails', '~> 7.1.5'
59
- gem 'rake'
60
- gem 'rvm'
61
- gem 'dartsass-rails'
62
- # Rails 7 no longer adds sprockets-rails to the default Gemfile; declare it
63
- # explicitly because the gem's own assets (foundation, jquery, etc.) live in
64
- # app/assets and rely on the Sprockets pipeline.
65
- gem 'sprockets-rails'
66
- # Rails 7 default JavaScript tooling: importmap-rails replaces Webpacker.
67
- gem 'importmap-rails'
68
- # Hotwire/Turbo. Loaded from layouts as `<script type="module">`; inline flows
69
- # use `<turbo-frame>` + HTML responses (see docs/ujs-to-turbo.md). Registers the
70
- # `turbo_stream` MIME type for optional stream responses.
71
- gem 'turbo-rails'
72
- gem 'tabs_on_rails', :git => 'https://github.com/acesuares/tabs_on_rails.git', :branch => 'update_remote_before_action'
73
- gem 'unicorn'
74
- gem 'validation_hints', '~> 6.3'
75
- gem 'will_paginate' #, git: 'https://github.com/acesuares/will_paginate.git'
76
-
77
- gem_group :test do
78
- # Rails 7 still expects Minitest 5; 6.x breaks the railties test runner.
79
- gem 'minitest', '~> 5.25'
80
- end
81
-
82
- gem_group :development do
83
- gem 'capistrano-bundler', require: false
84
- gem 'capistrano-rails', require: false
85
- gem 'capistrano', require: false
86
- gem 'capistrano3-unicorn'
87
- gem 'listen'
88
- gem 'rvm-capistrano', :require => false
89
- gem 'rvm1-capistrano3', require: false
90
- gem 'seed_dump', '~> 0.5.3'
91
- # Rails 6.1 ActiveRecord's sqlite3 adapter requires sqlite3 ~> 1.4; 2.x activates first and breaks.
92
- gem 'sqlite3', '~> 1.4'
93
- gem 'thin'
94
- gem 'yaml_db'
95
- end
96
-
97
- gem_group :production do
98
- gem 'mini_racer'
99
- gem 'uglifier'
100
- end
101
-
102
- say "- Running bundle..."
103
- run "gem install bundler"
104
- vh_gem_dirs = [
105
- ENV["VALIDATION_HINTS_ROOT"],
106
- File.expand_path("~/validation_hints"),
107
- File.expand_path("~/code/validation_hints"),
108
- File.expand_path("../validation_hints", GENERATOR_PATH)
109
- ].compact.uniq
110
- vh_gem = vh_gem_dirs.flat_map { |dir| Dir[File.join(dir, "validation_hints-*.gem")] }.sort.last
111
- if vh_gem && File.file?(vh_gem)
112
- say "- Installing #{File.basename(vh_gem)} (local build; not on RubyGems yet)..."
113
- run "gem install #{vh_gem} --no-document"
114
- end
115
- run "bundle install"
116
-
117
- say "- Dart Sass: inline_forms stylesheet entrypoints + initializer..."
118
- copy_file File.join(GENERATOR_PATH, "lib/installer_templates/dartsass/inline_forms_dartsass_builds.rb"),
119
- "config/initializers/inline_forms_dartsass_builds.rb"
120
- copy_file File.join(GENERATOR_PATH, "lib/installer_templates/dartsass/inline_forms_main.scss"),
121
- "app/assets/stylesheets/inline_forms_install/inline_forms_main.scss"
122
- copy_file File.join(GENERATOR_PATH, "lib/installer_templates/dartsass/devise_main.scss"),
123
- "app/assets/stylesheets/inline_forms_install/devise_main.scss"
124
-
125
- say "- Dart Sass: rails dartsass:install (builds/, manifest, Procfile.dev)..."
126
- run "bundle exec rails dartsass:install"
127
-
128
- say "- Dart Sass: drop default application.css (manifest links builds/*.css only)..."
129
- remove_file "app/assets/stylesheets/application.css"
130
-
131
- insert_into_file "test/test_helper.rb", <<~'DARTSASS_TEST', after: %(require "rails/test_help"\n)
132
-
133
- # Dart Sass writes CSS to app/assets/builds; Sprockets does not compile .scss.
134
- Rails.application.load_tasks
135
- Rake::Task["dartsass:build"].invoke
136
- DARTSASS_TEST
137
-
138
- say "- Database setup: creating config/database.yml with development database #{ENV['database']}"
139
- remove_file "config/database.yml" # the one that 'rails new' created
140
- if ENV['using_sqlite'] == 'true'
141
- create_file "config/database.yml", <<-END_DATABASEYML.strip_heredoc
142
- development:
143
- adapter: sqlite3
144
- database: db/development.sqlite3
145
- pool: 5
146
- timeout: 5000
147
-
148
- test:
149
- adapter: sqlite3
150
- database: db/test.sqlite3
151
- pool: 5
152
- timeout: 5000
153
-
154
- END_DATABASEYML
155
- else
156
- create_file "config/database.yml", <<-END_DATABASEYML.strip_heredoc
157
- development:
158
- adapter: mysql2
159
- database: <%= Rails.application.credentials[:db_name] %>
160
- username: <%= Rails.application.credentials[:db_username] %>
161
- password: <%= Rails.application.credentials[:db_password] %>
162
- END_DATABASEYML
163
-
164
- say "- Setting development database in credentials"
165
- create_file "temp_development_database_credentials", <<-END_DEV_DB_CRED.strip_heredoc
166
-
167
- # development database
168
- db_name: #{app_name.downcase}_dev
169
- db_username: #{app_name.downcase}_dev_user
170
- db_password: #{app_name.downcase}_dev_password
171
-
172
- END_DEV_DB_CRED
173
-
174
- run "EDITOR='cat temp_development_database_credentials >> ' rails credentials:edit"
175
-
176
- remove_file 'temp_development_database_credentials'
177
-
178
- say "\n *** Please make sure to create a mysql development database with the following credentials:
179
- db_name: #{app_name.downcase}_dev
180
- db_username: #{app_name.downcase}_dev_user
181
- db_password: #{app_name.downcase}_dev_password
182
-
183
- or use 'rails credentials:edit' to change these values.\n\n", :red
184
-
185
- end
186
- append_file "config/database.yml", <<-END_DATABASEYML.strip_heredoc
187
- production:
188
- adapter: mysql2
189
- database: <%= Rails.application.credentials[:db_name] %>
190
- username: <%= Rails.application.credentials[:db_username] %>
191
- password: <%= Rails.application.credentials[:db_password] %>
192
- END_DATABASEYML
193
-
194
- say "Setting production database in credentials"
195
- create_file "temp_production_database_credentials", <<-END_PROD_DB_CRED.strip_heredoc
196
-
197
- # production database
198
- db_name: #{app_name.downcase}_prod
199
- db_username: #{app_name.downcase}_prod_user
200
- db_password:
201
-
202
- END_PROD_DB_CRED
203
-
204
- run "EDITOR='cat temp_production_database_credentials >> ' rails credentials:edit --environment production"
205
-
206
- remove_file 'temp_production_database_credentials'
207
-
208
- say "\n *** Please make sure to create a mysql production database and use 'rails credentials:edit' to set the password.\n\n", :red
209
-
210
- say "- Devise install..."
211
- run "bundle exec rails g devise:install"
212
-
213
- say "- Create Devise route and add path_prefix..."
214
-
215
- route <<-ROUTE.strip_heredoc
216
- devise_for :users, :path_prefix => 'auth'
217
- resources :users do
218
- post 'revert', :on => :member
219
- get 'list_versions', :on => :member
220
- end
221
- ROUTE
222
-
223
- say "- Create devise migration file"
224
-
225
- sleep 1 # to get unique migration number
226
- create_file "db/migrate/" +
227
- Time.now.utc.strftime("%Y%m%d%H%M%S") +
228
- "_" +
229
- "devise_create_users.rb", <<-DEVISE_MIGRATION.strip_heredoc
230
- class DeviseCreateUsers < ActiveRecord::Migration[7.1]
231
-
232
- def change
233
- create_table(:users) do |t|
234
- ## Database authenticatable
235
- t.string :email, null: false, default: ""
236
- t.string :encrypted_password, null: false, default: ""
237
-
238
- ## Recoverable
239
- t.string :reset_password_token
240
- t.datetime :reset_password_sent_at
241
-
242
- ## Rememberable
243
- t.datetime :remember_created_at
244
-
245
- ## Trackable
246
- t.integer :sign_in_count, default: 0, null: false
247
- t.datetime :current_sign_in_at
248
- t.datetime :last_sign_in_at
249
- t.string :current_sign_in_ip
250
- t.string :last_sign_in_ip
251
-
252
- ## Confirmable
253
- # t.string :confirmation_token
254
- # t.datetime :confirmed_at
255
- # t.datetime :confirmation_sent_at
256
- # t.string :unconfirmed_email # Only if using reconfirmable
257
-
258
- ## Lockable
259
- # t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
260
- # t.string :unlock_token # Only if unlock strategy is :email or :both
261
- # t.datetime :locked_at
262
-
263
- t.string :name
264
- t.integer :locale_id
265
-
266
- t.timestamps
267
- end
268
-
269
- add_index :users, :email, unique: true
270
- add_index :users, :reset_password_token, unique: true
271
- # add_index :users, :confirmation_token, unique: true
272
- # add_index :users, :unlock_token, unique: true
273
- end
274
- end
275
- DEVISE_MIGRATION
276
-
277
- say "- Create User Controller..."
278
- create_file "app/controllers/users_controller.rb", <<-USERS_CONTROLLER.strip_heredoc
279
- class UsersController < InlineFormsController
280
- set_tab :user
281
- end
282
- USERS_CONTROLLER
283
-
284
- say "- Create User Model..."
285
- create_file "app/models/user.rb", <<-USER_MODEL.strip_heredoc
286
- class User < ApplicationRecord
287
-
288
- # devise options
289
- devise :database_authenticatable
290
- # devise :registerable # uncomment this if you want people to be able to register
291
- devise :recoverable
292
- devise :rememberable
293
- devise :trackable
294
- devise :validatable
295
- # devise :token_authenticatable
296
- # devise :confirmable,
297
- # devise :lockable
298
- # devise :timeoutable
299
- # devise :omniauthable
300
-
301
- # Setup accessible (or protected) attributes for your model
302
- attr_writer :inline_forms_attribute_list
303
- #attr_accessible :email, :password, :locale, :remember_me
304
-
305
- belongs_to :locale
306
- has_and_belongs_to_many :roles
307
-
308
- # validations
309
- validates :name, :presence => true
310
-
311
- default_scope {order :name}
312
-
313
- # pagination
314
- attr_reader :per_page
315
- @per_page = 7
316
-
317
- has_paper_trail
318
-
319
- def _presentation
320
- "\#{name}"
321
- end
322
-
323
- def role?(role)
324
- return !!self.roles.find_by_name(role)
325
- end
326
-
327
- def inline_forms_attribute_list
328
- @inline_forms_attribute_list ||= [
329
- [ :header_user_login, '', :header ],
330
- [ :name, '', :text_field ],
331
- [ :email, '', :text_field ],
332
- [ :locale , '', :dropdown ],
333
- [ :password, '', :devise_password_field ],
334
- [ :header_user_roles, '', :header ],
335
- [ :roles, '', :check_list ],
336
- [ :header_user_other_stuff, '', :header ],
337
- [ :encrypted_password, '', :info ],
338
- [ :reset_password_token, '', :info ],
339
- [ :reset_password_sent_at, '', :info],
340
- [ :remember_created_at, '', :info ],
341
- [ :sign_in_count, '', :info ],
342
- [ :current_sign_in_at, '', :info ],
343
- [ :last_sign_in_at, '', :info ],
344
- [ :current_sign_in_ip, '', :info ],
345
- [ :last_sign_in_ip, '', :info ],
346
- [ :created_at, '', :info ],
347
- [ :updated_at, '', :info ],
348
- ]
349
- end
350
-
351
- def self.not_accessible_through_html?
352
- false
353
- end
354
-
355
- def self.order_by_clause
356
- nil
357
- end
358
-
359
- end
360
- USER_MODEL
361
-
362
- # Create Locales
363
- say "- Create locales"
364
- generate "inline_forms", "Locale name:string title:string users:has_many _enabled:yes _presentation:\#{title}"
365
- append_to_file "db/seeds.rb", "Locale.create({ id: 1, name: 'en', title: 'English' })\n"
366
-
367
- # Create Roles
368
- say "- Create roles"
369
- generate "inline_forms", "Role name:string description:text users:has_and_belongs_to_many _enabled:yes _presentation:\#{name}"
370
- append_to_file "db/seeds.rb", "Role.create({ id: 1, name: 'superadmin', description: 'Super Admin can access all.' })\n"
371
-
372
- # Create Admin User
373
-
374
- say "- Adding admin user with email: #{ENV['email']}, password: #{ENV['password']} to seeds.rb"
375
- append_to_file "db/seeds.rb", "User.create({ id: 1, email: '#{ENV['email']}', locale_id: 1, name: 'Admin', password: '#{ENV['password']}', password_confirmation: '#{ENV['password']}' })\n"
376
-
377
-
378
- sleep 1 # to get unique migration number
379
- create_file "db/migrate/" +
380
- Time.now.utc.strftime("%Y%m%d%H%M%S") +
381
- "_" +
382
- "inline_forms_create_join_table_user_role.rb", <<-ROLES_MIGRATION.strip_heredoc
383
- class InlineFormsCreateJoinTableUserRole < ActiveRecord::Migration[7.1]
384
- def self.up
385
- create_table :roles_users, :id => false, :force => true do |t|
386
- t.integer :role_id
387
- t.integer :user_id
388
- end
389
- execute 'INSERT INTO roles_users VALUES (1,1);'
390
- end
391
-
392
- def self.down
393
- drop_table roles_users
394
- end
395
- end
396
- ROLES_MIGRATION
397
-
398
-
399
- say "- Installaing ZURB Foundation..."
400
- #generate "foundation:install", "-f"
401
-
402
- say "- Copy inline_forms_devise file for custom styles..."
403
- copy_file File.join(GENERATOR_PATH, 'lib/generators/assets/stylesheets/inline_forms_devise.css'), 'app/assets/stylesheets/inline_forms_devise.css'
404
-
405
- say "- Sprockets: link inline_forms_devise.css (logical path; dartsass:install drops link_directory ../stylesheets)..."
406
- append_to_file "app/assets/config/manifest.js", "//= link inline_forms_devise.css\n"
407
-
408
- say "- Add human_attribute_name in app/models/application_record.rb"
409
- remove_file 'app/models/application_record.rb' # the one that 'rails new' created
410
- copy_file File.join(GENERATOR_PATH, 'lib/generators/templates/application_record.rb'), "app/models/application_record.rb"
411
-
412
- say "- Install ActionText..."
413
- run "bundle exec rails active_storage:install"
414
- run "bundle exec rails action_text:install:migrations"
415
- run "bundle install"
416
-
417
- say "- Paper_trail install..."
418
- # Upstream paper_trail (>= 13) detects MySQL via the live ActiveRecord connection,
419
- # so the migration's InnoDB options are added only when the dev DB is mysql.
420
- # For mysql installs the user has been instructed above to create the development
421
- # database before continuing; for sqlite the file is created on first connection.
422
- generate "paper_trail:install --with-changes"
423
- # paper_trail emits two migrations in one second; the next generator would reuse that timestamp.
424
- sleep 1
425
-
426
- say "- Track ActionText (rich_text) edits with PaperTrail..."
427
- # `has_rich_text :foo` stores the body in the separate `action_text_rich_texts`
428
- # table, not on the parent model, so `has_paper_trail` on the parent never
429
- # sees rich_text edits. The standard fix (recommended by paper_trail's
430
- # maintainer in https://stackoverflow.com/q/55544935) is to declare
431
- # `has_paper_trail` directly on `ActionText::RichText`. We surface those
432
- # versions in the parent's versions panel from `inline_forms_versions_for`.
433
- #
434
- # Note: PaperTrail >= 16 raises if `has_paper_trail` is called twice on the
435
- # same model, so this initializer must be the only place it's added to
436
- # `ActionText::RichText` in the generated app.
437
- create_file 'config/initializers/rich_text_paper_trail.rb', <<-PT_RICH_TEXT.strip_heredoc
438
- # Generated by inline_forms.
439
- ActiveSupport.on_load(:action_text_rich_text) do
440
- has_paper_trail
441
- end
442
- PT_RICH_TEXT
443
-
444
- say "- Configure ActiveRecord YAML permitted classes for PaperTrail changesets..."
445
- # PaperTrail's YAML serializer (>= 13) uses `YAML.safe_load` and reads its
446
- # allow-list from `ActiveRecord.yaml_column_permitted_classes`. Rails 7's
447
- # default is `[Symbol]`, so any update that touches `updated_at`
448
- # (an `ActiveSupport::TimeWithZone`) raises `Psych::DisallowedClass` inside
449
- # `version.changeset`; PaperTrail rescues that and returns `{}`, which is why
450
- # the inline_forms versions list rendered every changeset as `empty`. Permit
451
- # the classes PT actually emits so `version.changeset` round-trips.
452
- create_file 'config/initializers/paper_trail_yaml_safe_load.rb', <<-PT_YAML.strip_heredoc
453
- # Generated by inline_forms.
454
- # See https://github.com/paper-trail-gem/paper_trail and
455
- # ActiveRecord::Coders::YAMLColumn safe-loading rules.
456
- Rails.application.config.active_record.yaml_column_permitted_classes ||= []
457
- Rails.application.config.active_record.yaml_column_permitted_classes |= [
458
- Symbol,
459
- Date,
460
- Time,
461
- BigDecimal,
462
- ActiveSupport::TimeWithZone,
463
- ActiveSupport::TimeZone,
464
- ActiveSupport::HashWithIndifferentAccess
465
- ]
466
- ActiveRecord.yaml_column_permitted_classes |= Rails.application.config.active_record.yaml_column_permitted_classes
467
- PT_YAML
468
-
469
- # Create Translations
470
- say "- Generate models and tables and views for translations..." # TODO Translations need to be done in inline_forms, and then generate a yml file, perhaps
471
- generate "inline_forms", "InlineFormsLocale name:string inline_forms_translations:belongs_to _enabled:yes _presentation:\#{name}"
472
- sleep 1 # unique migration timestamps per generator
473
- generate "inline_forms", "InlineFormsKey name:string inline_forms_translations:has_many inline_forms_translations:associated _enabled:yes _presentation:\#{name}"
474
- sleep 1
475
- generate "inline_forms", "InlineFormsTranslation inline_forms_key:belongs_to inline_forms_locale:dropdown value:text interpolations:text is_proc:boolean _presentation:\#{value}"
476
- # Plain long text uses :plain_text; ActionText-backed fields use :rich_text.
477
- sleep 1 # to get unique migration number
478
- create_file "db/migrate/" +
479
- Time.now.utc.strftime("%Y%m%d%H%M%S") +
480
- "_" +
481
- "inline_forms_create_view_for_translations.rb", <<-VIEW_MIGRATION.strip_heredoc
482
- class InlineFormsCreateViewForTranslations < ActiveRecord::Migration[7.1]
483
- def self.up
484
- execute 'CREATE VIEW translations
485
- AS
486
- SELECT L.name AS locale,
487
- K.name AS thekey,
488
- T.value AS value,
489
- T.interpolations AS interpolations,
490
- T.is_proc AS is_proc
491
- FROM inline_forms_keys K, inline_forms_locales L, inline_forms_translations T
492
- WHERE T.inline_forms_key_id = K.id AND T.inline_forms_locale_id = L.id '
493
- end
494
- def self.down
495
- execute 'DROP VIEW translations'
496
- end
497
- end
498
- VIEW_MIGRATION
499
-
500
- say "- Creating application title via locales..."
501
- create_file "config/locales/inline_forms_local.en.yml", <<-END_LOCALE.strip_heredoc
502
- en:
503
- inline_forms:
504
- general:
505
- application_title: #{app_name}
506
- devise:
507
- title_for_devise: #{app_name}
508
- welcome: Welcome to #{app_name}!
509
- END_LOCALE
510
-
511
- say "- Migrating Database (only when using sqlite)"
512
- run "bundle exec rake db:migrate" if ENV['using_sqlite'] == 'true'
513
-
514
- say "- Seeding the database (only when using sqlite)"
515
- run "bundle exec rake db:seed" if ENV['using_sqlite'] == 'true'
516
-
517
- say "- Recreating ApplicationHelper to set application_name and application_title..."
518
- remove_file "app/helpers/application_helper.rb" # the one that 'rails new' created
519
- create_file "app/helpers/application_helper.rb", <<-END_APPHELPER.strip_heredoc
520
- module ApplicationHelper
521
- def application_name
522
- '#{app_name}'
523
- end
524
- def application_title
525
- '#{app_name}'
526
- end
527
- end
528
- END_APPHELPER
529
-
530
- say "- Creating inline_forms initializer"
531
- create_file "config/initializers/inline_forms.rb", <<-END_INITIALIZER.strip_heredoc
532
- Rails.application.reloader.to_prepare do
533
- MODEL_TABS = %w()
534
- end
535
- END_INITIALIZER
536
-
537
- say "- Recreating ApplicationController to add devise, cancan, I18n stuff..."
538
- remove_file "app/controllers/application_controller.rb" # the one that 'rails new' created
539
- create_file "app/controllers/application_controller.rb", <<-END_APPCONTROLLER.strip_heredoc
540
- class ApplicationController < InlineFormsApplicationController
541
- protect_from_forgery
542
-
543
- # add whodunnit
544
- before_action :set_paper_trail_whodunnit
545
-
546
- # Comment next lines if you don't want Devise authentication
547
- before_action :authenticate_user!
548
- check_authorization unless: :devise_controller?
549
-
550
- rescue_from CanCan::AccessDenied do |exception|
551
- respond_to do |format|
552
- format.json { head :forbidden, content_type: 'text/html' }
553
- format.html { redirect_to main_app.root_url, notice: exception.message }
554
- format.js { head :forbidden, content_type: 'text/html' }
555
- end
556
- end
557
- # Comment previous lines if you don't want Devise authentication
558
-
559
- # Uncomment next line if you want I18n (based on subdomain)
560
- # before_action :set_locale
561
-
562
- # Uncomment next line and specify default locale
563
- # I18n.default_locale = :en
564
-
565
- # Uncomment next line and specify available locales
566
- # I18n.available_locales = [ :en, :nl, :pp ]
567
-
568
- # Uncomment next nine line if you want locale based on subdomain, like 'it.example.com, de.example.com'
569
- # def set_locale
570
- # I18n.locale = extract_locale_from_subdomain || I18n.default_locale
571
- # end
572
- #
573
- # def extract_locale_from_subdomain
574
- # locale = request.subdomains.first
575
- # return nil if locale.nil?
576
- # I18n.available_locales.include?(locale.to_sym) ? locale.to_s : nil
577
- # end
578
- end
579
- END_APPCONTROLLER
580
-
581
- say "- Creating Ability model so that the superadmin can access all..."
582
- create_file "app/models/ability.rb", <<-END_ABILITY.strip_heredoc
583
- class Ability
584
- include CanCan::Ability
585
-
586
- def initialize(user)
587
- # See the wiki for details: https://github.com/CanCanCommunity/cancancan/wiki/Defining-Abilities
588
-
589
- user ||= User.new # guest user
590
-
591
- # use this if you get stuck:
592
- # if user.id == 1 #quick hack
593
- # can :manage, :all
594
- if user.role? :superadmin
595
- can :manage, :all
596
- else
597
- # put restrictions for other users here
598
- end
599
- end
600
- end
601
- END_ABILITY
602
-
603
- # devise mailer stuff
604
- say "- Injecting devise mailer stuff in environments/production.rb..."
605
- # strip_heredoc_with_indent(2) became strip_heredoc(2), but only in rails 4... :-(
606
- insert_into_file "config/environments/production.rb", <<-DEVISE_MAILER_PROD_STUFF.strip_heredoc, :before => "end\n"
607
-
608
- # for devise
609
- config.action_mailer.default_url_options = { protocol: 'https', host: Rails.application.credentials[:smtp_app_host] }
610
- config.action_mailer.delivery_method = :smtp
611
- config.action_mailer.smtp_settings = {
612
- address: Rails.application.credentials[:smtp_host],
613
- enable_starttls_auto: true,
614
- password: Rails.application.credentials[:smtp_password] ,
615
- user_name: Rails.application.credentials[:smtp_username]
616
- }
617
-
618
- DEVISE_MAILER_PROD_STUFF
619
-
620
- say "Setting production smtp settings in credentials"
621
- create_file "temp_production_smtp_credentials", <<-END_PROD_SMTP_CRED.strip_heredoc
622
-
623
- # devise mailer stuff for production:
624
- smtp_app_host: APP_HOST
625
- smtp_host: SMTP_HOST
626
- smtp_username: USERNAME
627
- smtp_password: PASSWORD
628
-
629
- END_PROD_SMTP_CRED
630
-
631
- run "EDITOR='cat temp_production_smtp_credentials >> ' rails credentials:edit --environment production"
632
-
633
- remove_file 'temp_production_smtp_credentials'
634
-
635
- say "- Injecting devise mailer stuff in environments/development.rb..."
636
- # strip_heredoc_with_indent(2) became strip_heredoc(2), but only in rails 4... :-(
637
- insert_into_file "config/environments/development.rb", <<-DEVISE_MAILER_DEV_STUFF.strip_heredoc, :before => "\nend\n"
638
- # for devise
639
- config.action_mailer.default_url_options = { protocol: 'http', host: 'localhost', port: 3000 }
640
- config.action_mailer.delivery_method = :smtp
641
- config.action_mailer.smtp_settings = {
642
- address: Rails.application.credentials[:smtp_host],
643
- enable_starttls_auto: true,
644
- password: Rails.application.credentials[:smtp_password] ,
645
- user_name: Rails.application.credentials[:smtp_username]
646
- }
647
-
648
- DEVISE_MAILER_DEV_STUFF
649
-
650
- say "Setting development smtp settings in credentials"
651
- create_file "temp_development_smtp_credentials", <<-END_DEV_SMTP_CRED.strip_heredoc
652
-
653
- # devise mailers stuff for development:
654
- smtp_app_host: APP_HOST
655
- smtp_host: SMTP_HOST
656
- smtp_username: USERNAME
657
- smtp_password: PASSWORD
658
-
659
- END_DEV_SMTP_CRED
660
-
661
- run "EDITOR='cat temp_development_smtp_credentials >> ' rails credentials:edit"
662
-
663
- remove_file 'temp_development_smtp_credentials'
664
-
665
-
666
-
667
- # capify
668
- say "- Capify..."
669
- run 'bundle exec cap install'
670
- remove_file "config/deploy.rb" # remove the file capify created!
671
- copy_file File.join(GENERATOR_PATH,'lib/generators/templates/capistrano/deploy.rb'), "config/deploy.rb"
672
- remove_file "config/deploy/production.rb" # remove the production file capify created!
673
- copy_file File.join(GENERATOR_PATH,'lib/generators/templates/capistrano/production.rb'), "config/deploy/production.rb"
674
- remove_file "Capfile" # remove the Capfile file capify created!
675
- copy_file File.join(GENERATOR_PATH,'lib/generators/templates/capistrano/Capfile'), "Capfile"
676
-
677
- # Unicorn
678
- say "- Unicorn Config..."
679
- copy_file File.join(GENERATOR_PATH,'lib/generators/templates/unicorn/production.rb'), "config/unicorn/production.rb"
680
-
681
- # Git
682
- say "- adding and committing to git..."
683
-
684
- git add: "."
685
- git commit: " -a -m 'Initial Commit'"
686
-
687
- # example
688
- if ENV['install_example'] == 'true'
689
- say "\nInstalling example application..."
690
- run 'bundle exec rails g inline_forms Photo name:string caption:string image:image_field description:rich_text apartment:belongs_to _presentation:\'#{name}\''
691
- run 'bundle exec rails generate uploader Image'
692
- run 'bundle exec rails g inline_forms Apartment name:string title:string opening_date:date description:rich_text photos:has_many photos:associated _enabled:yes _presentation:\'#{name}\''
693
-
694
- say "- Apartment name is required..."
695
- inject_into_file "app/models/apartment.rb",
696
- "\n validates :name, presence: true\n",
697
- after: " has_paper_trail\n"
698
-
699
- # CarrierWave + PaperTrail history.
700
- # PaperTrail snapshots the column scalar (the stored filename) on update,
701
- # but CarrierWave's default `remove_previously_stored_files_after_update`
702
- # deletes the old file on disk and re-uses the same filename, so a
703
- # PaperTrail revert restores a filename whose bytes are gone.
704
- # We keep every uploaded file on disk and namespace filenames with a
705
- # per-upload token so successive uploads do not collide. See
706
- # https://stackoverflow.com/questions/9423279/papertrail-and-carrierwave
707
- # (Answers 2, 4 and 5).
708
- say "- Configuring CarrierWave to keep previously stored files (PaperTrail history)..."
709
- create_file "config/initializers/carrierwave.rb", <<-CWINIT.strip_heredoc
710
- # Keep previously stored files on disk so PaperTrail-driven restore
711
- # actually returns the previous image bytes. See
712
- # https://stackoverflow.com/questions/9423279/papertrail-and-carrierwave
713
- # The per-uploader overrides in app/uploaders/image_uploader.rb
714
- # complement this by giving every upload a unique on-disk filename
715
- # and by no-op'ing `remove!` so destroyed records keep their files.
716
- CarrierWave.configure do |config|
717
- config.remove_previously_stored_files_after_update = false
718
- end
719
- CWINIT
720
-
721
- inject_into_file "app/uploaders/image_uploader.rb",
722
- after: "class ImageUploader < CarrierWave::Uploader::Base\n" do
723
- <<-RUBY.strip_heredoc.gsub(/^/, " ")
724
- # PaperTrail history support. CarrierWave's default behaviour wipes the
725
- # previous file on update and reuses the same filename; PaperTrail only
726
- # stores the column scalar, so a plain `version.reify; save!` restores a
727
- # filename whose bytes are gone. The knobs below preserve every byte:
728
- #
729
- # * `remove_previously_stored_files_after_update = false` is set
730
- # globally in config/initializers/carrierwave.rb (covers
731
- # `multi_image_field` uploaders too).
732
- # * `remove!` is a no-op so hard-destroyed records keep their files
733
- # and revert-after-destroy still finds the bytes on disk.
734
- # * `filename` is prefixed with a per-upload UUID so successive
735
- # uploads never collide on disk.
736
- #
737
- # Trade-off: files accumulate on disk; sweeping is out of scope.
738
- # Source: https://stackoverflow.com/questions/9423279/papertrail-and-carrierwave
739
- def remove!
740
- # no-op: keep the file so PaperTrail revert can restore it.
741
- end
742
-
743
- def filename
744
- # CarrierWave 3.x calls `filename` again after storing to record the
745
- # persisted name; at that point `original_filename` may be nil and we
746
- # must still return the memoized name (see
747
- # https://github.com/carrierwaveuploader/carrierwave/issues/2708).
748
- @name ||= "\#{secure_token}-\#{original_filename}" if original_filename
749
- @name
750
- end
751
-
752
- private
753
-
754
- def secure_token
755
- var = :"@\#{mounted_as}_secure_token"
756
- model.instance_variable_get(var) || model.instance_variable_set(var, SecureRandom.uuid)
757
- end
758
- RUBY
759
- end
760
-
761
- say "- Lower Photo.per_page so the seeded gallery paginates..."
762
- # The model template (lib/generators/templates/model.erb) emits
763
- # attr_reader :per_page
764
- # @per_page = 7
765
- # which is a long-standing typo: `attr_reader :per_page` defines an
766
- # *instance* method, then `@per_page = 7` (executed in the class body)
767
- # actively *clobbers* the class-level per_page that will_paginate
768
- # exposes via `class_attribute :per_page` (its singleton-ivar storage
769
- # also lives on `@per_page`). Net effect: nothing reads 7 anywhere,
770
- # and the class-level per_page silently reverts to will_paginate's
771
- # 30-default. Replace the pair on Photo with a real `self.per_page = 5`
772
- # so the seeded gallery (12 photos) actually paginates 5/5/2.
773
- gsub_file "app/models/photo.rb",
774
- /^\s*attr_reader\s+:per_page\s*\n\s*@per_page\s*=\s*\d+\s*\n/,
775
- " self.per_page = 5\n"
776
-
777
- run 'bundle exec rake db:migrate'
778
-
779
- # Seed the photos gallery from a local `pics/` folder. The folder is
780
- # *gitignored* in the gem source (so the built .gem stays small and
781
- # the gallery images are not committed) which means GENERATOR_PATH/pics
782
- # exists only when the installer is run from the source repo, not when
783
- # it is run from an installed gem on the developer's box. We therefore
784
- # check, in order:
785
- # 1. ENV['INLINE_FORMS_SEED_PICS'] -- explicit override path
786
- # 2. GENERATOR_PATH/pics -- gem source repo checkout
787
- # 3. /home/code/inline_forms/pics -- local dev convention
788
- # and copy whichever is found into the generated app's db/seed_images/.
789
- # The migration generated below is what reads from db/seed_images at
790
- # `db:migrate` / `db:test:prepare` time, so this copy is only ever a
791
- # one-shot at app generation.
792
- pics_candidates = [
793
- ENV["INLINE_FORMS_SEED_PICS"],
794
- File.join(GENERATOR_PATH, "pics"),
795
- "/home/code/inline_forms/pics",
796
- ].compact
797
- pics_src = pics_candidates.find { |p| Dir.exist?(p) }
798
- if pics_src
799
- seed_pics = Dir.glob(File.join(pics_src, "*.{jpg,jpeg,JPG,JPEG,png,PNG,gif,GIF}")).sort
800
- if seed_pics.any?
801
- say "- Copying #{seed_pics.size} sample photo(s) into db/seed_images/..."
802
- empty_directory "db/seed_images"
803
- seed_pics.each do |abs|
804
- copy_file abs, File.join("db/seed_images", File.basename(abs))
805
- end
806
-
807
- say "- Generating Konferensha apartment + photos seed migration..."
808
- sleep 1 # unique migration timestamp
809
- seed_ts = Time.now.utc.strftime("%Y%m%d%H%M%S")
810
- create_file "db/migrate/#{seed_ts}_seed_konferensha_photos.rb", <<-SEED_MIGRATION.strip_heredoc
811
- class SeedKonferenshaPhotos < ActiveRecord::Migration[7.1]
812
- # Seed an Apartment with a gallery of photos so the nested
813
- # has_many list (apartments -> photos) has enough rows to
814
- # trigger pagination. Driven by db/seed_images/, which the
815
- # inline_forms installer copies from the gem's pics/ dir.
816
- # Runs in development (via db:migrate) and against the test
817
- # DB (via db:test:prepare), so integration tests can assert
818
- # the paginated <turbo-frame> renders without seeding manually.
819
- def up
820
- apartment = Apartment.find_or_create_by!(name: "Konferensha") do |a|
821
- a.title = "Konferensha sobre Papiamentu"
822
- a.opening_date = Date.new(2020, 5, 18)
823
- end
824
-
825
- seed_dir = Rails.root.join("db", "seed_images")
826
- return unless seed_dir.directory?
827
-
828
- Dir.glob(seed_dir.join("*.{jpg,jpeg,png,gif}"), File::FNM_CASEFOLD).sort.each do |abs|
829
- base = File.basename(abs)
830
- next if Photo.exists?(name: base, apartment_id: apartment.id)
831
- File.open(abs, "rb") do |io|
832
- Photo.create!(
833
- name: base,
834
- caption: "Konferensha foto \#{base}",
835
- apartment: apartment,
836
- image: io
837
- )
838
- end
839
- end
840
- end
841
-
842
- def down
843
- apartment = Apartment.find_by(name: "Konferensha")
844
- return unless apartment
845
- apartment.photos.destroy_all
846
- apartment.destroy
847
- end
848
- end
849
- SEED_MIGRATION
850
-
851
- run "bundle exec rake db:migrate"
852
- end
853
- end
854
-
855
- remove_file 'public/index.html'
856
-
857
- say "- Apartment name list demo (field-level inline edit without _show)..."
858
- inject_into_file "app/controllers/apartments_controller.rb",
859
- "\n skip_load_and_authorize_resource only: :name_list\n\n def name_list\n authorize! :read, Apartment\n @apartments = Apartment.accessible_by(current_ability).order(:id).limit(10)\n end\n",
860
- after: "set_tab :apartment\n"
861
-
862
- example_views_root = File.join(GENERATOR_PATH, "lib/installer_templates/example_app_views")
863
- Dir.glob(File.join(example_views_root, "**", "*")).sort.each do |abs|
864
- next unless File.file?(abs)
865
- rel = abs.delete_prefix(example_views_root + File::SEPARATOR).tr("\\", "/")
866
- create_file File.join("app/views", rel), File.read(abs)
867
- end
868
-
869
- route 'get "apartments/name_list", to: "apartments#name_list", as: :apartment_name_list'
870
- route "root :to => 'apartments#index'"
871
-
872
- say "- Adding example app regression tests (bundle exec rails test)..."
873
- example_tests_root = File.join(GENERATOR_PATH, "lib/installer_templates/example_app_tests")
874
- Dir.glob(File.join(example_tests_root, "**", "*.rb")).sort.each do |abs|
875
- rel = abs.delete_prefix(example_tests_root + File::SEPARATOR).tr("\\", "/")
876
- create_file rel, File.read(abs)
877
- end
878
-
879
- say "\nDone! Example app (Photo + Apartment) is ready.", :yellow
880
- say " bundle exec rails test # example regression tests", :yellow
881
- say " bundle exec rails s # then http://localhost:3000/apartments", :yellow
882
- say " More menu → Apartment names (first 10) # /apartments/name_list", :yellow
883
- say " Log in: #{ENV["email"]} / #{ENV["password"]}", :yellow
884
- end
885
- # done!
886
- say "\nDone! Now make your tables with 'bundle exec rails g inline_forms ...", :yellow