inline_forms_installer 8.1.20 → 8.1.40
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.
- checksums.yaml +4 -4
- data/inline_forms_installer.gemspec +9 -9
- data/lib/inline_forms_installer/creator.rb +8 -7
- data/lib/inline_forms_installer/installer_core.rb +130 -42
- data/lib/inline_forms_installer/user_model_config.rb +2 -1
- data/lib/inline_forms_installer/version.rb +2 -1
- data/lib/inline_forms_installer.rb +1 -0
- data/lib/installer_templates/example_app_assets/seed_images/apt1_bedroom.png +0 -0
- data/lib/installer_templates/example_app_assets/seed_images/apt1_kitchen.png +0 -0
- data/lib/installer_templates/example_app_assets/seed_images/apt1_living.png +0 -0
- data/lib/installer_templates/example_app_assets/seed_images/apt2_kitchen.png +0 -0
- data/lib/installer_templates/example_app_assets/seed_images/apt2_living.png +0 -0
- data/lib/installer_templates/example_app_assets/seed_images/apt2_terrace.png +0 -0
- data/lib/installer_templates/example_app_assets/seed_images/apt3_living.png +0 -0
- data/lib/installer_templates/example_app_assets/seed_images/apt3_loft.png +0 -0
- data/lib/installer_templates/example_app_assets/seed_images/apt3_view.png +0 -0
- data/lib/installer_templates/example_app_tests/test/integration/example_app_apartment_opening_date_test.rb +7 -6
- data/lib/installer_templates/example_app_tests/test/integration/example_app_apartment_top_level_new_test.rb +28 -0
- data/lib/installer_templates/example_app_tests/test/integration/example_app_photo_comments_test.rb +90 -0
- data/lib/installer_templates/example_app_tests/test/integration/example_app_production_ssl_test.rb +19 -0
- data/lib/installer_templates/example_app_tests/test/integration/example_app_showcase_date_time_fields_test.rb +69 -6
- data/lib/installer_templates/example_app_tests/test/integration/example_app_showcase_gallery_test.rb +68 -0
- data/lib/installer_templates/example_app_tests/test/integration/example_app_user_locale_test.rb +46 -0
- data/lib/installer_templates/example_app_tests/test/integration/example_app_user_theme_test.rb +76 -0
- data/lib/installer_templates/example_app_views/owners/_owner_tabs.html.erb +3 -3
- metadata +15 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 94ac8df294cd719110b2360768007fbfb8c26c95749c95323a0e61b969801b4e
|
|
4
|
+
data.tar.gz: b5c245794ab67879467b227eef31fb523927f5c8f27634bb48f173222246da0c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c8e07fcf3fa59b3a0c0a8b548ec06f78032e9659410351abe5ae13f2d03e2b8f66e4d3321f2111367dd9aec31daaa8263b1eeabfb79d2736fc2fb0745652ff12
|
|
7
|
+
data.tar.gz: 866e3d7586e1e03b3d19e5023f834f8665794d436e17b7a6d58f95a8c1aa095c3cff58687d34b18cd77be65fc875d24517a4c130d1792ee7a2c895341b903d9a
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
2
3
|
$:.push File.expand_path("../lib", __FILE__)
|
|
3
4
|
require "inline_forms/gem_files"
|
|
4
5
|
require "inline_forms_installer/version"
|
|
@@ -7,17 +8,17 @@ Gem::Specification.new do |s|
|
|
|
7
8
|
s.name = "inline_forms_installer"
|
|
8
9
|
s.version = InlineFormsInstaller::VERSION
|
|
9
10
|
s.platform = Gem::Platform::RUBY
|
|
10
|
-
s.authors = ["Ace Suares", "Lemuel Boyce", "Manuel Ortega"]
|
|
11
|
-
s.email = ["ace@suares.com"]
|
|
12
|
-
s.homepage = %q
|
|
13
|
-
s.summary = %q
|
|
14
|
-
s.description = %q
|
|
15
|
-
s.licenses = ["MIT"]
|
|
11
|
+
s.authors = [ "Ace Suares", "Lemuel Boyce", "Manuel Ortega" ]
|
|
12
|
+
s.email = [ "ace@suares.com" ]
|
|
13
|
+
s.homepage = %q(http://github.com/acesuares/inline_forms)
|
|
14
|
+
s.summary = %q(CLI and Rails app template for generating inline_forms applications.)
|
|
15
|
+
s.description = %q(Installs the `inline_forms` CLI and scaffolds opinionated Rails apps with Devise, CanCan, PaperTrail, and optional example data.)
|
|
16
|
+
s.licenses = [ "MIT" ]
|
|
16
17
|
s.required_ruby_version = ">= 4.0.0"
|
|
17
18
|
|
|
18
19
|
s.files = InlineFormsGemFiles.gem_files(include_installer: true)
|
|
19
|
-
s.executables = ["inline_forms"]
|
|
20
|
-
s.require_paths = ["lib"]
|
|
20
|
+
s.executables = [ "inline_forms" ]
|
|
21
|
+
s.require_paths = [ "lib" ]
|
|
21
22
|
|
|
22
23
|
s.add_dependency("inline_forms", "~> 8")
|
|
23
24
|
s.add_dependency("thor", ">= 1.0", "< 2.0")
|
|
@@ -27,5 +28,4 @@ Gem::Specification.new do |s|
|
|
|
27
28
|
# otherwise it installs without RVM (any version manager, or none, works —
|
|
28
29
|
# Bundler isolates deps per app via Gemfile.lock). Install `gem install rvm`
|
|
29
30
|
# to opt in to gemset integration, or pass `--no-rvm` to skip it explicitly.
|
|
30
|
-
|
|
31
31
|
end
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# -*- encoding : utf-8 -*-
|
|
2
|
+
|
|
2
3
|
require "securerandom"
|
|
3
4
|
require "thor"
|
|
4
5
|
require_relative "create_log"
|
|
@@ -16,13 +17,13 @@ module InlineFormsInstaller
|
|
|
16
17
|
end
|
|
17
18
|
|
|
18
19
|
desc "create APP", "create an application with inline_forms v#{InlineFormsInstaller.inline_forms_version}"
|
|
19
|
-
DATABASE_OPTIONS = %w
|
|
20
|
-
method_option :database, :
|
|
21
|
-
method_option :example, :
|
|
22
|
-
method_option :email, :
|
|
23
|
-
method_option :password, :
|
|
24
|
-
method_option :skiprvm, :
|
|
25
|
-
method_option :user_model, :
|
|
20
|
+
DATABASE_OPTIONS = %w[sqlite mysql]
|
|
21
|
+
method_option :database, aliases: "-d", banner: DATABASE_OPTIONS.join("|"), desc: "specify development database"
|
|
22
|
+
method_option :example, type: :boolean, desc: "install the example app. uses sqlite as development database"
|
|
23
|
+
method_option :email, aliases: "-e", default: "admin@example.com", desc: "specify admin email"
|
|
24
|
+
method_option :password, aliases: "-p", default: "admin999", desc: "specify admin password"
|
|
25
|
+
method_option :skiprvm, aliases: "--no-rvm", type: :boolean, default: false, desc: "install inline_forms without RVM"
|
|
26
|
+
method_option :user_model, aliases: "-U", default: "User", banner: "CLASS", desc: "Devise model class (e.g. Member); Warden scope stays :user (current_user)"
|
|
26
27
|
|
|
27
28
|
def create(app_name)
|
|
28
29
|
def self.skiprvm
|
|
@@ -103,7 +103,7 @@ end
|
|
|
103
103
|
|
|
104
104
|
add_source 'https://rubygems.org'
|
|
105
105
|
|
|
106
|
-
gem 'cancancan'
|
|
106
|
+
gem 'cancancan', '~> 3.6'
|
|
107
107
|
gem 'carrierwave', '~> 3.1'
|
|
108
108
|
gem 'devise', '~> 5.0'
|
|
109
109
|
gem 'devise-i18n', '~> 1.16'
|
|
@@ -119,12 +119,11 @@ if ENV["INLINE_FORMS_GEMFILE_PATH"] && File.directory?(ENV["INLINE_FORMS_GEMFILE
|
|
|
119
119
|
else
|
|
120
120
|
gem "inline_forms", "~> 8"
|
|
121
121
|
end
|
|
122
|
+
# jQuery is required by Foundation 6's JS only. jQuery UI is fully gone since
|
|
123
|
+
# 8.1.26 (native date/time/month inputs in 8.1.25; datalist combobox + range
|
|
124
|
+
# slider in 8.1.26), as are jquery-timepicker-rails, jquery-ui-rails and
|
|
125
|
+
# rails-jquery-autocomplete.
|
|
122
126
|
gem 'jquery-rails'
|
|
123
|
-
gem 'jquery-timepicker-rails'
|
|
124
|
-
# jQuery UI JavaScript (`//= require jquery.ui.all` in inline_forms.js). SCSS + PNGs
|
|
125
|
-
# are vendored in the inline_forms engine (Dart Sass cannot evaluate sass-rails
|
|
126
|
-
# `image-path()`). Pin matches former jquery-ui-sass-rails 4.0.3.x stack.
|
|
127
|
-
gem 'jquery-ui-rails', '4.0.3'
|
|
128
127
|
# Foundation Icons SCSS + fonts are vendored in the inline_forms engine (Dart Sass;
|
|
129
128
|
# foundation-icons-sass-rails depended on sass-rails).
|
|
130
129
|
gem 'mini_magick'
|
|
@@ -138,7 +137,6 @@ gem 'money-rails', '~> 3.0'
|
|
|
138
137
|
gem 'mysql2'
|
|
139
138
|
gem 'paper_trail', '~> 17.0'
|
|
140
139
|
gem 'rails-i18n', '~> 8.1'
|
|
141
|
-
gem 'rails-jquery-autocomplete'
|
|
142
140
|
gem 'rails', '~> 8.1'
|
|
143
141
|
gem 'rake'
|
|
144
142
|
# NOTE: the `rvm` gem is intentionally NOT a runtime dependency of generated
|
|
@@ -156,10 +154,11 @@ gem 'importmap-rails'
|
|
|
156
154
|
# use `<turbo-frame>` + HTML responses (see docs/ujs-to-turbo.md). Registers the
|
|
157
155
|
# `turbo_stream` MIME type for optional stream responses.
|
|
158
156
|
gem 'turbo-rails'
|
|
159
|
-
|
|
157
|
+
# tabs (set_tab / tabs_tag) are vendored in the inline_forms engine since
|
|
158
|
+
# 8.1.23 (InlineForms::Tabs) — the unmaintained tabs_on_rails gem is gone.
|
|
160
159
|
gem 'unicorn'
|
|
161
160
|
gem 'validation_hints', '~> 8'
|
|
162
|
-
gem 'will_paginate'
|
|
161
|
+
gem 'will_paginate', '~> 4.0'
|
|
163
162
|
|
|
164
163
|
gem_group :test do
|
|
165
164
|
# Rails 7 still expects Minitest 5; 6.x breaks the railties test runner.
|
|
@@ -353,6 +352,13 @@ class #{user_cfg.devise_migration_class} < ActiveRecord::Migration[8.1]
|
|
|
353
352
|
|
|
354
353
|
t.string :name
|
|
355
354
|
t.integer :locale_id
|
|
355
|
+
# Preset UI theme (inline_forms 8.1.29+): 0 default, 1 dark, 2 sepia,
|
|
356
|
+
# 3 high-contrast — see #{user_cfg.class_name}::INLINE_FORMS_THEMES.
|
|
357
|
+
t.integer :theme, default: 0, null: false
|
|
358
|
+
# Per-user color overrides (inline_forms 8.1.32+, theming Pattern 2):
|
|
359
|
+
# hex "#rrggbb" or NULL; rendered as --if-color-* overrides in <head>.
|
|
360
|
+
t.string :primary_color
|
|
361
|
+
t.string :accent_color
|
|
356
362
|
|
|
357
363
|
t.timestamps
|
|
358
364
|
end
|
|
@@ -396,6 +402,10 @@ create_file user_cfg.model_path, <<-USER_MODEL.strip_heredoc
|
|
|
396
402
|
|
|
397
403
|
# validations
|
|
398
404
|
validates :name, :presence => true
|
|
405
|
+
# Theming Pattern 2 color overrides; the color_field element only ever
|
|
406
|
+
# submits "#rrggbb" or nil, but validate anyway (console edits, imports).
|
|
407
|
+
validates :primary_color, format: { with: /\\A#\\h{6}\\z/i }, allow_blank: true
|
|
408
|
+
validates :accent_color, format: { with: /\\A#\\h{6}\\z/i }, allow_blank: true
|
|
399
409
|
|
|
400
410
|
# Default ordering for inline_forms list views (and any explicit caller
|
|
401
411
|
# via `#{user_cfg.class_name}.inline_forms_list`). Avoids `default_scope`
|
|
@@ -415,12 +425,32 @@ create_file user_cfg.model_path, <<-USER_MODEL.strip_heredoc
|
|
|
415
425
|
return !!self.roles.find_by_name(role)
|
|
416
426
|
end
|
|
417
427
|
|
|
428
|
+
# Preset UI themes (inline_forms Pattern 1 theming). The integer `theme`
|
|
429
|
+
# column maps to a body class `theme-<name>`; the palettes ship in the
|
|
430
|
+
# inline_forms engine (_theme.scss). The engine layout calls
|
|
431
|
+
# #inline_forms_theme on current_user when the method exists.
|
|
432
|
+
INLINE_FORMS_THEMES = { 0 => 'default', 1 => 'dark', 2 => 'sepia', 3 => 'high-contrast' }.freeze
|
|
433
|
+
|
|
434
|
+
def inline_forms_theme
|
|
435
|
+
INLINE_FORMS_THEMES.fetch(theme.to_i, 'default')
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
# Theming Pattern 2: --if-color-* overrides for this user, rendered as an
|
|
439
|
+
# inline <style> by the engine layout (inline_forms_user_color_overrides_style).
|
|
440
|
+
# Keys are --if-color- suffixes; values must be "#rrggbb".
|
|
441
|
+
def inline_forms_color_overrides
|
|
442
|
+
{ 'primary' => primary_color, 'accent' => accent_color }.compact
|
|
443
|
+
end
|
|
444
|
+
|
|
418
445
|
def inline_forms_attribute_list
|
|
419
446
|
@inline_forms_attribute_list ||= [
|
|
420
447
|
[ :header_user_login, :header ],
|
|
421
448
|
[ :name, :text_field ],
|
|
422
449
|
[ :email, :text_field ],
|
|
423
450
|
[ :locale, :dropdown ],
|
|
451
|
+
[ :theme, :dropdown_with_values, { 0 => 'default', 1 => 'dark', 2 => 'sepia', 3 => 'high contrast' } ],
|
|
452
|
+
[ :primary_color, :color_field ],
|
|
453
|
+
[ :accent_color, :color_field ],
|
|
424
454
|
[ :password, :devise_password_field ],
|
|
425
455
|
[ :header_user_roles, :header ],
|
|
426
456
|
[ :roles, :check_list ],
|
|
@@ -654,24 +684,36 @@ create_file "app/controllers/application_controller.rb", <<-END_APPCONTROLLER.st
|
|
|
654
684
|
end
|
|
655
685
|
# Comment previous lines if you don't want Devise authentication
|
|
656
686
|
|
|
657
|
-
#
|
|
658
|
-
#
|
|
659
|
-
|
|
660
|
-
#
|
|
661
|
-
#
|
|
687
|
+
# Per-user locale (inline_forms 8.1.31): the signed-in user's Locale
|
|
688
|
+
# record (its `name`: en/nl/...) drives I18n for the request. Unknown or
|
|
689
|
+
# blank names fall back to I18n.default_locale, and I18n.with_locale
|
|
690
|
+
# restores the previous locale afterwards, so nothing leaks across
|
|
691
|
+
# requests or threads. Users pick their locale on their own user panel
|
|
692
|
+
# (the [:locale, :dropdown] row).
|
|
693
|
+
around_action :switch_locale
|
|
694
|
+
|
|
695
|
+
def switch_locale(&action)
|
|
696
|
+
candidate = current_user&.locale&.name.to_s.strip
|
|
697
|
+
locale =
|
|
698
|
+
if I18n.available_locales.map(&:to_s).include?(candidate)
|
|
699
|
+
candidate
|
|
700
|
+
else
|
|
701
|
+
I18n.default_locale
|
|
702
|
+
end
|
|
703
|
+
I18n.with_locale(locale, &action)
|
|
704
|
+
end
|
|
662
705
|
|
|
663
|
-
#
|
|
664
|
-
#
|
|
706
|
+
# Restrict/extend the locale whitelist in config/application.rb:
|
|
707
|
+
# config.i18n.available_locales = [:en, :nl]
|
|
708
|
+
# config.i18n.default_locale = :en
|
|
665
709
|
|
|
666
|
-
# Uncomment
|
|
667
|
-
#
|
|
668
|
-
#
|
|
669
|
-
#
|
|
670
|
-
#
|
|
671
|
-
# def extract_locale_from_subdomain
|
|
710
|
+
# Uncomment if you prefer locale based on subdomain ('nl.example.com')
|
|
711
|
+
# instead of the per-user setting above:
|
|
712
|
+
# around_action :switch_locale_from_subdomain
|
|
713
|
+
# def switch_locale_from_subdomain(&action)
|
|
672
714
|
# locale = request.subdomains.first
|
|
673
|
-
#
|
|
674
|
-
# I18n.
|
|
715
|
+
# locale = nil unless locale && I18n.available_locales.map(&:to_s).include?(locale)
|
|
716
|
+
# I18n.with_locale(locale || I18n.default_locale, &action)
|
|
675
717
|
# end
|
|
676
718
|
end
|
|
677
719
|
END_APPCONTROLLER
|
|
@@ -698,6 +740,19 @@ create_file "app/models/ability.rb", <<-END_ABILITY.strip_heredoc
|
|
|
698
740
|
end
|
|
699
741
|
END_ABILITY
|
|
700
742
|
|
|
743
|
+
# SSL in production. `rails new` only enables these when Kamal is skipped
|
|
744
|
+
# (its proxy config takes over otherwise), but generated apps deploy via the
|
|
745
|
+
# shipped Capistrano/Unicorn setup behind an SSL-terminating reverse proxy —
|
|
746
|
+
# and the Devise mailer config below already assumes https. Enabling both
|
|
747
|
+
# matches the Rails 8 defaults and closes Brakeman's ForceSSL warning
|
|
748
|
+
# (full-gate CI scans the generated app). Serving plain HTTP internally?
|
|
749
|
+
# Re-comment these in your app's config/environments/production.rb.
|
|
750
|
+
say "- Enabling assume_ssl/force_ssl in environments/production.rb..."
|
|
751
|
+
gsub_file "config/environments/production.rb",
|
|
752
|
+
"# config.assume_ssl = true", "config.assume_ssl = true"
|
|
753
|
+
gsub_file "config/environments/production.rb",
|
|
754
|
+
"# config.force_ssl = true", "config.force_ssl = true"
|
|
755
|
+
|
|
701
756
|
# devise mailer stuff
|
|
702
757
|
say "- Injecting devise mailer stuff in environments/production.rb..."
|
|
703
758
|
# strip_heredoc_with_indent(2) became strip_heredoc(2), but only in rails 4... :-(
|
|
@@ -785,8 +840,16 @@ git commit: " -a -m 'Initial Commit'"
|
|
|
785
840
|
# example
|
|
786
841
|
if ENV['install_example'] == 'true'
|
|
787
842
|
say "\nInstalling example application..."
|
|
788
|
-
run 'bundle exec rails g inline_forms Photo name:string caption:string image:image_field description:rich_text apartment:belongs_to _list_order:name _presentation:\'#{name}\''
|
|
843
|
+
run 'bundle exec rails g inline_forms Photo name:string caption:string image:image_field description:rich_text apartment:belongs_to comments:has_many comments:associated _list_order:name _presentation:\'#{name}\''
|
|
789
844
|
run 'bundle exec rails generate uploader Image'
|
|
845
|
+
|
|
846
|
+
# Comment: second nesting level (Apartment -> Photo -> Comment) so the
|
|
847
|
+
# example app demonstrates :associated panels inside an opened nested row.
|
|
848
|
+
# Like Photo it has no `_enabled`, so it is not accessible through
|
|
849
|
+
# top-level HTML — only through the comments panel on an opened photo.
|
|
850
|
+
say "- Generating Comment model (nested under Photo -- nesting-in-nesting demo)..."
|
|
851
|
+
sleep 1
|
|
852
|
+
run 'bundle exec rails g inline_forms Comment body:text_area photo:belongs_to _list_order:body _presentation:\'#{body}\''
|
|
790
853
|
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 _list_order:name _list_search:name _presentation:\'#{name}\''
|
|
791
854
|
|
|
792
855
|
say "- Apartment name is required..."
|
|
@@ -861,23 +924,18 @@ if ENV['install_example'] == 'true'
|
|
|
861
924
|
|
|
862
925
|
run 'bundle exec rake db:migrate'
|
|
863
926
|
|
|
864
|
-
# Seed the photos gallery
|
|
865
|
-
#
|
|
866
|
-
#
|
|
867
|
-
#
|
|
868
|
-
#
|
|
869
|
-
#
|
|
870
|
-
#
|
|
871
|
-
#
|
|
872
|
-
#
|
|
873
|
-
# and copy whichever is found into the generated app's db/seed_images/.
|
|
874
|
-
# The migration generated below is what reads from db/seed_images at
|
|
875
|
-
# `db:migrate` / `db:test:prepare` time, so this copy is only ever a
|
|
876
|
-
# one-shot at app generation.
|
|
927
|
+
# Seed the photos gallery. The 9 CC0 placeholder images (solid pastel +
|
|
928
|
+
# "Apt N / Room" label, ~4 KB each, generated with ImageMagick — see
|
|
929
|
+
# stuff/2026-07-06-forgejo-ci-rubocop-brakeman.md) ship with the installer
|
|
930
|
+
# under lib/installer_templates/example_app_assets/seed_images/, so every
|
|
931
|
+
# install — laptop, CI container, anywhere — gets the same gallery. Before
|
|
932
|
+
# 8.1.35 this looked for a *gitignored* local pics/ folder, which existed
|
|
933
|
+
# only on the maintainer's box: gem installs elsewhere (e.g. the Forgejo
|
|
934
|
+
# full-gate runner) silently produced zero photos and 12 test failures.
|
|
935
|
+
# ENV['INLINE_FORMS_SEED_PICS'] still overrides with your own folder.
|
|
877
936
|
pics_candidates = [
|
|
878
937
|
ENV["INLINE_FORMS_SEED_PICS"],
|
|
879
|
-
File.join(INSTALLER_ROOT, "
|
|
880
|
-
"/home/code/inline_forms/pics",
|
|
938
|
+
File.join(INSTALLER_ROOT, "lib/installer_templates/example_app_assets/seed_images"),
|
|
881
939
|
].compact
|
|
882
940
|
pics_src = pics_candidates.find { |p| Dir.exist?(p) }
|
|
883
941
|
if pics_src
|
|
@@ -1043,6 +1101,13 @@ if ENV['install_example'] == 'true'
|
|
|
1043
1101
|
apartments: [] },
|
|
1044
1102
|
].freeze
|
|
1045
1103
|
|
|
1104
|
+
# Comments seeded on each apartment's first photo (Apartment ->
|
|
1105
|
+
# Photo -> Comment: the second nesting level).
|
|
1106
|
+
COMMENTS = [
|
|
1107
|
+
"Lovely light in this room!",
|
|
1108
|
+
"Could use a fresh coat of paint.",
|
|
1109
|
+
].freeze
|
|
1110
|
+
|
|
1046
1111
|
def up
|
|
1047
1112
|
seed_dir = Rails.root.join("db", "seed_images")
|
|
1048
1113
|
all_pics = seed_dir.directory? ?
|
|
@@ -1073,6 +1138,15 @@ if ENV['install_example'] == 'true'
|
|
|
1073
1138
|
end
|
|
1074
1139
|
end
|
|
1075
1140
|
|
|
1141
|
+
# Nesting-in-nesting demo data: two comments on each apartment's
|
|
1142
|
+
# first photo, so the comments panel inside an opened photo row
|
|
1143
|
+
# is visibly populated out of the box.
|
|
1144
|
+
if (first_photo = apt.photos.order(:id).first)
|
|
1145
|
+
COMMENTS.each do |body|
|
|
1146
|
+
Comment.find_or_create_by!(photo: first_photo, body: body)
|
|
1147
|
+
end
|
|
1148
|
+
end
|
|
1149
|
+
|
|
1076
1150
|
apt_records[spec[:name]] = apt
|
|
1077
1151
|
end
|
|
1078
1152
|
|
|
@@ -1099,6 +1173,7 @@ if ENV['install_example'] == 'true'
|
|
|
1099
1173
|
APARTMENTS.each do |spec|
|
|
1100
1174
|
apt = Apartment.find_by(name: spec[:name])
|
|
1101
1175
|
next unless apt
|
|
1176
|
+
apt.photos.each { |photo| photo.comments.destroy_all }
|
|
1102
1177
|
apt.photos.destroy_all
|
|
1103
1178
|
apt.destroy
|
|
1104
1179
|
end
|
|
@@ -1119,7 +1194,7 @@ if ENV['install_example'] == 'true'
|
|
|
1119
1194
|
# ---------------------------------------------------------------------
|
|
1120
1195
|
say "- Generating FormElementShowcase (one resource per kept Tier 1 form_element)..."
|
|
1121
1196
|
sleep 1
|
|
1122
|
-
run %q{bundle exec rails g inline_forms FormElementShowcase title:string body_plain_area:plain_text_area count:integer_field price:decimal_field amount:money_field latitude:decimal_field{9,6} longitude:decimal_field{10,6} meeting_date:date_select meeting_time:time_select birth_month:month_select start_month:month_year_picker is_active:check_box gender:radio_button rating_int:dropdown_with_integers priority:dropdown_with_values priority2:dropdown_with_values stars:dropdown_with_values_with_stars scale_int:scale_with_integers scale_val:scale_with_values attachment:file_field jingle:audio_field cover:image_field description:rich_text locales:has_and_belongs_to_many _enabled:yes _list_order:title _list_search:title _presentation:'#{title}'}
|
|
1197
|
+
run %q{bundle exec rails g inline_forms FormElementShowcase title:string body_plain_area:plain_text_area count:integer_field price:decimal_field amount:money_field latitude:decimal_field{9,6} longitude:decimal_field{10,6} meeting_date:date_select meeting_time:time_select birth_month:month_select start_month:month_year_picker is_active:check_box gender:radio_button rating_int:dropdown_with_integers priority:dropdown_with_values priority2:dropdown_with_values stars:dropdown_with_values_with_stars scale_int:scale_with_integers scale_val:scale_with_values attachment:file_field jingle:audio_field cover:image_field gallery:multi_image_field description:rich_text locales:has_and_belongs_to_many _enabled:yes _list_order:title _list_search:title _presentation:'#{title}'}
|
|
1123
1198
|
|
|
1124
1199
|
say "- Generating Attachment + Jingle uploaders (Cover reuses ImageUploader)..."
|
|
1125
1200
|
run "bundle exec rails generate uploader Attachment"
|
|
@@ -1150,6 +1225,9 @@ if ENV['install_example'] == 'true'
|
|
|
1150
1225
|
showcase_migration = Dir.glob("db/migrate/*_inline_forms_create_form_element_showcases.rb").first
|
|
1151
1226
|
if showcase_migration
|
|
1152
1227
|
gsub_file showcase_migration, /t\.integer :amount\b/, "t.integer :amount_cents, default: 0, null: false"
|
|
1228
|
+
# multi_image_field stores a JSON array of filenames (mount_uploaders);
|
|
1229
|
+
# the registry maps it to :string, widen to :text for the serialized list.
|
|
1230
|
+
gsub_file showcase_migration, /t\.string :gallery\b/, "t.text :gallery"
|
|
1153
1231
|
end
|
|
1154
1232
|
|
|
1155
1233
|
create_file "config/initializers/money.rb", <<-MONEY_INIT.strip_heredoc
|
|
@@ -1185,7 +1263,9 @@ if ENV['install_example'] == 'true'
|
|
|
1185
1263
|
" validates :price, numericality: true, allow_blank: true\n" \
|
|
1186
1264
|
" validates :latitude, numericality: { greater_than_or_equal_to: -90, less_than_or_equal_to: 90 }, allow_blank: true\n" \
|
|
1187
1265
|
" validates :longitude, numericality: { greater_than_or_equal_to: -180, less_than_or_equal_to: 180 }, allow_blank: true\n" \
|
|
1188
|
-
" mount_uploader :attachment, AttachmentUploader\n
|
|
1266
|
+
" mount_uploader :attachment, AttachmentUploader\n" \
|
|
1267
|
+
" serialize :gallery, coder: JSON\n mount_uploaders :gallery, ImageUploader\n" \
|
|
1268
|
+
" monetize :amount_cents\n\n def locales_display\n locales\n end\n",
|
|
1189
1269
|
after: "class FormElementShowcase < ApplicationRecord\n"
|
|
1190
1270
|
|
|
1191
1271
|
# Value-bearing rows for every form_element that needs a values hash
|
|
@@ -1272,6 +1352,7 @@ if ENV['install_example'] == 'true'
|
|
|
1272
1352
|
attachment: Attachment
|
|
1273
1353
|
jingle: Jingle
|
|
1274
1354
|
cover: Cover
|
|
1355
|
+
gallery: Gallery (multiple images)
|
|
1275
1356
|
description: Description
|
|
1276
1357
|
locales: Locales (editable)
|
|
1277
1358
|
locales_display: Locales (read-only)
|
|
@@ -1382,6 +1463,13 @@ if ENV['install_example'] == 'true'
|
|
|
1382
1463
|
next if full.public_send(attr).present?
|
|
1383
1464
|
File.open(path, "rb") { |io| full.public_send("\#{attr}=", io) }
|
|
1384
1465
|
end
|
|
1466
|
+
|
|
1467
|
+
# multi_image_field demo: an *array* assignment through the plural
|
|
1468
|
+
# mount_uploaders. One file is enough to exercise the array path.
|
|
1469
|
+
gallery_src = seed_uploads.join("sample_cover.png")
|
|
1470
|
+
if full.respond_to?(:gallery) && gallery_src.file? && full.gallery.blank?
|
|
1471
|
+
File.open(gallery_src, "rb") { |io| full.gallery = [io] }
|
|
1472
|
+
end
|
|
1385
1473
|
full.save! if full.changed?
|
|
1386
1474
|
|
|
1387
1475
|
# "Empty" refers to the role and uploader fields (their empty
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# -*- encoding : utf-8 -*-
|
|
2
|
+
|
|
2
3
|
require "active_support/core_ext/string/inflections"
|
|
3
4
|
|
|
4
5
|
module InlineFormsInstaller
|
|
@@ -47,7 +48,7 @@ module InlineFormsInstaller
|
|
|
47
48
|
def join_table
|
|
48
49
|
return "roles_users" if default?
|
|
49
50
|
|
|
50
|
-
[table_name, "roles"].sort.join("_")
|
|
51
|
+
[ table_name, "roles" ].sort.join("_")
|
|
51
52
|
end
|
|
52
53
|
|
|
53
54
|
def controller_name
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative "../example_app/example_integration_test_case"
|
|
4
4
|
|
|
5
|
-
# Example app includes opening_date:date on Apartment (
|
|
5
|
+
# Example app includes opening_date:date on Apartment (native date input since 8.1.25).
|
|
6
6
|
class ExampleAppApartmentOpeningDateTest < ExampleAppIntegrationTestCase
|
|
7
7
|
setup do
|
|
8
8
|
@apartment = Apartment.find_or_create_by!(name: "Datepicker Apt") do |a|
|
|
@@ -24,7 +24,7 @@ class ExampleAppApartmentOpeningDateTest < ExampleAppIntegrationTestCase
|
|
|
24
24
|
assert_includes @response.body, "15-03-2019"
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
-
test "inline edit opening_date
|
|
27
|
+
test "inline edit opening_date renders a native date input" do
|
|
28
28
|
get edit_apartment_path(
|
|
29
29
|
@apartment,
|
|
30
30
|
attribute: "opening_date",
|
|
@@ -33,17 +33,18 @@ class ExampleAppApartmentOpeningDateTest < ExampleAppIntegrationTestCase
|
|
|
33
33
|
), headers: @turbo_headers
|
|
34
34
|
|
|
35
35
|
assert_response :success
|
|
36
|
-
assert_includes @response.body, %(
|
|
36
|
+
assert_includes @response.body, %(type="date")
|
|
37
37
|
assert_includes @response.body, %(name="opening_date")
|
|
38
|
+
assert_includes @response.body, %(value="2019-03-15"), "value must be ISO 8601"
|
|
38
39
|
refute_includes @response.body, "<script",
|
|
39
|
-
"
|
|
40
|
+
"native inputs need no per-field init script"
|
|
40
41
|
end
|
|
41
42
|
|
|
42
|
-
test "new apartment form includes
|
|
43
|
+
test "new apartment form includes a native date input for opening_date" do
|
|
43
44
|
get new_apartment_path(update: @list_frame), headers: @list_headers
|
|
44
45
|
|
|
45
46
|
assert_response :success
|
|
46
47
|
assert_includes @response.body, %(name="opening_date")
|
|
47
|
-
assert_includes @response.body, %(
|
|
48
|
+
assert_includes @response.body, %(type="date")
|
|
48
49
|
end
|
|
49
50
|
end
|
|
@@ -67,4 +67,32 @@ class ExampleAppApartmentTopLevelNewTest < ExampleAppIntegrationTestCase
|
|
|
67
67
|
assert_includes @response.body, %(<turbo-frame id="#{@frame}">)
|
|
68
68
|
assert_includes @response.body, name
|
|
69
69
|
end
|
|
70
|
+
|
|
71
|
+
# "Open after create": Apartment has photos:associated, so a turbo-stream
|
|
72
|
+
# create response refreshes the list AND opens the new row, landing the user
|
|
73
|
+
# where the photos panel (only available on a persisted record) is usable.
|
|
74
|
+
test "top-level create with turbo-stream accept opens the new row" do
|
|
75
|
+
name = "AAA-OpenAfterCreate-#{SecureRandom.hex(4)}"
|
|
76
|
+
assert_difference("Apartment.count", 1) do
|
|
77
|
+
post apartments_path(update: @frame),
|
|
78
|
+
params: { name: name, title: "Open after create test" },
|
|
79
|
+
headers: { "Turbo-Frame" => @frame,
|
|
80
|
+
"Accept" => "text/vnd.turbo-stream.html, text/html" }
|
|
81
|
+
end
|
|
82
|
+
assert_response :success
|
|
83
|
+
assert_equal "text/vnd.turbo-stream.html", @response.media_type
|
|
84
|
+
|
|
85
|
+
apartment = Apartment.find_by!(name: name)
|
|
86
|
+
assert_includes @response.body,
|
|
87
|
+
%(<turbo-stream action="replace" target="#{@frame}">)
|
|
88
|
+
assert_includes @response.body,
|
|
89
|
+
%(<turbo-stream action="replace" target="apartment_#{apartment.id}">)
|
|
90
|
+
assert_includes @response.body,
|
|
91
|
+
%(id="apartment_#{apartment.id}_photos_list_auto_header")
|
|
92
|
+
assert_match(
|
|
93
|
+
%r{/photos/new\?[^"]*parent_id=#{apartment.id}},
|
|
94
|
+
@response.body,
|
|
95
|
+
"expected the photos panel's new link for the created apartment"
|
|
96
|
+
)
|
|
97
|
+
end
|
|
70
98
|
end
|
data/lib/installer_templates/example_app_tests/test/integration/example_app_photo_comments_test.rb
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../example_app/example_integration_test_case"
|
|
4
|
+
|
|
5
|
+
# Nesting-in-nesting (Apartment -> Photo -> Comment): a Photo opened inside an
|
|
6
|
+
# apartment's photos panel exposes its own :associated comments panel, and the
|
|
7
|
+
# whole nested CRUD contract works one level deeper.
|
|
8
|
+
class ExampleAppPhotoCommentsTest < ExampleAppIntegrationTestCase
|
|
9
|
+
setup do
|
|
10
|
+
@apartment = Apartment.create!(name: "Nested", title: "Nesting demo")
|
|
11
|
+
@photo = @apartment.photos.create!(name: "room.jpg", caption: "the room")
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
test "comments are not served as standalone html resource" do
|
|
15
|
+
assert Comment.not_accessible_through_html?
|
|
16
|
+
get comments_path
|
|
17
|
+
assert_not response.successful?,
|
|
18
|
+
"expected no standalone HTML index for not_accessible_through_html model (got #{response.status})"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
test "opened nested photo row exposes the comments panel with its new link" do
|
|
22
|
+
row_frame = "apartment_#{@apartment.id}_photo_#{@photo.id}"
|
|
23
|
+
get photo_path(@photo, update: row_frame),
|
|
24
|
+
headers: { "Turbo-Frame" => row_frame, "Accept" => "text/html" }
|
|
25
|
+
assert_response :success
|
|
26
|
+
|
|
27
|
+
panel = "photo_#{@photo.id}_comments"
|
|
28
|
+
assert_includes @response.body, %(id="#{panel}_list_auto_header")
|
|
29
|
+
assert_includes @response.body, %(<turbo-frame id="#{panel}")
|
|
30
|
+
assert_match(
|
|
31
|
+
%r{/comments/new\?[^"]*parent_id=#{@photo.id}},
|
|
32
|
+
@response.body,
|
|
33
|
+
"expected the comments panel's new-record link for the photo"
|
|
34
|
+
)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
test "comments panel lists existing comments" do
|
|
38
|
+
comment = @photo.comments.create!(body: "First!")
|
|
39
|
+
row_frame = "apartment_#{@apartment.id}_photo_#{@photo.id}"
|
|
40
|
+
get photo_path(@photo, update: row_frame),
|
|
41
|
+
headers: { "Turbo-Frame" => row_frame, "Accept" => "text/html" }
|
|
42
|
+
assert_response :success
|
|
43
|
+
assert_includes @response.body,
|
|
44
|
+
%(<turbo-frame id="photo_#{@photo.id}_comment_#{comment.id}">)
|
|
45
|
+
assert_includes @response.body, "First!"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
test "new comment form renders inside the comments panel frame" do
|
|
49
|
+
panel = "photo_#{@photo.id}_comments"
|
|
50
|
+
get new_comment_path(update: panel, parent_class: "Photo", parent_id: @photo.id),
|
|
51
|
+
headers: { "Turbo-Frame" => panel, "Accept" => "text/html" }
|
|
52
|
+
assert_response :success
|
|
53
|
+
assert_includes @response.body, %(<turbo-frame id="#{panel}">)
|
|
54
|
+
assert_includes @response.body, 'name="body"'
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
test "nested comment create persists with the photo FK and returns the panel frame" do
|
|
58
|
+
panel = "photo_#{@photo.id}_comments"
|
|
59
|
+
assert_difference("Comment.count", 1) do
|
|
60
|
+
post comments_path(update: panel, parent_class: "Photo", parent_id: @photo.id),
|
|
61
|
+
params: { body: "Great view from here" },
|
|
62
|
+
headers: { "Turbo-Frame" => panel,
|
|
63
|
+
"Accept" => "text/vnd.turbo-stream.html, text/html" }
|
|
64
|
+
end
|
|
65
|
+
assert_response :success
|
|
66
|
+
# Comment has no :associated panel of its own, so open-after-create does
|
|
67
|
+
# not fire; the nested flow keeps the plain frame response.
|
|
68
|
+
assert_equal "text/html", @response.media_type
|
|
69
|
+
assert_includes @response.body, %(<turbo-frame id="#{panel}">)
|
|
70
|
+
|
|
71
|
+
comment = Comment.find_by!(body: "Great view from here")
|
|
72
|
+
assert_equal @photo.id, comment.photo_id
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
test "nested comment row opens and closes" do
|
|
76
|
+
comment = @photo.comments.create!(body: "Open me")
|
|
77
|
+
row_frame = "photo_#{@photo.id}_comment_#{comment.id}"
|
|
78
|
+
|
|
79
|
+
get comment_path(comment, update: row_frame),
|
|
80
|
+
headers: { "Turbo-Frame" => row_frame, "Accept" => "text/html" }
|
|
81
|
+
assert_response :success
|
|
82
|
+
assert_includes @response.body, %(<turbo-frame id="#{row_frame}">)
|
|
83
|
+
assert_includes @response.body, "Open me"
|
|
84
|
+
|
|
85
|
+
get comment_path(comment, update: row_frame, close: true),
|
|
86
|
+
headers: { "Turbo-Frame" => row_frame, "Accept" => "text/html" }
|
|
87
|
+
assert_response :success
|
|
88
|
+
assert_includes @response.body, %(<turbo-frame id="#{row_frame}">)
|
|
89
|
+
end
|
|
90
|
+
end
|
data/lib/installer_templates/example_app_tests/test/integration/example_app_production_ssl_test.rb
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../example_app/example_integration_test_case"
|
|
4
|
+
|
|
5
|
+
# The installer enables assume_ssl/force_ssl in the generated production
|
|
6
|
+
# config (8.1.36): generated apps deploy behind an SSL-terminating proxy and
|
|
7
|
+
# the Devise mailer config assumes https. Guards Brakeman's ForceSSL check
|
|
8
|
+
# (the full-gate CI scans this app) against a silent regression, e.g. the
|
|
9
|
+
# Rails template shifting its comment format so the installer's gsub misses.
|
|
10
|
+
class ExampleAppProductionSslTest < ExampleAppIntegrationTestCase
|
|
11
|
+
test "production config forces SSL" do
|
|
12
|
+
config = File.read(Rails.root.join("config", "environments", "production.rb"))
|
|
13
|
+
|
|
14
|
+
assert_match(/^\s*config\.assume_ssl = true/, config,
|
|
15
|
+
"expected the installer to enable config.assume_ssl in production.rb")
|
|
16
|
+
assert_match(/^\s*config\.force_ssl = true/, config,
|
|
17
|
+
"expected the installer to enable config.force_ssl in production.rb")
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -12,7 +12,8 @@ class ExampleAppShowcaseDateTimeFieldsTest < ExampleAppIntegrationTestCase
|
|
|
12
12
|
@showcase = FormElementShowcase.find_or_create_by!(title: "Date/time demo")
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
-
test "date_select meeting_date renders
|
|
15
|
+
test "date_select meeting_date renders native date input on edit" do
|
|
16
|
+
@showcase.update!(meeting_date: Date.new(2026, 5, 17))
|
|
16
17
|
frame = "form_element_showcase_#{@showcase.id}_meeting_date"
|
|
17
18
|
headers = { "Turbo-Frame" => frame, "Accept" => "text/html" }
|
|
18
19
|
|
|
@@ -25,11 +26,13 @@ class ExampleAppShowcaseDateTimeFieldsTest < ExampleAppIntegrationTestCase
|
|
|
25
26
|
|
|
26
27
|
assert_response :success
|
|
27
28
|
assert_includes @response.body, %(<turbo-frame id="#{frame}">)
|
|
28
|
-
assert_includes @response.body, %(
|
|
29
|
+
assert_includes @response.body, %(type="date")
|
|
29
30
|
assert_includes @response.body, %(name="meeting_date")
|
|
31
|
+
assert_includes @response.body, %(value="2026-05-17"), "value must be ISO 8601"
|
|
32
|
+
refute_includes @response.body, "<script", "no inline picker init"
|
|
30
33
|
end
|
|
31
34
|
|
|
32
|
-
test "time_select meeting_time renders
|
|
35
|
+
test "time_select meeting_time renders native time input on edit" do
|
|
33
36
|
@showcase.update!(meeting_time: Time.utc(2000, 1, 1, 9, 15))
|
|
34
37
|
frame = "form_element_showcase_#{@showcase.id}_meeting_time"
|
|
35
38
|
headers = { "Turbo-Frame" => frame, "Accept" => "text/html" }
|
|
@@ -43,8 +46,42 @@ class ExampleAppShowcaseDateTimeFieldsTest < ExampleAppIntegrationTestCase
|
|
|
43
46
|
|
|
44
47
|
assert_response :success
|
|
45
48
|
assert_includes @response.body, %(<turbo-frame id="#{frame}">)
|
|
46
|
-
assert_includes @response.body, %(
|
|
49
|
+
assert_includes @response.body, %(type="time")
|
|
47
50
|
assert_includes @response.body, %(name="meeting_time")
|
|
51
|
+
assert_includes @response.body, %(value="09:15"), "value must be 24h HH:MM"
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
test "time_select round-trips a native HH:MM submission" do
|
|
55
|
+
@showcase.update!(meeting_time: Time.utc(2000, 1, 1, 9, 15))
|
|
56
|
+
frame = "form_element_showcase_#{@showcase.id}_meeting_time"
|
|
57
|
+
|
|
58
|
+
put form_element_showcase_path(
|
|
59
|
+
@showcase,
|
|
60
|
+
attribute: "meeting_time",
|
|
61
|
+
form_element: "time_select",
|
|
62
|
+
update: frame
|
|
63
|
+
), params: { meeting_time: "14:30" },
|
|
64
|
+
headers: { "Turbo-Frame" => frame, "Accept" => "text/html" }
|
|
65
|
+
|
|
66
|
+
assert_response :success
|
|
67
|
+
@showcase.reload
|
|
68
|
+
assert_equal 14, @showcase.meeting_time.hour
|
|
69
|
+
assert_equal 30, @showcase.meeting_time.min
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
test "date_select round-trips a native ISO submission" do
|
|
73
|
+
frame = "form_element_showcase_#{@showcase.id}_meeting_date"
|
|
74
|
+
|
|
75
|
+
put form_element_showcase_path(
|
|
76
|
+
@showcase,
|
|
77
|
+
attribute: "meeting_date",
|
|
78
|
+
form_element: "date_select",
|
|
79
|
+
update: frame
|
|
80
|
+
), params: { meeting_date: "2027-01-09" },
|
|
81
|
+
headers: { "Turbo-Frame" => frame, "Accept" => "text/html" }
|
|
82
|
+
|
|
83
|
+
assert_response :success
|
|
84
|
+
assert_equal Date.new(2027, 1, 9), @showcase.reload.meeting_date
|
|
48
85
|
end
|
|
49
86
|
|
|
50
87
|
test "month_select birth_month renders 12 options on edit" do
|
|
@@ -64,7 +101,7 @@ class ExampleAppShowcaseDateTimeFieldsTest < ExampleAppIntegrationTestCase
|
|
|
64
101
|
assert_match(/<select[^>]+name="date\[birth_month\]"/, @response.body)
|
|
65
102
|
end
|
|
66
103
|
|
|
67
|
-
test "month_year_picker start_month renders
|
|
104
|
+
test "month_year_picker start_month renders native month input on edit" do
|
|
68
105
|
@showcase.update!(start_month: Date.new(2026, 5, 1))
|
|
69
106
|
frame = "form_element_showcase_#{@showcase.id}_start_month"
|
|
70
107
|
headers = { "Turbo-Frame" => frame, "Accept" => "text/html" }
|
|
@@ -78,7 +115,33 @@ class ExampleAppShowcaseDateTimeFieldsTest < ExampleAppIntegrationTestCase
|
|
|
78
115
|
|
|
79
116
|
assert_response :success
|
|
80
117
|
assert_includes @response.body, %(<turbo-frame id="#{frame}">)
|
|
81
|
-
assert_includes @response.body, %(
|
|
118
|
+
assert_includes @response.body, %(type="month")
|
|
82
119
|
assert_includes @response.body, %(name="start_month")
|
|
120
|
+
assert_includes @response.body, %(value="2026-05"), "value must be ISO YYYY-MM"
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
test "month_year_picker round-trips native YYYY-MM and legacy 'Month YYYY' submissions" do
|
|
124
|
+
frame = "form_element_showcase_#{@showcase.id}_start_month"
|
|
125
|
+
headers = { "Turbo-Frame" => frame, "Accept" => "text/html" }
|
|
126
|
+
|
|
127
|
+
# Native <input type="month"> value.
|
|
128
|
+
put form_element_showcase_path(
|
|
129
|
+
@showcase,
|
|
130
|
+
attribute: "start_month",
|
|
131
|
+
form_element: "month_year_picker",
|
|
132
|
+
update: frame
|
|
133
|
+
), params: { start_month: "2026-09" }, headers: headers
|
|
134
|
+
assert_response :success
|
|
135
|
+
assert_equal Date.new(2026, 9, 1), @showcase.reload.start_month
|
|
136
|
+
|
|
137
|
+
# Legacy pre-8.1.25 display format still parses via the fallback.
|
|
138
|
+
put form_element_showcase_path(
|
|
139
|
+
@showcase,
|
|
140
|
+
attribute: "start_month",
|
|
141
|
+
form_element: "month_year_picker",
|
|
142
|
+
update: frame
|
|
143
|
+
), params: { start_month: "November 2027" }, headers: headers
|
|
144
|
+
assert_response :success
|
|
145
|
+
assert_equal Date.new(2027, 11, 1), @showcase.reload.start_month
|
|
83
146
|
end
|
|
84
147
|
end
|
data/lib/installer_templates/example_app_tests/test/integration/example_app_showcase_gallery_test.rb
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../example_app/example_integration_test_case"
|
|
4
|
+
|
|
5
|
+
# multi_image_field (FormElementShowcase#gallery, 8.1.28): a mount_uploaders
|
|
6
|
+
# (plural) CarrierWave column serialized as a JSON array. First example-app
|
|
7
|
+
# coverage for this Tier 1 element.
|
|
8
|
+
class ExampleAppShowcaseGalleryTest < ExampleAppIntegrationTestCase
|
|
9
|
+
setup do
|
|
10
|
+
@showcase = FormElementShowcase.find_or_create_by!(title: "Gallery demo")
|
|
11
|
+
src = Rails.root.join("db", "seed_uploads", "sample_cover.png")
|
|
12
|
+
if @showcase.gallery.blank? && src.file?
|
|
13
|
+
File.open(src, "rb") { |io| @showcase.gallery = [io] }
|
|
14
|
+
@showcase.save!
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
test "show renders the gallery images from the plural uploader" do
|
|
19
|
+
skip "seed image missing" if @showcase.gallery.blank?
|
|
20
|
+
|
|
21
|
+
frame = "form_element_showcase_#{@showcase.id}"
|
|
22
|
+
get form_element_showcase_path(@showcase, update: frame),
|
|
23
|
+
headers: { "Turbo-Frame" => frame, "Accept" => "text/html" }
|
|
24
|
+
|
|
25
|
+
assert_response :success
|
|
26
|
+
assert_includes @response.body, %(<turbo-frame id="#{frame}_gallery">)
|
|
27
|
+
# mount_uploaders store_dir => uploads/form_element_showcase/gallery/:id/
|
|
28
|
+
assert_includes @response.body, "uploads/form_element_showcase/gallery",
|
|
29
|
+
"expected an <img> from the gallery uploader array"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
test "edit renders a multiple file input submitting gallery[]" do
|
|
33
|
+
frame = "form_element_showcase_#{@showcase.id}_gallery"
|
|
34
|
+
get edit_form_element_showcase_path(
|
|
35
|
+
@showcase,
|
|
36
|
+
attribute: "gallery",
|
|
37
|
+
form_element: "multi_image_field",
|
|
38
|
+
update: frame
|
|
39
|
+
), headers: { "Turbo-Frame" => frame, "Accept" => "text/html" }
|
|
40
|
+
|
|
41
|
+
assert_response :success
|
|
42
|
+
assert_includes @response.body, %(type="file")
|
|
43
|
+
assert_includes @response.body, %(name="gallery[]")
|
|
44
|
+
assert_includes @response.body, %(multiple="multiple")
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
test "empty gallery renders the inline-edit plus link" do
|
|
48
|
+
empty = FormElementShowcase.find_or_create_by!(title: "Gallery-less demo") do |s|
|
|
49
|
+
s.is_active = false
|
|
50
|
+
s.gender = 1
|
|
51
|
+
s.rating_int = 1
|
|
52
|
+
s.priority = 1
|
|
53
|
+
s.priority2 = 1
|
|
54
|
+
s.stars = 1
|
|
55
|
+
s.scale_int = 1
|
|
56
|
+
s.scale_val = 1
|
|
57
|
+
end
|
|
58
|
+
frame = "form_element_showcase_#{empty.id}"
|
|
59
|
+
|
|
60
|
+
get form_element_showcase_path(empty, update: frame),
|
|
61
|
+
headers: { "Turbo-Frame" => frame, "Accept" => "text/html" }
|
|
62
|
+
|
|
63
|
+
assert_response :success
|
|
64
|
+
gallery_frame = @response.body[/<turbo-frame id="#{frame}_gallery">.*?<\/turbo-frame>/m]
|
|
65
|
+
assert gallery_frame, "expected the gallery attribute frame"
|
|
66
|
+
assert_includes gallery_frame, "fi-plus"
|
|
67
|
+
end
|
|
68
|
+
end
|
data/lib/installer_templates/example_app_tests/test/integration/example_app_user_locale_test.rb
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../example_app/example_integration_test_case"
|
|
4
|
+
|
|
5
|
+
# Per-user locale (inline_forms 8.1.31): ApplicationController#switch_locale
|
|
6
|
+
# wraps every request in I18n.with_locale(current_user.locale.name), falling
|
|
7
|
+
# back to the default for unknown/blank names. Observable via the layout's
|
|
8
|
+
# <html lang="..."> attribute.
|
|
9
|
+
class ExampleAppUserLocaleTest < ExampleAppIntegrationTestCase
|
|
10
|
+
test "html lang reflects the default locale" do
|
|
11
|
+
get apartments_path
|
|
12
|
+
|
|
13
|
+
assert_response :success
|
|
14
|
+
assert_includes @response.body, %(<html lang="en">)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
test "user locale preference switches I18n for the request" do
|
|
18
|
+
admin = User.find_by!(email: "admin@example.com")
|
|
19
|
+
nl = Locale.find_or_create_by!(name: "nl") { |l| l.title = "Nederlands" }
|
|
20
|
+
admin.update!(locale: nl)
|
|
21
|
+
sign_in admin
|
|
22
|
+
|
|
23
|
+
get apartments_path
|
|
24
|
+
|
|
25
|
+
assert_response :success
|
|
26
|
+
assert_includes @response.body, %(<html lang="nl">)
|
|
27
|
+
ensure
|
|
28
|
+
en = Locale.find_by(name: "en")
|
|
29
|
+
admin&.update!(locale: en) if en
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
test "unknown locale name falls back to the default" do
|
|
33
|
+
admin = User.find_by!(email: "admin@example.com")
|
|
34
|
+
xx = Locale.find_or_create_by!(name: "xx") { |l| l.title = "Not a locale" }
|
|
35
|
+
admin.update!(locale: xx)
|
|
36
|
+
sign_in admin
|
|
37
|
+
|
|
38
|
+
get apartments_path
|
|
39
|
+
|
|
40
|
+
assert_response :success
|
|
41
|
+
assert_includes @response.body, %(<html lang="en">)
|
|
42
|
+
ensure
|
|
43
|
+
en = Locale.find_by(name: "en")
|
|
44
|
+
admin&.update!(locale: en) if en
|
|
45
|
+
end
|
|
46
|
+
end
|
data/lib/installer_templates/example_app_tests/test/integration/example_app_user_theme_test.rb
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../example_app/example_integration_test_case"
|
|
4
|
+
|
|
5
|
+
# Pattern 1 preset theming (inline_forms 8.1.29): the user's integer `theme`
|
|
6
|
+
# column maps to a `theme-<name>` body class; palettes live in the engine's
|
|
7
|
+
# _theme.scss as --if-color-* custom-property overrides.
|
|
8
|
+
class ExampleAppUserThemeTest < ExampleAppIntegrationTestCase
|
|
9
|
+
test "body carries the default theme class" do
|
|
10
|
+
get apartments_path
|
|
11
|
+
|
|
12
|
+
assert_response :success
|
|
13
|
+
assert_includes @response.body, %(<body class="theme-default">)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
test "user theme preference switches the body class" do
|
|
17
|
+
admin = User.find_by!(email: "admin@example.com")
|
|
18
|
+
admin.update!(theme: 1)
|
|
19
|
+
# Warden's test login caches the user *object* from setup's sign_in;
|
|
20
|
+
# re-sign-in so current_user carries the updated theme.
|
|
21
|
+
sign_in admin
|
|
22
|
+
|
|
23
|
+
get apartments_path
|
|
24
|
+
|
|
25
|
+
assert_response :success
|
|
26
|
+
assert_includes @response.body, %(<body class="theme-dark">)
|
|
27
|
+
ensure
|
|
28
|
+
admin&.update!(theme: 0)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
test "high-contrast maps the css-safe class name" do
|
|
32
|
+
admin = User.find_by!(email: "admin@example.com")
|
|
33
|
+
admin.update!(theme: 3)
|
|
34
|
+
sign_in admin
|
|
35
|
+
|
|
36
|
+
get apartments_path
|
|
37
|
+
|
|
38
|
+
assert_response :success
|
|
39
|
+
assert_includes @response.body, %(<body class="theme-high-contrast">)
|
|
40
|
+
ensure
|
|
41
|
+
admin&.update!(theme: 0)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
test "theme dropdown renders on the user show panel" do
|
|
45
|
+
admin = User.find_by!(email: "admin@example.com")
|
|
46
|
+
frame = "user_#{admin.id}"
|
|
47
|
+
|
|
48
|
+
get user_path(admin, update: frame),
|
|
49
|
+
headers: { "Turbo-Frame" => frame, "Accept" => "text/html" }
|
|
50
|
+
|
|
51
|
+
assert_response :success
|
|
52
|
+
assert_includes @response.body, %(<turbo-frame id="user_#{admin.id}_theme">)
|
|
53
|
+
assert_includes @response.body, %(<turbo-frame id="user_#{admin.id}_primary_color">)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
test "user color overrides render as a scoped inline style (Pattern 2)" do
|
|
57
|
+
admin = User.find_by!(email: "admin@example.com")
|
|
58
|
+
admin.update!(primary_color: "#2563eb")
|
|
59
|
+
sign_in admin
|
|
60
|
+
|
|
61
|
+
get apartments_path
|
|
62
|
+
|
|
63
|
+
assert_response :success
|
|
64
|
+
assert_includes @response.body, %(id="inline_forms_user_color_overrides")
|
|
65
|
+
assert_includes @response.body, "body.theme-default { --if-color-primary: #2563eb; }"
|
|
66
|
+
ensure
|
|
67
|
+
admin&.update!(primary_color: nil)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
test "no override style tag renders when the user has no custom colors" do
|
|
71
|
+
get apartments_path
|
|
72
|
+
|
|
73
|
+
assert_response :success
|
|
74
|
+
refute_includes @response.body, %(id="inline_forms_user_color_overrides")
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
Owner sub-tabs (Turbo). Each tab link targets the surrounding row
|
|
3
3
|
turbo-frame (id == @update_span) so clicking a tab re-fetches
|
|
4
4
|
OwnersController#show?tab=... and swaps just that frame. Active-tab
|
|
5
|
-
highlighting is driven by set_tab / current_tab? (
|
|
6
|
-
InlineForms::TurboTabsBuilder threads
|
|
7
|
-
|
|
5
|
+
highlighting is driven by set_tab / current_tab? (InlineForms::Tabs,
|
|
6
|
+
vendored since 8.1.23). InlineForms::TurboTabsBuilder threads
|
|
7
|
+
link_options: through to the anchor.
|
|
8
8
|
-%>
|
|
9
9
|
<div class="row owner_tabs_row">
|
|
10
10
|
<div class='medium-1 large-1 column'> </div>
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: inline_forms_installer
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 8.1.
|
|
4
|
+
version: 8.1.40
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ace Suares
|
|
@@ -77,6 +77,15 @@ files:
|
|
|
77
77
|
- lib/installer_templates/example_app_assets/sample.txt
|
|
78
78
|
- lib/installer_templates/example_app_assets/sample.wav
|
|
79
79
|
- lib/installer_templates/example_app_assets/sample_cover.png
|
|
80
|
+
- lib/installer_templates/example_app_assets/seed_images/apt1_bedroom.png
|
|
81
|
+
- lib/installer_templates/example_app_assets/seed_images/apt1_kitchen.png
|
|
82
|
+
- lib/installer_templates/example_app_assets/seed_images/apt1_living.png
|
|
83
|
+
- lib/installer_templates/example_app_assets/seed_images/apt2_kitchen.png
|
|
84
|
+
- lib/installer_templates/example_app_assets/seed_images/apt2_living.png
|
|
85
|
+
- lib/installer_templates/example_app_assets/seed_images/apt2_terrace.png
|
|
86
|
+
- lib/installer_templates/example_app_assets/seed_images/apt3_living.png
|
|
87
|
+
- lib/installer_templates/example_app_assets/seed_images/apt3_loft.png
|
|
88
|
+
- lib/installer_templates/example_app_assets/seed_images/apt3_view.png
|
|
80
89
|
- lib/installer_templates/example_app_tests/test/example_app/example_integration_test_case.rb
|
|
81
90
|
- lib/installer_templates/example_app_tests/test/integration/example_app_apartment_field_turbo_test.rb
|
|
82
91
|
- lib/installer_templates/example_app_tests/test/integration/example_app_apartment_name_list_test.rb
|
|
@@ -89,11 +98,14 @@ files:
|
|
|
89
98
|
- lib/installer_templates/example_app_tests/test/integration/example_app_apartment_versions_turbo_test.rb
|
|
90
99
|
- lib/installer_templates/example_app_tests/test/integration/example_app_guest_access_test.rb
|
|
91
100
|
- lib/installer_templates/example_app_tests/test/integration/example_app_owner_tabs_test.rb
|
|
101
|
+
- lib/installer_templates/example_app_tests/test/integration/example_app_photo_comments_test.rb
|
|
92
102
|
- lib/installer_templates/example_app_tests/test/integration/example_app_photo_revert_test.rb
|
|
93
103
|
- lib/installer_templates/example_app_tests/test/integration/example_app_photos_test.rb
|
|
104
|
+
- lib/installer_templates/example_app_tests/test/integration/example_app_production_ssl_test.rb
|
|
94
105
|
- lib/installer_templates/example_app_tests/test/integration/example_app_routing_test.rb
|
|
95
106
|
- lib/installer_templates/example_app_tests/test/integration/example_app_showcase_choice_scale_fields_test.rb
|
|
96
107
|
- lib/installer_templates/example_app_tests/test/integration/example_app_showcase_date_time_fields_test.rb
|
|
108
|
+
- lib/installer_templates/example_app_tests/test/integration/example_app_showcase_gallery_test.rb
|
|
97
109
|
- lib/installer_templates/example_app_tests/test/integration/example_app_showcase_locales_associations_test.rb
|
|
98
110
|
- lib/installer_templates/example_app_tests/test/integration/example_app_showcase_money_update_test.rb
|
|
99
111
|
- lib/installer_templates/example_app_tests/test/integration/example_app_showcase_numeric_fields_test.rb
|
|
@@ -102,6 +114,8 @@ files:
|
|
|
102
114
|
- lib/installer_templates/example_app_tests/test/integration/example_app_showcase_text_fields_test.rb
|
|
103
115
|
- lib/installer_templates/example_app_tests/test/integration/example_app_showcase_validation_hints_test.rb
|
|
104
116
|
- lib/installer_templates/example_app_tests/test/integration/example_app_turbo_layout_test.rb
|
|
117
|
+
- lib/installer_templates/example_app_tests/test/integration/example_app_user_locale_test.rb
|
|
118
|
+
- lib/installer_templates/example_app_tests/test/integration/example_app_user_theme_test.rb
|
|
105
119
|
- lib/installer_templates/example_app_tests/test/integration/example_app_validation_hints_test.rb
|
|
106
120
|
- lib/installer_templates/example_app_tests/test/models/example_app_apartment_name_validation_test.rb
|
|
107
121
|
- lib/installer_templates/example_app_tests/test/models/example_app_apartment_photo_test.rb
|