inline_forms 8.0.4 → 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: 83fc3ebab88e414806da9e2c7f9a47088eb1d69579f607af783e647f374ada2b
4
- data.tar.gz: 76348168e6d35fb88a7142e66e92938ae6938b83cd606a894f1985c37c7cd219
3
+ metadata.gz: b3b934b94b7f4b5298dea2e2a51b8c97c37c06969a3484d528e336edf17ddfde
4
+ data.tar.gz: fedfafb7fba445712299b4c240fc905418c665d8f29e139c17cb9abc4138fe36
5
5
  SHA512:
6
- metadata.gz: d3d2b9b5a2cc0239857123cf16cd6605f58d27e324accddb8232015445767e3b06214c4c869d6089218b42aa72f737a11611d83abc3b53e731573ea628987944
7
- data.tar.gz: 29d10dd86bb1c3bff98a5653b513de213e08250ab43b3fd3969e1aeaa71cd690f73f9034f51c64e775debeff69abd6180b5895867e7c4ca0a9aa10bda879fe07
6
+ metadata.gz: b9d1abfecdf6fa9c75c4d79967e8604e0bb408a40a08475fb7106e12b0b301a437e8192f836a39b9b5c75261ac97d5dfade6f2ba5ecea9005015814bd257028f
7
+ data.tar.gz: 5f62f997de89dd93334a6324d2d02fe3bd283036d3f68ee39947cd400ab57f6d3a2d959745498d2b7f5e5c567889cd02e258ed7c15fef3cf3f051f3a21ee3d60
data/CHANGELOG.md CHANGED
@@ -4,6 +4,38 @@ 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
+
7
39
  ## [8.0.4] - 2026-05-24
8
40
 
9
41
  ### Added
@@ -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?
@@ -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
@@ -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.4", "< 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.4"
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
 
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
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.4
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.4
30
+ version: 8.1.0
31
31
  - - "<"
32
32
  - !ruby/object:Gem::Version
33
33
  version: '9.0'
@@ -492,18 +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/rails-8-release.md
501
- - docs/turbo-stream-audit.md
502
- - docs/ujs-to-turbo.md
503
- - docs/zeitwerk-and-load-paths.md
504
495
  - inline_forms.gemspec
505
496
  - lib/generators/USAGE
506
- - lib/generators/assets/stylesheets/inline_forms.scss
507
497
  - lib/generators/assets/stylesheets/inline_forms_devise.css
508
498
  - lib/generators/inline_forms_generator.rb
509
499
  - lib/generators/templates/_inline_forms_tabs.html.erb
