inline_forms 8.1.44 → 8.1.45

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: e8208cb1ccfd70968b65e5a5245b30586ab35034059749a20b4dc27ffde59bbe
4
- data.tar.gz: 91f66242ff63e1d1c7ff22aa744939b05140f8b95cb8ac009956a1e70a2c9ca9
3
+ metadata.gz: e972af3c243b856d22f2d070a2b2499250aee36a480f2a91747748849588c040
4
+ data.tar.gz: 154f76c200bb124fb4db651c720e23db9170ee9c0dd9c809256be33d3401526c
5
5
  SHA512:
6
- metadata.gz: 6c7060b4c597512ab705c0eb8f104dbdb754ca4aa121e9214906e15e8ba3c64a942159e1a2d6312509465c02ab72f5299973742f317746d6f5ee99399283460f
7
- data.tar.gz: 4411cae2c459f7de8b46651abe65a12360854d7434232b6a283c29a9fae4647cc5b49b548ab87fced3847f4e155975de8cf44d5b6d9ff5c610e83248b197edb8
6
+ metadata.gz: 1a0a82d1235c438a8e6bc14eab60086b86ced441f6bd8ced137cd9c5a85d76fbf6cf49b60ace8beabac928c223c4128122fbaf159bd9e3847f91d9620b0d9a1f
7
+ data.tar.gz: 0f83cf85fb2f8e25109d1e9197222bad148c85f91e074b570082c8605d8f6541d95d304ed5c72c49c9a9d053d76e4abb37e61b8bb2325019cbd1dc1cb6cfc198
data/CHANGELOG.md CHANGED
@@ -4,6 +4,24 @@ All notable changes to this project are documented in this file.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [8.1.45] - 2026-07-24
8
+
9
+ ### Added
10
+
11
+ - **Engine-owned model top bar** (`app/views/inline_forms/_model_top_bar.html.erb`): title, new-record button, and optional search region ship from the gem instead of a per-app `_inline_forms_tabs.html.erb` copy. The layout now renders `inline_forms/model_top_bar`; apps can still override via `app/views/inline_forms/_model_top_bar.html.erb`.
12
+ - **`inline_forms_search_on :col, …` macro** (`InlineForms::Searchable`): declarative opt-in for list search — defines `scope :inline_forms_search` and `inline_forms_searchable?`. Only searchable models get the engine's generic single-field search box (`_generic_search.html.erb`); apps can supply a bespoke `app/views/_<model>_search.html.erb` partial instead (bespoke wins).
13
+ - **`+ field` schema link** in the engine top bar when `inline_forms_schema_new_path` is available (replaces installer injection into the retired `_inline_forms_tabs` partial).
14
+
15
+ ### Changed
16
+
17
+ - **Retired per-app `_inline_forms_tabs.html.erb` scaffolding.** The generator no longer copies `lib/generators/templates/_inline_forms_tabs.html.erb` into new apps. Existing apps must delete their copy and move any bespoke search markup to `_<model>_search.html.erb` (see `stuff/2026-07-24-default-model-top-bar-and-search.md`).
18
+ - **`ApplicationRecord` template:** removed the no-op `scope :inline_forms_search, ->(_q) { all }`; models opt in via `inline_forms_search_on`. `_list_search:<col>` in `rails g inline_forms` / `inline_forms_addto` now emits `inline_forms_search_on :col`.
19
+ - **Installer user model** uses `inline_forms_search_on :name, :email` instead of a hand-written search scope.
20
+
21
+ ### Lockstep
22
+
23
+ - validation_hints 8.1.45, inline_forms_installer 8.1.45, inline_forms_schema_edit 8.1.45.
24
+
7
25
  ## [8.1.44] - 2026-07-24
8
26
 
9
27
  ### Fixed
