nice_partials 0.1.6 → 0.1.9

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: 1a2bc1c656f72b37d7766bd5353a4320fc516dd2bfa808352f8b96217991c78b
4
- data.tar.gz: fefb7742857fec22b402f714ce10e5d4e6b33279b3fe7abdb84b1408b31cb38c
3
+ metadata.gz: dc6e751d8a192aa46fe3ee5401fe924c4dc1f5c6c3c7fabe8c22f468ff3a344c
4
+ data.tar.gz: de5a48e0d84e96a40b85293933af3dadd4977e6dfa2dd06522c85e1a6fbd0576
5
5
  SHA512:
6
- metadata.gz: 40b811eb0e0231da0ed04d023af718200400880dffac5e64cb8f8c80de2f30ae187fb0e4c43235d305f1a60e74c321b370c4ff6d07a18fc975becea8c5faa975
7
- data.tar.gz: 9ae278eb935fc27bb1f7ae46bb71efcdbf84d3ef0157e0897f4412208a1c8864251a8f8cec5ea9961d01c1884f451b6695c2a8862d2456c4f7ee2698a124362a
6
+ metadata.gz: 5f56fb3b92520c7a818b6ced0225def224a265a9ca2400d70e283300df8eef3fc266c6b65fa2e4e24856d7eccb6c0b39bdadf056bdec76c499be68fd9d90a982
7
+ data.tar.gz: 6a58763a68c0aa76cfee32432c730166cd96a19b2c8a1e8428c26260663ff706fc4883af07e9b1782da0d7e493ca7f4f8883fde223b75674967acce7a3780bc1
data/.travis.yml CHANGED
@@ -2,6 +2,7 @@ sudo: false
2
2
  language: ruby
3
3
 
4
4
  rvm:
5
+ - 3.1
5
6
  - 3.0
6
7
  - 2.7
7
8
  - 2.6
data/CHANGELOG.md CHANGED
@@ -1,9 +1,63 @@
1
1
  ## CHANGELOG
2
2
 
3
+ * Remove need to insert `<% yield p = np %>` in partials.
4
+
5
+ Nice Partials now automatically captures blocks passed to `render`.
6
+ Instead of `p`, a `partial` method has been added to access the
7
+ current `NicePartials::Partial` object.
8
+
9
+ Here's a script to help update your view code:
10
+
11
+ ```ruby
12
+ files_to_inspect = []
13
+
14
+ Dir["app/views/**/*.html.erb"].each do |path|
15
+ if contents = File.read(path).match(/(<%=? yield\(?.*? = np\)? %>\n+)/m)&.post_match
16
+ files_to_inspect << path if contents.match?(/render.*?do \|/)
17
+
18
+ contents.gsub! /\bp\.(?=yield|helpers|content_for|content_for\?)/, "partial."
19
+ File.write path, contents
20
+ end
21
+ end
22
+
23
+ if files_to_inspect.any?
24
+ puts "These files had render calls with a block parameter and likely require some manual edits:"
25
+ puts files_to_inspect
26
+ else
27
+ puts "No files with render calls with a block parameter found, you're likely all set"
28
+ end
29
+ ```
30
+
31
+ * Support manual `yield`s in partials.
32
+
33
+ Due to the automatic yield support above, support has also been added for manual `yield some_object` calls.
34
+
35
+ Nice Partials automatically appends the `partial` to the yielded arguments, so you can
36
+ change `render … do |some_object|` to `render … do |some_object, partial|`.
37
+
38
+ * Deprecate `p` as the partial object access. Use `partial` instead.
39
+
40
+ * Expose `partial.yield` to access the captured output buffer.
41
+
42
+ Lets you access what a `<%= yield %>` call returned, like this:
43
+
44
+ ```erb
45
+ <%= render "card" do %>
46
+ This is the content of the internal output buffer
47
+ <% end %>
48
+ ```
49
+
50
+ ```erb
51
+ # app/views/cards/_card.html.erb
52
+ # This can be replaced with `partial.yield`.
53
+ <%= yield %> # Will output "This is the content of the internal output buffer"
54
+ ```
55
+
56
+ ### 0.1.7
57
+
3
58
  * Rely on `ActiveSupport.on_load :action_view`
4
59
  * Add support for Ruby 3.0
5
60
 
6
61
  ### 0.1.0
7
62
 
8
63
  * Initial release
9
-
data/README.md CHANGED
@@ -1,34 +1,34 @@
1
1
  # nice_partials [![[version]](https://badge.fury.io/rb/nice_partials.svg)](https://badge.fury.io/rb/nice_partials) [![[travis]](https://travis-ci.org/andrewculver/nice_partials.svg)](https://travis-ci.org/andrewculver/nice_partials)
