nice_partials 0.9.3 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 14580892ce441161fda38b893e61d4b4733b1fd1c0130f22cf73dc7170f9ddd2
4
- data.tar.gz: '0696801e28b1ab5f3574cca9d71bcb03a702fa76d5076a25881ef0dfaa883638'
3
+ metadata.gz: dc5dac27b668f89801b376053aa1c335623c160f7787763a7ea116d2c536a47d
4
+ data.tar.gz: e51012246b8ddebf3dab57e3d4c5b20df1f3d5d0c400fc8a3244269c0ee4a761
5
5
  SHA512:
6
- metadata.gz: 302fdc205c1c433b670bcdd22d45a0a66bccdcdc15fae841ef88ae0209079a77cd2bddff199060a17be941164a7a8e852c16d6b508674483e123eebdb8f5c892
7
- data.tar.gz: 8b9cff626b4a6c713d274609427c6ed28dbc20a31b9825aa52b21e8670c59f37e5c1f5552460a357ff06b47aca3af7a0fd78c937882bf6779c8f0281ac2b6b0c
6
+ metadata.gz: fe1ac04c91971f99d3f25fc72409ae48747d8341f341a479e79f96115fdc495cccf858824951cc7124c4d68e2eeec8e2c65d04c1a2af9ecee7da685cd281f46a
7
+ data.tar.gz: 786ae831e3d51b59fba639bf0e7185ccf853f54c1dec14e5bb97fede30d46ef912f17b81e4a0651b59a246431e9e7a1919557f9d0bbac517c1c7cb4484c25d51
data/CHANGELOG.md CHANGED
@@ -1,5 +1,67 @@
1
1
  ## CHANGELOG
2
2
 
3
+ * Feature: partial's expose `local_assigns` + `locals` alias
4
+
5
+ ```html+erb
6
+ <%# app/views/articles/show.html.erb %>
7
+ <%= render "section", id: "an_article" %>
8
+
9
+ <%# app/views/application/_section.html.erb %>
10
+ <%# We can access the passed `id:` like this: %>
11
+ <% partial.local_assigns[:id] %>
12
+ <% partial.locals[:id] %>
13
+ ```
14
+
15
+ Note: this is equal to the default partial local variable of `local_assigns`, but it becomes more useful with the next feature below.
16
+
17
+ * Feature: partial helpers can access `partial`
18
+
19
+ ```html+erb
20
+ <%# app/views/articles/show.html.erb %>
21
+ <% render "section", id: "an_article" do |section| %>
22
+ <%= tag.h1 "An Article", id: section.labelledby %>
23
+ <% end %>
24
+
25
+ <%# app/views/application/_section.html.erb %>
26
+ <%
27
+ partial.helpers do
28
+ def aria
29
+ partial.locals.fetch(:aria, {}).with_defaults(labelledby:)
30
+ end
31
+
32
+ def labelledby
33
+ id = partial.locals[:id] and "#{id}_label"
34
+ end
35
+ end
36
+ %>
37
+
38
+ <%= tag.section partial.yield, id:, aria: partial.aria %>
39
+ ```
40
+
41
+ ### 0.9.4
42
+
43
+ * Feature: declare contents via `required` and `optional`
44
+
45
+ ```html+erb
46
+ <% if partial.title? %>
47
+ <h1 class="text-xl">
48
+ <%= partial.title %>
49
+ </h1>
50
+ <% end %>
51
+
52
+ <div><%= partial.body %></div>
53
+ ```
54
+
55
+ Can now become:
56
+
57
+ ```html+erb
58
+ <%= partial.title.optional.h1 class: "text-xl" %><%# Will not output any HTML element if no content has been provided. %>
59
+
60
+ <div><%= partial.body.required %></div> <%# Raises when this line is hit if no content has been provided %>
61
+ ```
62
+
63
+ See the README for more.
64
+
3
65
  ### 0.9.3
4
66
 
5
67
  * Fixed: section predicates not respecting `local_assigns` content