@@ -0,0 +1,16 @@
1
+ <%= form_tag request.path, method: :get do %>
2
+ <div class="row collapse">
3
+ <div class="large-1 small-1 columns">
4
+ <%= link_to "<i class='fi-x'></i>".html_safe, request.path, title: "reset", class: "button expanded inline_forms_model_top_bar_buttons" %>
5
+ </div>
6
+ <div class="large-6 small-6 columns">
7
+ <%= text_field_tag :search, params[:search], placeholder: "zoek op naam...", id: "input_search" %>
8
+ </div>
9
+ <div class="large-3 small-3 columns">
10
+ <%= submit_tag "zoek", class: "button expanded inline_forms_model_top_bar_buttons" %>
11
+ </div>
12
+ <div class="large-2 small-2 columns">
13
+ &nbsp;
14
+ </div>
15
+ </div>
16
+ <% end %>
@@ -0,0 +1,37 @@
1
+ <div class="contain-to-grid fixed" id="inline_forms_model_top_bar_container">
2
+ <nav id="inline_forms_model_top_bar" class="top-bar">
3
+ <div class="top-bar-left">
4
+ <ul class="menu">
5
+ <li class="menu-text" id="inline_forms_menu">
6
+ <h1 style="margin: 0; font-size: inherit;">
7
+ <a>
8
+ <%= t(controller_name) %>
9
+ </a>
10
+ </h1>
11
+ </li>
12
+ <% if respond_to?(:inline_forms_schema_new_path) %>
13
+ <li class="menu-text">
14
+ <%= link_to "+ field", inline_forms_schema_new_path, data: { turbo: false }, title: "Add a field to a model (dev only)" %>
15
+ </li>
16
+ <% end %>
17
+ </ul>
18
+ </div>
19
+ <div class="top-bar-right">
20
+ <ul class="menu">
21
+ <% search_partial = "#{controller_name}_search" %>
22
+ <% if lookup_context.exists?(search_partial, [], true) %>
23
+ <li>
24
+ <%= render partial: "/#{search_partial}" %>
25
+ </li>
26
+ <% elsif @Klass.respond_to?(:inline_forms_searchable?) && @Klass.inline_forms_searchable? %>
27
+ <li>
28
+ <%= render "inline_forms/generic_search" %>
29
+ </li>
30
+ <% end %>
31
+ <li>
32
+ <%= link_to_new_record(@Klass, "new_#{@Klass.to_s.singularize.underscore}_path", @Klass.to_s.pluralize.downcase + "_list") %>
33
+ </li>
34
+ </ul>
35
+ </div>
36
+ </nav>
37
+ </div>
@@ -24,7 +24,7 @@
24
24
  <% if flash_alert.present? %><div class="flash alert"><%= flash_alert %></div><% end %>
25
25
  </div>
26
26
  <% end %>
27
- <%= render "/inline_forms_tabs" %>
27
+ <%= render "inline_forms/model_top_bar" %>
28
28
  <div id="outer_container">
29
29
  <%= yield %>
30
30
  </div>
@@ -444,11 +444,15 @@ class InlineFormsAddtoGenerator < Rails::Generators::NamedBase
444
444
  end
445
445
  when "_list_search"
446
446
  col = attribute.type
447
- new_scope = " scope :inline_forms_search, ->(q) { where(\"#{col} LIKE ?\", \"%\#{q}%\") }\n"
448
- if File.read(File.join(destination_root, model_file_path)).match?(/scope :inline_forms_search\b/)
449
- gsub_file model_file_path, / scope :inline_forms_search, ->.*\n/, new_scope
447
+ new_line = " inline_forms_search_on :#{col}\n"
448
+ path = File.join(destination_root, model_file_path)
449
+ content = File.read(path)
450
+ if content.match?(/inline_forms_search_on\b/)
451
+ gsub_file model_file_path, / inline_forms_search_on[^\n]*\n/, new_line
452
+ elsif content.match?(/scope :inline_forms_search\b/)
453
+ gsub_file model_file_path, / scope :inline_forms_search, ->.*\n/, new_line
450
454
  else
451
- inject_into_class model_file_path, model_class_name, new_scope
455
+ inject_into_class model_file_path, model_class_name, new_line
452
456
  end
453
457
  end
454
458
  end
@@ -116,7 +116,7 @@ module InlineForms
116
116
  @list_scopes << " scope :inline_forms_list, -> { order(:#{attribute.type}, :id) }\n"
117
117
  end
118
118
  if attribute.name == '_list_search'
119
- @list_scopes << " scope :inline_forms_search, ->(q) { where(\"#{attribute.type} LIKE ?\", \"%\#{q}%\") }\n"
119
+ @list_scopes << " inline_forms_search_on :#{attribute.type}\n"
120
120
  end
121
121
  if attribute.attribute?
122
122
  attribute.attribute_type == :unknown ? commenter = '#' : commenter = ' '
@@ -196,10 +196,6 @@ module InlineForms
196
196
  end
197
197
  end
198
198
 
199
- def add_second_top_bar
200
- copy_file "_inline_forms_tabs.html.erb", "app/views/_inline_forms_tabs.html.erb" unless File.exist?('app/views/_inline_forms_tabs.html.erb')
201
- end
202
-
203
199
  def add_tab