2
2
 
3
- Nice Partials extends the concept of [`content_for` blocks and `yield`](https://guides.rubyonrails.org/layouts_and_rendering.html#using-the-content-for-method) for those times when a partial needs to provide one or more named "content areas" or "slots". This thin, optional layer of magic helps make traditional Rails view partials an even better fit for extracting components from your views, like so:
3
+ Nice Partials lets [`content_for` and `yield`](https://guides.rubyonrails.org/layouts_and_rendering.html#using-the-content-for-method) calls be partial specific, to provide named "content areas" or "slots". This optional layer of magic helps make traditional Rails view partials a better fit for extracting components from your views, like so:
4
4
 
5
5
  `app/views/components/_card.html.erb`:
6
6
  ```html+erb
7
7
  <div class="card">
8
- <%= p.yield :image %>
8
+ <%= partial.yield :image %>
9
9
  <div class="card-body">
10
10
  <h5 class="card-title"><%= title %></h5>
11
- <% if p.content_for? :body %>
11
+ <% if partial.content_for? :body %>
12
12
  <p class="card-text">
13
- <%= p.yield :body %>
13
+ <%= partial.yield :body %>
14
14
  </p>
15
15
  <% end %>
16
16
  </div>
17
17
  </div>
18
18
  ```
19
19
 
20
- These partials can still be utilized with a standard `render` call, but you can specify how to populate the content areas like so:
20
+ Then you can call `render`, but specify how to populate the content areas:
21
21
 
22
22
  ```html+erb
23
- <%= render 'components/card', title: 'Some Title' do |p| %>
24
- <% p.content_for :body do %>
23
+ <%= render 'components/card', title: 'Some Title' do |partial| %>
24
+ <% partial.content_for :body do %>
25
25
  Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
26
26
  tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
27
27
  <strong>quis nostrud exercitation ullamco laboris</strong> nisi ut aliquip
28
28
  ex ea commodo consequat.
29
29
  <% end %>
30
30
 
31
- <% p.content_for :image do %>
31
+ <% partial.content_for :image do %>
32
32
  <%= image_tag image_path('example.jpg'), alt: 'An example image' %>
33
33
  <% end %>
34
34
  <% end %>
@@ -104,32 +104,46 @@ You only need to use Nice Partials when:
104
104
 
105
105
  - you want to specifically isolate your helper methods for a specific partial.
106
106
 
107
- ### Use Nice Partials in a partial
107
+ ### Using Nice Partials
108
108
 
109
- To invoke nice partials, start your partial file with the following:
109
+ Nice Partials is invoked automatically when you render your partial with a block like so:
110
110
 
111
111
  ```html+erb
112
- <% yield p = np %>
112
+ <%= render 'components/card' do |partial| %>
113
+ <%= partial.content_for :some_section do %>
114
+ Some content!
115
+ <% end %>
116
+ <% end %>
113
117
  ```
114
118
 
115
- Here's what is happening here:
119
+ Now within the partial file itself, you can use `<%= partial.yield :some_section %>` to render whatever content areas you want to be passed into your partial.
116
120
 
117
- - `yield` executes the block we receive when someone uses our partial.
118
- - `np` fetches an instance of the generic class that helps isolate our content buffers and helper methods.
119
- - `p = np` ensures we have a reference to that object in this partial.
120
- - `yield p = np` ensures the developer using this partial also has a reference to that object, so they can define what goes in the various content buffers.
121
+ ### Accessing the content returned from `yield`
121
122
 
122
- (This is, [as far as we know](https://github.com/bullet-train-co/nice_partials/issues/1), the minimum viable invocation.)
123
+ In a regular Rails partial:
123
124
 
124
- Once you've done this at the top of your partial file, you can then use `<%= p.yield :some_section %>` to render whatever content areas you want to be passed into your partial.
125
+ ```html+erb
126
+ <%= render 'components/card' do %>
127
+ Some content!
128
+ Yet more content!
129
+ <% end %>
130
+ ```
131
+
132
+ You can access the inner content lines through what's returned from `yield`:
133
+
134
+ ```html+erb
135
+ <%# app/views/components/_card.html.erb %>
136
+ <%= yield %> # => "Some content!\n\nYet more content!"
137
+ ```
125
138
 
139
+ With Nice Partials, you can call `partial.yield` without arguments and return the same `"Some content!\n\nYet more content!"`.
126
140
 
127
141
  ### Defining and using well isolated helper methods
128
142
 
129
143
  To minimize the amount of pollution in the global helper namespace, you can use the shared context object to define helper methods specifically for your partials _within your partial_ like so:
130
144
 
131
145
  ```html+erb
132
- <% p.helpers do
146
+ <% partial.helpers do
133
147
 
134
148
  # references should be a link if the user can drill down, otherwise just a text label.
135
149
  def reference_to(user)
@@ -147,7 +161,7 @@ end %>
147
161
  Then later in the partial you can use the helper method like so:
148
162
 
149
163
  ```html+erb
150
- <td><%= p.reference_to(user) %></td>
164
+ <td><%= partial.reference_to(user) %></td>
151
165
  ```
152
166
 
153
167
  ## Development
data/Rakefile CHANGED
@@ -32,6 +32,6 @@ end
32
32
 
33
33
  desc "#{gemspec.name} | Test"
34
34
  task :test do
35
- sh "for file in test/*_test.rb; do ruby $file; done"
35
+ sh "for file in test/{**/,}*_test.rb; do ruby -Ilib:test $file; done"
36
36
  end
37
37
  task default: :test
@@ -1,25 +1,8 @@
1
1
  require_relative "partial"
2
2
 
3
3
  module NicePartials::Helper
4
- def np
4
+ def nice_partial
5
5
  NicePartials::Partial.new(self)
6
6
  end
7
-
8
- def nice_partials_push_t_prefix(prefix)
9
- @_nice_partials_t_prefixes ||= []
10
- @_nice_partials_t_prefixes << prefix
11
- end
12
-
13
- def nice_partials_pop_t_prefix
14
- @_nice_partials_t_prefixes ||= []
15
- @_nice_partials_t_prefixes.pop
16
- end
17
-
18
- def t(key, options = {})
19
- if @_nice_partials_t_prefixes&.any? && key.first == '.'
20
- key = "#{@_nice_partials_t_prefixes.last}#{key}"
21
- end
22
-
23
- super(key, **options)
24
- end
7
+ alias_method :np, :nice_partial
25
8
  end
@@ -1,59 +1,120 @@
1
1
  # Monkey patch required to make `t` work as expected. Is this evil?
2
2
  # TODO Do we need to monkey patch other types of renderers as well?
3
- class ActionView::PartialRenderer
4
- alias_method :original_render, :render
5
-
6
- # See `content_for` in `lib/nice_partials/partial.rb` for something similar.
7
- def render(partial, context, block)
8
- if block
9
- partial_prefix = nice_partials_locale_prefix_from_view_context_and_block(context, block)
10
- context.nice_partials_push_t_prefix partial_prefix
11
- else
12
- # Render partial calls with no block should disable any prefix magic.
13
- context.nice_partials_push_t_prefix ''
3
+ module NicePartials::RenderingWithLocalePrefix
4
+ ActionView::Base.prepend self
5
+
6
+ def capture(*, &block)
7
+ with_nice_partials_t_prefix(lookup_context, block) { super }
8
+ end
9
+
10
+ def t(key, options = {})
11
+ if (prefix = @_nice_partials_t_prefix) && key.first == '.'
12
+ key = "#{prefix}#{key}"
14
13
  end
15
14
 
16
- begin
17
- result = original_render(partial, context, block)
18
- rescue Exception => exception
19
- # If there was some sort of exception thrown, we also need to pop the `t` prefix.
20
- # This provides compatibility with other libraries that depend on catching exceptions from the view renderer.
21
- context.nice_partials_pop_t_prefix
22
- raise exception
15
+ super(key, **options)
16
+ end
17
+
18
+ private
19
+
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
26
+ end
27
+ end
28
+
29
+ require "active_support/deprecation"
30
+ NicePartials::DEPRECATOR = ActiveSupport::Deprecation.new("1.0", "nice_partials")
31
+
32
+ module NicePartials::RenderingWithAutoContext
33
+ ActionView::Base.prepend self
34
+
35
+ def __partials
36
+ @__partials ||= NicePartials::Partial::Stack.new
37
+ end
38
+ delegate :partial, to: :__partials
39
+
40
+ def p(*args)
41
+ if args.empty?
42
+ NicePartials::DEPRECATOR.deprecation_warning :p, :partial # In-branch printing so we don't warn on legit `Kernel.p` calls.
43
+ partial
44
+ else
45
+ super # …we're really Kernel.p
23
46
  end
47
+ end
24
48
 
25
- # Whether there was a block or not, pop off whatever we put on the stack.
26
- context.nice_partials_pop_t_prefix
49
+ # Overrides `ActionView::Helpers::RenderingHelper#render` to push a new `nice_partial`
50
+ # on the stack, so rendering has a fresh `partial` to store content in.
51
+ def render(*)
52
+ __partials.prepend nice_partial
53
+ super
54
+ ensure
55
+ __partials.shift
56
+ end
27
57
 
28
- return result
58
+ # Since Action View passes any `yield`s in partials through `_layout_for`, we
59
+ # override `_layout_for` to detects if it's a capturing yield and append the
60
+ # current partial to the arguments.
61
+ #
62
+ # So `render … do |some_object|` can become `render … do |some_object, partial|`
63
+ # without needing to find and update the inner `yield some_object` call.
64
+ def _layout_for(*arguments, &block)
65
+ if block && !arguments.first.is_a?(Symbol)
66
+ capture_with_outer_partial_access(*arguments, &block)
67
+ else
68
+ super
69
+ end
29
70
  end
30
71
 
31
- # This and ActionView::Template#render below are for compatibility
32
- # with Ruby 3, as opposed to altering the original functionality.
72
+ # Reverts `partial` to return the outer partial before the `render` call started.
73
+ #
74
+ # So we don't clobber the `partial` shown here:
75
+ #
76
+ # <%= render "card" do |inner_partial| %>
77
+ # <% inner_partial.content_for :title, partial.content_for(:title) %>
78
+ # <% end %>
79
+ #
80
+ # Note: this happens because the `@partial` instance variable is shared between all
81
+ # `render` calls since rendering happens in one `ActionView::Base` instance.
82
+ def capture_with_outer_partial_access(*arguments, &block)
83
+ __partials.locate_previous
84
+ __partials.first.capture(*arguments, &block)
85
+ ensure
86
+ __partials.reset_locator
87
+ end
88
+ end
89
+
90
+ module NicePartials::PartialRendering
91
+ ActionView::PartialRenderer.prepend self
92
+
93
+ # Automatically captures the `block` in case the partial has no manual capturing `yield` call.
94
+ #
95
+ # This manual equivalent would be inserting this:
96
+ #
97
+ # <% yield partial %>
33
98
  def render_partial_template(view, locals, template, layout, block)
34
- ActiveSupport::Notifications.instrument(
35
- "render_partial.action_view",
36
- identifier: template.identifier,
37
- layout: layout && layout.virtual_path
38
- ) do |payload|
39
- content = template.render(view, locals, ActionView::OutputBuffer.new, {add_to_stack: !block}) do |*name|
40
- view._layout_for(*name, &block)
41
- end
42
-
43
- content = layout.render(view, locals) { content } if layout
44
- payload[:cache_hit] = view.view_renderer.cache_hits[template.virtual_path]
45
- build_rendered_template(content, template)
46
- end
99
+ view.capture_with_outer_partial_access(&block) if block && !template.has_capturing_yield?
100
+ super
47
101
  end
48
102
  end
49
103
 
50
- class ActionView::Template
51
- def render(view, locals, buffer = ActionView::OutputBuffer.new, flag = {add_to_stack: true}, &block)
52
- instrument_render_template do
53
- compile!(view)
54
- view._run(method_name, self, locals, buffer, **flag, &block)
55
- end
56
- rescue => e
57
- handle_render_error(view, e)
104
+ module NicePartials::CapturingYieldDetection
105
+ ActionView::Template.include self
106
+
107
+ # Matches yields that'll end up calling `capture`:
108
+ # <%= yield %>
109
+ # <%= yield something_else %>
110
+ #
111
+ # Doesn't match obfuscated `content_for` invocations, nor custom yields:
112
+ # <%= yield :message %>
113
+ # <%= something.yield %>
114
+ #
115
+ # Note: `<%= yield %>` becomes `yield :layout` with no `render` `block`, though this method assumes a block is passed.
116
+ def has_capturing_yield?
117
+ defined?(@has_capturing_yield) ? @has_capturing_yield :
118
+ @has_capturing_yield = source.match?(/\byield[\(? ]+(%>|[^:])/)
58
119
  end
59
120
  end
@@ -0,0 +1,43 @@
1
+ class NicePartials::Partial::Section
2
+ def initialize(view_context)
3
+ @view_context = view_context
4
+ @content = @pending_content = nil
5
+ end
6
+
7
+ def content_for(*arguments, &block)
8
+ if write_content_for(arguments.first, &block)
9
+ nil
10
+ else
11
+ capture_content_for(*arguments) if pending?
12
+ @content
13
+ end
14
+ end
15
+
16
+ def content?
17
+ pending? || @content
18
+ end
19
+
20
+ private
21
+
22
+ def write_content_for(content = nil, &block)
23
+ if content && !pending?
24
+ concat content
25
+ else
26
+ @pending_content = block if block
27
+ end
28
+ end
29
+
30
+ def capture_content_for(*arguments)
31
+ concat @view_context.capture(*arguments, &@pending_content)
32
+ @pending_content = nil
33
+ end
34
+
35
+ def concat(string)
36
+ @content ||= ActiveSupport::SafeBuffer.new
37
+ @content << string.to_s if string.present?
38
+ end
39
+
40
+ def pending?
41
+ @pending_content
42
+ end
43
+ end
@@ -0,0 +1,19 @@
1
+ class NicePartials::Partial::Stack
2
+ def initialize
3
+ @partials = []
4
+ reset_locator
5
+ end
6
+ delegate :prepend, :shift, :first, to: :@partials
7
+
8
+ def partial
9
+ @partials.public_send @locator
10
+ end
11
+
12
+ def locate_previous
13
+ @locator = :second
14
+ end
15
+
16
+ def reset_locator
17
+ @locator = :first
18
+ end
19
+ end
@@ -1,39 +1,57 @@
1
1
  module NicePartials
2
2
  class Partial
3
+ autoload :Section, "nice_partials/partial/section"
4
+ autoload :Stack, "nice_partials/partial/stack"
5
+
3
6
  delegate_missing_to :@view_context
4
7
 
8
+ # <%= render "nice_partial" do |p| %>
9
+ # <% p.content_for :title, "Yo" %>
10
+ # This content can be accessed through calling `yield`.
11
+ # <% end %>
12
+ #
13
+ # Then in the nice_partial:
14
+ # <%= content.content_for :title %> # => "Yo"
15
+ # <%= content.output_buffer %> # => "This line is printed to the `output_buffer`."
16
+ attr_accessor :output_buffer
17
+
5
18
  def initialize(view_context)
6
19
  @view_context = view_context
7
- @key = SecureRandom.uuid
20
+ @contents = Hash.new { |h, k| h[k] = Section.new(@view_context) }
8
21
  end
9
22
 
10
- def yield(name = nil)
11
- raise "You can only use Nice Partial's yield method to retrieve the content of named content area blocks. If you're not trying to fetch the content of a named content area block, you don't need Nice Partials! You can just call the vanilla Rails `yield`." unless name
12
- content_for(name)
23
+ def yield(*arguments, &block)
24
+ if arguments.empty?
25
+ output_buffer
26
+ else
27
+ content_for(*arguments, &block)
28
+ end
13
29
  end
14
30
 
15
31
  def helpers(&block)
16
32
  class_eval &block
17
33
  end
18
34
 
19
- # See the `ActionView::PartialRenderer` monkey patch in `lib/nice_partials/monkey_patch.rb` for something similar.
20
- def content_for(name, content = nil, options = {}, &block)
21
- if block_given?
22
- partial_prefix = nice_partials_locale_prefix_from_view_context_and_block(@view_context, block)
23
- @view_context.nice_partials_push_t_prefix(partial_prefix)
24
- end
25
-
26
- result = @view_context.content_for("#{name}_#{@key}".to_sym, content, options, &block)
27
-
28
- if block_given?
29
- @view_context.nice_partials_pop_t_prefix
30
- end
31
-
32
- return result
35
+ # Similar to Rails' built-in `content_for` except it defers any block execution
36
+ # and lets you pass arguments into it, like so:
37
+ #
38
+ # # Here we store a block with some eventual content…
39
+ # <% partial.content_for :title do |tag|
40
+ # <%= tag.h1 %>
41
+ # <% end %>
42
+ #
43
+ # # …which is then invoked with some predefined options later.
44
+ # <%= partial.content_for :title, tag.with_options(class: "text-bold") %>
45
+ def content_for(name, content = nil, *arguments, &block)
46
+ @contents[name].content_for(content, *arguments, &block)
33
47
  end
34
48
 
35
49
  def content_for?(name)
36
- @view_context.content_for?("#{name}_#{@key}".to_sym)
50
+ @contents[name].content?
51
+ end
52
+
53
+ def capture(*arguments, &block)
54
+ self.output_buffer = @view_context.capture(*arguments, self, &block)
37
55
  end
38
56
  end
39
57
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module NicePartials
4
- VERSION = "0.1.6"
4
+ VERSION = "0.1.9"
5
5
  end
data/lib/nice_partials.rb CHANGED
@@ -1,20 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "nice_partials/version"
4
- require_relative "nice_partials/helper"
5
- require_relative "nice_partials/monkey_patch"
6
4
 
7
5
  module NicePartials
8
- end
9
-
10
- # TODO Is there somewhere better we can put this?
11
- def nice_partials_locale_prefix_from_view_context_and_block(context, block)
12
- root_paths = context.view_renderer.lookup_context.view_paths.map(&:path)
13
- partial_location = block.source_location.first.dup
14
- root_paths.each { |path| partial_location.gsub!(/^#{path}\//, '') }
15
- partial_location.split('.').first.gsub('/_', '/').gsub('/', '.')
6
+ def self.locale_prefix_from(lookup_context, block)
7
+ root_paths = lookup_context.view_paths.map(&:path)
8
+ partial_location = block.source_location.first.dup
9
+ root_paths.each { |path| partial_location.gsub!(/^#{path}\//, '') }
10
+ partial_location.split('.').first.gsub('/_', '/').gsub('/', '.')
11
+ end
16
12
  end
17
13
 
18
14
  ActiveSupport.on_load :action_view do
15
+ require_relative "nice_partials/monkey_patch"
16
+
17
+ require_relative "nice_partials/helper"
19
18
  include NicePartials::Helper
20
19
  end
Binary file
@@ -22,4 +22,5 @@ Gem::Specification.new do |gem|
22
22
  gem.add_dependency "actionview", '>= 4.2.6'
23
23
 
24
24
  gem.add_development_dependency "rails"
25
+ gem.add_development_dependency "standard"
25
26
  end
@@ -1,2 +1 @@
1
- <% yield p = np %>
2
- <%= p.yield :message %>
1
+ <%= partial.yield :message %>
@@ -1,12 +1,10 @@
1
- <% yield p = np %>
2
-
3
1
  <div class="card">
4
- <%= p.yield :image %>
2
+ <%= partial.yield :image %>
5
3
  <div class="card-body">
6
4
  <h5 class="card-title"><%= title %></h5>
7
- <% if p.content_for? :body %>
5
+ <% if partial.content_for? :body %>
8
6
  <p class="card-text">
9
- <%= p.content_for :body %>
7
+ <%= partial.content_for :body, tag.with_options(class: "text-bold") %>
10
8
  </p>
11
9
  <% end %>
12
10
  </div>
@@ -0,0 +1,3 @@
1
+ <% p "it's clobbering time" %>
2
+ <%# TODO: Remove this file once `p` has been removed post deprecation, but don't change it yet. %>
3
+ <%= p.yield :message %>
@@ -0,0 +1,11 @@
1
+ <% partial.content_for :original_message, "hello" %>
2
+
3
+ <%= render "basic" do |cp| %>
4
+ <% cp.content_for :message, partial.content_for(:original_message) %>
5
+ <% end %>
6
+
7
+ <%= render "basic" do |cp| %>
8
+ <% cp.content_for :message, "goodbye" %>
9
+ <% end %>
10
+
11
+ <span><%= partial.content_for :message %></span>
@@ -1,6 +1,6 @@
1
1
  <%= render "card", title: "Some Title" do |p| %>
2
- <% p.content_for :body do %>
3
- Lorem Ipsum
2
+ <% p.content_for :body do |tag| %>
3
+ <%= tag.p "Lorem Ipsum" %>
4
4
  <% end %>
5
5
 
6
6
  <% p.content_for :image do %>
@@ -0,0 +1,3 @@
1
+ <%= render("basic") do |partial| %>
2
+ <%= partial.content_for :message, t(".message") %>
3
+ <% end %>
@@ -0,0 +1 @@
1
+ <%= t ".message" %>
@@ -0,0 +1 @@
1
+ <%= yield Hash.new(custom_key: :custom_value) %>
@@ -0,0 +1 @@
1
+ <%= yield %>
@@ -0,0 +1,3 @@
1
+ <%= render "yields/plain" do %>
2
+ <%= yield %>
3
+ <% end %>
@@ -0,0 +1 @@
1
+ <%= yield :message %>
@@ -0,0 +1,26 @@
1
+ require "test_helper"
2
+
3
+ module Renderer; end
4
+
5
+ class Renderer::TranslationTest < NicePartials::Test
6
+ setup do
7
+ I18n.backend.store_translations "en", { translations: {
8
+ translated: { message: "message" },
9
+ nice_partials_translated: { message: "nice_partials" }
10
+ } }
11
+ end
12
+
13
+ teardown { I18n.reload! }
14
+
15
+ test "clean translation render" do
16
+ render "translations/translated"
17
+
18
+ assert_rendered "message"
19
+ end
20
+
21
+ test "translations insert prefix from originating partial" do
22
+ render "translations/nice_partials_translated"
23
+
24
+ assert_rendered "nice_partials"
25
+ end
26
+ end
@@ -1,57 +1,83 @@
1
- require_relative "./test_helper"
1
+ require "test_helper"
2
2
 
3
- class RendererTest < ActiveSupport::TestCase
4
- # from actionview/render_test
5
- class TestController < ActionController::Base
3
+ class RendererTest < NicePartials::Test
4
+ test "render basic nice partial" do
5
+ render("basic") { |p| p.content_for :message, "hello from nice partials" }
6
+
7
+ assert_rendered "hello from nice partials"
6
8
  end
7
-
8
- def setup_view(paths)
9
- ActionView::Base.include(NicePartials::Helper)
10
9
 
11
- @assigns = { secret: "in the sauce" }
10
+ test "render nice partial in card template" do
11
+ render(template: "card_test")
12
12
 
13
- @view = Class.new(ActionView::Base.with_empty_template_cache) do
14
- def view_cache_dependencies; []; end
13
+ assert_rendered "Some Title"
14
+ assert_rendered '<p class="text-bold">Lorem Ipsum</p>'
15
+ assert_rendered "https://example.com/image.jpg"
16
+ end
15
17
 
16
- def combined_fragment_cache_key(key)
17
- [:views, key]
18
- end
19
- end.with_view_paths(paths, @assigns)
18
+ test "accessing partial in outer context won't leak state to inner render" do
19
+ render "partial_accessed_in_outer_context"
20
20
 
21
- controller = TestController.new
22
- controller.perform_caching = true
23
- controller.cache_store = :memory_store
24
- @view.controller = controller
21
+ assert_rendered "hello"
22
+ assert_rendered "goodbye"
23
+ assert_rendered "<span></span>"
24
+ assert_not_includes rendered, "hellogoodbye"
25
+ end
25
26
 
26
- @controller_view = controller.view_context_class.with_empty_template_cache.new(
27
- controller.lookup_context,
28
- controller.view_assigns,
29
- controller)
27
+ test "explicit yield without any arguments auto-captures passed block" do
28
+ render "yields/plain" do |partial, auto_capture_shouldnt_pass_extra_argument|
29
+ assert_kind_of NicePartials::Partial, partial
30
+ assert_nil auto_capture_shouldnt_pass_extra_argument
31
+ end
30
32
  end
31
33
 
32
- def setup
33
- ActionView::LookupContext::DetailsKey.clear
34
- path = ActionView::FileSystemResolver.new(FIXTURE_LOAD_PATH)
35
- view_paths = ActionView::PathSet.new([path])
36
- assert_equal ActionView::FileSystemResolver.new(FIXTURE_LOAD_PATH), view_paths.first
37
- setup_view(view_paths)
34
+ test "explicit yield with symbol auto-captures passed block" do
35
+ render "yields/symbol" do |partial, auto_capture_shouldnt_pass_extra_argument|
36
+ assert_kind_of NicePartials::Partial, partial
37
+ assert_nil auto_capture_shouldnt_pass_extra_argument
38
+ end
38
39
  end
39
40
 
40
- def teardown
41
- ActionController::Base.view_paths.map(&:clear_cache)
41
+ test "explicit yield with object won't auto-capture but make partial available in capture" do
42
+ render "yields/object" do |object, partial|
43
+ assert_equal Hash.new(custom_key: :custom_value), object
44
+ assert_kind_of NicePartials::Partial, partial
45
+ end
42
46
  end
43
47
 
44
- test "render basic nice partial" do
45
- rendered = @view.render("basic") { |p| p.content_for :message, "hello from nice partials" }.squish
48
+ test "explicit yield without any arguments with nesting" do
49
+ render "yields/plain_nested" do
50
+ tag.span "Output in outer partial through yield"
51
+ end
46
52
 
47
- assert_equal "hello from nice partials", rendered
53
+ assert_rendered "<span>Output in outer partial through yield</span>"
48
54
  end
49
55
 
50
- test "render nice partial in card template" do
51
- rendered = @view.render(template: "card_test").squish
56
+ test "output_buffer captures content not written via yield/content_for" do
57
+ nice_partial = nil
58
+ render "basic" do |p|
59
+ nice_partial = p
60
+ p.content_for :message, "hello from nice partials"
61
+ "Some extra content"
62
+ end
63
+
64
+ assert_rendered "hello from nice partials"
65
+ assert_equal "Some extra content", nice_partial.yield
66
+ end
67
+
68
+ test "doesn't clobber Kernel.p" do
69
+ assert_output "\"it's clobbering time\"\n" do
70
+ render("clobberer") { |p| p.content_for :message, "hello from nice partials" }
71
+ end
52
72
 
53
- assert_match "Some Title", rendered
54
- assert_match "Lorem Ipsum", rendered
55
- assert_match "https://example.com/image.jpg", rendered
73
+ assert_rendered "hello from nice partials"
74
+ end
75
+
76
+ test "deprecates top-level access through p method" do
77
+ assert_deprecated /p is deprecated and will be removed from nice_partials \d/, NicePartials::DEPRECATOR do
78
+ assert_output "\"it's clobbering time\"\n" do
79
+ render("clobberer") { |p| p.content_for :message, "hello from nice partials" }
80
+ end
81
+ end
56
82
  end
57
83
  end
data/test/test_helper.rb CHANGED
@@ -2,6 +2,16 @@ require "active_support"
2
2
  require "active_support/testing/autorun"
3
3
  require "action_controller"
4
4
  require "action_view"
5
+ require "action_view/test_case"
6
+
5
7
  require "nice_partials"
6
8
 
7
- FIXTURE_LOAD_PATH = File.expand_path("fixtures", __dir__)
9
+ class NicePartials::Test < ActionView::TestCase
10
+ TestController.view_paths << "test/fixtures"
11
+
12
+ private
13
+
14
+ def assert_rendered(matcher)
15
+ assert_match matcher, rendered
16
+ end
17
+ end
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.1.6
4
+ version: 0.1.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Culver
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2022-02-10 00:00:00.000000000 Z
12
+ date: 2022-08-15 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: actionview
@@ -39,6 +39,20 @@ dependencies:
39
39
  - - ">="
40
40
  - !ruby/object:Gem::Version
41
41
  version: '0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: standard
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
42
56
  description: A little bit of magic to make partials perfect for components.
43
57
  email:
44
58
  - andrew.culver@gmail.com
@@ -59,11 +73,23 @@ files:
59
73
  - lib/nice_partials/helper.rb
60
74
  - lib/nice_partials/monkey_patch.rb
61
75
  - lib/nice_partials/partial.rb
76
+ - lib/nice_partials/partial/section.rb
77
+ - lib/nice_partials/partial/stack.rb
62
78
  - lib/nice_partials/version.rb
79
+ - nice_partials-0.1.8.gem
63
80
  - nice_partials.gemspec
64
81
  - test/fixtures/_basic.html.erb
65
82
  - test/fixtures/_card.html.erb
83
+ - test/fixtures/_clobberer.html.erb
84
+ - test/fixtures/_partial_accessed_in_outer_context.html.erb
66
85
  - test/fixtures/card_test.html.erb
86
+ - test/fixtures/translations/_nice_partials_translated.html.erb
87
+ - test/fixtures/translations/_translated.html.erb
88
+ - test/fixtures/yields/_object.html.erb
89
+ - test/fixtures/yields/_plain.html.erb
90
+ - test/fixtures/yields/_plain_nested.html.erb
91
+ - test/fixtures/yields/_symbol.html.erb
92
+ - test/renderer/translation_test.rb
67
93
  - test/renderer_test.rb
68
94
  - test/test_helper.rb
69
95
  homepage: https://github.com/bullet-train-co/nice_partials
@@ -85,13 +111,22 @@ required_rubygems_version: !ruby/object:Gem::Requirement
85
111
  - !ruby/object:Gem::Version
86
112
  version: '0'
87
113
  requirements: []
88
- rubygems_version: 3.2.22
114
+ rubygems_version: 3.3.7
89
115
  signing_key:
90
116
  specification_version: 4
91
117
  summary: A little bit of magic to make partials perfect for components.
92
118
  test_files:
93
119
  - test/fixtures/_basic.html.erb
94
120
  - test/fixtures/_card.html.erb
121
+ - test/fixtures/_clobberer.html.erb
122
+ - test/fixtures/_partial_accessed_in_outer_context.html.erb
95
123
  - test/fixtures/card_test.html.erb
124
+ - test/fixtures/translations/_nice_partials_translated.html.erb
125
+ - test/fixtures/translations/_translated.html.erb
126
+ - test/fixtures/yields/_object.html.erb
127
+ - test/fixtures/yields/_plain.html.erb
128
+ - test/fixtures/yields/_plain_nested.html.erb
129
+ - test/fixtures/yields/_symbol.html.erb
130
+ - test/renderer/translation_test.rb
96
131
  - test/renderer_test.rb
97
132
  - test/test_helper.rb