view_component 2.52.0 → 2.62.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of view_component might be problematic. Click here for more details.

@@ -3,7 +3,7 @@
3
3
  module ViewComponent
4
4
  module RenderingComponentHelper # :nodoc:
5
5
  def render_component(component)
6
- self.response_body = component.render_in(self.view_context)
6
+ self.response_body = component.render_in(view_context)
7
7
  end
8
8
  end
9
9
  end
@@ -4,7 +4,7 @@ module ViewComponent
4
4
  module RenderingMonkeyPatch # :nodoc:
5
5
  def render(options = {}, args = {})
6
6
  if options.respond_to?(:render_in)
7
- self.response_body = options.render_in(self.view_context)
7
+ self.response_body = options.render_in(view_context)
8
8
  else
9
9
  super
10
10
  end
@@ -30,7 +30,7 @@ module ViewComponent
30
30
 
31
31
  slot_names.each do |slot_name|
32
32
  # Ensure slot_name isn't already declared
33
- if self.slots.key?(slot_name)
33
+ if slots.key?(slot_name)
34
34
  raise ArgumentError.new("#{slot_name} slot declared multiple times")
35
35
  end
36
36
 
@@ -73,7 +73,7 @@ module ViewComponent
73
73
  class_name = "ViewComponent::Slot" unless class_name.present?
74
74
 
75
75
  # Register the slot on the component
