keynote 2.0.2 → 2.0.3
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.
- checksums.yaml +4 -4
- data/lib/.rbnext/3.1/keynote/inline.rb +207 -0
- data/lib/.rbnext/3.4/keynote/presenter.rb +125 -0
- data/lib/keynote/version.rb +1 -1
- metadata +5 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4a0f6c0a47e0115c3af5240175cf1f4c357df31666aef44cfe7b3f1a546e1114
|
4
|
+
data.tar.gz: edc7a75afe4ddda3928ea5fdd70d642ca1fe08cc76e7448dba3f209b10717820
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 032c27e729b093dd88685231e9b3b89ba23a0340fb3f09e4fd47be0d3716c80de0061fa668579103e433d93a35e6eb449f86d79a83745355761d34b3ab2f1cf5
|
7
|
+
data.tar.gz: da2b02ca64b35543003b90d94ccaf47460fe4567fb47cba06433fbfaaad9adf3431e6adb65414448bbf91b1a74801a410cf6fd0f84173350e4bae3f62d0b5c51
|
@@ -0,0 +1,207 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
|
3
|
+
require "action_view"
|
4
|
+
|
5
|
+
module Keynote
|
6
|
+
# The `Inline` mixin lets you write inline templates as comments inside the
|
7
|
+
# body of a presenter method. You can use any template language supported by
|
8
|
+
# Rails.
|
9
|
+
#
|
10
|
+
# ## Basic usage
|
11
|
+
#
|
12
|
+
# After extending the `Keynote::Inline` module in your presenter class, you
|
13
|
+
# can generate HTML by calling the `erb` method and passing a template as a string
|
14
|
+
# to it:
|
15
|
+
#
|
16
|
+
# def link
|
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
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# Calling this method renders the ERB template, including passing the calls
|
28
|
+
# to `link_to`, `user_url`, `current_user`, and `image_tag` back to the
|
29
|
+
# presenter object (and then to the view).
|
30
|
+
#
|
31
|
+
# ## Passing variables
|
32
|
+
#
|
33
|
+
# You can pass locals as keyword arguments to the `erb` method:
|
34
|
+
#
|
35
|
+
# def with_locals
|
36
|
+
# x = 1
|
37
|
+
# y = 2
|
38
|
+
#
|
39
|
+
# erb(x:, y:) do
|
40
|
+
# %q(<%= x + y %>)
|
41
|
+
# end
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# ## The `inline` method
|
45
|
+
#
|
46
|
+
# If you want to use template languages other than ERB, you have to define
|
47
|
+
# methods for them by calling the {Keynote::Inline#inline} method on a
|
48
|
+
# presenter class:
|
49
|
+
#
|
50
|
+
# class MyPresenter < Keynote::Presenter
|
51
|
+
# extend Keynote::Inline
|
52
|
+
# presents :user, :account
|
53
|
+
# inline :haml
|
54
|
+
# end
|
55
|
+
#
|
56
|
+
# This defines a `#haml` instance method on the `MyPresenter` class.
|
57
|
+
#
|
58
|
+
# If you want to make inline templates available to all of your presenters,
|
59
|
+
# you can add an initializer like this to your application:
|
60
|
+
#
|
61
|
+
# class Keynote::Presenter
|
62
|
+
# extend Keynote::Inline
|
63
|
+
# inline :haml, :slim
|
64
|
+
# end
|
65
|
+
#
|
66
|
+
# This will add `#erb`, `#haml`, and `#slim` instance methods to all of your
|
67
|
+
# presenters.
|
68
|
+
module Inline
|
69
|
+
# For each template format given as a parameter, add an instance method
|
70
|
+
# that can be called to render an inline template in that format. Any
|
71
|
+
# file extension supported by Rails is a valid parameter.
|
72
|
+
# @example
|
73
|
+
# class UserPresenter < Keynote::Presenter
|
74
|
+
# presents :user
|
75
|
+
# inline :haml
|
76
|
+
#
|
77
|
+
# def header
|
78
|
+
# full_name = "#{user.first_name} #{user.last_name}"
|
79
|
+
#
|
80
|
+
# haml(full_name:) do
|
81
|
+
# <<~HAML
|
82
|
+
# div#header
|
83
|
+
# h1= full_name
|
84
|
+
# h3= user.most_recent_status
|
85
|
+
# HAML
|
86
|
+
# end
|
87
|
+
# end
|
88
|
+
# end
|
89
|
+
def inline(*formats)
|
90
|
+
require "action_view/context"
|
91
|
+
|
92
|
+
Array(formats).each do |format|
|
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
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# Extending `Keynote::Inline` automatically creates an `erb` method on the
|
101
|
+
# base class.
|
102
|
+
def self.extended(base)
|
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
|
117
|
+
end
|
118
|
+
|
119
|
+
# @private
|
120
|
+
class Renderer
|
121
|
+
def initialize(presenter, locals, loc, source, format)
|
122
|
+
@presenter = presenter
|
123
|
+
@locals = locals
|
124
|
+
@template = Cache.fetch(loc.path, loc.lineno, source, format, locals)
|
125
|
+
end
|
126
|
+
|
127
|
+
def render
|
128
|
+
@template.render(@presenter, @locals)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# @private
|
133
|
+
class Cache
|
134
|
+
def self.fetch(source_file, line, source, format, locals)
|
135
|
+
instance = (Thread.current[:_keynote_template_cache] ||= Cache.new)
|
136
|
+
instance.fetch(source_file, line, source, format, locals)
|
137
|
+
end
|
138
|
+
|
139
|
+
def self.reset
|
140
|
+
Thread.current[:_keynote_template_cache] = nil
|
141
|
+
end
|
142
|
+
|
143
|
+
def initialize
|
144
|
+
@cache = {}
|
145
|
+
end
|
146
|
+
|
147
|
+
def fetch(source_file, line, source, format, locals)
|
148
|
+
local_names = locals.keys.sort
|
149
|
+
cache_key = ["#{source_file}:#{line}", *local_names].freeze
|
150
|
+
new_mtime = File.mtime(source_file).to_f
|
151
|
+
|
152
|
+
template, mtime = @cache[cache_key]
|
153
|
+
|
154
|
+
if new_mtime != mtime
|
155
|
+
source = unindent(source.chomp)
|
156
|
+
template = Template.new(source, cache_key[0],
|
157
|
+
handler_for_format(format), format: format, locals: local_names)
|
158
|
+
|
159
|
+
@cache[cache_key] = [template, new_mtime]
|
160
|
+
end
|
161
|
+
|
162
|
+
template
|
163
|
+
end
|
164
|
+
|
165
|
+
private
|
166
|
+
|
167
|
+
def handler_for_format(format)
|
168
|
+
ActionView::Template.handler_for_extension(format.to_s)
|
169
|
+
end
|
170
|
+
|
171
|
+
# Borrowed from Pry, which borrowed it from Python.
|
172
|
+
def unindent(text, left_padding = 0)
|
173
|
+
margin = text.scan(/^[ \t]*(?=[^ \t\n])/).inject do |current_margin, next_indent|
|
174
|
+
if next_indent.start_with?(current_margin)
|
175
|
+
current_margin
|
176
|
+
elsif current_margin.start_with?(next_indent)
|
177
|
+
next_indent
|
178
|
+
else
|
179
|
+
""
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
text.gsub(/^#{margin}/, " " * left_padding)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
# @private
|
188
|
+
class Template < ActionView::Template
|
189
|
+
# The only difference between this #compile! and the normal one is that
|
190
|
+
# we call `view.class` instead of `view.singleton_class`, so that the
|
191
|
+
# template method gets defined as an instance method on the presenter
|
192
|
+
# and therefore sticks around between presenter instances.
|
193
|
+
def compile!(view)
|
194
|
+
return if @compiled
|
195
|
+
|
196
|
+
@compile_mutex.synchronize do
|
197
|
+
return if @compiled
|
198
|
+
|
199
|
+
compile(view.class)
|
200
|
+
|
201
|
+
@source = nil if defined?(@virtual_path) && @virtual_path
|
202
|
+
@compiled = true
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Keynote
|
4
|
+
# Keynote::Presenter is a base class for presenters, objects that encapsulate
|
5
|
+
# view logic.
|
6
|
+
#
|
7
|
+
# @see file:README.md
|
8
|
+
class Presenter
|
9
|
+
include Keynote::Rumble
|
10
|
+
|
11
|
+
class << self
|
12
|
+
attr_writer :object_names
|
13
|
+
|
14
|
+
# Define the names and number of the objects presented by this class.
|
15
|
+
# This replaces the default one-parameter constructor with one that takes
|
16
|
+
# an extra parameter for each presented object.
|
17
|
+
#
|
18
|
+
# @param [Array<Symbol>] objects One symbol for each of the models that
|
19
|
+
# will be required to instantiate this presenter. Each symbol will be
|
20
|
+
# used as the accessor and instance variable name for its associated
|
21
|
+
# parameter, but an object of any class can be given for any parameter.
|
22
|
+
#
|
23
|
+
# @example
|
24
|
+
# class PostPresenter
|
25
|
+
# presents :blog_post, :author
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# # In a view
|
29
|
+
# presenter = k(:post, @some_post, @some_user)
|
30
|
+
# presenter.blog_post # == @some_post
|
31
|
+
# presenter.author # == @some_user
|
32
|
+
#
|
33
|
+
def presents(*objects)
|
34
|
+
self.object_names = objects.dup
|
35
|
+
|
36
|
+
objects.unshift :view
|
37
|
+
attr_reader(*objects)
|
38
|
+
|
39
|
+
param_list = objects.join(", ")
|
40
|
+
ivar_list = objects.map { it = _1;"@#{it}" }.join(", ")
|
41
|
+
|
42
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
43
|
+
def initialize(#{param_list}) # def initialize(view, foo)
|
44
|
+
#{ivar_list} = #{param_list} # @view, @foo = view, foo
|
45
|
+
end # end
|
46
|
+
RUBY
|
47
|
+
end
|
48
|
+
|
49
|
+
# Define a more complete set of HTML5 tag methods. The extra tags are
|
50
|
+
# listed in {Keynote::Rumble::COMPLETE}.
|
51
|
+
def use_html_5_tags = Rumble.use_html_5_tags(self)
|
52
|
+
|
53
|
+
# List the object names this presenter wraps. The default is an empty
|
54
|
+
# array; calling `presents :foo, :bar` in the presenter's class body will
|
55
|
+
# cause `object_names` to return `[:foo, :bar]`.
|
56
|
+
# @return [Array<Symbol>]
|
57
|
+
def object_names
|
58
|
+
@object_names ||= []
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# @private (used by Keynote::Cache to keep the view context up-to-date)
|
63
|
+
attr_writer :view
|
64
|
+
|
65
|
+
# Create a presenter. The default constructor takes one parameter, but
|
66
|
+
# calling `presents` replaces it with a generated constructor.
|
67
|
+
# @param [ActionView::Base] view_context
|
68
|
+
# @see Keynote::Presenter.presents
|
69
|
+
def initialize(view_context)
|
70
|
+
@view = view_context
|
71
|
+
end
|
72
|
+
|
73
|
+
# Instantiate another presenter.
|
74
|
+
# @see Keynote.present
|
75
|
+
def present(...)
|
76
|
+
Keynote.present(@view, ...)
|
77
|
+
end
|
78
|
+
alias_method :k, :present
|
79
|
+
|
80
|
+
# @private
|
81
|
+
def inspect
|
82
|
+
objects = self.class.object_names
|
83
|
+
render = proc { |name| "#{name}: #{send(name).inspect}" }
|
84
|
+
|
85
|
+
if objects.any?
|
86
|
+
"#<#{self.class} #{objects.map(&render).join(", ")}>"
|
87
|
+
else
|
88
|
+
"#<#{self.class}>"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# @private
|
93
|
+
def respond_to_missing?(method_name, include_private = true)
|
94
|
+
@view.respond_to?(method_name, true)
|
95
|
+
end
|
96
|
+
|
97
|
+
# Presenters proxy unknown method calls to the view context, allowing you
|
98
|
+
# to call `h`, `link_to`, and anything else defined in a helper module.
|
99
|
+
#
|
100
|
+
# @example
|
101
|
+
# def title_link
|
102
|
+
# link_to blog_post_url(blog_post) do
|
103
|
+
# "#{h author.name} — #{h blog_post.title}".html_safe
|
104
|
+
# end
|
105
|
+
# end
|
106
|
+
def method_missing(method_name, ...)
|
107
|
+
if @view.respond_to?(method_name, true)
|
108
|
+
@view.send(method_name, ...)
|
109
|
+
else
|
110
|
+
super
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# @private
|
115
|
+
# We have to make a logger method available so that ActionView::Template
|
116
|
+
# can safely treat a presenter as a view object.
|
117
|
+
def logger = Rails.logger
|
118
|
+
|
119
|
+
private
|
120
|
+
|
121
|
+
# We have to explicitly proxy `#capture` because ActiveSupport creates a
|
122
|
+
# `Kernel#capture` method.
|
123
|
+
def capture(...) = @view.capture(...)
|
124
|
+
end
|
125
|
+
end
|
data/lib/keynote/version.rb
CHANGED
metadata
CHANGED
@@ -1,15 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: keynote
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryan Fitzgerald
|
8
8
|
- Vladimir Dementyev
|
9
|
-
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
11
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: actionview
|
@@ -207,6 +206,8 @@ files:
|
|
207
206
|
- CHANGELOG.md
|
208
207
|
- LICENSE.txt
|
209
208
|
- README.md
|
209
|
+
- lib/.rbnext/3.1/keynote/inline.rb
|
210
|
+
- lib/.rbnext/3.4/keynote/presenter.rb
|
210
211
|
- lib/generators/presenter_generator.rb
|
211
212
|
- lib/generators/templates/keynote_mini_test_spec.rb.tt
|
212
213
|
- lib/generators/templates/keynote_mini_test_unit.rb.tt
|
@@ -235,7 +236,6 @@ metadata:
|
|
235
236
|
documentation_uri: https://rubydoc.info/gems/keynote
|
236
237
|
homepage_uri: https://github.com/evilmartians/keynote
|
237
238
|
source_code_uri: https://github.com/evilmartians/keynote
|
238
|
-
post_install_message:
|
239
239
|
rdoc_options: []
|
240
240
|
require_paths:
|
241
241
|
- lib
|
@@ -250,8 +250,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
250
250
|
- !ruby/object:Gem::Version
|
251
251
|
version: '0'
|
252
252
|
requirements: []
|
253
|
-
rubygems_version: 3.
|
254
|
-
signing_key:
|
253
|
+
rubygems_version: 3.6.9
|
255
254
|
specification_version: 4
|
256
255
|
summary: Flexible presenters for Rails
|
257
256
|
test_files: []
|