204
200
  return if @flag_not_accessible_through_html
205
201
 
@@ -1,6 +1,8 @@
1
1
  class ApplicationRecord < ActiveRecord::Base
2
2
  self.abstract_class = true
3
3
 
4
+ include InlineForms::Searchable
5
+
4
6
  # PaperTrail 16 defaults to `on: [:create, :update, :destroy, :touch]`.
5
7
  # ActionText's `belongs_to :record, polymorphic: true, touch: true` (set by
6
8
  # `has_rich_text`) calls `parent.touch` on every rich-text save, which
@@ -24,12 +26,6 @@ class ApplicationRecord < ActiveRecord::Base
24
26
  # `unscope`/`reorder` cleanly.
25
27
  scope :inline_forms_list, -> { all }
26
28
 
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
-
33
29
  # Wrapper for @model.human_attribute_name -> Model.human_attribute_name
34
30
  def human_attribute_name(*args)
35
31
  self.class.human_attribute_name(*args)
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module InlineForms
4
+ # Opt-in list search for inline_forms index views. Models declare searchable
5
+ # columns with `inline_forms_search_on :col, …`; only those models get the
6
+ # engine's generic search box (or a bespoke `_<model>_search` partial).
7
+ module Searchable
8
+ extend ActiveSupport::Concern
9
+
10
+ class_methods do
11
+ def inline_forms_searchable?
12
+ @inline_forms_searchable == true
13
+ end
14
+
15
+ def inline_forms_search_on(*columns)
16
+ cols = columns.flatten.map(&:to_s)
17
+ if cols.empty?
18
+ raise ArgumentError, "inline_forms_search_on requires at least one column"
19
+ end
20
+
21
+ @inline_forms_searchable = true
22
+ table = table_name
23
+ clause = cols.map { |c|
24
+ "#{connection.quote_table_name(table)}.#{connection.quote_column_name(c)} LIKE :q"
25
+ }.join(" OR ")
26
+ scope :inline_forms_search, ->(q) {
27
+ where(clause, q: "%#{sanitize_sql_like(q.to_s)}%")
28
+ }
29
+ end
30
+ end
31
+ end
32
+ end
@@ -1,5 +1,5 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
 
3
3
  module InlineForms
4
- VERSION = "8.1.44"
4
+ VERSION = "8.1.45"
5
5
  end
data/lib/inline_forms.rb CHANGED
@@ -7,6 +7,7 @@ require "inline_forms/schema_preview"
7
7
  require "inline_forms/schema_apply"
8
8
  require "inline_forms/schema_label"
9
9
  require "inline_forms/form_element_from_callee"
10
+ require "inline_forms/searchable"
10
11
  require "inline_forms/archived_form_elements"
11
12
  require "inline_forms/form_element_registry"
12
13
  require "inline_forms/form_elements"
@@ -7,6 +7,8 @@
7
7
  class ApplicationRecord < ActiveRecord::Base
8
8
  self.abstract_class = true
9
9
 
10
+ include InlineForms::Searchable
11
+
10
12
  has_paper_trail on: [ :create, :update, :destroy ]
11
13
 
12
14
  attr_writer :inline_forms_attribute_list
@@ -14,7 +16,6 @@ class ApplicationRecord < ActiveRecord::Base
14
16
  self.per_page = 7
15
17
 
16
18
  scope :inline_forms_list, -> { all }
17
- scope :inline_forms_search, ->(_q) { all }
18
19
 
19
20
  def human_attribute_name(*args)
20
21
  self.class.human_attribute_name(*args)
@@ -8,7 +8,7 @@ class Widget < ApplicationRecord
8
8
  validates :name, presence: true
9
9
 
10
10
  scope :inline_forms_list, -> { order(:name, :id) }
11
- scope :inline_forms_search, ->(q) { where("name LIKE ?", "%#{q}%") }
11
+ inline_forms_search_on :name
12
12
 
13
13
  def _presentation
14
14
  "#{name}"
@@ -0,0 +1,3 @@
1
+ <%= form_tag request.path, method: :get do %>
2
+ <span id="bespoke_gizmo_search">bespoke gizmo search</span>
3
+ <% end %>
@@ -155,7 +155,7 @@ class InlineFormsGeneratorTest < Minitest::Test
155
155
 
156
156
  model = read("app/models/apartment.rb")
157
157
  assert_includes(model, "scope :inline_forms_list, -> { order(:name, :id) }")
