keynote 1.1.1 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +71 -27
  3. data/{LICENSE → LICENSE.txt} +2 -6
  4. data/README.md +120 -47
  5. data/lib/generators/presenter_generator.rb +16 -24
  6. data/lib/keynote/cache.rb +1 -1
  7. data/lib/keynote/controller.rb +4 -5
  8. data/lib/keynote/core.rb +64 -0
  9. data/lib/keynote/helper.rb +4 -5
  10. data/lib/keynote/inline.rb +51 -109
  11. data/lib/keynote/presenter.rb +13 -19
  12. data/lib/keynote/railtie.rb +7 -24
  13. data/lib/keynote/rumble.rb +21 -12
  14. data/lib/keynote/testing/{minitest_rails.rb → minitest.rb} +1 -2
  15. data/lib/keynote/testing/rspec.rb +5 -5
  16. data/lib/keynote/testing/test_present_method.rb +2 -2
  17. data/lib/keynote/testing/test_unit.rb +1 -1
  18. data/lib/keynote/version.rb +2 -2
  19. data/lib/keynote.rb +7 -65
  20. metadata +80 -94
  21. data/.editorconfig +0 -14
  22. data/.gitignore +0 -18
  23. data/.travis.yml +0 -11
  24. data/.yardopts +0 -4
  25. data/Gemfile +0 -4
  26. data/Rakefile +0 -21
  27. data/keynote.gemspec +0 -37
  28. data/lib/generators/templates/keynote_mini_test_unit.rb +0 -11
  29. data/lib/keynote/testing/minitest_rails.rake +0 -7
  30. data/scenarios/rails31.docker-compose.yml +0 -15
  31. data/scenarios/rails31.dockerfile +0 -5
  32. data/scenarios/rails31.gemfile +0 -7
  33. data/scenarios/rails32.docker-compose.yml +0 -15
  34. data/scenarios/rails32.dockerfile +0 -5
  35. data/scenarios/rails32.gemfile +0 -6
  36. data/scenarios/rails40.docker-compose.yml +0 -15
  37. data/scenarios/rails40.dockerfile +0 -5
  38. data/scenarios/rails40.gemfile +0 -6
  39. data/scenarios/rails41.docker-compose.yml +0 -15
  40. data/scenarios/rails41.dockerfile +0 -5
  41. data/scenarios/rails41.gemfile +0 -6
  42. data/scenarios/rails42.docker-compose.yml +0 -15
  43. data/scenarios/rails42.dockerfile +0 -5
  44. data/scenarios/rails42.gemfile +0 -6
  45. data/scenarios/rails50.docker-compose.yml +0 -15
  46. data/scenarios/rails50.dockerfile +0 -5
  47. data/scenarios/rails50.gemfile +0 -6
  48. data/scenarios/rails51.docker-compose.yml +0 -15
  49. data/scenarios/rails51.dockerfile +0 -5
  50. data/scenarios/rails51.gemfile +0 -6
  51. data/scenarios.yml +0 -25
  52. data/spec/benchmarks.rb +0 -73
  53. data/spec/generator_spec.rb +0 -159
  54. data/spec/helper.rb +0 -90
  55. data/spec/inline_spec.rb +0 -157
  56. data/spec/keynote_spec.rb +0 -130
  57. data/spec/presenter_spec.rb +0 -179
  58. data/spec/railtie_spec.rb +0 -33
  59. data/spec/rumble_spec.rb +0 -277
  60. data/spec/test_case_spec.rb +0 -12
  61. /data/lib/generators/templates/{keynote_mini_test_spec.rb → keynote_mini_test_spec.rb.tt} +0 -0
  62. /data/lib/generators/templates/{keynote_test_unit.rb → keynote_mini_test_unit.rb.tt} +0 -0
  63. /data/lib/generators/templates/{keynote_presenter.rb → keynote_presenter.rb.tt} +0 -0
  64. /data/lib/generators/templates/{keynote_rspec.rb → keynote_rspec.rb.tt} +0 -0
@@ -1,7 +1,6 @@
1
- # encoding: UTF-8
1
+ # frozen_string_literal: false
2
2
 
3
3
  require "action_view"
4
- require "thread"
5
4
 
6
5
  module Keynote
7
6
  # The `Inline` mixin lets you write inline templates as comments inside the