data/Gemfile.lock ADDED
@@ -0,0 +1,221 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ nice_partials (0.10.0)
5
+ actionview (>= 4.2.6)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ actioncable (7.0.4.2)
11
+ actionpack (= 7.0.4.2)
12
+ activesupport (= 7.0.4.2)
13
+ nio4r (~> 2.0)
14
+ websocket-driver (>= 0.6.1)
15
+ actionmailbox (7.0.4.2)
16
+ actionpack (= 7.0.4.2)
17
+ activejob (= 7.0.4.2)
18
+ activerecord (= 7.0.4.2)
19
+ activestorage (= 7.0.4.2)
20
+ activesupport (= 7.0.4.2)
21
+ mail (>= 2.7.1)
22
+ net-imap
23
+ net-pop
24
+ net-smtp
25
+ actionmailer (7.0.4.2)
26
+ actionpack (= 7.0.4.2)
27
+ actionview (= 7.0.4.2)
28
+ activejob (= 7.0.4.2)
29
+ activesupport (= 7.0.4.2)
30
+ mail (~> 2.5, >= 2.5.4)
31
+ net-imap
32
+ net-pop
33
+ net-smtp
34
+ rails-dom-testing (~> 2.0)
35
+ actionpack (7.0.4.2)
36
+ actionview (= 7.0.4.2)
37
+ activesupport (= 7.0.4.2)
38
+ rack (~> 2.0, >= 2.2.0)
39
+ rack-test (>= 0.6.3)
40
+ rails-dom-testing (~> 2.0)
41
+ rails-html-sanitizer (~> 1.0, >= 1.2.0)
42
+ actiontext (7.0.4.2)
43
+ actionpack (= 7.0.4.2)
44
+ activerecord (= 7.0.4.2)
45
+ activestorage (= 7.0.4.2)
46
+ activesupport (= 7.0.4.2)
47
+ globalid (>= 0.6.0)
48
+ nokogiri (>= 1.8.5)
49
+ actionview (7.0.4.2)
50
+ activesupport (= 7.0.4.2)
51
+ builder (~> 3.1)
52
+ erubi (~> 1.4)
53
+ rails-dom-testing (~> 2.0)
54
+ rails-html-sanitizer (~> 1.1, >= 1.2.0)
55
+ activejob (7.0.4.2)
56
+ activesupport (= 7.0.4.2)
57
+ globalid (>= 0.3.6)
58
+ activemodel (7.0.4.2)
59
+ activesupport (= 7.0.4.2)
60
+ activerecord (7.0.4.2)
61
+ activemodel (= 7.0.4.2)
62
+ activesupport (= 7.0.4.2)
63
+ activestorage (7.0.4.2)
64
+ actionpack (= 7.0.4.2)
65
+ activejob (= 7.0.4.2)
66
+ activerecord (= 7.0.4.2)
67
+ activesupport (= 7.0.4.2)
68
+ marcel (~> 1.0)
69
+ mini_mime (>= 1.1.0)
70
+ activesupport (7.0.4.2)
71
+ concurrent-ruby (~> 1.0, >= 1.0.2)
72
+ i18n (>= 1.6, < 2)
73
+ minitest (>= 5.1)
74
+ tzinfo (~> 2.0)
75
+ addressable (2.8.1)
76
+ public_suffix (>= 2.0.2, < 6.0)
77
+ ast (2.4.2)
78
+ builder (3.2.4)
79
+ capybara (3.38.0)
80
+ addressable
81
+ matrix
82
+ mini_mime (>= 0.1.3)
83
+ nokogiri (~> 1.8)
84
+ rack (>= 1.6.0)
85
+ rack-test (>= 0.6.3)
86
+ regexp_parser (>= 1.5, < 3.0)
87
+ xpath (~> 3.2)
88
+ concurrent-ruby (1.2.2)
89
+ crass (1.0.6)
90
+ date (3.3.3)
91
+ erubi (1.12.0)
92
+ globalid (1.1.0)
93
+ activesupport (>= 5.0)
94
+ i18n (1.12.0)
95
+ concurrent-ruby (~> 1.0)
96
+ io-console (0.6.0)
97
+ irb (1.6.3)
98
+ reline (>= 0.3.0)
99
+ json (2.6.3)
100
+ language_server-protocol (3.17.0.3)
101
+ loofah (2.19.1)
102
+ crass (~> 1.0.2)
103
+ nokogiri (>= 1.5.9)
104
+ mail (2.8.1)
105
+ mini_mime (>= 0.1.1)
106
+ net-imap
107
+ net-pop
108
+ net-smtp
109
+ marcel (1.0.2)
110
+ matrix (0.4.2)
111
+ method_source (1.0.0)
112
+ mini_mime (1.1.2)
113
+ minitest (5.18.0)
114
+ net-imap (0.3.4)
115
+ date
116
+ net-protocol
117
+ net-pop (0.1.2)
118
+ net-protocol
119
+ net-protocol (0.2.1)
120
+ timeout
121
+ net-smtp (0.3.3)
122
+ net-protocol
123
+ nio4r (2.5.8)
124
+ nokogiri (1.14.2-arm64-darwin)
125
+ racc (~> 1.4)
126
+ nokogiri (1.14.2-x86_64-linux)
127
+ racc (~> 1.4)
128
+ parallel (1.22.1)
129
+ parser (3.2.1.1)
130
+ ast (~> 2.4.1)
131
+ public_suffix (5.0.1)
132
+ racc (1.6.2)
133
+ rack (2.2.6.3)
134
+ rack-test (2.0.2)
135
+ rack (>= 1.3)
136
+ rails (7.0.4.2)
137
+ actioncable (= 7.0.4.2)
138
+ actionmailbox (= 7.0.4.2)
139
+ actionmailer (= 7.0.4.2)
140
+ actionpack (= 7.0.4.2)
141
+ actiontext (= 7.0.4.2)
142
+ actionview (= 7.0.4.2)
143
+ activejob (= 7.0.4.2)
144
+ activemodel (= 7.0.4.2)
145
+ activerecord (= 7.0.4.2)
146
+ activestorage (= 7.0.4.2)
147
+ activesupport (= 7.0.4.2)
148
+ bundler (>= 1.15.0)
149
+ railties (= 7.0.4.2)
150
+ rails-dom-testing (2.0.3)
151
+ activesupport (>= 4.2.0)
152
+ nokogiri (>= 1.6)
153
+ rails-html-sanitizer (1.5.0)
154
+ loofah (~> 2.19, >= 2.19.1)
155
+ railties (7.0.4.2)
156
+ actionpack (= 7.0.4.2)
157
+ activesupport (= 7.0.4.2)
158
+ method_source
159
+ rake (>= 12.2)
160
+ thor (~> 1.0)
161
+ zeitwerk (~> 2.5)
162
+ rainbow (3.1.1)
163
+ rake (13.0.6)
164
+ regexp_parser (2.7.0)
165
+ reline (0.3.2)
166
+ io-console (~> 0.5)
167
+ rexml (3.2.5)
168
+ rubocop (1.44.1)
169
+ json (~> 2.3)
170
+ parallel (~> 1.10)
171
+ parser (>= 3.2.0.0)
172
+ rainbow (>= 2.2.2, < 4.0)
173
+ regexp_parser (>= 1.8, < 3.0)
174
+ rexml (>= 3.2.5, < 4.0)
175
+ rubocop-ast (>= 1.24.1, < 2.0)
176
+ ruby-progressbar (~> 1.7)
177
+ unicode-display_width (>= 2.4.0, < 3.0)
178
+ rubocop-ast (1.27.0)
179
+ parser (>= 3.2.1.0)
180
+ rubocop-performance (1.15.2)
181
+ rubocop (>= 1.7.0, < 2.0)
182
+ rubocop-ast (>= 0.4.0)
183
+ ruby-progressbar (1.13.0)
184
+ standard (1.24.3)
185
+ language_server-protocol (~> 3.17.0.2)
186
+ rubocop (= 1.44.1)
187
+ rubocop-performance (= 1.15.2)
188
+ thor (1.2.1)
189
+ timeout (0.3.2)
190
+ tzinfo (2.0.6)
191
+ concurrent-ruby (~> 1.0)
192
+ unicode-display_width (2.4.2)
193
+ view_component (2.82.0)
194
+ activesupport (>= 5.2.0, < 8.0)
195
+ concurrent-ruby (~> 1.0)
196
+ method_source (~> 1.0)
197
+ websocket-driver (0.7.5)
198
+ websocket-extensions (>= 0.1.0)
199
+ websocket-extensions (0.1.5)
200
+ xpath (3.2.0)
201
+ nokogiri (~> 1.8)
202
+ zeitwerk (2.6.7)
203
+
204
+ PLATFORMS
205
+ arm64-darwin-20
206
+ arm64-darwin-21
207
+ arm64-darwin-22
208
+ x86_64-linux
209
+
210
+ DEPENDENCIES
211
+ capybara
212
+ irb
213
+ minitest
214
+ nice_partials!
215
+ rails
216
+ rake
217
+ standard
218
+ view_component
219
+
220
+ BUNDLED WITH
221
+ 2.4.8
data/README.md CHANGED
@@ -24,7 +24,7 @@ See, here we're outputting the `image`, `title`, and `body` sections:
24
24
  Then in `render` we populate them:
