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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dbf71e495b41149f884052c32a5cfb1a0d01148c2cc957373e63803ac4bc7c00
4
- data.tar.gz: 108b9f3bdd412bbff043bea5808a5920172a7c33f3ff126afa209684af33fb3e
3
+ metadata.gz: b3b934b94b7f4b5298dea2e2a51b8c97c37c06969a3484d528e336edf17ddfde
4
+ data.tar.gz: fedfafb7fba445712299b4c240fc905418c665d8f29e139c17cb9abc4138fe36
5
5
  SHA512:
6
- metadata.gz: c10d87d29d229849facdb2b06e0403de7ad6cb0442eb3f10126dc723894b2c28a0cd1e0e76a3c4acad9a7e4efa3fd292bc53604293eb124cd99b0de1d76d45a8
7
- data.tar.gz: 202650e4e15e768e585110e94f8901498f6ae718f35c47c2f81c9f18e94d38350fb1a4a3fbf8cc33b2f8976d90bd9db0daca322fdb901e1873afc13b6b3ca950
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
- # if the parent_class is not nill, we are in associated list and we don't search there.
40
- # also, make sure the Model that you want to do a search on has a :name attribute. TODO
41
- conditions = nil
42
- if @parent_class.nil? || @Klass.reflect_on_association(@parent_class.underscore.to_sym).nil?
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
- conditions = [ "#{foreign_key} = ?", @parent_id ]
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.order(@Klass.table_name + "." + @Klass.order_by_clause) if @Klass.respond_to?(:order_by_clause) && ! @Klass.order_by_clause.nil?
57
- @objects = @objects.where(conditions).paginate(:page => params[:page])
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
- # for the logic behind the :conditions see the #index method.
143
- conditions = nil
144
- if @parent_class.nil? || @Klass.reflect_on_association(@parent_class.underscore.to_sym).nil?
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
- conditions = [ "#{foreign_key} = ?", @parent_id ]
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.accessible_by(current_ability) if cancan_enabled?
156
- @objects = @objects.order(@Klass.table_name + "." + @Klass.order_by_clause) if @Klass.respond_to?(:order_by_clause) && ! @Klass.order_by_clause.nil?
157
- @objects = @objects.where(conditions).paginate(:page => params[:page])
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 id
351
- user = User.find_by_id id
352
- user.nil? ? 'Unknown' : user.name
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.order(attribute.to_s.singularize.camelcase.constantize.order_by_clause) if attribute.to_s.singularize.camelcase.constantize.respond_to?(:order_by_clause) && ! attribute.to_s.singularize.camelcase.constantize.order_by_clause.nil? %>
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. Versions after 6.2.14 are currently broken.}
14
- s.description = %q{Inline Forms aims to ease the setup of forms that provide inline editing. The field list can be specified in the model. Versions after 6.2.14 are currently broken, and we will post a notice when the gem is good again.}
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.2", "< 9.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 == "_presentation" ||
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 == '_presentation' ||
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
- @order_by_clause = " def self.order_by_clause\n" +
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
- if attribute.name == '_order'
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
- @order_by_clause = " def self.order_by_clause\n" +
174
- " \"#{attribute.type}\"\n" +
175
- " end\n" +
176
- "\n"
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
- <%= @flag_not_accessible_through_html %>
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
- if cancan_enabled?
23
- values = object.send(attribute).first.class.name.constantize.accessible_by(current_ability).order(attribute.to_s.singularize.camelcase.constantize.order_by_clause)
24
- else
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')}'>"
@@ -1,4 +1,4 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
  module InlineForms
3
- VERSION = "8.0.3"
3
+ VERSION = "8.1.0"
4
4
  end
@@ -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.3
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.2
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.2
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 aims to ease the setup of forms that provide inline editing.
89
- The field list can be specified in the model. Versions after 6.2.14 are currently
90
- broken, and we will post a notice when the gem is good again.
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. Versions after 6.2.14 are currently broken.
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