@@ -11,15 +10,18 @@ module Keynote
11
10
  # ## Basic usage
12
11
  #
13
12
  # After extending the `Keynote::Inline` module in your presenter class, you
14
- # can generate HTML by calling the `erb` method and immediately following it
15
- # with a block of comments containing your template:
13
+ # can generate HTML by calling the `erb` method and passing a template as a string
14
+ # to it:
16
15
  #
17
16
  # def link
18
- # erb
19
- # # <%= link_to user_url(current_user) do %>
20
- # # <%= image_tag("image1.jpg") %>
21
- # # <%= image_tag("image2.jpg") %>
22
- # # <% end %>
17
+ # erb do
18
+ # <<~ERB
19
+ # <%= link_to user_url(current_user) do %>
20
+ # <%= image_tag("image1.jpg") %>
21
+ # <%= image_tag("image2.jpg") %>
22
+ # <% end %>
23
+ # ERB
24
+ # end
23
25
  # end
24
26
  #
25
27
  # Calling this method renders the ERB template, including passing the calls
@@ -28,23 +30,15 @@ module Keynote
28
30
  #
29
31
  # ## Passing variables
30
32
  #
31
- # There are a couple of different ways to pass local variables into an inline
32
- # template. The easiest is to pass the `binding` object into the template
33
- # method, giving access to all local variables:
33
+ # You can pass locals as keyword arguments to the `erb` method:
34
34
  #
35
- # def local_binding
35
+ # def with_locals
36
36
  # x = 1
37
37
  # y = 2
38
38
  #
39
- # erb binding
40
- # # <%= x + y %>
41
- # end
42
- #
43
- # You can also pass a hash of variable names and values instead:
44
- #
45
- # def local_binding
46
- # erb x: 1, y: 2
47
- # # <%= x + y %>
39
+ # erb(x:, y:) do
40
+ # %q(<%= x + y %>)
41
+ # end
48
42
  # end
49
43
  #
50
44
  # ## The `inline` method
@@ -83,18 +77,22 @@ module Keynote
83
77
  # def header
84
78
  # full_name = "#{user.first_name} #{user.last_name}"
85
79
  #
86
- # haml binding
87
- # # div#header
88
- # # h1= full_name
89
- # # h3= user.most_recent_status
80
+ # haml(full_name:) do
81
+ # <<~HAML
82
+ # div#header
83
+ # h1= full_name
84
+ # h3= user.most_recent_status
85
+ # HAML
86
+ # end
90
87
  # end
91
88
  # end
92
89
  def inline(*formats)
93
90
  require "action_view/context"
94
91
 
95
92
  Array(formats).each do |format|
96
- define_method format do |locals = {}|
97
- Renderer.new(self, locals, caller(1)[0], format).render
93
+ define_method format do |**locals, &block|
94
+ raise ArgumentError, "You must pass a block to the ##{format} method" unless block
95
+ Renderer.new(self, locals, caller_locations(1, 1).first, block.call, format).render
98
96
  end
99
97
  end
100
98
  end
@@ -103,45 +101,39 @@ module Keynote
103
101
  # base class.
104
102
  def self.extended(base)
105
103
  base.inline :erb
104
+ base.include InstanceMethods
105
+ end
106
+
107
+ module InstanceMethods
108
+ # A callback for Action View: https://github.com/rails/rails/blob/a32eab53a58540b9ca57da429c5f64564db43126/actionview/lib/action_view/base.rb#L261
109
+ def _run(method, template, locals, buffer, add_to_stack: true, has_strict_locals: false, &block)
110
+ old_output_buffer, old_virtual_path, old_template = @output_buffer, @virtual_path, @current_template
111
+ @current_template = template if add_to_stack
112
+ @output_buffer = buffer
113
+ public_send(method, locals, buffer, &block)
114
+ ensure
115
+ @output_buffer, @virtual_path, @current_template = old_output_buffer, old_virtual_path, old_template
116
+ end
106
117
  end
107
118
 
108
119
  # @private
109
120
  class Renderer
110
- def initialize(presenter, locals, caller_line, format)
121
+ def initialize(presenter, locals, loc, source, format)
111
122
  @presenter = presenter
112
- @locals = extract_locals(locals)
113
- @template = Cache.fetch(*parse_caller(caller_line), format, @locals)
123
+ @locals = locals
124
+ @template = Cache.fetch(loc.path, loc.lineno, source, format, locals)
114
125
  end
