keynote 1.1.1 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGELOG.md +71 -27
- data/{LICENSE → LICENSE.txt} +2 -6
- data/README.md +119 -47
- data/lib/generators/presenter_generator.rb +16 -24
- data/lib/keynote/cache.rb +1 -1
- data/lib/keynote/controller.rb +4 -5
- data/lib/keynote/core.rb +64 -0
- data/lib/keynote/helper.rb +4 -5
- data/lib/keynote/inline.rb +51 -109
- data/lib/keynote/presenter.rb +13 -19
- data/lib/keynote/railtie.rb +7 -24
- data/lib/keynote/rumble.rb +21 -12
- data/lib/keynote/testing/{minitest_rails.rb → minitest.rb} +1 -2
- data/lib/keynote/testing/rspec.rb +5 -5
- data/lib/keynote/testing/test_present_method.rb +2 -2
- data/lib/keynote/testing/test_unit.rb +1 -1
- data/lib/keynote/version.rb +2 -2
- data/lib/keynote.rb +7 -65
- metadata +68 -96
- data/.editorconfig +0 -14
- data/.gitignore +0 -18
- data/.travis.yml +0 -11
- data/.yardopts +0 -4
- data/Gemfile +0 -4
- data/Rakefile +0 -21
- data/keynote.gemspec +0 -37
- data/lib/generators/templates/keynote_mini_test_unit.rb +0 -11
- data/lib/keynote/testing/minitest_rails.rake +0 -7
- data/scenarios/rails31.docker-compose.yml +0 -15
- data/scenarios/rails31.dockerfile +0 -5
- data/scenarios/rails31.gemfile +0 -7
- data/scenarios/rails32.docker-compose.yml +0 -15
- data/scenarios/rails32.dockerfile +0 -5
- data/scenarios/rails32.gemfile +0 -6
- data/scenarios/rails40.docker-compose.yml +0 -15
- data/scenarios/rails40.dockerfile +0 -5
- data/scenarios/rails40.gemfile +0 -6
- data/scenarios/rails41.docker-compose.yml +0 -15
- data/scenarios/rails41.dockerfile +0 -5
- data/scenarios/rails41.gemfile +0 -6
- data/scenarios/rails42.docker-compose.yml +0 -15
- data/scenarios/rails42.dockerfile +0 -5
- data/scenarios/rails42.gemfile +0 -6
- data/scenarios/rails50.docker-compose.yml +0 -15
- data/scenarios/rails50.dockerfile +0 -5
- data/scenarios/rails50.gemfile +0 -6
- data/scenarios/rails51.docker-compose.yml +0 -15
- data/scenarios/rails51.dockerfile +0 -5
- data/scenarios/rails51.gemfile +0 -6
- data/scenarios.yml +0 -25
- data/spec/benchmarks.rb +0 -73
- data/spec/generator_spec.rb +0 -159
- data/spec/helper.rb +0 -90
- data/spec/inline_spec.rb +0 -157
- data/spec/keynote_spec.rb +0 -130
- data/spec/presenter_spec.rb +0 -179
- data/spec/railtie_spec.rb +0 -33
- data/spec/rumble_spec.rb +0 -277
- data/spec/test_case_spec.rb +0 -12
- /data/lib/generators/templates/{keynote_mini_test_spec.rb → keynote_mini_test_spec.rb.tt} +0 -0
- /data/lib/generators/templates/{keynote_test_unit.rb → keynote_mini_test_unit.rb.tt} +0 -0
- /data/lib/generators/templates/{keynote_presenter.rb → keynote_presenter.rb.tt} +0 -0
- /data/lib/generators/templates/{keynote_rspec.rb → keynote_rspec.rb.tt} +0 -0
data/lib/keynote/inline.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
|
-
#
|
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
|
15
|
-
#
|
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
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
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
|
-
#
|
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
|
35
|
+
# def with_locals
|
36
36
|
# x = 1
|
37
37
|
# y = 2
|
38
38
|
#
|
39
|
-
# erb
|
40
|
-
#
|
41
|
-
#
|
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
|
87
|
-
#
|
88
|
-
#
|
89
|
-
#
|
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
|
97
|
-
|
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,
|
121
|
+
def initialize(presenter, locals, loc, source, format)
|
111
122
|
@presenter = presenter
|
112
|
-
@locals =
|
113
|
-
@template = Cache.fetch(
|
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
|
-
|
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
|
158
|
-
new_mtime
|
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 =
|
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}/,
|
183
|
+
text.gsub(/^#{margin}/, " " * left_padding)
|
207
184
|
end
|
208
185
|
end
|
209
186
|
|
210
187
|
# @private
|
211
|
-
class
|
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
|
data/lib/keynote/presenter.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
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
|
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(
|
78
|
-
Keynote.present(@view,
|
75
|
+
def present(...)
|
76
|
+
Keynote.present(@view, ...)
|
79
77
|
end
|
80
|
-
|
78
|
+
alias_method :k, :present
|
81
79
|
|
82
80
|
# @private
|
83
81
|
def inspect
|
84
82
|
objects = self.class.object_names
|
85
|
-
render
|
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} — #{h blog_post.title}".html_safe
|
106
104
|
# end
|
107
105
|
# end
|
108
|
-
def method_missing(method_name,
|
106
|
+
def method_missing(method_name, ...)
|
109
107
|
if @view.respond_to?(method_name, true)
|
110
|
-
@view.send(method_name,
|
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(
|
128
|
-
@view.capture(*args, &block)
|
129
|
-
end
|
123
|
+
def capture(...) = @view.capture(...)
|
130
124
|
end
|
131
125
|
end
|
data/lib/keynote/railtie.rb
CHANGED
@@ -1,12 +1,11 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
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
|
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
|
-
|
49
|
-
|
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
|
data/lib/keynote/rumble.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
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
|
-
|
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
|
296
|
-
|
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
|
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('"'
|
356
|
+
res << " #{name}=\"#{value.gsub('"', """)}\""
|
348
357
|
res
|
349
358
|
end
|
350
359
|
end
|
@@ -1,10 +1,9 @@
|
|
1
|
-
#
|
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
|
-
#
|
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
|
-
:
|
20
|
-
:
|
19
|
+
type: :presenter,
|
20
|
+
file_path: %r{spec.presenters}
|
21
21
|
else
|
22
22
|
config.include Keynote::ExampleGroup,
|
23
|
-
:
|
24
|
-
:
|
23
|
+
type: :presenter,
|
24
|
+
example_group: {file_path: %r{spec.presenters}}
|
25
25
|
end
|
26
26
|
end
|
data/lib/keynote/version.rb
CHANGED
data/lib/keynote.rb
CHANGED
@@ -1,73 +1,15 @@
|
|
1
|
-
#
|
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
|