76
- self.slots[slot_name] = {
76
+ slots[slot_name] = {
77
77
  class_name: class_name,
78
78
  instance_variable_name: instance_variable_name,
79
79
  collection: collection
@@ -84,7 +84,7 @@ module ViewComponent
84
84
  def inherited(child)
85
85
  # Clone slot configuration into child class
86
86
  # see #test_slots_pollution
87
- child.slots = self.slots.clone
87
+ child.slots = slots.clone
88
88
 
89
89
  super
90
90
  end
@@ -106,7 +106,7 @@ module ViewComponent
106
106
  #
107
107
  def slot(slot_name, **args, &block)
108
108
  # Raise ArgumentError if `slot` doesn't exist
109
- unless slots.keys.include?(slot_name)
109
+ unless slots.key?(slot_name)
110
110
  raise ArgumentError.new "Unknown slot '#{slot_name}' - expected one of '#{slots.keys}'"
111
111
  end
112
112
 
@@ -123,8 +123,7 @@ module ViewComponent
123
123
  slot_instance = args.present? ? slot_class.new(**args) : slot_class.new
124
124
 
125
125
  # Capture block and assign to slot_instance#content
126
- # rubocop:disable Rails/OutputSafety
127
- slot_instance.content = view_context.capture(&block).to_s.strip.html_safe if block_given?
126
+ slot_instance.content = view_context.capture(&block).to_s.strip.html_safe if block
128
127
 
129
128
  if slot[:collection]
130
129
  # Initialize instance variable as an empty array
@@ -9,7 +9,7 @@ module ViewComponent
9
9
 
10
10
  RESERVED_NAMES = {
11
11
  singular: %i[content render].freeze,
12
- plural: %i[contents renders].freeze,
12
+ plural: %i[contents renders].freeze
13
13
  }.freeze
14
14
 
15
15
  # Setup component slot state
@@ -61,20 +61,26 @@ module ViewComponent
61
61
  # = Setting sub-component content
62
62
  #
63
63
  # Consumers of the component can render a sub-component by calling a
64
- # helper method with the same name as the slot.
64
+ # helper method with the same name as the slot prefixed with `with_`.
65
65
  #
66
66
  # <%= render_inline(MyComponent.new) do |component| %>
67
- # <% component.header(classes: "Foo") do %>
67
+ # <% component.with_header(classes: "Foo") do %>
68
68
  # <p>Bar</p>
69
69
  # <% end %>
70
70
  # <% end %>
71
71
  def renders_one(slot_name, callable = nil)
72
72
  validate_singular_slot_name(slot_name)
73
73
 
74
+ define_method :"with_#{slot_name}" do |*args, &block|
75
+ set_slot(slot_name, nil, *args, &block)
76
+ end
77
+ ruby2_keywords(:"with_#{slot_name}") if respond_to?(:ruby2_keywords, true)
78
+
74
79
  define_method slot_name do |*args, &block|
75
80
  if args.empty? && block.nil?
76
81
  get_slot(slot_name)
77
82
  else
83
+ # Deprecated: Will remove in 3.0
78
84
  set_slot(slot_name, nil, *args, &block)
79
85
  end
80
86
  end
@@ -112,15 +118,15 @@ module ViewComponent
112
118
  # = Setting sub-component content
113
119
  #
114
120
  # Consumers of the component can set the content of a slot by calling a
115
- # helper method with the same name as the slot. The method can be
116
- # called multiple times to append to the slot.
121
+ # helper method with the same name as the slot prefixed with `with_`. The
122
+ # method can be called multiple times to append to the slot.
117
123
  #
118
124
  # <%= render_inline(MyComponent.new) do |component| %>
119
- # <% component.item(name: "Foo") do %>
125
+ # <% component.with_item(name: "Foo") do %>
120
126
  # <p>One</p>
121
127
  # <% end %>
122
128
  #
123
- # <% component.item(name: "Bar") do %>
129
+ # <% component.with_item(name: "Bar") do %>
124
130
  # <p>two</p>
125
131
  # <% end %>
126
132
  # <% end %>
@@ -132,17 +138,31 @@ module ViewComponent
132
138
  # Define setter for singular names
133
139
  # for example `renders_many :items` allows fetching all tabs with
134
140
  # `component.tabs` and setting a tab with `component.tab`
141
+ #
142
+ # Deprecated: Will remove in 3.0
135
143
  define_method singular_name do |*args, &block|
136
144
  set_slot(slot_name, nil, *args, &block)
137
145
  end
138
146
  ruby2_keywords(singular_name.to_sym) if respond_to?(:ruby2_keywords, true)
139
147
 
148
+ define_method :"with_#{singular_name}" do |*args, &block|
149
+ set_slot(slot_name, nil, *args, &block)
150
+ end
151
+ ruby2_keywords(:"with_#{singular_name}") if respond_to?(:ruby2_keywords, true)
152
+
153
+ define_method :"with_#{slot_name}" do |collection_args = nil, &block|
154
+ collection_args.map do |args|
155
+ set_slot(slot_name, nil, **args, &block)
156
+ end
157
+ end
158
+
140
159
  # Instantiates and and adds multiple slots forwarding the first
141
160
  # argument to each slot constructor
142
161
  define_method slot_name do |collection_args = nil, &block|
143
162
  if collection_args.nil? && block.nil?
144
163
  get_slot(slot_name)
145
164
  else
165
+ # Deprecated: Will remove in 3.0
146
166
  collection_args.map do |args|
147
167
  set_slot(slot_name, nil, **args, &block)
148
168
  end
@@ -170,20 +190,20 @@ module ViewComponent
170
190
  # Clone slot configuration into child class
171
191
  # see #test_slots_pollution
172
192
  def inherited(child)
173
- child.registered_slots = self.registered_slots.clone
193
+ child.registered_slots = registered_slots.clone
174
194
  super
175
195
  end
176
196
 
177
197
  private
178
198
 
179
199
  def register_slot(slot_name, **kwargs)
180
- self.registered_slots[slot_name] = define_slot(slot_name, **kwargs)
200
+ registered_slots[slot_name] = define_slot(slot_name, **kwargs)
181
201
  end
182
202
 
183
203
  def define_slot(slot_name, collection:, callable:)
184
204
  # Setup basic slot data
185
205
  slot = {
186
- collection: collection,
206
+ collection: collection
187
207
  }
188
208
  return slot unless callable
189
209
 
@@ -234,7 +254,7 @@ module ViewComponent
234
254
  end
235
255
 
236
256
  def raise_if_slot_registered(slot_name)
237
- if self.registered_slots.key?(slot_name)
257
+ if registered_slots.key?(slot_name)
238
258
  # TODO remove? This breaks overriding slots when slots are inherited
239
259
  raise ArgumentError.new(
240
260
  "#{self} declares the #{slot_name} slot multiple times.\n\n" \
@@ -246,8 +266,8 @@ module ViewComponent
246
266
  def raise_if_slot_ends_with_question_mark(slot_name)
247
267
  if slot_name.to_s.ends_with?("?")
248
268
  raise ArgumentError.new(
249
- "#{self} declares a slot named #{slot_name}, which ends with a question mark.\n\n"\
250
- "This is not allowed because the ViewComponent framework already provides predicate "\
269
+ "#{self} declares a slot named #{slot_name}, which ends with a question mark.\n\n" \
270
+ "This is not allowed because the ViewComponent framework already provides predicate " \
251
271
  "methods ending in `?`.\n\n" \
252
272
  "To fix this issue, choose a different name."
253
273
  )
@@ -267,8 +287,6 @@ module ViewComponent
267
287
 
268
288
  if slot[:collection]
269
289
  []
270
- else
271
- nil
272
290
  end
273
291
  end
274
292
 
@@ -285,7 +303,7 @@ module ViewComponent
285
303
  # 2. Since we've to pass block content to components when calling
286
304
  # `render`, evaluating the block here would require us to call
287
305
  # `view_context.capture` twice, which is slower
288
- slot.__vc_content_block = block if block_given?
306
+ slot.__vc_content_block = block if block
289
307
 
290
308
  # If class
291
309
  if slot_definition[:renderable]
@@ -301,7 +319,7 @@ module ViewComponent
301
319
  # methods like `content_tag` as well as parent component state.
302
320
  renderable_function = slot_definition[:renderable_function].bind(self)
303
321
  renderable_value =
304
- if block_given?
322
+ if block
305
323
  renderable_function.call(*args) do |*rargs|
306
324
  view_context.capture(*rargs, &block)
307
325
  end
@@ -1,13 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "view_component/render_preview_helper"
4
+
3
5
  module ViewComponent
4
6
  module TestHelpers
5
7
  begin
6
8
  require "capybara/minitest"
9
+
7
10
  include Capybara::Minitest::Assertions
8
11
 
9
12
  def page
10
- Capybara::Node::Simple.new(@rendered_component)
13
+ @page ||= Capybara::Node::Simple.new(rendered_content)
11
14
  end
12
15
 
13
16
  def refute_component_rendered
@@ -19,8 +22,8 @@ module ViewComponent
19
22
  # :nocov:
20
23
  if ENV["DEBUG"]
21
24
  warn(
22
- "WARNING in `ViewComponent::TestHelpers`: You must add `capybara` " \
23
- "to your Gemfile to use Capybara assertions."
25
+ "WARNING in `ViewComponent::TestHelpers`: Add `capybara` " \
26
+ "to Gemfile to use Capybara assertions."
24
27
  )
25
28
  end
26
29
 
@@ -28,7 +31,19 @@ module ViewComponent
28
31
  end
29
32
 
30
33
  # @private
31
- attr_reader :rendered_component
34
+ attr_reader :rendered_content
35
+
36
+ # Returns the result of a render_inline call.
37
+ #
38
+ # @return [String]
39
+ def rendered_component
40
+ ViewComponent::Deprecation.warn(
41
+ "`rendered_component` is deprecated and will be removed in v3.0.0. " \
42
+ "Use `page` instead."
43
+ )
44
+
45
+ rendered_content
46
+ end
32
47
 
33
48
  # Render a component inline. Internally sets `page` to be a `Capybara::Node::Simple`,
34
49
  # allowing for Capybara assertions to be used:
@@ -41,14 +56,31 @@ module ViewComponent
41
56
  # @param component [ViewComponent::Base, ViewComponent::Collection] The instance of the component to be rendered.
42
57
  # @return [Nokogiri::HTML]
43
58
  def render_inline(component, **args, &block)
44
- @rendered_component =
59
+ @page = nil
60
+ @rendered_content =
45
61
  if Rails.version.to_f >= 6.1
46
62
  controller.view_context.render(component, args, &block)
47
63
  else
48
64
  controller.view_context.render_component(component, &block)
49
65
  end
50
66
 
51
- Nokogiri::HTML.fragment(@rendered_component)
67
+ Nokogiri::HTML.fragment(@rendered_content)
68
+ end
69
+
70
+ # Execute the given block in the view context. Internally sets `page` to be a
71
+ # `Capybara::Node::Simple`, allowing for Capybara assertions to be used:
72
+ #
73
+ # ```ruby
74
+ # render_in_view_context do
75
+ # render(MyComponent.new)
76
+ # end
77
+ #
78
+ # assert_text("Hello, World!")
79
+ # ```
80
+ def render_in_view_context(&block)
81
+ @page = nil
82
+ @rendered_content = controller.view_context.instance_exec(&block)
83
+ Nokogiri::HTML.fragment(@rendered_content)
52
84
  end
53
85
 
54
86
  # @private
@@ -121,7 +153,7 @@ module ViewComponent
121
153
 
122
154
  request.path_info = path
123
155
  request.path_parameters = Rails.application.routes.recognize_path(path)
124
- request.set_header("action_dispatch.request.query_parameters", Rack::Utils.parse_query(path.split("?")[1]))
156
+ request.set_header("action_dispatch.request.query_parameters", Rack::Utils.parse_nested_query(path.split("?")[1]))
125
157
  request.set_header(Rack::QUERY_STRING, path.split("?")[1])
126
158
  yield
127
159
  ensure
@@ -3,14 +3,13 @@
3
3
  require "erb"
4
4
  require "set"
5
5
  require "i18n"
6
- require "action_view/helpers/translation_helper"
7
6
  require "active_support/concern"
8
7
 
9
8
  module ViewComponent
10
9
  module Translatable
11
10
  extend ActiveSupport::Concern
12
11
 
13
- HTML_SAFE_TRANSLATION_KEY = /(?:_|\b)html\z/.freeze
12
+ HTML_SAFE_TRANSLATION_KEY = /(?:_|\b)html\z/
14
13
 
15
14
  included do
16
15
  class_attribute :i18n_backend, instance_writer: false, instance_predicate: false
@@ -21,19 +20,16 @@ module ViewComponent
21
20
  @i18n_scope ||= virtual_path.sub(%r{^/}, "").gsub(%r{/_?}, ".")
22
21
  end
23
22
 
24
- def _after_compile
25
- super
26
-
23
+ def build_i18n_backend
27
24
  return if CompileCache.compiled? self
28
25
 
29
- if (translation_files = _sidecar_files(%w[yml yaml])).any?
30
- self.i18n_backend = I18nBackend.new(
26
+ self.i18n_backend = if (translation_files = _sidecar_files(%w[yml yaml])).any?
27
+ # Returning nil cleans up if translations file has been removed since the last compilation
28
+
29
+ I18nBackend.new(
31
30
  i18n_scope: i18n_scope,
32
- load_paths: translation_files,
31
+ load_paths: translation_files
33
32
  )
34
- else
35
- # Cleanup if translations file has been removed since the last compilation
36
- self.i18n_backend = nil
37
33
  end
38
34
  end
39
35
  end
@@ -53,7 +49,7 @@ module ViewComponent
53
49
 
54
50
  def scope_data(data)
55
51
  @i18n_scope.reverse_each do |part|
56
- data = { part => data }
52
+ data = {part => data}
57
53
  end
58
54
  data
59
55
  end
@@ -68,7 +64,10 @@ module ViewComponent
68
64
  return key.map { |k| translate(k, **options) } if key.is_a?(Array)
69
65
 
70
66
  locale = options.delete(:locale) || ::I18n.locale
67
+ scope = options.delete(:scope)
68
+ scope = scope.join(".") if scope.is_a? Array
71
69
  key = key&.to_s unless key.is_a?(String)
70
+ key = "#{scope}.#{key}" if scope
72
71
  key = "#{i18n_scope}#{key}" if key.start_with?(".")
73
72
 
74
73
  if HTML_SAFE_TRANSLATION_KEY.match?(key)
@@ -95,7 +94,7 @@ module ViewComponent
95
94
  super(key, locale: locale, **options)
96
95
  end
97
96
  end
98
- alias :t :translate
97
+ alias_method :t, :translate
99
98
 
100
99
  # Exposes .i18n_scope as an instance method
101
100
  def i18n_scope
@@ -109,7 +108,7 @@ module ViewComponent
109
108
  # It's assumed here that objects loaded by the i18n backend will respond to `#html_safe?`.
110
109
  # It's reasonable that if we're in Rails, `active_support/core_ext/string/output_safety.rb`
111
110
  # will provide this to `Object`.
112
- translation.html_safe # rubocop:disable Rails/OutputSafety
111
+ translation.html_safe
113
112
  end
114
113
  end
115
114
 
@@ -3,7 +3,7 @@
3
3
  module ViewComponent
4
4
  module VERSION
5
5
  MAJOR = 2
6
- MINOR = 52
6
+ MINOR = 62
7
7
  PATCH = 0
8
8
 
9
9
  STRING = [MAJOR, MINOR, PATCH].join(".")
@@ -11,9 +11,7 @@ module ViewComponent
11
11
  autoload :CompileCache
12
12
  autoload :ComponentError
13
13
  autoload :Deprecation
14
- autoload :GlobalOutputBuffer
15
14
  autoload :Instrumentation
16
- autoload :OutputBufferStack
17
15
  autoload :Preview
18
16
  autoload :PreviewTemplateError
19
17
  autoload :TestHelpers
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: view_component
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.52.0
4
+ version: 2.62.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - GitHub Open Source
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-03-25 00:00:00.000000000 Z
11
+ date: 2022-07-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -185,19 +185,19 @@ dependencies:
185
185
  - !ruby/object:Gem::Version
186
186
  version: '13.0'
187
187
  - !ruby/object:Gem::Dependency
188
- name: rubocop-github
188
+ name: standard
189
189
  requirement: !ruby/object:Gem::Requirement
190
190
  requirements:
191
191
  - - "~>"
192
192
  - !ruby/object:Gem::Version
193
- version: 0.16.1
193
+ version: '1'
194
194
  type: :development
195
195
  prerelease: false
196
196
  version_requirements: !ruby/object:Gem::Requirement
197
197
  requirements:
198
198
  - - "~>"
199
199
  - !ruby/object:Gem::Version
200
- version: 0.16.1
200
+ version: '1'
201
201
  - !ruby/object:Gem::Dependency
202
202
  name: simplecov
203
203
  requirement: !ruby/object:Gem::Requirement
@@ -293,6 +293,7 @@ files:
293
293
  - README.md
294
294
  - app/assets/vendor/prism.css
295
295
  - app/assets/vendor/prism.min.js
296
+ - app/controllers/concerns/view_component/preview_actions.rb
296
297
  - app/controllers/view_components_controller.rb
297
298
  - app/helpers/preview_helper.rb
298
299
  - app/views/test_mailer/test_email.html.erb
@@ -333,9 +334,7 @@ files:
333
334
  - lib/view_component/docs_builder_component.html.erb
334
335
  - lib/view_component/docs_builder_component.rb
335
336
  - lib/view_component/engine.rb
336
- - lib/view_component/global_output_buffer.rb
337
337
  - lib/view_component/instrumentation.rb
338
- - lib/view_component/output_buffer_stack.rb
339
338
  - lib/view_component/polymorphic_slots.rb
340
339
  - lib/view_component/preview.rb
341
340
  - lib/view_component/preview_template_error.rb
@@ -344,6 +343,7 @@ files:
344
343
  - lib/view_component/render_component_helper.rb
345
344
  - lib/view_component/render_component_to_string_helper.rb
346
345
  - lib/view_component/render_monkey_patch.rb
346
+ - lib/view_component/render_preview_helper.rb
347
347
  - lib/view_component/render_to_string_monkey_patch.rb
348
348
  - lib/view_component/rendering_component_helper.rb
349
349
  - lib/view_component/rendering_monkey_patch.rb
@@ -1,99 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ViewComponent
4
- module GlobalOutputBuffer
5
- def render_in(view_context, &block)
6
- unless view_context.output_buffer.is_a?(OutputBufferStack)
7
- # use instance_variable_set here to avoid triggering the code in the #output_buffer= method below
8
- view_context.instance_variable_set(:@output_buffer, OutputBufferStack.new(view_context.output_buffer))
9
- end
10
-
11
- @output_buffer = view_context.output_buffer
12
- @global_buffer_in_use = true
13
-
14
- super(view_context, &block)
15
- end
16
-
17
- def perform_render
18
- # HAML unhelpfully assigns to @output_buffer directly, so we hold onto a reference to
19
- # it and restore @output_buffer when the HAML engine is finished. In non-HAML cases,
20
- # @output_buffer and orig_buf will point to the same object, making the reassignment
21
- # statements no-ops.
22
- orig_buf = @output_buffer
23
- @output_buffer.push
24
- result = render_template_for(@__vc_variant).to_s + _output_postamble
25
- @output_buffer = orig_buf
26
- @output_buffer.pop
27
- result
28
- end
29
-
30
- def output_buffer=(other_buffer)
31
- @output_buffer.replace(other_buffer)
32
- end
33
-
34
- def with_output_buffer(buf = nil)
35
- unless buf
36
- buf = ActionView::OutputBuffer.new
37
- if output_buffer && output_buffer.respond_to?(:encoding)
38
- buf.force_encoding(output_buffer.encoding)
39
- end
40
- end
41
-
42
- output_buffer.push(buf)
43
- result = nil
44
-
45
- begin
46
- yield
47
- ensure
48
- # assign result here to avoid a return statement, which will
49
- # immediately return to the caller and swallow any errors
50
- result = output_buffer.pop
51
- end
52
-
53
- result
54
- end
55
-
56
- module ActionViewMods
57
- def output_buffer=(other_buffer)
58
- if @output_buffer.is_a?(OutputBufferStack)
59
- @output_buffer.replace(other_buffer)
60
- else
61
- super
62
- end
63
- end
64
-
65
- def with_output_buffer(buf = nil)
66
- unless buf
67
- buf = ActionView::OutputBuffer.new
68
- if @output_buffer && @output_buffer.respond_to?(:encoding)
69
- buf.force_encoding(@output_buffer.encoding)
70
- end
71
- end
72
-
73
- result = nil
74
-
75
- if @output_buffer.is_a?(OutputBufferStack)
76
- @output_buffer.push(buf)
77
-
78
- begin
79
- yield
80
- ensure
81
- result = @output_buffer.pop
82
- end
83
-
84
- result
85
- else
86
- @output_buffer, old_buffer = buf, output_buffer
87
-
88
- begin
89
- yield
90
- ensure
91
- @output_buffer = old_buffer
92
- end
93
-
94
- buf
95
- end
96
- end
97
- end
98
- end
99
- end
@@ -1,67 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ViewComponent
4
- class OutputBufferStack
5
- delegate_missing_to :@current_buffer
6
- delegate :presence, :present?, :html_safe?, to: :@current_buffer
7
-
8
- attr_reader :buffer_stack
9
-
10
- def self.make_frame(*args)
11
- ActionView::OutputBuffer.new(*args)
12
- end
13
-
14
- def initialize(initial_buffer = nil)
15
- if initial_buffer.is_a?(self.class)
16
- @current_buffer = self.class.make_frame(initial_buffer.current)
17
- @buffer_stack = [*initial_buffer.buffer_stack[0..-2], @current_buffer]
18
- else
19
- @current_buffer = initial_buffer || self.class.make_frame
20
- @buffer_stack = [@current_buffer]
21
- end
22
- end
23
-
24
- def replace(buffer)
25
- return if self == buffer
26
-
27
- @current_buffer = buffer.current
28
- @buffer_stack = buffer.buffer_stack
29
- end
30
-
31
- def append=(arg)
32
- @current_buffer.append = arg
33
- end
34
-
35
- def safe_append=(arg)
36
- @current_buffer.safe_append = arg
37
- end
38
-
39
- def safe_concat(arg)
40
- # rubocop:disable Rails/OutputSafety
41
- @current_buffer.safe_concat(arg)
42
- # rubocop:enable Rails/OutputSafety
43
- end
44
-
45
- def length
46
- @current_buffer.length
47
- end
48
-
49
- def push(buffer = nil)
50
- buffer ||= self.class.make_frame
51
- @buffer_stack.push(buffer)
52
- @current_buffer = buffer
53
- end
54
-
55
- def pop
56
- @buffer_stack.pop.tap do
57
- @current_buffer = @buffer_stack.last
58
- end
59
- end
60
-
61
- def to_s
62
- @current_buffer
63
- end
64
-
65
- alias_method :current, :to_s
66
- end
67
- end