115
126
 
116
127
  def render
117
128
  @template.render(@presenter, @locals)
118
129
  end
119
-
120
- private
121
-
122
- def extract_locals(locals)
123
- return locals unless locals.is_a?(Binding)
124
-
125
- Hash[locals.eval("local_variables").map do |local|
126
- [local, locals.eval(local.to_s)]
127
- end]
128
- end
129
-
130
- def parse_caller(caller_line)
131
- file, rest = caller_line.split ":", 2
132
- line, _ = rest.split " ", 2
133
-
134
- [file.strip, line.to_i]
135
- end
136
130
  end
137
131
 
138
132
  # @private
139
133
  class Cache
140
- COMMENTED_LINE = /^\s*#(.*)$/
141
-
142
- def self.fetch(source_file, line, format, locals)
134
+ def self.fetch(source_file, line, source, format, locals)
143
135
  instance = (Thread.current[:_keynote_template_cache] ||= Cache.new)
144
- instance.fetch(source_file, line, format, locals)
136
+ instance.fetch(source_file, line, source, format, locals)
145
137
  end
146
138
 
147
139
  def self.reset
@@ -152,18 +144,17 @@ module Keynote
152
144
  @cache = {}
153
145
  end
154
146
 
155
- def fetch(source_file, line, format, locals)
147
+ def fetch(source_file, line, source, format, locals)
156
148
  local_names = locals.keys.sort
157
- cache_key = ["#{source_file}:#{line}", *local_names].freeze
158
- new_mtime = File.mtime(source_file).to_f
149
+ cache_key = ["#{source_file}:#{line}", *local_names].freeze
150
+ new_mtime = File.mtime(source_file).to_f
159
151
 
160
152
  template, mtime = @cache[cache_key]
161
153
 
162
154
  if new_mtime != mtime
163
- source = read_template(source_file, line)
164
-
155
+ source = unindent(source.chomp)
165
156
  template = Template.new(source, cache_key[0],
166
- handler_for_format(format), locals: local_names)
157
+ handler_for_format(format), format:, locals: local_names)
167
158
 
168
159
  @cache[cache_key] = [template, new_mtime]
169
160
  end
@@ -173,20 +164,6 @@ module Keynote
173
164
 
174
165
  private
175
166
 
176
- def read_template(source_file, line_num)
177
- result = ""
178
-
179
- File.foreach(source_file).drop(line_num).each do |line|
180
- if line =~ COMMENTED_LINE
181
- result << $1 << "\n"
182
- else
183
- break
184
- end
185
- end
186
-
187
- unindent result.chomp
188
- end
189
-
190
167
  def handler_for_format(format)
191
168
  ActionView::Template.handler_for_extension(format.to_s)
192
169
  end
@@ -203,39 +180,12 @@ module Keynote
203
180
  end
204
181
  end
205
182
 
