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