158
- assert_includes(model, "scope :inline_forms_search, ->(q) { where(\"name LIKE ?\", \"%\#{q}%\") }")
158
+ assert_includes(model, "inline_forms_search_on :name")
159
159
  migration = read_single_migration_for("apartments")
160
160
  refute_includes(migration, "_list_order")
161
161
  refute_includes(migration, "_list_search")
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../integration_test_helper"
4
+
5
+ # Engine-owned model top bar: title + new-record + optional search region.
6
+ class ModelTopBarTest < InlineFormsIntegrationTestCase
7
+ setup do
8
+ @widget = Widget.create!(name: "Alpha")
9
+ @machine = Machine.create!(name: "Press")
10
+ end
11
+
12
+ test "searchable model renders the engine generic search box" do
13
+ get widgets_path
14
+
15
+ assert_response :success
16
+ assert_includes response.body, %(id="input_search")
17
+ assert_includes response.body, %(id="inline_forms_model_top_bar")
18
+ refute_includes response.body, "bespoke gizmo search"
19
+ end
20
+
21
+ test "non-searchable model renders no search box" do
22
+ get machines_path
23
+
24
+ assert_response :success
25
+ assert_includes response.body, %(id="inline_forms_model_top_bar")
26
+ refute_includes response.body, %(id="input_search")
27
+ end
28
+
29
+ test "bespoke _<model>_search partial wins over generic search" do
30
+ get gizmos_path
31
+
32
+ assert_response :success
33
+ assert_includes response.body, %(id="bespoke_gizmo_search")
34
+ refute_includes response.body, %(id="input_search")
35
+ end
36
+
37
+ test "generic search box filters the list via inline_forms_search_on" do
38
+ Widget.create!(name: "Beta")
39
+
40
+ get widgets_path(search: "Alp")
41
+
42
+ assert_response :success
43
+ assert_includes response.body, "Alpha"
44
+ refute_includes response.body, "Beta"
45
+ end
46
+ 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.1.44
4
+ version: 8.1.45
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ace Suares
@@ -148,8 +148,10 @@ files:
148
148
  - app/views/inline_forms/_close.html.erb
149
149
  - app/views/inline_forms/_edit.html.erb
150
150
  - app/views/inline_forms/_flash.html.erb
151
+ - app/views/inline_forms/_generic_search.html.erb
151
152
  - app/views/inline_forms/_header.html.erb
152
153
  - app/views/inline_forms/_list.html.erb
154
+ - app/views/inline_forms/_model_top_bar.html.erb
153
155
  - app/views/inline_forms/_new.html.erb
154
156
  - app/views/inline_forms/_new_nested.html.erb
155
157
  - app/views/inline_forms/_pending_migration_row.html.erb
@@ -202,7 +204,6 @@ files:
202
204
  - lib/generators/inline_forms_addto_generator.rb
203
205
  - lib/generators/inline_forms_attribute_overrides.rb
204
206
  - lib/generators/inline_forms_generator.rb
205
- - lib/generators/templates/_inline_forms_tabs.html.erb
206
207
  - lib/generators/templates/application_record.rb
207
208
  - lib/generators/templates/controller.erb
208
209
  - lib/generators/templates/migration.erb
@@ -254,6 +255,7 @@ files:
254
255
  - lib/inline_forms/schema_intent.rb
255
256
  - lib/inline_forms/schema_label.rb
256
257
  - lib/inline_forms/schema_preview.rb
258
+ - lib/inline_forms/searchable.rb
257
259
  - lib/inline_forms/tabs.rb
258
260
  - lib/inline_forms/turbo_tabs_builder.rb
259
261
  - lib/inline_forms/version.rb
@@ -278,7 +280,7 @@ files:
278
280
  - test/dummy/app/models/machine.rb
279
281
  - test/dummy/app/models/part.rb
280
282
  - test/dummy/app/models/widget.rb
281
- - test/dummy/app/views/_inline_forms_tabs.html.erb
283
+ - test/dummy/app/views/_gizmos_search.html.erb
282
284
  - test/dummy/config/application.rb
283
285
  - test/dummy/config/database.yml
284
286
  - test/dummy/config/environment.rb
@@ -291,6 +293,7 @@ files:
291
293
  - test/inline_forms_generator_test.rb
292
294
  - test/integration/inline_forms_crud_test.rb
293
295
  - test/integration/legacy_elements_test.rb
296
+ - test/integration/model_top_bar_test.rb
294
297
  - test/integration/native_inputs_test.rb
295
298
  - test/integration/open_after_create_test.rb
296
299
  - test/integration/pending_migration_gate_test.rb