206
- text.gsub(/^#{margin}/, ' ' * left_padding)
183
+ text.gsub(/^#{margin}/, " " * left_padding)
207
184
  end
208
185
  end
209
186
 
210
187
  # @private
211
- class TemplateFor41AndLower < ActionView::Template
212
- # Older versions of Rails don't have this mutex, but we probably want it,
213
- # so let's make sure it's there.
214
- def initialize(*)
215
- super
216
- @compile_mutex = Mutex.new
217
- end
218
-
219
- # The only difference between this #compile! and the normal one is that
220
- # we call `view.class` instead of `view.singleton_class`, so that the
221
- # template method gets defined as an instance method on the presenter
222
- # and therefore sticks around between presenter instances.
223
- def compile!(view)
224
- return if @compiled
225
-
226
- @compile_mutex.synchronize do
227
- return if @compiled
228
-
229
- compile(view, view.class)
230
-
231
- @source = nil if defined?(@virtual_path) && @virtual_path
232
- @compiled = true
233
- end
234
- end
235
- end
236
-
237
- # @private
238
- class TemplateFor42AndHigher < ActionView::Template
188
+ class Template < ActionView::Template
239
189
  # The only difference between this #compile! and the normal one is that
240
190
  # we call `view.class` instead of `view.singleton_class`, so that the
241
191
  # template method gets defined as an instance method on the presenter
@@ -253,13 +203,5 @@ module Keynote
253
203
  end
254
204
  end
255
205
  end
256
-
257
- if Rails.version.to_f < 4.2
258
- # @private
259
- Template = TemplateFor41AndLower
260
- else
261
- # @private
262
- Template = TemplateFor42AndHigher
263
- end
264
206
  end
265
207
  end
@@ -1,4 +1,4 @@
1
- # encoding: UTF-8
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Keynote
4
4
  # Keynote::Presenter is a base class for presenters, objects that encapsulate
@@ -36,10 +36,10 @@ module Keynote
36
36
  objects.unshift :view
37
37
  attr_reader(*objects)
38
38
 
39
- param_list = objects.join(', ')
40
- ivar_list = objects.map { |o| "@#{o}" }.join(', ')
39
+ param_list = objects.join(", ")
40
+ ivar_list = objects.map { "@#{it}" }.join(", ")
41
41
 
42
- class_eval <<-RUBY
42
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
43
43
  def initialize(#{param_list}) # def initialize(view, foo)
44
44
  #{ivar_list} = #{param_list} # @view, @foo = view, foo
45
45
  end # end
@@ -48,9 +48,7 @@ module Keynote
48
48
 
49
49
  # Define a more complete set of HTML5 tag methods. The extra tags are
50
50
  # listed in {Keynote::Rumble::COMPLETE}.
51
- def use_html_5_tags
52
- Rumble.use_html_5_tags(self)
53
- end
51
+ def use_html_5_tags = Rumble.use_html_5_tags(self)
54
52
 
55
53
  # List the object names this presenter wraps. The default is an empty
56
54
  # array; calling `presents :foo, :bar` in the presenter's class body will
@@ -74,15 +72,15 @@ module Keynote
74
72
 
75
73
  # Instantiate another presenter.
76
74
  # @see Keynote.present
77
- def present(*objects, &blk)
78
- Keynote.present(@view, *objects, &blk)
75
+ def present(...)
76
+ Keynote.present(@view, ...)
79
77
  end
80
- alias k present
78
+ alias_method :k, :present
81
79
 
82
80
  # @private
83
81
  def inspect
84
82
  objects = self.class.object_names
85
- render = proc { |name| "#{name}: #{send(name).inspect}" }
83
+ render = proc { |name| "#{name}: #{send(name).inspect}" }
86
84
 
87
85
  if objects.any?
88
86
  "#<#{self.class} #{objects.map(&render).join(", ")}>"
@@ -105,9 +103,9 @@ module Keynote
105
103
  # "#{h author.name} &mdash; #{h blog_post.title}".html_safe
106
104
  # end
107
105
  # end
108
- def method_missing(method_name, *args, &block)
106
+ def method_missing(method_name, ...)
109
107
  if @view.respond_to?(method_name, true)
110
- @view.send(method_name, *args, &block)
108
+ @view.send(method_name, ...)
111
109
  else
112
110
  super
113
111
  end
@@ -116,16 +114,12 @@ module Keynote
116
114
  # @private
117
115
  # We have to make a logger method available so that ActionView::Template
118
116
  # can safely treat a presenter as a view object.
119
- def logger
120
- Rails.logger
121
- end
117
+ def logger = Rails.logger
122
118
 
123
119
  private
124
120
 
125
121
  # We have to explicitly proxy `#capture` because ActiveSupport creates a
126
122
  # `Kernel#capture` method.
127
- def capture(*args, &block)
128
- @view.capture(*args, &block)
129
- end
123
+ def capture(...) = @view.capture(...)
130
124
  end
131
125
  end
@@ -1,12 +1,11 @@
1
- # encoding: UTF-8
1
+ # frozen_string_literal: true
2
2
 
3
- require 'rails/railtie'
3
+ require "rails/railtie"
4
4
 
5
5
  module Keynote
6
6
  # @private
7
7
  class Railtie < Rails::Railtie
8
8
  config.after_initialize do |app|
9
- add_presenters_to_paths(app)
10
9
  load_test_integration
11
10
  end
12
11
 
@@ -22,31 +21,15 @@ module Keynote
22
21
  include Keynote::Controller
23
22
  end
24
23
 
25
- rake_tasks do
26
- if defined?(MiniTest::Rails)
27
- load File.expand_path("../testing/minitest_rails.rake", __FILE__)
28
- end
29
- end
30
-
31
- def self.add_presenters_to_paths(app)
32
- if ::Rails.version.to_f >= 4
33
- app.config.paths.add 'app/presenters'
34
- else
35
- app.config.paths.add 'app/presenters', :eager_load => true
36
- end
37
- end
38
-
39
24
  def self.load_test_integration
40
25
  if defined?(RSpec::Rails)
41
- require 'keynote/testing/rspec'
42
- end
43
-
44
- if defined?(MiniTest::Rails)
45
- require 'keynote/testing/minitest_rails'
26
+ require "keynote/testing/rspec"
46
27
  end
47
28
 
48
- if !defined?(MiniTest::Rails)
49
- require "keynote/testing/test_unit"
29
+ begin
30
+ ::ActionView::TestCase # rubocop:disable Lint/Void
31
+ require "keynote/testing/minitest"
32
+ rescue
50
33
  end
51
34
  end
52
35
  end
@@ -1,4 +1,4 @@
1
- # encoding: UTF-8
1
+ # frozen_string_literal: false
2
2
 
3
3
  module Keynote
4
4
  # HTML markup in Ruby.
@@ -189,7 +189,7 @@ module Keynote
189
189
  tags.each do |tag|
190
190
  sc = SELFCLOSING.include?(tag).inspect
191
191
 
192
- base.class_eval <<-RUBY
192
+ base.class_eval <<-RUBY, __FILE__, __LINE__ + 1
193
193
  def #{tag}(*args, &blk) # def a(*args, &blk)
194
194
  rumble_tag :#{tag}, #{sc}, *args, &blk # rumble_tag :a, false, *args, &blk
195
195
  end # end
@@ -243,17 +243,19 @@ module Keynote
243
243
  end
244
244
  end
245
245
 
246
+ def respond_to_missing?(name, include_private = false)
247
+ true
248
+ end
249
+
246
250
  def method_missing(name, content = nil, attrs = nil, &blk)
247
251
  name = name.to_s
248
252
 
249
- if name[-1] == ?!
253
+ if name[-1] == "!"
250
254
  attributes[:id] = name[0..-2]
255
+ elsif attributes.has_key?(:class)
256
+ attributes[:class] += " #{name}"
251
257
  else
252
- if attributes.has_key?(:class)
253
- attributes[:class] += " #{name}"
254
- else
255
- attributes[:class] = name
256
- end
258
+ attributes[:class] = name
257
259
  end
258
260
 
259
261
  insert(content, attrs, &blk)
@@ -292,8 +294,13 @@ module Keynote
292
294
  raise $!
293
295
  end
294
296
 
295
- def to_ary; nil end
296
- def to_str; to_s end
297
+ def to_ary
298
+ nil
299
+ end
300
+
301
+ def to_str
302
+ to_s
303
+ end
297
304
 
298
305
  def html_safe?
299
306
  true
@@ -313,7 +320,9 @@ module Keynote
313
320
  end
314
321
  end
315
322
 
316
- def inspect; to_s.inspect end
323
+ def inspect
324
+ to_s.inspect
325
+ end
317
326
 
318
327
  private
319
328
 
@@ -344,7 +353,7 @@ module Keynote
344
353
  Rumble.html_escape(value)
345
354
  end
346
355
 
347
- res << " #{name}=\"#{value.gsub('"'.freeze, '&quot;'.freeze)}\""
356
+ res << " #{name}=\"#{value.gsub('"', "&quot;")}\""
348
357
  res
349
358
  end
350
359
  end
@@ -1,10 +1,9 @@
1
- # encoding: UTF-8
1
+ # frozen_string_literal: true
2
2
 
3
3
  # Mutually exclusive with the Keynote::TestCase defined in
4
4
  # keynote/testing/test_unit. This is kind of icky but consistent with how
5
5
  # MT::R itself replaces the Rails test case classes.
6
6
 
7
- require "minitest/rails"
8
7
  require "keynote/testing/test_present_method"
9
8
 
10
9
  module Keynote
@@ -1,4 +1,4 @@
1
- # encoding: UTF-8
1
+ # frozen_string_literal: true
2
2
 
3
3
  require "rspec/core"
4
4
  require "keynote/testing/test_present_method"
@@ -16,11 +16,11 @@ end
16
16
  RSpec.configure do |config|
17
17
  if RSpec::Core::Version::STRING.starts_with?("3")
18
18
  config.include Keynote::ExampleGroup,
19
- :type => :presenter,
20
- :file_path => %r/spec.presenters/
19
+ type: :presenter,
20
+ file_path: %r{spec.presenters}
21
21
  else
22
22
  config.include Keynote::ExampleGroup,
23
- :type => :presenter,
24
- :example_group => { :file_path => %r/spec.presenters/ }
23
+ type: :presenter,
24
+ example_group: {file_path: %r{spec.presenters}}
25
25
  end
26
26
  end
@@ -1,10 +1,10 @@
1
- # encoding: UTF-8
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Keynote
4
4
  module TestPresentMethod
5
5
  def present(*objects, &blk)
6
6
  Keynote.present(view, *objects, &blk)
7
7
  end
8
- alias k present
8
+ alias_method :k, :present
9
9
  end
10
10
  end
@@ -1,4 +1,4 @@
1
- # encoding: UTF-8
1
+ # frozen_string_literal: true
2
2
 
3
3
  require "keynote/testing/test_present_method"
4
4
 
@@ -1,6 +1,6 @@
1
- # encoding: UTF-8
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Keynote
4
4
  # @private
5
- VERSION = "1.1.1"
5
+ VERSION = "2.0.1"
6
6
  end
data/lib/keynote.rb CHANGED
@@ -1,73 +1,15 @@
1
- # encoding: UTF-8
1
+ # frozen_string_literal: true
2
2
 
3
+ require "ruby-next"
4
+ require "ruby-next/language/setup"
5
+ RubyNext::Language.setup_gem_load_path(transpile: true)
6
+
7
+ require "keynote/core"
3
8
  require "keynote/version"
4
9
  require "keynote/rumble"
5
10
  require "keynote/inline"
6
11
  require "keynote/presenter"
7
12
  require "keynote/controller"
8
13
  require "keynote/helper"
9
- require "keynote/railtie"
14
+ require "keynote/railtie" if defined?(Rails::Railtie)
10
15
  require "keynote/cache"
11
-
12
- # Keynote is a library for defining and instantiating presenters,
13
- # objects that encapsulate view logic.
14
- #
15
- # @see file:README.md
16
- module Keynote
17
- class << self
18
- # Create or retrieve a presenter wrapping zero or more objects.
19
- # If a block is given, yield the presenter into it.
20
- #
21
- # The first parameter is a Rails view context, but you'll usually access
22
- # this method through `Keynote::Helper#present`,
23
- # `Keynote::Controller#present`, or `Keynote::Presenter#present`, all of
24
- # which handle the view context parameter automatically.
25
- #
26
- # @see Keynote::Helper#present
27
- # @see Keynote::Controller#present
28
- # @see Keynote::Presenter#present
29
- #
30
- # @overload present(view, *objects)
31
- # Return a presenter wrapping the given objects. The type of the
32
- # presenter will be inferred from the type of the first object.
33
- # @example
34
- # present(view, MyModel.new) # => #<MyModelPresenter:0x0001>
35
- # @param [ActionView::Base] view
36
- # @param [Array] objects
37
- # @return [Keynote::Presenter]
38
- #
39
- # @overload present(view, presenter_name, *objects)
40
- # Return a presenter wrapping the given objects. The type of the
41
- # presenter is specified in underscore form by the first parameter.
42
- # @example
43
- # present(view, :foo_bar, MyModel.new) # => #<FooBarPresenter:0x0002>
44
- # @param [ActionView::Base] view
45
- # @param [Symbol, String] presenter_name
46
- # @param [Array] objects
47
- # @return [Keynote::Presenter]
48
- #
49
- def present(view, *objects)
50
- if objects[0].is_a?(Symbol) || objects[0].is_a?(String)
51
- name = objects.shift
52
- else
53
- name = presenter_name_from_object(objects[0])
54
- end
55
-
56
- Cache.fetch(name, view, *objects) do
57
- presenter_from_name(name).new(view, *objects).tap do |presenter|
58
- yield presenter if block_given?
59
- end
60
- end
61
- end
62
-
63
- private
64
-
65
- def presenter_name_from_object(object)
66
- object.class.to_s.underscore
67
- end
68
-
69
- def presenter_from_name(name)
70
- "#{name.to_s.camelize}Presenter".constantize
71
- end
72
- end
73
- end