keynote 1.1.0 → 2.0.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.
Files changed (64) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +73 -25
  3. data/{LICENSE → LICENSE.txt} +2 -6
  4. data/README.md +119 -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 +22 -13
  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 +68 -96
  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 -271
  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
 
@@ -339,12 +348,12 @@ module Keynote
339
348
  if value.is_a?(Array)
340
349
  value.map { |val| Rumble.html_escape(val) }.join(" ")
341
350
  elsif value == true
342
- name
351
+ name.to_s
343
352
  else
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.0"
5
+ VERSION = "2.0.0"
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