25
25
 
26
26
  ```html+erb
27
- <%= render "components/card", title: "Some Title" do |partial| %>
27
+ <%= render "components/card" do |partial| %>
28
28
  <% partial.title t(".title") %> # Same as `partial.content_for :title, t(".title")`
29
29
 
30
30
  <% partial.body do %>
@@ -116,6 +116,40 @@ You can also use `slice` to pass on content from an outer partial:
116
116
  <%= render "components/card", partial.slice(:title, :byline) %>
117
117
  ```
118
118
 
119
+ ### Declaring content as optional or required
120
+
121
+ In traditional Rails partials, you'll see lots of checks for whether or not we have content to then output an element.
122
+
123
+ With Nice Partials, it would look like this:
124
+
125
+ ```html+erb
126
+ <% if partial.title? %>
127
+ <h1 class="text-xl"><%= partial.title %></h1>
128
+ <% end %>
129
+ ```
130
+
131
+ However, we can remove the conditional using `optional`:
132
+
133
+ ```html+erb
134
+ <%= partial.title.optional.then do |title| %>
135
+ <h1 class="text-xl"><%= title %></h1>
136
+ <% end %>
137
+ ```
138
+
139
+ This will avoid outputting an empty tag, which could mess with your markup, in case there's no content provided for `title`.
140
+
141
+ Note: with Nice Partials tag helpers support, this example could also be shortened to `<%= partial.title.optional.h1 class: "text-xl" %>`.
142
+
143
+ #### Required
144
+
145
+ Alternatively, if `title` is a section that we require to be provided, we can do:
146
+
147
+ ```html+erb
148
+ <h1 class="text-xl"><%= partial.title.required %></h1>
149
+ ```
150
+
151
+ Here, `required` will raise in case there's been no `title` content provided by that point.
152
+
119
153
  ### Appending content from the view into a section