@@ -1,93 +0,0 @@
1
- # Git-sourced installer dependencies — assessment
2
-
3
- **Date:** 2026-05-19
4
- **Context:** Generated apps from `inline_forms create` used three non-RubyGems pins: `tabs_on_rails` (git + branch), `i18n-active_record` (git), and a commented `will_paginate` fork. This doc records why they existed and what replaced them.
5
-
6
- **Related (risk callouts only):** `validation_hints/stuff/rails-7.2-zeitwerk-plan.md` (Track A3), `validation_hints/stuff/rails-8-checklist.md` (Phase 3).
7
-
8
- ---
9
-
10
- ## Summary (7.13.5+)
11
-
12
- | Gem | Was | Now | Reason |
13
- |-----|-----|-----|--------|
14
- | `will_paginate` | RubyGems + commented acesuares fork | RubyGems only | Fork never active; comment removed |
15
- | `tabs_on_rails` | `acesuares/tabs_on_rails` branch `update_remote_before_action` | `~> 3.0` (weppos, RubyGems) | Fork only added unused `:remote` on tab links; upstream 3.0.0 already uses `before_action` |
16
- | `i18n-active_record` | `acesuares/i18n-active_record` (2012) | **Removed** (7.13.18: no DB translation tables) | Never configured `I18n.backend`; DB tables/view and `extract_translations` removed |
17
-
18
- **No generated-app Gemfile should use `:git` for these anymore.**
19
-
20
- ---
21
-
22
- ## `will_paginate`
23
-
24
- - **Fork:** `https://github.com/acesuares/will_paginate.git` (commented out for years).
25
- - **Usage:** Nested/top-level list pagination in `_list.html.erb`; Turbo Frame nav since 7.5.x (no `:remote => true`).
26
- - **Verdict:** RubyGems `will_paginate` is sufficient. No acesuares-specific behavior required.
27
-
28
- ---
29
-
30
- ## `tabs_on_rails`
31
-
32
- ### Why the fork existed
33
-
34
- Branch `update_remote_before_action` on `acesuares/tabs_on_rails` (2019) did two things:
35
-
36
- 1. `before_filter` → `before_action` (Rails 4+ rename).
37
- 2. Optional `:remote => true` on `tab_for` links (`tabs_builder.rb`).
38
-
39
- ### What inline_forms actually uses
40
-
41
- - Controllers call **`set_tab :resource`** only (generator template + installer).
42
- - Top bar is **not** built with `tabs_tag` / `tab_for`; `_inline_forms_tabs.html.erb` is a Foundation top-bar with search + new button.
43
- - Turbo migration removed **`data-remote`** from inline UI; no code passes `:remote` into tabs.
44
-
45
- ### Upstream today
46
-
47
- - **RubyGems:** `tabs_on_rails` **3.0.0** (weppos, 2017).
48
- - **`set_tab`** already uses **`before_action`** in `lib/tabs_on_rails/action_controller.rb`.
49
- - **Missing vs fork:** `:remote` on tab links only — **unused by inline_forms**.
50
-
51
- ### Verdict
52
-
53
- **Use `gem 'tabs_on_rails', '~> 3.0'`.** No runtime dependency on the fork. Re-test on Rails 7.2+ / 8.0 when upgrading (gem is unmaintained but tiny).
54
-
55
- ---
56
-
57
- ## `i18n-active_record`
58
-
59
- ### History
60
-
61
- - Fork existed because MySQL reserves `KEY`; acesuares fork used **`thekey`**.
62
- - **7.13.5–7.13.17:** gem removed from Gemfile; installer still generated locale/key/translation tables + `translations` view; **`InlineForms::TranslationRecord`** read the view; **`extract_translations`** exported YAML (never routed by default).
63
- - **`I18n.backend` was never** `I18n::Backend::ActiveRecord` in generated apps.
64
-
65
- ### Verdict (7.13.18+)
66
-
67
- **No DB translation layer.** Generated apps use Rails I18n YAML only (`config/locales/inline_forms_local.en.yml`, etc.). If **I18n::Backend::ActiveRecord** is needed later, add **`i18n-active_record ~> 1.4`** and migrations separately—not via inline_forms installer.
68
-
69
- ---
70
-
71
- ## Previously removed forks (reference)
72
-
73
- | Gem | Removed in | Replacement |
74
- |-----|------------|-------------|
75
- | `devise-i18n` | 7.3.4 / 7.10+ | RubyGems `~> 1.16` |
76
- | `paper_trail` | 7.0.x | RubyGems `~> 16.0` |
77
- | `switch_user` | 7.2.x | Removed from installer |
78
-
79
- ---
80
-
81
- ## Verification
82
-
83
- After changing `installer_core.rb`:
84
-
85
- ```bash
86
- cd /home/code/inline_forms && rvm use . && gem build inline_forms.gemspec && gem build inline_forms_installer.gemspec
87
- cd /home/code/testInline && rvm use .
88
- gem install /home/code/inline_forms/inline_forms-*.gem /home/code/inline_forms/inline_forms_installer-*.gem
89
- rm -rf MyApp && inline_forms create MyApp -d sqlite --example
90
- cd MyApp && rvm use . && bundle exec rails test
91
- ```
92
-
93
- Expect **0 failures**; `Gemfile.lock` should list `tabs_on_rails (3.0.0)` and **no** `i18n-active_record` git source.
@@ -1,25 +0,0 @@
1
- # jQuery widget migration (Phase 4)
2
-
3
- UJS removal (7.8.0) is complete. These jQuery-based widgets remain in the Sprockets bundle:
4
-
5
- | Widget | Asset | Status |
6
- |--------|-------|--------|
7
- | Datepicker | `jquery.ui.all` | **Centralized** — `initInlineFormsWidgets` in `inline_forms.js`; helpers use `class="datepicker"` only |
8
- | Month/year picker | jQuery UI datepicker | **Centralized** — `class="datepicker datepicker-month-year"` |
9
- | Timepicker | `jquery.timepicker.js` | **Centralized** — `class="timepicker"` |
10
- | Autocomplete | `autocomplete-rails` + inline scripts in `dropdown_with_other` | Still inline / jQuery UI |
11
- | Foundation | `foundation` jQuery plugin | Required for layout chrome |
12
-
13
- ## Central init (7.9.8+)
14
-
15
- `initInlineFormsWidgets(root)` runs on:
16
-
17
- - DOM ready (after `$.datepicker.setDefaults`)
18
- - `turbo:load`
19
- - `turbo:frame-load`
20
-
21
- Form element `_edit` helpers must **not** emit per-field `<script>` tags; class hooks only.
22
-
23
- ## Future removal (not started)
24
-
25
- Replace with Stimulus or vanilla + `turbo:frame-load` one widget at a time; keep example-app tests green after each.
@@ -1,5 +0,0 @@
1
- # Keep this directory for checked-in prompt docs.
2
- # Ignore local scratch files by default.
3
- *
4
- !.gitignore
5
- !*.md
@@ -1,32 +0,0 @@
1
- You are in the inline_forms gem repo at /home/code/inline_forms.
2
-
3
- Goal:
4
- Build the current gem, install it into /home/code/testInline, recreate MyApp from scratch using --example, and verify it with Rails tests.
5
-
6
- Do all steps end-to-end without asking for confirmation unless a command fails.
7
-
8
- Steps:
9
- 1) In /home/code/inline_forms:
10
- - Ensure latest code is used.
11
- - Run: rvm use .
12
- - Build gems: gem build inline_forms.gemspec && gem build inline_forms_installer.gemspec
13
- - Confirm the built file names/versions (inline_forms-<version>.gem, inline_forms_installer-<version>.gem).
14
-
15
- 2) In /home/code/testInline:
16
- - Remove old app if present: /home/code/testInline/MyApp
17
- - Run: rvm use .
18
- - Install the freshly built gems from /home/code/inline_forms/inline_forms-<version>.gem and inline_forms_installer-<version>.gem
19
-
20
- 3) Still in /home/code/testInline:
21
- - Generate fresh example app:
22
- inline_forms create MyApp -d sqlite --example
23
-
24
- 4) In /home/code/testInline/MyApp:
25
- - Run: rvm use .
26
- - Run verification: bundle exec rails test
27
-
28
- Output requirements:
29
- - Briefly report each phase result (build, install, app generation, test run).
30
- - Include exact commands run.
31
- - Include test summary (runs/assertions/failures/errors/skips).
32
- - If anything fails, stop at the failing step and include the exact error and the next corrective command you recommend.
@@ -1,39 +0,0 @@
1
- # Rails 8 — Phase 4 audit (framework defaults & cleanup)
2
-
3
- **Date:** 2026-05-22
4
- **Scope:** Post–Phase 3 (`inline_forms` 8.0.2 installer + example app gate).
5
-
6
- ## 4.1 Framework defaults (`rails new` 8.0.5)
7
-
8
- Vanilla `rails _8.0.5_ new` (importmap, `--skip-bundle`):
9
-
10
- - `config/application.rb`: `config.load_defaults 8.0`, `config.autoload_lib(ignore: %w[assets tasks])`
11
- - Generator **removes** `config/initializers/new_framework_defaults_8_0.rb` (opt-in flags live only in the railties template until uncommented)
12
- - Optional 8.0 toggles (all commented in upstream template): `active_support.to_time_preserves_timezone`, `action_dispatch.strict_freshness`, `Regexp.timeout`
13
-
14
- **Installer decision:** Match vanilla Rails 8 — rely on `load_defaults 8.0` only; do **not** ship `new_framework_defaults_8_0.rb` in generated apps. Keep a belt-and-suspenders `gsub` to `load_defaults 8.0` if an older `rails new` left another minor.
15
-
16
- ## 4.2 Rails 8 API / deprecation grep
17
-
18
- | Area | Result |
19
- |------|--------|
20
- | `form_with` / `model: nil` | No engine usage; inline fields use custom helpers / Turbo, not `form_with` |
21
- | `form_for` | Devise templates only (upstream Devise 5) |
22
- | `ActiveRecord::Base.connection` | Unicorn `before_fork` template updated → `connection_pool.disconnect!` |
23
- | `ActionController` deprecated flags | None in `lib/` |
24
- | Generator migrations | `ActiveRecord::Migration[8.0]` (tests assert) |
25
-
26
- ## 4.3 Dart Sass / SCSS
27
-
28
- Foundation + engine SCSS still emit Dart Sass 1.x deprecation warnings during `dartsass:build`. Accepted for 8.0 ship; migrate to `@use` / `color.scale` before Dart Sass 3.0 (see `stuff/pipeline.md`).
29
-
30
- ## 4.4 bundler-audit
31
-
32
- | Lockfile | Result (2026-05-22) |
33
- |----------|---------------------|
34
- | `validation_hints/Gemfile.lock` | No vulnerabilities found |
35
- | Example app `MyApp/Gemfile.lock` | No vulnerabilities found (`bundler-audit` on default gemset) |
36
-
37
- ## 4.5 Example app gate (unchanged baseline)
38
-
39
- `inline_forms create MyApp -d sqlite --example` → **88 runs, 502 assertions, 0 failures, 0 errors, 0 skips** (Phase 3, Rails 8.0.5 stack).