@@ -346,7 +349,7 @@ test_files:
346
349
  - test/dummy/app/models/machine.rb
347
350
  - test/dummy/app/models/part.rb
348
351
  - test/dummy/app/models/widget.rb
349
- - test/dummy/app/views/_inline_forms_tabs.html.erb
352
+ - test/dummy/app/views/_gizmos_search.html.erb
350
353
  - test/dummy/config/application.rb
351
354
  - test/dummy/config/database.yml
352
355
  - test/dummy/config/environment.rb
@@ -359,6 +362,7 @@ test_files:
359
362
  - test/inline_forms_generator_test.rb
360
363
  - test/integration/inline_forms_crud_test.rb
361
364
  - test/integration/legacy_elements_test.rb
365
+ - test/integration/model_top_bar_test.rb
362
366
  - test/integration/native_inputs_test.rb
363
367
  - test/integration/open_after_create_test.rb
364
368
  - test/integration/pending_migration_gate_test.rb
@@ -1,40 +0,0 @@
1
- <div class="contain-to-grid fixed" id="inline_forms_model_top_bar_container">
2
- <nav id="inline_forms_model_top_bar" class="top-bar">
3
- <div class="top-bar-left">
4
- <ul class="menu">
5
- <li class="menu-text" id="inline_forms_menu">
6
- <h1 style="margin: 0; font-size: inherit;">
7
- <a>
8
- <%= t(controller_name) %>
9
- </a>
10
- </h1>
11
- </li>
12
- </ul>
13
- </div>
14
- <div class="top-bar-right">
15
- <ul class="menu">
16
- <li>
17
- <%= form_tag request.path, :method => 'get' do %>
18
- <div class="row collapse">
19
- <div class="large-1 small-1 columns">
20
- <%= link_to "<i class='fi-x'></i>".html_safe, request.path, :title => 'reset', :class => "button expanded inline_forms_model_top_bar_buttons" %>
21
- </div>
22
- <div class="large-6 small-6 columns">
23
- <%= text_field_tag :search, params[:search], :placeholder => 'zoek op naam...', :id => 'input_search' %>
24
- </div>
25
- <div class="large-3 small-3 columns">
26
- <%= submit_tag 'zoek', :class => "button expanded inline_forms_model_top_bar_buttons" %>
27
- </div>
28
- <div class="large-2 small-2 columns">
29
- &nbsp;
30
- </div>
31
- </div>
32
- <% end %>
33
- </li>
34
- <li>
35
- <%= link_to_new_record(@Klass, "new_#{@Klass.to_s.singularize.underscore}_path", @Klass.to_s.pluralize.downcase + '_list') %>
36
- </li>
37
- </ul>
38
- </div>
39
- </nav>
40
- </div>
@@ -1,40 +0,0 @@
1
- <div class="contain-to-grid fixed" id="inline_forms_model_top_bar_container">
2
- <nav id="inline_forms_model_top_bar" class="top-bar">
3
- <div class="top-bar-left">
4
- <ul class="menu">
5
- <li class="menu-text" id="inline_forms_menu">
6
- <h1 style="margin: 0; font-size: inherit;">
7
- <a>
8
- <%= t(controller_name) %>
9
- </a>
10
- </h1>
11
- </li>
12
- </ul>
13
- </div>
14
- <div class="top-bar-right">
15
- <ul class="menu">
16
- <li>
17
- <%= form_tag request.path, :method => 'get' do %>
18
- <div class="row collapse">
19
- <div class="large-1 small-1 columns">
20
- <%= link_to "<i class='fi-x'></i>".html_safe, request.path, :title => 'reset', :class => "button expanded inline_forms_model_top_bar_buttons" %>
21
- </div>
22
- <div class="large-6 small-6 columns">
23
- <%= text_field_tag :search, params[:search], :placeholder => 'zoek op naam...', :id => 'input_search' %>
24
- </div>
25
- <div class="large-3 small-3 columns">
26
- <%= submit_tag 'zoek', :class => "button expanded inline_forms_model_top_bar_buttons" %>
27
- </div>
28
- <div class="large-2 small-2 columns">
29
- &nbsp;
30
- </div>
31
- </div>
32
- <% end %>
33
- </li>
34
- <li>
35
- <%= link_to_new_record(@Klass, "new_#{@Klass.to_s.singularize.underscore}_path", @Klass.to_s.pluralize.downcase + '_list') %>
36
- </li>
37
- </ul>
38
- </div>
39
- </nav>
40
- </div>