120
154
 
121
155
  Nice Partials supports calling any method on `ActionView::Base`, like the helpers shown here, and then have them auto-append to the section.
@@ -182,24 +216,6 @@ But the partial can also prepare tag builders that the rendering block can then
182
216
  <% partial.title.yield tag.with_options(class: "text-m4", data: { controller: "title" }) %> # => <h1 class="text-m4" data-controller="title">Title content</h1>
183
217
  ```
184
218
 
185
- ### Smoother conditional rendering
186
-
187
- In regular Rails partials it's common to see `content_for?` used to conditionally rendering something. With Nice Partials we can do this:
188
-
189
- ```html+erb
190
- <% if partial.title? %>
191
- <% partial.title.h1 %>
192
- <% end %>
193
- ```
194
-
195
- But since sections respond to and leverage `present?`, we can shorten the above to:
196
-
197
- ```html+erb
198
- <% partial.title.presence&.h1 %>
199
- ```
200
-
201
- This way no empty h1 element is rendered.
202
-
203
219
  ### Accessing the content returned via `partial.yield`
204
220
 
205
221
  To access the inner content lines in the block here, partials have to manually insert a `<%= yield %>` call.
@@ -1,28 +1,41 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Monkey patch required to make `t` work as expected. Is this evil?
2
4
  # TODO Do we need to monkey patch other types of renderers as well?
3
5
  module NicePartials::RenderingWithLocalePrefix
4
- ActionView::Base.prepend self
6
+ module BaseIntegration
7
+ ::ActionView::Base.prepend self
5
8
 
6
- def capture(*, &block)
7
- with_nice_partials_t_prefix(lookup_context, block) { super }
8
- end
9
+ def t(key, options = {})
10
+ if (template = @_nice_partials_translate_template) && key&.start_with?(".")
11
+ key = "#{virtual_path_translate_key_prefix(template.virtual_path)}#{key}"
12
+ end
13
+
14
+ super(key, **options)
15
+ end
9
16
 
10
- def t(key, options = {})
11
- if (prefix = @_nice_partials_t_prefix) && key&.start_with?(".")
12
- key = "#{prefix}#{key}"
17
+ def with_nice_partials_t_prefix(block)
18
+ old_nice_partials_translate_template = @_nice_partials_translate_template
19
+ @_nice_partials_translate_template = block ? @current_template : nil
20
+ yield
21
+ ensure
22
+ @_nice_partials_translate_template = old_nice_partials_translate_template
13
23
  end
14
24
 
15
- super(key, **options)
25
+ private
26
+
27
+ def virtual_path_translate_key_prefix(virtual_path)
28
+ @_scope_key_by_partial_cache ||= {} # Reuses Rails' existing `t` cache.
29
+ @_scope_key_by_partial_cache[virtual_path] ||= virtual_path.gsub(%r{/_?}, ".")
30
+ end
16
31
  end
17
32
 
18
- private
33
+ module PartialRendererIntegration
34
+ ActionView::PartialRenderer.prepend self
19
35
 
20
- def with_nice_partials_t_prefix(lookup_context, block)
21
- _nice_partials_t_prefix = @_nice_partials_t_prefix
22
- @_nice_partials_t_prefix = block ? NicePartials.locale_prefix_from(lookup_context, block) : nil
23
- yield
24
- ensure
25
- @_nice_partials_t_prefix = _nice_partials_t_prefix
36
+ def render(partial, view, block)
37
+ view.with_nice_partials_t_prefix(block) { super }
38
+ end
26
39
  end
27
40
  end
28
41
 
@@ -1,4 +1,39 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class NicePartials::Partial::Section < NicePartials::Partial::Content
4
+ class RequiredError < ArgumentError; end
5
+
6
+ class Empty < BasicObject
7
+ def initialize(section)
8
+ @section = section
9
+ end
10
+ delegate :blank?, :present?, :presence, to: :@section
11
+
12
+ def nil?
13
+ true
14
+ end
15
+
16
+ def method_missing(...)
17
+ present? ? super : ""
18
+ end
19
+ end
20
+
21
+ # Returns self if `present?`, or raises.
22
+ # Useful to declare content that you require to be supplied during a `render` call.
23
+ #
24
+ # <%= partial.title.required.div class: "text-xl" %>
25
+ def required
26
+ present? ? self : raise(RequiredError, "Section expected to have content, but wasn't supplied during render")
27
+ end
28
+
29
+ # Returns self if `present?`, or returns a Null object that won't output any content.
30
+ # Useful to declare optional content sections, that you also don't want to print any HTML elements for.
31
+ #
32
+ # <%= partial.title.optional.div class: "text-xl" %> # => "" # Won't output an empty `<div>` that can mess with HTML markups.
33
+ def optional
34
+ present? ? self : Empty.new(self)
35
+ end
36
+
2
37
  def yield(*arguments)
3
38
  chunks.each { append @view_context.capture(*arguments, &_1) }
4
39
  self
@@ -4,6 +4,9 @@ module NicePartials
4
4
  autoload :Section, "nice_partials/partial/section"
5
5
  autoload :Stack, "nice_partials/partial/stack"
6
6
 
7
+ attr_reader :local_assigns
8
+ alias_method :locals, :local_assigns
9
+
7
10
  def initialize(view_context, local_assigns = nil)
8
11
  @view_context, @local_assigns = view_context, local_assigns
9
12
  end
@@ -107,13 +110,23 @@ module NicePartials
107
110
  end
108
111
 
109
112
  def helpers_context
110
- @helpers_context ||= Helpers.new(@view_context)
113
+ @helpers_context ||= Helpers.new(@view_context, self)
111
114
  end
112
115
 
113
116
  class Helpers < SimpleDelegator
114
117
  def self.method_added(name)
115
118
  super
116
- NicePartials::Partial.delegate name, to: :helpers_context
119
+
120
+ unless name == :initialize || name == :partial
121
+ NicePartials::Partial.delegate name, to: :helpers_context
122
+ end
123
+ end
124
+
125
+ attr_reader :partial
126
+
127
+ def initialize(view_context, partial)
128
+ super(view_context)
129
+ @partial = partial
117
130
  end
118
131
  end
119
132
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module NicePartials
4
- VERSION = "0.9.3"
4
+ VERSION = "0.10.0"
5
5
  end
data/lib/nice_partials.rb CHANGED
@@ -3,11 +3,6 @@
3
3
  require_relative "nice_partials/version"
4
4
 
5
5
  module NicePartials
6
- def self.locale_prefix_from(lookup_context, block)
7
- partial_location = block.source_location.first.dup
8
- lookup_context.view_paths.each { partial_location.delete_prefix!(_1.path)&.delete_prefix!("/") }
9
- partial_location.split('.').first.gsub('/_', '/').gsub('/', '.')
10
- end
11
6
  end
12
7
 
13
8
  ActiveSupport.on_load :action_view do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nice_partials
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.3
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Culver
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2023-03-05 00:00:00.000000000 Z
12
+ date: 2023-08-31 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: actionview
@@ -64,6 +64,7 @@ files:
64
64
  - CHANGELOG.md
65
65
  - CODE_OF_CONDUCT.md
66
66
  - Gemfile
67
+ - Gemfile.lock
67
68
  - MIT-LICENSE.txt
68
69
  - README.md
69
70
  - Rakefile
@@ -95,7 +96,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
95
96
  - !ruby/object:Gem::Version
96
97
  version: '0'
97
98
  requirements: []
98
- rubygems_version: 3.4.7
99
+ rubygems_version: 3.4.19
99
100
  signing_key:
100
101
  specification_version: 4
101
102
  summary: A little bit of magic to make partials perfect for components.