inline_forms 8.0.3 → 8.1.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +48 -0
- data/README.rdoc +6 -0
- data/app/controllers/inline_forms_controller.rb +23 -20
- data/app/helpers/inline_forms_helper.rb +8 -3
- data/app/views/inline_forms/_list.html.erb +2 -1
- data/inline_forms.gemspec +3 -3
- data/lib/generators/inline_forms_generator.rb +29 -17
- data/lib/generators/templates/application_record.rb +32 -0
- data/lib/generators/templates/model.erb +7 -14
- data/lib/inline_forms/form_elements/check_list_helper.rb +3 -5
- data/lib/inline_forms/version.rb +1 -1
- data/test/inline_forms_generator_test.rb +61 -0
- data/test/user_model_config_test.rb +43 -0
- metadata +9 -15
- data/docs/git-deps-assessment.md +0 -93
- data/docs/jquery-widgets.md +0 -25
- data/docs/prompt/.gitignore +0 -5
- data/docs/prompt/test-the-example-app.md +0 -32
- data/docs/rails-8-phase4-audit.md +0 -39
- data/docs/turbo-stream-audit.md +0 -16
- data/docs/ujs-to-turbo.md +0 -207
- data/lib/generators/assets/stylesheets/inline_forms.scss +0 -491
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b3b934b94b7f4b5298dea2e2a51b8c97c37c06969a3484d528e336edf17ddfde
|
|
4
|
+
data.tar.gz: fedfafb7fba445712299b4c240fc905418c665d8f29e139c17cb9abc4138fe36
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b9d1abfecdf6fa9c75c4d79967e8604e0bb408a40a08475fb7106e12b0b301a437e8192f836a39b9b5c75261ac97d5dfade6f2ba5ecea9005015814bd257028f
|
|
7
|
+
data.tar.gz: 5f62f997de89dd93334a6324d2d02fe3bd283036d3f68ee39947cd400ab57f6d3a2d959745498d2b7f5e5c567889cd02e258ed7c15fef3cf3f051f3a21ee3d60
|
data/CHANGELOG.md
CHANGED
|
@@ -4,13 +4,61 @@ All notable changes to this project are documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [8.1.0] - 2026-05-25
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **`ApplicationRecord` template carries shared model behavior:** `has_paper_trail on: [:create, :update, :destroy]`, `attr_writer :inline_forms_attribute_list`, `self.per_page = 7`, `human_attribute_name` instance wrapper, and `def self.not_accessible_through_html?` default. Generated models inherit all of this; `lib/generators/templates/model.erb` only emits per-model differences (associations, `inline_forms_attribute_list`, `_presentation`, `<=>`, `not_accessible_through_html?` override when `_enabled` is absent).
|
|
12
|
+
- **`scope :inline_forms_list, -> { all }` and `scope :inline_forms_search, ->(_q) { all }`** on `ApplicationRecord`. Both are no-ops by default; subclasses opt in. Replaces the old `def self.order_by_clause` contract with composable Active Record relations.
|
|
13
|
+
- **Generator flags `_list_order:col` and `_list_search:col`:** emit `scope :inline_forms_list, -> { order(:col, :id) }` and `scope :inline_forms_search, ->(q) { where("col LIKE ?", "%#{q}%") }` respectively. `_list_order` also still emits `def <=>(other)` for in-memory sort on associations (used by `check_list` show). Tie-break on `:id` is added automatically so will_paginate pages stay stable.
|
|
14
|
+
- **`SPECIAL_GENERATOR_NAMES` constant** in `InlineForms::InlineFormsGenerator` consolidates `_presentation`, `_order`, `_list_order`, `_list_search`, `_enabled`, `_id`, `_no_migration`, `_no_model` so `migration?` / `attribute?` no longer drift.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- **`order_by_clause` is gone from the gem.** `InlineFormsController#index` / `#create`, `check_list_helper.rb`, and `app/views/inline_forms/_list.html.erb` now compose `merge(@Klass.inline_forms_list)` and `merge(@Klass.inline_forms_search(params[:search]))` instead of building raw `"table.col"` strings. Search and order are now independent (one column had to do both jobs before); models can declare each separately or neither.
|
|
19
|
+
- **Per-page pagination fix in `ApplicationRecord`:** `self.per_page = 7` (class-level, what will_paginate's `class_attribute :per_page` reads). The old `attr_reader :per_page` + `@per_page = 7` instance pair on every generated model was a no-op (described at length in 8.0.x notes); models that need a different value override with `self.per_page = N` (Photo uses `5`). The installer’s Photo override now uses `inject_into_class "app/models/photo.rb", "Photo", " self.per_page = 5\n"` instead of patching the legacy `attr_reader` block out.
|
|
20
|
+
- **`_order:col` is deprecated as an alias for `_list_order:col`.** Still works (emits the same scope plus `<=>`); the generator prints `say_status :deprecated, "_order:col is deprecated; use _list_order:col"` so existing scripts keep generating but the migration path is visible.
|
|
21
|
+
- **Installer User model:** dropped `default_scope { order :name }` and the `def self.order_by_clause; nil; end` stub. Added `scope :inline_forms_list, -> { order(:name, :id) }` with a comment about why named scopes beat `default_scope` (no `unscope`/`reorder` foot-guns; cleaner `update_all` / association inheritance).
|
|
22
|
+
- **Installer example-app generator calls** for `Locale`, `Role`, `Photo`, `Apartment`, `Owner` now pass `_list_order:name` (and `_list_search:name` for the searchable top-level models `Apartment` and `Owner`) to declare ordering and search explicitly. Replaces the implicit `order_by_clause = "name"` default that the old generator produced.
|
|
23
|
+
|
|
24
|
+
### Removed
|
|
25
|
+
|
|
26
|
+
- **`lib/generators/assets/javascripts/`** (including the empty `ckeditor/` subfolder). Leftover from the CKEditor era; nothing in the gem or installer copied from it. The engine still ships JS under `app/assets/javascripts/` and `vendor/assets/javascripts/`.
|
|
27
|
+
- **`lib/generators/assets/stylesheets/inline_forms.scss`** mirror. The installer doesn't copy it; the gem stylesheet at `app/assets/stylesheets/inline_forms/inline_forms.scss` is the single source consumed via `@use "inline_forms/inline_forms"`. `inline_forms_devise.css` stays — it is the host-app override hook copied by the installer and linked from `layouts/devise.html.erb`.
|
|
28
|
+
|
|
29
|
+
### Migration notes
|
|
30
|
+
|
|
31
|
+
Existing apps upgrading from 8.0.x:
|
|
32
|
+
|
|
33
|
+
1. Replace your `app/models/application_record.rb` with the contents of `lib/generators/templates/application_record.rb` (or just merge in the new `has_paper_trail`, `self.per_page`, `inline_forms_list` / `inline_forms_search` scopes, and `not_accessible_through_html?` default).
|
|
34
|
+
2. For each model that had `def self.order_by_clause; "col"; end`, replace with `scope :inline_forms_list, -> { order(:col, :id) }`. Drop the old method; the gem no longer reads it.
|
|
35
|
+
3. If you used `default_scope { order(:name) }` for inline_forms list ordering, swap to the named scope above.
|
|
36
|
+
4. Per-model `attr_reader :per_page` / `@per_page = N` pairs are no-ops; replace with `self.per_page = N` (or remove and inherit `7` from `ApplicationRecord`).
|
|
37
|
+
5. `_order:col` in your generator scripts still works but emits a deprecation notice; switch to `_list_order:col` and add `_list_search:col` if you want the search box to filter on that column.
|
|
38
|
+
|
|
39
|
+
## [8.0.4] - 2026-05-24
|
|
40
|
+
|
|
41
|
+
### Added
|
|
42
|
+
|
|
43
|
+
- **`inline_forms create --user-model` (`-U`):** choose the Devise-backed model class (e.g. `Member`). Default remains `User`. Non-default models use `devise_for :users, class_name: "…", path: "…"` so Warden scope stays `:user` (`current_user`, `destroy_user_session_path`, etc.). Installer generates matching table, routes, controller, Locale/Role associations, HABTM join table (`members_roles`, etc.), seeds, Ability guest user, and `--example` test substitutions.
|
|
44
|
+
|
|
45
|
+
### Changed
|
|
46
|
+
|
|
47
|
+
- **`version_modified_by`:** resolves the auth model via `Devise.mappings[:users]` instead of hard-coding `User` (PaperTrail “modified by” on custom auth models).
|
|
48
|
+
|
|
7
49
|
## [8.0.3] - 2026-05-22
|
|
8
50
|
|
|
51
|
+
### Added
|
|
52
|
+
|
|
53
|
+
- **Rails 8 release docs (Phase 5):** [`docs/rails-8-release.md`](docs/rails-8-release.md) (gate results, stack table); [`docs/zeitwerk-and-load-paths.md`](docs/zeitwerk-and-load-paths.md) (intentional ignores vs autoloaded paths).
|
|
54
|
+
- **Example app gate (recorded):** `inline_forms create MyApp -d sqlite --example` → **88 runs, 502 assertions, 0 failures**; `rails zeitwerk:check` → clean.
|
|
55
|
+
|
|
9
56
|
### Changed
|
|
10
57
|
|
|
11
58
|
- **Rails 8 (Phase 4):** framework-defaults audit documented in [`docs/rails-8-phase4-audit.md`](docs/rails-8-phase4-audit.md); generated apps stay on `load_defaults 8.0` only (no `new_framework_defaults_8_0.rb`, matching `rails new` 8.0.5).
|
|
12
59
|
- **Unicorn template:** `before_fork` uses `ActiveRecord::Base.connection_pool.disconnect!` (Rails 8–compatible).
|
|
13
60
|
- **README.rdoc:** requirements table for Ruby 4 / Rails 8 / validation_hints 8; remove stale “broken after 6.2.14” notice; `rails-i18n ~> 8.0` narrative.
|
|
61
|
+
- **`inline_forms.gemspec`:** summary/description updated for Rails 8 (remove stale “broken after 6.2.14” text).
|
|
14
62
|
|
|
15
63
|
## [8.0.2] - 2026-05-22
|
|
16
64
|
|
data/README.rdoc
CHANGED
|
@@ -30,6 +30,12 @@ If you want to install the example application:
|
|
|
30
30
|
|
|
31
31
|
inline_forms create MyApp -d sqlite --example
|
|
32
32
|
|
|
33
|
+
To use a different Devise model (e.g. +Member+ on a +members+ table) while keeping Warden scope +:user+ so +current_user+ works in inline_forms:
|
|
34
|
+
|
|
35
|
+
inline_forms create MyApp -d sqlite --example --user-model Member
|
|
36
|
+
|
|
37
|
+
The installer emits +devise_for :users, class_name: "Member", path: "members"+ (sign-in at +/auth/members/sign_in+), +resources :members+, and the usual +authenticate_user!+ / +destroy_user_session_path+ helpers.
|
|
38
|
+
|
|
33
39
|
Then point your browser to http://localhost:3000/apartments and log in with admin@example.com / admin999. The example also adds integration and model tests; run +bundle exec rails test+ in +MyApp+, then start the server with +bundle exec rails s+ when you want the UI.
|
|
34
40
|
|
|
35
41
|
The example app now ships three models:
|
|
@@ -36,14 +36,12 @@ class InlineFormsController < ApplicationController
|
|
|
36
36
|
@parent_class = params[:parent_class]
|
|
37
37
|
@parent_id = params[:parent_id]
|
|
38
38
|
@ul_needed = params[:ul_needed]
|
|
39
|
-
#
|
|
40
|
-
#
|
|
41
|
-
|
|
42
|
-
if @parent_class.
|
|
43
|
-
conditions = [ @Klass.table_name + "." + @Klass.order_by_clause + " like ?", "%#{params[:search]}%" ] if @Klass.respond_to?(:order_by_clause) && ! @Klass.order_by_clause.nil?
|
|
44
|
-
else
|
|
39
|
+
# Nested associated lists scope to the parent FK. Top-level lists may
|
|
40
|
+
# apply the model's `inline_forms_search` scope when ?search= is passed.
|
|
41
|
+
fk_conditions = nil
|
|
42
|
+
if @parent_class.present? && @Klass.reflect_on_association(@parent_class.underscore.to_sym)
|
|
45
43
|
foreign_key = @Klass.reflect_on_association(@parent_class.underscore.to_sym).options[:foreign_key] || @parent_class.foreign_key
|
|
46
|
-
|
|
44
|
+
fk_conditions = [ "#{foreign_key} = ?", @parent_id ]
|
|
47
45
|
end
|
|
48
46
|
# CanCan's load_and_authorize_resource sets @apartments (etc.); keep @objects in sync.
|
|
49
47
|
collection_ivar = :"@#{controller_name}"
|
|
@@ -52,9 +50,13 @@ class InlineFormsController < ApplicationController
|
|
|
52
50
|
@objects = loaded unless loaded.nil?
|
|
53
51
|
end
|
|
54
52
|
@objects ||= @Klass.accessible_by(current_ability) if cancan_enabled?
|
|
55
|
-
@objects ||= @Klass
|
|
56
|
-
@objects = @objects.
|
|
57
|
-
|
|
53
|
+
@objects ||= @Klass.all
|
|
54
|
+
@objects = @objects.merge(@Klass.inline_forms_list) if @Klass.respond_to?(:inline_forms_list)
|
|
55
|
+
if fk_conditions.nil? && params[:search].present? && @Klass.respond_to?(:inline_forms_search)
|
|
56
|
+
@objects = @objects.merge(@Klass.inline_forms_search(params[:search]))
|
|
57
|
+
end
|
|
58
|
+
@objects = @objects.where(fk_conditions) if fk_conditions
|
|
59
|
+
@objects = @objects.paginate(:page => params[:page])
|
|
58
60
|
respond_to do |format|
|
|
59
61
|
# `not_accessible_through_html?` is about preventing direct top-level
|
|
60
62
|
# HTML CRUD on this resource (e.g. /photos when only Apartment is the
|
|
@@ -139,22 +141,23 @@ class InlineFormsController < ApplicationController
|
|
|
139
141
|
end
|
|
140
142
|
@parent_class = params[:parent_class]
|
|
141
143
|
@parent_id = params[:parent_id]
|
|
142
|
-
#
|
|
143
|
-
|
|
144
|
-
if @parent_class.
|
|
145
|
-
conditions = [ @Klass.table_name + "." + @Klass.order_by_clause + " like ?", "%#{params[:search]}%" ] if @Klass.respond_to?(:order_by_clause) && ! @Klass.order_by_clause.nil?
|
|
146
|
-
else
|
|
144
|
+
# See #index for the order/search/parent-fk decomposition.
|
|
145
|
+
fk_conditions = nil
|
|
146
|
+
if @parent_class.present? && @Klass.reflect_on_association(@parent_class.underscore.to_sym)
|
|
147
147
|
foreign_key = @Klass.reflect_on_association(@parent_class.underscore.to_sym).options[:foreign_key] || @parent_class.foreign_key
|
|
148
|
-
|
|
148
|
+
fk_conditions = [ "#{foreign_key} = ?", @parent_id ]
|
|
149
149
|
@object[foreign_key] = @parent_id
|
|
150
150
|
end
|
|
151
151
|
|
|
152
152
|
if @object.save
|
|
153
153
|
flash.now[:success] = t('success', :message => @object.class.model_name.human)
|
|
154
|
-
@objects = @Klass
|
|
155
|
-
@objects = @Klass.
|
|
156
|
-
|
|
157
|
-
|
|
154
|
+
@objects = cancan_enabled? ? @Klass.accessible_by(current_ability) : @Klass.all
|
|
155
|
+
@objects = @objects.merge(@Klass.inline_forms_list) if @Klass.respond_to?(:inline_forms_list)
|
|
156
|
+
if fk_conditions.nil? && params[:search].present? && @Klass.respond_to?(:inline_forms_search)
|
|
157
|
+
@objects = @objects.merge(@Klass.inline_forms_search(params[:search]))
|
|
158
|
+
end
|
|
159
|
+
@objects = @objects.where(fk_conditions) if fk_conditions
|
|
160
|
+
@objects = @objects.paginate(:page => params[:page])
|
|
158
161
|
@object = nil
|
|
159
162
|
respond_to do |format|
|
|
160
163
|
format.html { render_list_frame_after_save } if html_list_flow_allowed?
|
|
@@ -347,9 +347,14 @@ module InlineFormsHelper
|
|
|
347
347
|
values
|
|
348
348
|
end
|
|
349
349
|
|
|
350
|
-
def version_modified_by
|
|
351
|
-
|
|
352
|
-
|
|
350
|
+
def version_modified_by(id)
|
|
351
|
+
return "Unknown" if id.blank?
|
|
352
|
+
|
|
353
|
+
user_class = Devise.mappings[:users]&.to
|
|
354
|
+
return "Unknown" unless user_class
|
|
355
|
+
|
|
356
|
+
user = user_class.find_by(id: id)
|
|
357
|
+
user.nil? ? "Unknown" : user.name
|
|
353
358
|
end
|
|
354
359
|
|
|
355
360
|
end
|
|
@@ -34,9 +34,10 @@
|
|
|
34
34
|
<% foreign_key = parent_class.reflect_on_association(attribute.to_sym).options[:foreign_key] || parent_class.name.foreign_key -%>
|
|
35
35
|
<% model = attribute.to_s.singularize.camelcase.constantize %>
|
|
36
36
|
<% conditions = [ "#{model.table_name}.#{foreign_key} = ?", parent_id ] %>
|
|
37
|
+
<% klass = attribute.to_s.singularize.camelcase.constantize %>
|
|
37
38
|
<% objects = parent_class.find(parent_id).send(attribute) %>
|
|
38
39
|
<% objects = parent_class.find(parent_id).send(attribute).accessible_by(current_ability) if cancan_enabled? %>
|
|
39
|
-
<% objects = objects.
|
|
40
|
+
<% objects = objects.merge(klass.inline_forms_list) if klass.respond_to?(:inline_forms_list) %>
|
|
40
41
|
<% objects = objects.where(conditions).paginate(:page => params[:page]) %>
|
|
41
42
|
<% end %>
|
|
42
43
|
<% end %>
|
data/inline_forms.gemspec
CHANGED
|
@@ -10,8 +10,8 @@ Gem::Specification.new do |s|
|
|
|
10
10
|
s.authors = ["Ace Suares", "Lemuel Boyce", "Manuel Ortega"]
|
|
11
11
|
s.email = ["ace@suares.com"]
|
|
12
12
|
s.homepage = %q{http://github.com/acesuares/inline_forms}
|
|
13
|
-
s.summary = %q{Inline editing of forms
|
|
14
|
-
s.description = %q{Inline Forms
|
|
13
|
+
s.summary = %q{Inline editing of forms for Rails 8.}
|
|
14
|
+
s.description = %q{Inline Forms eases setup of admin-style forms with inline editing. Field lists are declared on the model. Requires Rails 8.0.x, Ruby >= 4.0, and validation_hints ~> 8.}
|
|
15
15
|
s.licenses = ["MIT"]
|
|
16
16
|
s.required_ruby_version = ">= 4.0.0"
|
|
17
17
|
|
|
@@ -19,7 +19,7 @@ Gem::Specification.new do |s|
|
|
|
19
19
|
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
|
20
20
|
s.require_paths = ["lib"]
|
|
21
21
|
|
|
22
|
-
s.add_dependency("validation_hints", ">= 8.0
|
|
22
|
+
s.add_dependency("validation_hints", ">= 8.1.0", "< 9.0")
|
|
23
23
|
s.add_dependency("rails", ">= 8.0", "< 8.1")
|
|
24
24
|
s.add_dependency("rails-i18n", ">= 8.0", "< 9.0")
|
|
25
25
|
|
|
@@ -64,19 +64,26 @@ module InlineForms
|
|
|
64
64
|
RELATIONS.has_key?(type) || special_relation?
|
|
65
65
|
end
|
|
66
66
|
|
|
67
|
+
# Special "attribute" names that drive the generator (presentation,
|
|
68
|
+
# ordering, search, etc.) but never become real columns or fields.
|
|
69
|
+
SPECIAL_GENERATOR_NAMES = %w[
|
|
70
|
+
_presentation
|
|
71
|
+
_order
|
|
72
|
+
_list_order
|
|
73
|
+
_list_search
|
|
74
|
+
_enabled
|
|
75
|
+
_id
|
|
76
|
+
_no_migration
|
|
77
|
+
_no_model
|
|
78
|
+
].freeze
|
|
79
|
+
|
|
67
80
|
def migration?
|
|
68
81
|
not ( column_type == :no_migration ||
|
|
69
|
-
name
|
|
70
|
-
name == "_order" ||
|
|
71
|
-
name == "_enabled" ||
|
|
72
|
-
name == "_id" )
|
|
82
|
+
SPECIAL_GENERATOR_NAMES.include?(name) )
|
|
73
83
|
end
|
|
74
84
|
|
|
75
85
|
def attribute?
|
|
76
|
-
not ( name
|
|
77
|
-
name == '_order' ||
|
|
78
|
-
name == '_enabled' ||
|
|
79
|
-
name == "_id" ||
|
|
86
|
+
not ( SPECIAL_GENERATOR_NAMES.include?(name) ||
|
|
80
87
|
relation? )
|
|
81
88
|
end
|
|
82
89
|
|
|
@@ -126,10 +133,7 @@ module InlineForms
|
|
|
126
133
|
@has_attached_files = "\n"
|
|
127
134
|
@presentation = "\n"
|
|
128
135
|
@order = "\n"
|
|
129
|
-
@
|
|
130
|
-
" \"name\"\n" +
|
|
131
|
-
" end\n" +
|
|
132
|
-
"\n"
|
|
136
|
+
@list_scopes = ""
|
|
133
137
|
@carrierwave_mounters = "\n"
|
|
134
138
|
@inline_forms_attribute_list = String.new
|
|
135
139
|
|
|
@@ -165,15 +169,23 @@ module InlineForms
|
|
|
165
169
|
" end\n" +
|
|
166
170
|
"\n"
|
|
167
171
|
end
|
|
168
|
-
|
|
172
|
+
# `_list_order:col` (preferred) and the legacy alias `_order:col`
|
|
173
|
+
# both emit the inline_forms_list scope plus a Ruby `<=>` for
|
|
174
|
+
# in-memory sort (used by check_list show on the association).
|
|
175
|
+
if attribute.name == '_order' || attribute.name == '_list_order'
|
|
176
|
+
if attribute.name == '_order'
|
|
177
|
+
say_status :deprecated,
|
|
178
|
+
"_order:#{attribute.type} is deprecated; use _list_order:#{attribute.type}",
|
|
179
|
+
:yellow
|
|
180
|
+
end
|
|
169
181
|
@order << " def <=>(other)\n" +
|
|
170
182
|
" self.#{attribute.type} <=> other.#{attribute.type}\n" +
|
|
171
183
|
" end\n" +
|
|
172
184
|
"\n"
|
|
173
|
-
@
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
185
|
+
@list_scopes << " scope :inline_forms_list, -> { order(:#{attribute.type}, :id) }\n"
|
|
186
|
+
end
|
|
187
|
+
if attribute.name == '_list_search'
|
|
188
|
+
@list_scopes << " scope :inline_forms_search, ->(q) { where(\"#{attribute.type} LIKE ?\", \"%\#{q}%\") }\n"
|
|
177
189
|
end
|
|
178
190
|
if attribute.attribute?
|
|
179
191
|
attribute.attribute_type == :unknown ? commenter = '#' : commenter = ' '
|
|
@@ -1,9 +1,41 @@
|
|
|
1
1
|
class ApplicationRecord < ActiveRecord::Base
|
|
2
2
|
self.abstract_class = true
|
|
3
3
|
|
|
4
|
+
# PaperTrail 16 defaults to `on: [:create, :update, :destroy, :touch]`.
|
|
5
|
+
# ActionText's `belongs_to :record, polymorphic: true, touch: true` (set by
|
|
6
|
+
# `has_rich_text`) calls `parent.touch` on every rich-text save, which
|
|
7
|
+
# produced a parent-side `update` version with an empty changeset — visible
|
|
8
|
+
# in the inline_forms versions panel as a meaningless "empty" row whose
|
|
9
|
+
# Restore link reifies the same state (no-op). Excluding `:touch` here
|
|
10
|
+
# suppresses that noise without affecting real attribute updates.
|
|
11
|
+
has_paper_trail on: [:create, :update, :destroy]
|
|
12
|
+
|
|
13
|
+
attr_writer :inline_forms_attribute_list
|
|
14
|
+
|
|
15
|
+
# will_paginate reads Klass.per_page (class level), not instance ivars.
|
|
16
|
+
self.per_page = 7
|
|
17
|
+
|
|
18
|
+
# Default list ordering for inline_forms #index/#create and nested
|
|
19
|
+
# associated lists. Subclasses override via
|
|
20
|
+
# scope :inline_forms_list, -> { order(:col, :id) }
|
|
21
|
+
# (typically emitted by `rails g inline_forms <Model> _list_order:<col>`).
|
|
22
|
+
# `all` is a no-op so the gem adds no ORDER BY by default; pair with
|
|
23
|
+
# explicit named scopes rather than `default_scope` so callers can
|
|
24
|
+
# `unscope`/`reorder` cleanly.
|
|
25
|
+
scope :inline_forms_list, -> { all }
|
|
26
|
+
|
|
27
|
+
# Default list search; no-op so the gem's search box is inert until a model
|
|
28
|
+
# opts in via
|
|
29
|
+
# scope :inline_forms_search, ->(q) { where("col LIKE ?", "%#{q}%") }
|
|
30
|
+
# (emitted by `_list_search:<col>` in the generator).
|
|
31
|
+
scope :inline_forms_search, ->(_q) { all }
|
|
32
|
+
|
|
4
33
|
# Wrapper for @model.human_attribute_name -> Model.human_attribute_name
|
|
5
34
|
def human_attribute_name(*args)
|
|
6
35
|
self.class.human_attribute_name(*args)
|
|
7
36
|
end
|
|
8
37
|
|
|
38
|
+
def self.not_accessible_through_html?
|
|
39
|
+
false
|
|
40
|
+
end
|
|
9
41
|
end
|
|
@@ -1,15 +1,4 @@
|
|
|
1
1
|
class <%= name %> < ApplicationRecord
|
|
2
|
-
attr_reader :per_page
|
|
3
|
-
@per_page = 7
|
|
4
|
-
attr_writer :inline_forms_attribute_list
|
|
5
|
-
# PaperTrail 16 defaults to `on: [:create, :update, :destroy, :touch]`.
|
|
6
|
-
# ActionText's `belongs_to :record, polymorphic: true, touch: true` (set by
|
|
7
|
-
# `has_rich_text`) calls `parent.touch` on every rich-text save, which
|
|
8
|
-
# produced a parent-side `update` version with an empty changeset — visible
|
|
9
|
-
# in the inline_forms versions panel as a meaningless "empty" row whose
|
|
10
|
-
# Restore link reifies the same state (no-op). Excluding `:touch` here
|
|
11
|
-
# suppresses that noise without affecting real attribute updates.
|
|
12
|
-
has_paper_trail on: [:create, :update, :destroy]
|
|
13
2
|
<%= @carrierwave_mounters if @carrierwave_mounters.length > 1 -%>
|
|
14
3
|
<%= @belongs_to if @belongs_to.length > 1 -%>
|
|
15
4
|
<%= @has_many if @has_many.length > 1 -%>
|
|
@@ -17,14 +6,18 @@ class <%= name %> < ApplicationRecord
|
|
|
17
6
|
<%= @habtm if @habtm.length > 1 -%>
|
|
18
7
|
<%= @has_rich_text if @has_rich_text.length > 1 -%>
|
|
19
8
|
<%= @has_attached_files if @has_attached_files.length > 1 -%>
|
|
9
|
+
<% if @list_scopes && !@list_scopes.empty? -%>
|
|
10
|
+
|
|
11
|
+
<%= @list_scopes -%>
|
|
12
|
+
<% end -%>
|
|
20
13
|
<%= @presentation if @presentation.length > 1 -%>
|
|
21
14
|
<%= @inline_forms_attribute_list -%>
|
|
22
15
|
<%= @order if @order.length > 1 -%>
|
|
16
|
+
<% if @flag_not_accessible_through_html -%>
|
|
23
17
|
|
|
24
18
|
def self.not_accessible_through_html?
|
|
25
|
-
|
|
19
|
+
true
|
|
26
20
|
end
|
|
27
|
-
|
|
28
|
-
<%= @order_by_clause -%>
|
|
21
|
+
<% end -%>
|
|
29
22
|
|
|
30
23
|
end
|
|
@@ -19,11 +19,9 @@ module InlineForms
|
|
|
19
19
|
|
|
20
20
|
def check_list_edit(object, attribute)
|
|
21
21
|
object.send(attribute).build if object.send(attribute).empty?
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
values = object.send(attribute).first.class.name.constantize.order(attribute.to_s.singularize.camelcase.constantize.order_by_clause)
|
|
26
|
-
end
|
|
22
|
+
klass = object.send(attribute).first.class
|
|
23
|
+
values = cancan_enabled? ? klass.accessible_by(current_ability) : klass.all
|
|
24
|
+
values = values.merge(klass.inline_forms_list) if klass.respond_to?(:inline_forms_list)
|
|
27
25
|
out = ''
|
|
28
26
|
values.each do | item |
|
|
29
27
|
out << "<div class='row #{cycle('odd', 'even')}'>"
|
data/lib/inline_forms/version.rb
CHANGED
|
@@ -36,6 +36,12 @@ class InlineFormsGeneratorTest < Minitest::Test
|
|
|
36
36
|
migration = read_single_migration_for("things")
|
|
37
37
|
|
|
38
38
|
assert_includes(model, "class Thing < ApplicationRecord")
|
|
39
|
+
refute_includes(model, "has_paper_trail")
|
|
40
|
+
refute_includes(model, "attr_reader :per_page")
|
|
41
|
+
refute_includes(model, 'def self.not_accessible_through_html?')
|
|
42
|
+
refute_includes(model, "def self.order_by_clause")
|
|
43
|
+
refute_includes(model, "scope :inline_forms_list")
|
|
44
|
+
refute_includes(model, "scope :inline_forms_search")
|
|
39
45
|
assert_includes(model, "belongs_to :category")
|
|
40
46
|
assert_includes(model, "has_many :photos")
|
|
41
47
|
assert_includes(model, "[ :name , \"name\", :text_field ]")
|
|
@@ -100,6 +106,61 @@ class InlineFormsGeneratorTest < Minitest::Test
|
|
|
100
106
|
refute_includes(migration, "t.text :content")
|
|
101
107
|
end
|
|
102
108
|
|
|
109
|
+
def test_not_accessible_and_list_order_emits_scope_and_spaceship
|
|
110
|
+
run_generator(
|
|
111
|
+
"Photo",
|
|
112
|
+
"name:string",
|
|
113
|
+
"album:belongs_to",
|
|
114
|
+
"_presentation:\\#{name}",
|
|
115
|
+
"_list_order:caption"
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
model = read("app/models/photo.rb")
|
|
119
|
+
assert_includes(model, "def self.not_accessible_through_html?\n true")
|
|
120
|
+
assert_includes(model, "scope :inline_forms_list, -> { order(:caption, :id) }")
|
|
121
|
+
assert_includes(model, "def <=>(other)")
|
|
122
|
+
assert_includes(model, "self.caption <=> other.caption")
|
|
123
|
+
refute_includes(model, "def self.order_by_clause")
|
|
124
|
+
refute_includes(model, "has_paper_trail")
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def test_legacy_order_alias_still_emits_scope_with_deprecation_warning
|
|
128
|
+
stdout, stderr = capture_io do
|
|
129
|
+
run_generator(
|
|
130
|
+
"Photo",
|
|
131
|
+
"name:string",
|
|
132
|
+
"_presentation:\\#{name}",
|
|
133
|
+
"_order:caption"
|
|
134
|
+
)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
output = "#{stdout}#{stderr}"
|
|
138
|
+
assert_includes(output, "_order:caption is deprecated")
|
|
139
|
+
assert_includes(output, "_list_order:caption")
|
|
140
|
+
|
|
141
|
+
model = read("app/models/photo.rb")
|
|
142
|
+
assert_includes(model, "scope :inline_forms_list, -> { order(:caption, :id) }")
|
|
143
|
+
refute_includes(model, "def self.order_by_clause")
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def test_list_search_emits_search_scope
|
|
147
|
+
run_generator(
|
|
148
|
+
"Apartment",
|
|
149
|
+
"name:string",
|
|
150
|
+
"_enabled:yes",
|
|
151
|
+
"_list_order:name",
|
|
152
|
+
"_list_search:name",
|
|
153
|
+
"_presentation:\\#{name}"
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
model = read("app/models/apartment.rb")
|
|
157
|
+
assert_includes(model, "scope :inline_forms_list, -> { order(:name, :id) }")
|
|
158
|
+
assert_includes(model, "scope :inline_forms_search, ->(q) { where(\"name LIKE ?\", \"%\#{q}%\") }")
|
|
159
|
+
migration = read_single_migration_for("apartments")
|
|
160
|
+
refute_includes(migration, "_list_order")
|
|
161
|
+
refute_includes(migration, "_list_search")
|
|
162
|
+
end
|
|
163
|
+
|
|
103
164
|
def test_plain_text_generates_text_column_and_plain_text_form_element
|
|
104
165
|
run_generator("Note", "title:string", "description:plain_text")
|
|
105
166
|
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
|
2
|
+
require "minitest/autorun"
|
|
3
|
+
require "inline_forms_installer/user_model_config"
|
|
4
|
+
|
|
5
|
+
class UserModelConfigTest < Minitest::Test
|
|
6
|
+
def test_default_user
|
|
7
|
+
cfg = InlineFormsInstaller::UserModelConfig.from_name("User")
|
|
8
|
+
assert cfg.default?
|
|
9
|
+
assert_equal "users", cfg.table_name
|
|
10
|
+
assert_equal "devise_for :users, :path_prefix => 'auth'", cfg.devise_route_line
|
|
11
|
+
assert_equal "/auth/users/sign_in", cfg.sign_in_path_fragment
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def test_member
|
|
15
|
+
cfg = InlineFormsInstaller::UserModelConfig.from_name("Member")
|
|
16
|
+
refute cfg.default?
|
|
17
|
+
assert_equal "Member", cfg.class_name
|
|
18
|
+
assert_equal "members", cfg.table_name
|
|
19
|
+
assert_equal "member_id", cfg.foreign_key
|
|
20
|
+
assert_equal "members_roles", cfg.join_table
|
|
21
|
+
assert_equal "MembersController", cfg.controller_name
|
|
22
|
+
assert_includes cfg.devise_route_line, 'class_name: "Member"'
|
|
23
|
+
assert_includes cfg.devise_route_line, 'path: "members"'
|
|
24
|
+
assert_equal "/auth/members/sign_in", cfg.sign_in_path_fragment
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def test_adapts_example_tests
|
|
28
|
+
cfg = InlineFormsInstaller::UserModelConfig.from_name("Member")
|
|
29
|
+
src = "user = User.find_or_initialize_by(email: \"admin@example.com\")\n"
|
|
30
|
+
assert_includes cfg.adapt_example_test_source(src), "Member.find_or_initialize_by"
|
|
31
|
+
guest = "assert_match %r{/auth/users/sign_in}, @response.redirect_url\n"
|
|
32
|
+
assert_includes cfg.adapt_example_test_source(guest), "/auth/members/sign_in"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def test_rejects_invalid_constant
|
|
36
|
+
assert_raises(ArgumentError) { InlineFormsInstaller::UserModelConfig.from_name("not-a-class") }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def test_from_env_defaults_to_user
|
|
40
|
+
env = { "user_model" => "" }
|
|
41
|
+
assert InlineFormsInstaller::UserModelConfig.from_env(env).default?
|
|
42
|
+
end
|
|
43
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: inline_forms
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 8.0
|
|
4
|
+
version: 8.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ace Suares
|
|
@@ -17,7 +17,7 @@ dependencies:
|
|
|
17
17
|
requirements:
|
|
18
18
|
- - ">="
|
|
19
19
|
- !ruby/object:Gem::Version
|
|
20
|
-
version: 8.0
|
|
20
|
+
version: 8.1.0
|
|
21
21
|
- - "<"
|
|
22
22
|
- !ruby/object:Gem::Version
|
|
23
23
|
version: '9.0'
|
|
@@ -27,7 +27,7 @@ dependencies:
|
|
|
27
27
|
requirements:
|
|
28
28
|
- - ">="
|
|
29
29
|
- !ruby/object:Gem::Version
|
|
30
|
-
version: 8.0
|
|
30
|
+
version: 8.1.0
|
|
31
31
|
- - "<"
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
33
|
version: '9.0'
|
|
@@ -85,9 +85,9 @@ dependencies:
|
|
|
85
85
|
- - "~>"
|
|
86
86
|
- !ruby/object:Gem::Version
|
|
87
87
|
version: '5.0'
|
|
88
|
-
description: Inline Forms
|
|
89
|
-
|
|
90
|
-
|
|
88
|
+
description: Inline Forms eases setup of admin-style forms with inline editing. Field
|
|
89
|
+
lists are declared on the model. Requires Rails 8.0.x, Ruby >= 4.0, and validation_hints
|
|
90
|
+
~> 8.
|
|
91
91
|
email:
|
|
92
92
|
- ace@suares.com
|
|
93
93
|
executables: []
|
|
@@ -492,16 +492,8 @@ files:
|
|
|
492
492
|
- archived/form_elements/tree/app/helpers/form_elements/move.rb
|
|
493
493
|
- archived/form_elements/tree/app/views/inline_forms/_show_tree.html.erb
|
|
494
494
|
- archived/form_elements/tree/app/views/inline_forms/_tree.html.erb
|
|
495
|
-
- docs/git-deps-assessment.md
|
|
496
|
-
- docs/jquery-widgets.md
|
|
497
|
-
- docs/prompt/.gitignore
|
|
498
|
-
- docs/prompt/test-the-example-app.md
|
|
499
|
-
- docs/rails-8-phase4-audit.md
|
|
500
|
-
- docs/turbo-stream-audit.md
|
|
501
|
-
- docs/ujs-to-turbo.md
|
|
502
495
|
- inline_forms.gemspec
|
|
503
496
|
- lib/generators/USAGE
|
|
504
|
-
- lib/generators/assets/stylesheets/inline_forms.scss
|
|
505
497
|
- lib/generators/assets/stylesheets/inline_forms_devise.css
|
|
506
498
|
- lib/generators/inline_forms_generator.rb
|
|
507
499
|
- lib/generators/templates/_inline_forms_tabs.html.erb
|
|
@@ -569,6 +561,7 @@ files:
|
|
|
569
561
|
- test/inline_forms_generator_test.rb
|
|
570
562
|
- test/plain_text_configuration_test.rb
|
|
571
563
|
- test/test_helper.rb
|
|
564
|
+
- test/user_model_config_test.rb
|
|
572
565
|
- vendor/assets/javascripts/popper.min.js
|
|
573
566
|
- vendor/assets/javascripts/tippy-bundle.umd.min.js
|
|
574
567
|
- vendor/assets/stylesheets/tippy.css
|
|
@@ -592,7 +585,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
592
585
|
requirements: []
|
|
593
586
|
rubygems_version: 4.0.10
|
|
594
587
|
specification_version: 4
|
|
595
|
-
summary: Inline editing of forms
|
|
588
|
+
summary: Inline editing of forms for Rails 8.
|
|
596
589
|
test_files:
|
|
597
590
|
- test/archived_form_elements_test.rb
|
|
598
591
|
- test/form_element_from_callee_test.rb
|
|
@@ -600,3 +593,4 @@ test_files:
|
|
|
600
593
|
- test/inline_forms_generator_test.rb
|
|
601
594
|
- test/plain_text_configuration_test.rb
|
|
602
595
|
- test/test_helper.rb
|
|
596
|
+
- test/user_model_config_test.rb
|