phlexible 3.1.1 → 3.2.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: 335d7a198a6dd2e58d52abe064552a739cfd77c9910f294022bd4a96adf4ef92
4
- data.tar.gz: ee505e0e79afba25172584437407b99bc9ab52609d0d7c02cf2e29edc91ac20f
3
+ metadata.gz: ba0c7764b691fdb2ea92f5d8ef9389e1eb31bd97483f210e1b5e8f7c9fcd0c09
4
+ data.tar.gz: 80b397992cf20e11e9e971d744f6a7cea5bfa56a1b1186c4a629ec04bb836624
5
5
  SHA512:
6
- metadata.gz: 58bd0f6dba980c3328127363960d501e9abbc2e84543b3264d418eb30a5e0a3fad8e25a7fa5a1f9a2b3aaa2b4f0e2bc4057813cdc963a63c7c276611052e6502
7
- data.tar.gz: 7c082ee6bdfc6ae5f46f680d872b16e6d125dadfd94771b7da5d87151655a4405faf4c5616ae4bb5d56277a3df8dcfa45cfd928b7b0d97f678bcf2543d65f84d
6
+ metadata.gz: 375b7283fa50d41cf9e94bdc131f5faa469d73f67c8f2f8fdf34badb6c722edf193c9d9d64ffb7fee6f565d7d2a7a853a91f2d3f4ffed901e8d8b4421b967e9b
7
+ data.tar.gz: b5fba4880d3a63faca0e67fe09a422a34b12b322ae6dd73ebca5b3a4b0137db0b80f177df306f18b27942a9d57fcd1501da6a2b8d9bafc5c3f6899fa729f4b90
@@ -0,0 +1,31 @@
1
+ You are a RuboCop style reviewer for the Phlexible Ruby gem.
2
+
3
+ ## Project Style Rules
4
+
5
+ This project enforces strict RuboCop rules. Review changed Ruby files and flag violations.
6
+
7
+ ### Disabled Syntax (hard errors)
8
+ - **No `unless`** — use `if !` or negated conditions instead
9
+ - **No `and` / `or` / `not`** — use `&&` / `||` / `!` instead
10
+ - **No numbered parameters** (`_1`, `_2`) — use named block parameters
11
+
12
+ ### Formatting
13
+ - **Max line length**: 100 characters
14
+ - **Private method indentation**: `indented_internal_methods` — private/protected methods are indented one extra level beneath the access modifier
15
+ - **String literals**: prefer double quotes
16
+
17
+ ### Enabled Plugins
18
+ Review against these RuboCop plugins:
19
+ - `rubocop-rails`
20
+ - `rubocop-minitest`
21
+ - `rubocop-performance`
22
+ - `rubocop-packaging`
23
+ - `rubocop-rake`
24
+
25
+ ## Instructions
26
+
27
+ 1. Read the files that were changed (use `jj diff --name-only` or check recent edits).
28
+ 2. For each `.rb` file, check for violations of the rules above.
29
+ 3. Run `bundle exec rubocop -P --fail-level C --force-exclusion <file>` to confirm.
30
+ 4. Report any issues found, grouped by file, with the specific rule violated and a suggested fix.
31
+ 5. If no issues are found, confirm the code passes review.
@@ -0,0 +1,11 @@
1
+ #!/bin/bash
2
+ INPUT=$(cat)
3
+ FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path')
4
+
5
+ # Block edits to lock files and generated gemfiles
6
+ if [[ "$FILE_PATH" == *.lock ]] || [[ "$FILE_PATH" == */gemfiles/*.gemfile ]]; then
7
+ echo "Do not edit lock files or generated Appraisal gemfiles directly." >&2
8
+ exit 2
9
+ fi
10
+
11
+ exit 0
@@ -0,0 +1,18 @@
1
+ #!/bin/bash
2
+ INPUT=$(cat)
3
+ FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path')
4
+
5
+ # Only lint Ruby files
6
+ if [[ "$FILE_PATH" != *.rb ]]; then
7
+ exit 0
8
+ fi
9
+
10
+ RESULT=$(bundle exec rubocop -P --fail-level C --force-exclusion "$FILE_PATH" 2>&1)
11
+ EXIT_CODE=$?
12
+
13
+ if [ $EXIT_CODE -ne 0 ]; then
14
+ echo "$RESULT" >&2
15
+ exit 2
16
+ fi
17
+
18
+ exit 0
@@ -0,0 +1,27 @@
1
+ {
2
+ "hooks": {
3
+ "PreToolUse": [
4
+ {
5
+ "matcher": "Edit|Write",
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-files.sh"
10
+ }
11
+ ]
12
+ }
13
+ ],
14
+ "PostToolUse": [
15
+ {
16
+ "matcher": "Edit|Write",
17
+ "hooks": [
18
+ {
19
+ "type": "command",
20
+ "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/rubocop-check.sh",
21
+ "timeout": 30
22
+ }
23
+ ]
24
+ }
25
+ ]
26
+ }
27
+ }
@@ -0,0 +1,21 @@
1
+ ---
2
+ name: test
3
+ description: Run tests for the Phlexible gem across the Appraisal matrix
4
+ arguments:
5
+ - name: appraisal
6
+ description: "Appraisal to test: phlex1-rails7, phlex1-rails8, phlex2-rails7, phlex2-rails8, or 'all' (default: phlex2-rails8)"
7
+ default: "phlex2-rails8"
8
+ - name: file
9
+ description: "Optional test file path, or path:line for a single test"
10
+ disable-model-invocation: true
11
+ ---
12
+
13
+ Run Phlexible tests using the Appraisal gem for multi-version testing.
14
+
15
+ ## Instructions
16
+
17
+ 1. If `appraisal` is "all", run: `bundle exec appraisal rails test {file}`
18
+ 2. Otherwise, run: `bundle exec appraisal {appraisal} rails test {file}`
19
+ 3. If no `file` is given, omit it to run the full suite for that appraisal.
20
+ 4. Report pass/fail/skip counts from the test output.
21
+ 5. If tests fail, summarise which tests failed and why.
data/.rubocop.yml CHANGED
@@ -12,6 +12,7 @@ AllCops:
12
12
  SuggestExtensions: false
13
13
  Exclude:
14
14
  - "gemfiles/**/*"
15
+ - "vendor/**/*"
15
16
 
16
17
  Naming/FileName:
17
18
  Exclude:
data/CLAUDE.md ADDED
@@ -0,0 +1,80 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Overview
6
+
7
+ Phlexible is a Ruby gem providing helpers and extensions for [Phlex](https://phlex.fun) views in Rails applications. It supports Phlex 1.x and 2.x across Rails 7.2+ and Ruby >= 3.3.
8
+
9
+ ## Commands
10
+
11
+ ### Tests
12
+
13
+ Tests use Minitest (with maxitest, minitest-focus, minitest-spec-rails). The project uses [Appraisal](https://github.com/thoughtbot/appraisal) to test across multiple Phlex/Rails version combinations defined in `Appraisals`.
14
+
15
+ ```bash
16
+ # Run all tests across all appraisals (phlex1/rails7, phlex1/rails8, phlex2/rails7, phlex2/rails8)
17
+ bundle exec appraisal rails test
18
+
19
+ # Run tests for a specific appraisal
20
+ bundle exec appraisal phlex2/rails8 rails test
21
+
22
+ # Run a single test file
23
+ bundle exec appraisal phlex2/rails8 rails test test/phlexible/alias_view_test.rb
24
+
25
+ # Run a single test by line number
26
+ bundle exec appraisal phlex2/rails8 rails test test/phlexible/alias_view_test.rb:10
27
+
28
+ # Focus a single test (add `focus` before the test method, provided by minitest-focus)
29
+ ```
30
+
31
+ ### Lint
32
+
33
+ ```bash
34
+ bundle exec rubocop -P --fail-level C
35
+ ```
36
+
37
+ ### Setup
38
+
39
+ ```bash
40
+ bin/setup
41
+ bundle exec appraisal install
42
+ ```
43
+
44
+ ## Architecture
45
+
46
+ All source code lives under `lib/phlexible/`. Autoloading is handled by Zeitwerk (`Zeitwerk::Loader.for_gem` in `lib/phlexible.rb`).
47
+
48
+ ### Module Organization
49
+
50
+ Modules are split into two categories:
51
+
52
+ **Standalone modules** (no Rails dependency):
53
+ - `Phlexible::AliasView` — `extend` in a view to create shortcut methods for rendering other components
54
+ - `Phlexible::Callbacks` — `include` for ActiveSupport::Callbacks-based `before_template`/`after_template`/`around_template` hooks. Also provides `before_layout`/`after_layout`/`around_layout` when used with `AutoLayout`.
55
+ - `Phlexible::PageTitle` — `include` for hierarchical page title management across nested views
56
+ - `Phlexible::ProcessAttributes` — `extend` to intercept and modify HTML element attributes before rendering (Phlex 2.x only; Phlex 1.x has this built-in). Prepends wrappers onto all StandardElements, VoidElements, and custom `register_element` methods.
57
+
58
+ **Rails-specific modules** (`lib/phlexible/rails/`):
59
+ - `ActionController::ImplicitRender` — convention-based automatic Phlex view rendering (resolves `UsersController#index` to `Users::IndexView`)
60
+ - `ControllerVariables` — explicit interface to expose controller instance variables to Phlex views via `controller_variable` class method
61
+ - `AElement` — overrides `a` tag to pass `href` through Rails `url_for`
62
+ - `ButtonTo` — Phlex component replacing Rails `button_to` helper
63
+ - `Responder` — integration with the [Responders](https://github.com/heartcombo/responders) gem
64
+ - `AutoLayout` — automatic layout wrapping based on view namespace conventions (e.g., `Views::Admin::Index` resolves to `Views::Layouts::Admin`). Includes `ViewAssigns` and `Callbacks`. Layout resolution is cached per class in production.
65
+ - `MetaTags` / `MetaTagsComponent` — define meta tags in controllers, render in views
66
+
67
+ ### Key Patterns
68
+
69
+ - Modules use `extend` (AliasView, ProcessAttributes) or `include` (Callbacks, PageTitle, ControllerVariables) depending on whether they add class-level or instance-level behavior.
70
+ - `ProcessAttributes` uses `class_eval` with string interpolation to dynamically wrap every HTML element method — be careful when modifying this.
71
+ - `ControllerVariables` depends on both `ViewAssigns` and `Callbacks` internally.
72
+ - `AutoLayout` depends on both `ViewAssigns` and `Callbacks`. Layout resolution is cached in production via `resolved_layout` class method; use `reset_resolved_layout!` to clear.
73
+ - Tests use a Rails dummy app at `test/dummy/` for integration testing with real controllers/routes.
74
+
75
+ ## Style
76
+
77
+ - RuboCop enforced with `rubocop-rails`, `rubocop-minitest`, `rubocop-performance`, `rubocop-packaging`, `rubocop-rake`.
78
+ - `unless`, `and`/`or`/`not`, and numbered parameters are **disabled** via `rubocop-disable_syntax`.
79
+ - `indented_internal_methods` indentation style (private methods indented one extra level).
80
+ - Max line length: 100 characters.
data/README.md CHANGED
@@ -14,9 +14,7 @@ If bundler is not being used to manage dependencies, install the gem by executin
14
14
 
15
15
  ## Usage
16
16
 
17
- ### Rails
18
-
19
- #### `ActionController::ImplicitRender`
17
+ ### `Rails::ActionController::ImplicitRender`
20
18
 
21
19
  Adds support for default and `action_missing` rendering of Phlex views. So instead of this:
22
20
 
@@ -25,6 +23,10 @@ class UsersController
25
23
  def index
26
24
  render Views::Users::Index.new
27
25
  end
26
+
27
+ def show
28
+ render Views::Users::Show.new
29
+ end
28
30
  end
29
31
  ```
30
32
 
@@ -36,9 +38,9 @@ class UsersController
36
38
  end
37
39
  ```
38
40
 
39
- ##### View Resolution
41
+ #### View Resolution
40
42
 
41
- By default, views are resolved using the `phlex_view_path` method, which constructs a path based on the controller and action name. For example, `UsersController#index` will look for `Users::IndexView`.
43
+ By default, views are resolved using the `phlex_view_path` method, which constructs a path based on the controller and action name. For example, `UsersController#index` will look for `Views::Users::IndexView`.
42
44
 
43
45
  You can customize this behavior by overriding `phlex_view_path` in your controller:
44
46
 
@@ -56,7 +58,7 @@ end
56
58
 
57
59
  This would resolve `UsersController#index` to `Views::Users::Index` instead.
58
60
 
59
- #### `Callbacks`
61
+ ### `Callbacks`
60
62
 
61
63
  While Phlex does have `before_template`, `after_template`, and `around_template` hooks, they must be defined as regular Ruby methods, meaning you have to always remember to call `super` when redefining any hook method.
62
64
 
@@ -82,9 +84,105 @@ end
82
84
 
83
85
  You can still use the regular `before_template`, `after_template`, and `around_template` hooks as well, but I recommend that if you include this module, that you use callbacks instead.
84
86
 
85
- #### `ControllerVariables`
87
+ When used with `Rails::AutoLayout`, layout callbacks (`before_layout`, `after_layout`, `around_layout`) are also available. See the `Rails::AutoLayout` section below.
88
+
89
+ ### `Rails::AutoLayout`
90
+
91
+ Automatically wraps Phlex views in a layout component based on namespace conventions. Include this module in your view classes to enable automatic layout resolution.
92
+
93
+ ```ruby
94
+ class Views::Admin::Index < Phlex::HTML
95
+ include Phlexible::Rails::AutoLayout
96
+
97
+ def view_template
98
+ span { 'admin index' }
99
+ end
100
+ end
101
+ ```
102
+
103
+ #### Layout Resolution
104
+
105
+ Layouts are resolved by mapping the view's namespace to a layout class under `Views::Layouts::`. For example:
106
+
107
+ | View class | Layout resolved |
108
+ |---|---|
109
+ | `Views::Admin::Index` | `Views::Layouts::Admin` |
110
+ | `Views::Admin::Users::Show` | `Views::Layouts::Admin::Users` (falls back to `Views::Layouts::Admin` if not found) |
111
+ | `Views::Dashboard::Index` | `Views::Layouts::Application` (default fallback) |
112
+
113
+ Layout classes receive the view instance as a constructor argument and yield the view content:
114
+
115
+ ```ruby
116
+ class Views::Layouts::Admin < Phlex::HTML
117
+ def initialize(view)
118
+ @view = view
119
+ end
120
+
121
+ def view_template(&block)
122
+ div(id: 'admin-layout', &block)
123
+ end
124
+ end
125
+ ```
126
+
127
+ #### Controller-Assigned Layouts
128
+
129
+ You can override automatic resolution by setting a `@layout` instance variable in your controller. The view must also include `ViewAssigns` (which `AutoLayout` includes automatically):
130
+
131
+ ```ruby
132
+ class AdminController < ApplicationController
133
+ def index
134
+ @layout = Views::Layouts::Custom
135
+ end
136
+ end
137
+ ```
138
+
139
+ #### Configuration
140
+
141
+ Three class attributes control layout resolution:
142
+
143
+ | Attribute | Default | Description |
144
+ |---|---|---|
145
+ | `auto_layout_view_prefix` | `'Views::'` | Only views matching this prefix get auto-layout. Set to `nil` to match all view classes. |
146
+ | `auto_layout_namespace` | `'Views::Layouts::'` | Namespace where layout classes are looked up. |
147
+ | `auto_layout_default` | `'Views::Layouts::Application'` | Fallback layout when no namespace match is found. Set to `nil` to render without a layout. |
148
+
149
+ ```ruby
150
+ class Views::Base < Phlex::HTML
151
+ include Phlexible::Rails::AutoLayout
152
+
153
+ self.auto_layout_view_prefix = 'Views::'
154
+ self.auto_layout_namespace = 'Views::Layouts::'
155
+ self.auto_layout_default = 'Views::Layouts::Application'
156
+ end
157
+ ```
158
+
159
+ #### Layout Callbacks
160
+
161
+ When `Rails::AutoLayout` is included, `before_layout`, `after_layout`, and `around_layout` callbacks become available (provided by the `Callbacks` module):
162
+
163
+ ```ruby
164
+ class Views::Admin::Index < Phlex::HTML
165
+ include Phlexible::Rails::AutoLayout
166
+
167
+ before_layout :log_render
168
+
169
+ def view_template
170
+ span { 'admin index' }
171
+ end
172
+
173
+ private
86
174
 
87
- > Available in **>= 1.0.0**
175
+ def log_render
176
+ Rails.logger.info "Rendering with layout"
177
+ end
178
+ end
179
+ ```
180
+
181
+ #### Caching
182
+
183
+ Layout resolution is cached per class in production. In development (when `Rails.configuration.enable_reloading` is enabled), layouts are resolved fresh on each render. You can manually clear the cache with `reset_resolved_layout!`.
184
+
185
+ ### `Rails::ControllerVariables`
88
186
 
89
187
  > **NOTE:** Prior to **1.0.0**, this module was called `ControllerAttributes` with a very different API. This is no longer available since **1.0.0**.
90
188
 
@@ -102,7 +200,7 @@ class Views::Users::Index < Views::Base
102
200
  end
103
201
  ```
104
202
 
105
- ##### Options
203
+ #### Options
106
204
 
107
205
  `controller_variable` accepts one or many symbols, or a hash of symbols to options.
108
206
 
@@ -127,9 +225,9 @@ end
127
225
 
128
226
  Please note that defining a variable with the same name as an existing variable in the view will be overwritten.
129
227
 
130
- #### `Responder`
228
+ ### `Rails::Responder`
131
229
 
132
- If you use [Responders](https://github.com/heartcombo/responders), Phlexible provides a responder to support implicit rendering similar to `ActionController::ImplicitRender` above. It will render the Phlex view using `respond_with` if one exists, and fall back to default rendering.
230
+ If you use [Responders](https://github.com/heartcombo/responders), Phlexible provides a responder to support implicit rendering similar to `Rails::ActionController::ImplicitRender` above. It will render the Phlex view using `respond_with` if one exists, and fall back to default rendering.
133
231
 
134
232
  Just include it in your ApplicationResponder:
135
233
 
@@ -156,9 +254,9 @@ end
156
254
 
157
255
  This responder requires the use of `ActionController::ImplicitRender`, so don't forget to include that in your `ApplicationController`.
158
256
 
159
- If you use `ControllerVariables` in your view, and define a `resource` attribute, the responder will pass that to your view.
257
+ If you use `Rails::ControllerVariables` in your view, and define a `resource` attribute, the responder will pass that to your view.
160
258
 
161
- #### `AElement`
259
+ ### `Rails::AElement`
162
260
 
163
261
  No need to call Rails `link_to` helper, when you can simply render an anchor tag directly with Phlex. But unfortunately that means you lose some of the magic that `link_to` provides. Especially the automatic resolution of URL's and Rails routes.
164
262
 
@@ -180,7 +278,7 @@ class MyView < Phlex::HTML
180
278
  end
181
279
  ```
182
280
 
183
- #### 'ButtonTo`
281
+ ### `Rails::ButtonTo`
184
282
 
185
283
  Generates a form containing a single button that submits to the URL created by the set of options.
186
284
 
@@ -198,7 +296,7 @@ The form submits a POST request by default. You can specify a different HTTP ver
198
296
  Phlexible::Rails::ButtonTo.new(:root, method: :patch) { 'My Button' }
199
297
  ```
200
298
 
201
- ##### Options
299
+ #### Options
202
300
 
203
301
  - `:class` - Specify the HTML class name of the button (not the form).
204
302
  - `:form_attributes` - Hash of HTML attributes for the form tag.
@@ -207,9 +305,7 @@ Phlexible::Rails::ButtonTo.new(:root, method: :patch) { 'My Button' }
207
305
  - `:method` - Symbol of the HTTP verb. Supported verbs are :post (default), :get, :delete, :patch,
208
306
  and :put.
209
307
 
210
- #### `MetaTags`
211
-
212
- > Available in **>= 1.0.0**
308
+ ### `Rails::MetaTags`
213
309
 
214
310
  A super simple way to define and render meta tags in your Phlex views. Just render the
215
311
  `Phlexible::Rails::MetaTagsComponent` component in the head element of your page, and define the
@@ -269,12 +365,13 @@ class MyView < Phlex::HTML
269
365
  end
270
366
  ```
271
367
 
272
- ### PageTitle
368
+ ### `PageTitle`
273
369
 
274
370
  Helper to assist in defining page titles within Phlex views. Also includes support for nested views, where each desendent view class will have its title prepended to the page title. Simply include *Phlexible::PageTitle* module and assign the title to the `page_title` class variable:
275
371
 
276
372
  ```ruby
277
373
  class MyView
374
+ include Phlexible::PageTitle
278
375
  self.page_title = 'My Title'
279
376
  end
280
377
  ```
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- phlexible (3.0.0)
4
+ phlexible (3.1.1)
5
5
  phlex (>= 1.10.0, < 3.0.0)
6
6
  phlex-rails (>= 1.2.0, < 3.0.0)
7
7
  rails (>= 7.2.0, < 9.0.0)
@@ -416,7 +416,7 @@ CHECKSUMS
416
416
  parser (3.3.10.1) sha256=06f6a725d2cd91e5e7f2b7c32ba143631e1f7c8ae2fb918fc4cebec187e6a688
417
417
  phlex (1.11.0) sha256=979548e79a205c981612f1ab613addc8fa128c8092694d02f41aad4cea905e73
418
418
  phlex-rails (1.2.2) sha256=a20218449e71bc9fa5a71b672fbede8a654c6b32a58f1c4ea83ddc1682307a4c
419
- phlexible (3.0.0)
419
+ phlexible (3.1.1)
420
420
  pp (0.6.3) sha256=2951d514450b93ccfeb1df7d021cae0da16e0a7f95ee1e2273719669d0ab9df6
421
421
  pretty_please (0.2.0) sha256=1f00296f946ae8ffd53db25803ed3998d615b9cc07526517dc75fcca6af3a0d3
422
422
  prettyprint (0.2.0) sha256=2bc9e15581a94742064a3cc8b0fb9d45aae3d03a1baa6ef80922627a0766f193
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- phlexible (3.0.0)
4
+ phlexible (3.1.1)
5
5
  phlex (>= 1.10.0, < 3.0.0)
6
6
  phlex-rails (>= 1.2.0, < 3.0.0)
7
7
  rails (>= 7.2.0, < 9.0.0)
@@ -414,7 +414,7 @@ CHECKSUMS
414
414
  parser (3.3.10.1) sha256=06f6a725d2cd91e5e7f2b7c32ba143631e1f7c8ae2fb918fc4cebec187e6a688
415
415
  phlex (1.11.0) sha256=979548e79a205c981612f1ab613addc8fa128c8092694d02f41aad4cea905e73
416
416
  phlex-rails (1.2.2) sha256=a20218449e71bc9fa5a71b672fbede8a654c6b32a58f1c4ea83ddc1682307a4c
417
- phlexible (3.0.0)
417
+ phlexible (3.1.1)
418
418
  pp (0.6.3) sha256=2951d514450b93ccfeb1df7d021cae0da16e0a7f95ee1e2273719669d0ab9df6
419
419
  pretty_please (0.2.0) sha256=1f00296f946ae8ffd53db25803ed3998d615b9cc07526517dc75fcca6af3a0d3
420
420
  prettyprint (0.2.0) sha256=2bc9e15581a94742064a3cc8b0fb9d45aae3d03a1baa6ef80922627a0766f193
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- phlexible (3.0.0)
4
+ phlexible (3.1.1)
5
5
  phlex (>= 1.10.0, < 3.0.0)
6
6
  phlex-rails (>= 1.2.0, < 3.0.0)
7
7
  rails (>= 7.2.0, < 9.0.0)
@@ -422,7 +422,7 @@ CHECKSUMS
422
422
  parser (3.3.10.1) sha256=06f6a725d2cd91e5e7f2b7c32ba143631e1f7c8ae2fb918fc4cebec187e6a688
423
423
  phlex (2.4.0) sha256=8aad2f0cd792d7b1bd287f15a1f89474c9cacac28120dc33d2a81467ae934067
424
424
  phlex-rails (2.4.0) sha256=2bcddbd488681acb25753bab1887d3ac150e644244ff8ba307f2171a4d0195f5
425
- phlexible (3.0.0)
425
+ phlexible (3.1.1)
426
426
  pp (0.6.3) sha256=2951d514450b93ccfeb1df7d021cae0da16e0a7f95ee1e2273719669d0ab9df6
427
427
  pretty_please (0.2.0) sha256=1f00296f946ae8ffd53db25803ed3998d615b9cc07526517dc75fcca6af3a0d3
428
428
  prettyprint (0.2.0) sha256=2bc9e15581a94742064a3cc8b0fb9d45aae3d03a1baa6ef80922627a0766f193
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- phlexible (3.0.0)
4
+ phlexible (3.1.1)
5
5
  phlex (>= 1.10.0, < 3.0.0)
6
6
  phlex-rails (>= 1.2.0, < 3.0.0)
7
7
  rails (>= 7.2.0, < 9.0.0)
@@ -420,7 +420,7 @@ CHECKSUMS
420
420
  parser (3.3.10.1) sha256=06f6a725d2cd91e5e7f2b7c32ba143631e1f7c8ae2fb918fc4cebec187e6a688
421
421
  phlex (2.4.0) sha256=8aad2f0cd792d7b1bd287f15a1f89474c9cacac28120dc33d2a81467ae934067
422
422
  phlex-rails (2.4.0) sha256=2bcddbd488681acb25753bab1887d3ac150e644244ff8ba307f2171a4d0195f5
423
- phlexible (3.0.0)
423
+ phlexible (3.1.1)
424
424
  pp (0.6.3) sha256=2951d514450b93ccfeb1df7d021cae0da16e0a7f95ee1e2273719669d0ab9df6
425
425
  pretty_please (0.2.0) sha256=1f00296f946ae8ffd53db25803ed3998d615b9cc07526517dc75fcca6af3a0d3
426
426
  prettyprint (0.2.0) sha256=2bc9e15581a94742064a3cc8b0fb9d45aae3d03a1baa6ef80922627a0766f193
@@ -33,6 +33,18 @@ module Phlexible
33
33
  def after_template(*names, &block)
34
34
  set_callback(:template, :after, *names, &block)
35
35
  end
36
+
37
+ def before_layout(*names, &block)
38
+ set_callback(:layout, :before, *names, &block)
39
+ end
40
+
41
+ def after_layout(*names, &block)
42
+ set_callback(:layout, :after, *names, &block)
43
+ end
44
+
45
+ def around_layout(*names, &block)
46
+ set_callback(:layout, :around, *names, &block)
47
+ end
36
48
  end
37
49
 
38
50
  def around_template
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phlexible
4
+ module Rails
5
+ module AutoLayout
6
+ extend ActiveSupport::Concern
7
+ include Phlexible::Rails::ViewAssigns
8
+ include Phlexible::Callbacks
9
+
10
+ included do
11
+ define_callbacks :layout
12
+
13
+ class_attribute :auto_layout_view_prefix, instance_writer: false, default: 'Views::'
14
+ class_attribute :auto_layout_namespace, instance_writer: false, default: 'Views::Layouts::'
15
+ class_attribute :auto_layout_default, instance_writer: false, default: 'Views::Layouts::Application'
16
+ end
17
+
18
+ def around_template
19
+ layout_class = controller_assigned_layout || self.class.resolved_layout
20
+ if layout_class
21
+ run_callbacks :layout do
22
+ render(@layout = layout_class.new(self)) { super }
23
+ end
24
+ else
25
+ super
26
+ end
27
+ end
28
+
29
+ class_methods do
30
+ def resolved_layout
31
+ if ::Rails.configuration.enable_reloading
32
+ resolve_layout
33
+ else
34
+ return @resolved_layout if defined?(@resolved_layout)
35
+
36
+ @resolved_layout = resolve_layout
37
+ end
38
+ end
39
+
40
+ def reset_resolved_layout!
41
+ remove_instance_variable(:@resolved_layout) if defined?(@resolved_layout)
42
+ end
43
+
44
+ private
45
+
46
+ def resolve_layout
47
+ view_name = resolve_view_name
48
+ return nil if view_name.blank?
49
+
50
+ prefix = auto_layout_view_prefix
51
+ return nil if prefix && !view_name.start_with?(prefix)
52
+
53
+ resolve_layout_from_namespace(view_name, prefix) ||
54
+ auto_layout_default&.safe_constantize
55
+ end
56
+
57
+ def resolve_layout_from_namespace(view_name, prefix)
58
+ strip_prefix = prefix || infer_prefix_from_namespace || ''
59
+ segments = view_name.delete_prefix(strip_prefix).split('::')[..-2]
60
+
61
+ segments.length.downto(1) do |i|
62
+ klass = "#{auto_layout_namespace}#{segments.first(i).join('::')}".safe_constantize
63
+ return klass if klass.is_a?(Class)
64
+ end
65
+
66
+ nil
67
+ end
68
+
69
+ def infer_prefix_from_namespace
70
+ root = auto_layout_namespace&.split('::')&.first
71
+ "#{root}::" if root
72
+ end
73
+
74
+ def resolve_view_name
75
+ ancestors.each do |ancestor|
76
+ return ancestor.name if ancestor.is_a?(Class) && ancestor.name.present?
77
+ end
78
+ nil
79
+ end
80
+ end
81
+
82
+ private
83
+
84
+ def controller_assigned_layout
85
+ return nil if !respond_to?(:view_assigns, true)
86
+
87
+ view_assigns['layout']
88
+ end
89
+ end
90
+ end
91
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Phlexible
4
- VERSION = '3.1.1'
4
+ VERSION = '3.2.0'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: phlexible
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.1
4
+ version: 3.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joel Moss
@@ -91,9 +91,15 @@ executables: []
91
91
  extensions: []
92
92
  extra_rdoc_files: []
93
93
  files:
94
+ - ".claude/agents/rubocop-reviewer.md"
95
+ - ".claude/hooks/protect-files.sh"
96
+ - ".claude/hooks/rubocop-check.sh"
97
+ - ".claude/settings.json"
98
+ - ".claude/skills/test/SKILL.md"
94
99
  - ".rubocop.yml"
95
100
  - ".ruby-version"
96
101
  - Appraisals
102
+ - CLAUDE.md
97
103
  - CODE_OF_CONDUCT.md
98
104
  - LICENSE.txt
99
105
  - README.md
@@ -115,6 +121,7 @@ files:
115
121
  - lib/phlexible/rails/a_element.rb
116
122
  - lib/phlexible/rails/action_controller/implicit_render.rb
117
123
  - lib/phlexible/rails/action_controller/meta_tags.rb
124
+ - lib/phlexible/rails/auto_layout.rb
118
125
  - lib/phlexible/rails/button_to.rb
119
126
  - lib/phlexible/rails/button_to_concerns.rb
120
127
  - lib/phlexible/rails/controller_variables.rb
@@ -144,7 +151,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
144
151
  - !ruby/object:Gem::Version
145
152
  version: '0'
146
153
  requirements: []
147
- rubygems_version: 4.0.4
154
+ rubygems_version: 4.0.6
148
155
  specification_version: 4
149
156
  summary: A bunch of helpers and goodies intended to make life with Phlex even easier!
150
157
  test_files: []