view_component 2.52.0 → 2.62.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.

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