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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '080b3677d432939095ffd3087facd1ffd826ed3b842985109d1719bc9e183b51'
4
- data.tar.gz: 9fa73d9dd1301f7518058a550a588bc7bbd1c5e67ace7254f5d1508998e9f16b
3
+ metadata.gz: 4a0f6c0a47e0115c3af5240175cf1f4c357df31666aef44cfe7b3f1a546e1114
4
+ data.tar.gz: edc7a75afe4ddda3928ea5fdd70d642ca1fe08cc76e7448dba3f209b10717820
5
5
  SHA512:
6
- metadata.gz: 3f3ff1f02d8ccf004d3a735855a186ae3a115dcf46452896cbbfde2a10f46690527eaa5192a102514f2323dc5a8994bdbd11134bf1eebcb697299437122005b5
7
- data.tar.gz: 42a785dd6adc4eea5547feb515f0dc9e9c1007380571ea81b6b2ea13d18b9ed1f239bf2dbbe150c1f32a421564aacf80021bdf160042f3134b115626b0a5b2bc
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} &mdash; #{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
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Keynote
4
4
  # @private
5
- VERSION = "2.0.2"
5
+ VERSION = "2.0.3"
6
6
  end
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.2
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: 2025-01-13 00:00:00.000000000 Z
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.5.22
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: []