props_template 0.14.0 → 0.20.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -34,6 +34,8 @@ module Props
34
34
 
35
35
  type, rest = options[:defer]
36
36
  placeholder = rest[:placeholder]
37
+ success_action = rest[:success_action]
38
+ fail_action = rest[:fail_action]
37
39
 
38
40
  if type.to_sym == :auto && options[:key]
39
41
  key, val = options[:key]
@@ -50,11 +52,17 @@ module Props
50
52
 
51
53
  uri.query = ::URI.encode_www_form(qry)
52
54
 
53
- @deferred.push(
55
+ deferral = {
54
56
  url: uri.to_s,
55
57
  path: path,
56
- type: type.to_s
57
- )
58
+ type: type.to_s,
59
+ }
60
+
61
+ # camelize for JS land
62
+ deferral[:successAction] = success_action if success_action
63
+ deferral[:failAction] = fail_action if fail_action
64
+
65
+ @deferred.push(deferral)
58
66
 
59
67
  placeholder
60
68
  end
@@ -1,14 +1,10 @@
1
- require 'digest'
2
-
3
1
  module Props
4
2
  class Fragment
5
3
  attr_reader :fragments
6
- attr_accessor :name
7
4
 
8
- def initialize(base, fragments={})
5
+ def initialize(base, fragments=[])
9
6
  @base = base
10
7
  @fragments = fragments
11
- @digest = Digest::SHA2.new(256)
12
8
  end
13
9
 
14
10
  def handle(options)
@@ -20,30 +16,10 @@ module Props
20
16
  fragment_name = fragment.to_s
21
17
  path = @base.traveled_path.join('.')
22
18
  @name = fragment_name
23
- @fragments[fragment_name] ||= []
24
- @fragments[fragment_name].push(path)
25
- end
26
-
27
- if fragment == true
28
- locals = partial_opts[:locals]
29
19
 
30
- identity = {}
31
- locals
32
- .clone
33
- .tap{|h| h.delete(:json)}
34
- .each do |key, value|
35
- if value.respond_to?(:to_global_id)
36
- identity[key] = value.to_global_id.to_s
37
- else
38
- identity[key] = value
39
- end
40
- end
41
-
42
- path = @base.traveled_path.join('.')
43
- fragment_name = @digest.hexdigest("#{partial_name}#{identity.to_json}")
44
- @name = fragment_name
45
- @fragments[fragment_name] ||= []
46
- @fragments[fragment_name].push(path)
20
+ @fragments.push(
21
+ { type: fragment_name, partial: partial_name, path: path }
22
+ )
47
23
  end
48
24
  end
49
25
  end
@@ -1,23 +1,56 @@
1
- require "concurrent/map"
2
1
  require 'action_view'
3
2
 
4
3
  module Props
4
+ class RenderedTemplate
5
+ attr_reader :body, :layout, :template
6
+
7
+ def initialize(body, layout, template)
8
+ @body = body
9
+ @layout = layout
10
+ @template = template
11
+ end
12
+
13
+ def format
14
+ template.format
15
+ end
16
+ end
17
+
5
18
  class Partialer
19
+ INVALID_PARTIAL_MESSAGE = "The partial name must be a string, but received (%s)."
20
+
6
21
  def initialize(base, context, builder)
7
22
  @context = context
8
23
  @builder = builder
9
24
  @base = base
10
25
  end
11
26
 
27
+ def extract_details(options) # :doc:
28
+ @context.lookup_context.registered_details.each_with_object({}) do |key, details|
29
+ value = options[key]
30
+
31
+ details[key] = Array(value) if value
32
+ end
33
+ end
34
+
12
35
  def find_and_add_template(all_options)
13
36
  first_opts = all_options[0]
14
37
 
15
38
  if first_opts[:partial]
16
39
  partial_opts = block_opts_to_render_opts(@builder, first_opts)
17
- renderer = PartialRenderer.new(@context, partial_opts)
40
+ .merge(formats: [:json])
41
+ partial_opts.delete(:handlers)
42
+ partial = partial_opts[:partial]
43
+
44
+ if !(String === partial)
45
+ raise ArgumentError.new(INVALID_PARTIAL_MESSAGE % (partial.inspect))
46
+ end
47
+
48
+ template_keys = retrieve_template_keys(partial_opts)
49
+ details = extract_details(partial_opts)
50
+ template = find_template(partial, template_keys, details)
18
51
 
19
52
  all_options.map do |opts|
20
- opts[:_template] = renderer.template
53
+ opts[:_template] = template
21
54
  opts
22
55
  end
23
56
  else
@@ -25,6 +58,17 @@ module Props
25
58
  end
26
59
  end
27
60
 
61
+ def find_template(path, locals, details)
62
+ prefixes = path.include?(?/) ? [] : @context.lookup_context.prefixes
63
+ @context.lookup_context.find_template(path, prefixes, true, locals, details)
64
+ end
65
+
66
+ def retrieve_template_keys(options)
67
+ template_keys = options[:locals].keys
68
+ template_keys << options[:as] if options[:as]
69
+ template_keys
70
+ end
71
+
28
72
  def block_opts_to_render_opts(builder, options)
29
73
  partial, pass_opts = [*options[:partial]]
30
74
  pass_opts ||= {}
@@ -46,9 +90,20 @@ module Props
46
90
 
47
91
  renderer.render(template, pass_opts)
48
92
  end
93
+
94
+ def render(template, options)
95
+ view = @context
96
+ instrument(:partial, identifier: template.identifier) do |payload|
97
+ locals = options[:locals]
98
+ content = template.render(view, locals)
99
+
100
+ payload[:cache_hit] = view.view_renderer.cache_hits[template.virtual_path]
101
+ build_rendered_template(content, template)
102
+ end
103
+ end
49
104
  end
50
105
 
51
- class PartialRenderer < ActionView::AbstractRenderer
106
+ class PartialRenderer
52
107
  OPTION_AS_ERROR_MESSAGE = "The value (%s) of the option `as` is not a valid Ruby identifier; " \
53
108
  "make sure it starts with lowercase letter, " \
54
109
  "and is followed by any combination of letters, numbers and underscores."
@@ -57,21 +112,6 @@ module Props
57
112
 
58
113
  INVALID_PARTIAL_MESSAGE = "The partial name must be a string, but received (%s)."
59
114
 
60
- def self.find_and_add_template(builder, context, all_options)
61
- first_opts = all_options[0]
62
-
63
- if first_opts[:partial]
64
- partial_opts = block_opts_to_render_opts(builder, first_opts)
65
- renderer = new(context, partial_opts)
66
-
67
- all_options.map do |opts|
68
- opts[:_template] = renderer.template
69
- opts
70
- end
71
- else
72
- all_options
73
- end
74
- end
75
115
 
76
116
  def self.raise_invalid_option_as(as)
77
117
  raise ArgumentError.new(OPTION_AS_ERROR_MESSAGE % (as))
@@ -81,7 +121,6 @@ module Props
81
121
  raise ArgumentError.new(IDENTIFIER_ERROR_MESSAGE % (path))
82
122
  end
83
123
 
84
-
85
124
  def self.retrieve_variable(path)
86
125
  base = path[-1] == "/" ? "" : File.basename(path)
87
126
  raise_invalid_identifier(path) unless base =~ /\A_?(.*?)(?:\.\w+)*\z/
@@ -108,8 +147,7 @@ module Props
108
147
  locals[as] = item
109
148
 
110
149
  if fragment_name = rest[:fragment]
111
- fragment_name = Proc === fragment_name ? fragment_name.call(item) : fragment_name.to_s
112
- rest[:fragment] = fragment_name
150
+ rest[:fragment] = fragment_name.to_s
113
151
  end
114
152
  end
115
153
 
@@ -123,7 +161,6 @@ module Props
123
161
 
124
162
  def initialize(context, options)
125
163
  @context = context
126
- super(@context.lookup_context)
127
164
  @options = options.merge(formats: [:json])
128
165
  @options.delete(:handlers)
129
166
  @details = extract_details(@options)
@@ -135,7 +172,7 @@ module Props
135
172
  end
136
173
 
137
174
  @path = partial
138
- @context_prefix = @lookup_context.prefixes.first
175
+
139
176
  template_keys = retrieve_template_keys(@options)
140
177
  @template = find_template(@path, template_keys)
141
178
  end
@@ -147,6 +184,19 @@ module Props
147
184
  end
148
185
 
149
186
  private
187
+ def extract_details(options) # :doc:
188
+ @context.lookup_context.registered_details.each_with_object({}) do |key, details|
189
+ value = options[key]
190
+
191
+ details[key] = Array(value) if value
192
+ end
193
+ end
194
+
195
+ def instrument(name, **options) # :doc:
196
+ ActiveSupport::Notifications.instrument("render_#{name}.action_view", options) do |payload|
197
+ yield payload
198
+ end
199
+ end
150
200
 
151
201
  def render_partial(template, view, options)
152
202
  template ||= @template
@@ -161,17 +211,13 @@ module Props
161
211
  end
162
212
  end
163
213
 
164
- # Sets up instance variables needed for rendering a partial. This method
165
- # finds the options and details and extracts them. The method also contains
166
- # logic that handles the type of object passed in as the partial.
167
- #
168
- # If +options[:partial]+ is a string, then the <tt>@path</tt> instance variable is
169
- # set to that string. Otherwise, the +options[:partial]+ object must
170
- # respond to +to_partial_path+ in order to setup the path.
214
+ def build_rendered_template(content, template, layout = nil)
215
+ RenderedTemplate.new content, layout, template
216
+ end
171
217
 
172
218
  def find_template(path, locals)
173
- prefixes = path.include?(?/) ? [] : @lookup_context.prefixes
174
- @lookup_context.find_template(path, prefixes, true, locals, @details)
219
+ prefixes = path.include?(?/) ? [] : @context.lookup_context.prefixes
220
+ @context.lookup_context.find_template(path, prefixes, true, locals, @details)
175
221
  end
176
222
 
177
223
  def retrieve_template_keys(options)
@@ -10,13 +10,5 @@ module Props
10
10
  require 'props_template/layout_patch'
11
11
  end
12
12
  end
13
-
14
- # if Rails::VERSION::MAJOR >= 4
15
- # generators do |app|
16
- # Rails::Generators.configure! app.config.generators
17
- # Rails::Generators.hidden_namespaces.uniq!
18
- # require 'generators/rails/scaffold_controller_generator'
19
- # end
20
- # end
21
13
  end
22
14
  end
@@ -23,9 +23,6 @@ module Props
23
23
  []
24
24
  end
25
25
 
26
- def fragment_digest!
27
- end
28
-
29
26
  def found!
30
27
  pass_opts = @found_options.clone || {}
31
28
  pass_opts.delete(:defer)
@@ -0,0 +1,3 @@
1
+ module Props
2
+ VERSION = "0.20.0".freeze
3
+ end
data/spec/layout_spec.rb CHANGED
@@ -1,16 +1,24 @@
1
- require_relative './support/helper'
2
- require_relative './support/rails_helper'
3
- require 'props_template/layout_patch'
1
+ require_relative "./support/helper"
2
+ require_relative "./support/rails_helper"
3
+ require "props_template/layout_patch"
4
+ require "action_controller"
4
5
 
5
- RSpec.describe 'Props::Template' do
6
- it 'uses a layout to render' do
7
- view_path = File.join(File.dirname(__FILE__),'./fixtures')
8
- controller = ActionView::TestCase::TestController.new
6
+ RSpec.describe "Props::Template" do
7
+ class TestController < ActionController::Base
8
+ protect_from_forgery
9
+
10
+ def self.controller_path
11
+ ""
12
+ end
13
+ end
14
+
15
+ it "uses a layout to render" do
16
+ view_path = File.join(File.dirname(__FILE__), "./fixtures")
17
+ controller = TestController.new
9
18
  controller.prepend_view_path(view_path)
10
- controller.response.headers['Content-Type']='application/json'
11
- controller.request.path = '/some_url'
12
19
 
13
- json = controller.render('200', layout: 'application')
20
+ json = controller.render_to_string("200", layout: "application")
21
+
14
22
  expect(json.strip).to eql('{"data":{"success":"ok"}}')
15
23
  end
16
24
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: props_template
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.14.0
4
+ version: 0.20.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Johny Ho
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-06-05 00:00:00.000000000 Z
11
+ date: 2021-06-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -52,22 +52,10 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '3.9'
55
- - !ruby/object:Gem::Dependency
56
- name: concurrent-ruby
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - ">="
60
- - !ruby/object:Gem::Version
61
- version: '1.1'
62
- type: :runtime
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - ">="
67
- - !ruby/object:Gem::Version
68
- version: '1.1'
69
- description: A JSON builder for your React props
70
- email: jho406@gmail.com
55
+ description: PropsTemplate is a direct-to-Oj, JBuilder-like DSL for building JSON.
56
+ It has support for Russian-Doll caching, layouts, and can be queried by giving the
57
+ root a key path.
58
+ email: johny@thoughtbot.com
71
59
  executables: []
72
60
  extensions: []
73
61
  extra_rdoc_files: []
@@ -77,6 +65,7 @@ files:
77
65
  - lib/props_template/base.rb
78
66
  - lib/props_template/base_with_extensions.rb
79
67
  - lib/props_template/core_ext.rb
68
+ - lib/props_template/debug_writer.rb
80
69
  - lib/props_template/dependency_tracker.rb
81
70
  - lib/props_template/extension_manager.rb
82
71
  - lib/props_template/extensions/cache.rb
@@ -84,18 +73,18 @@ files:
84
73
  - lib/props_template/extensions/fragment.rb
85
74
  - lib/props_template/extensions/partial_renderer.rb
86
75
  - lib/props_template/handler.rb
87
- - lib/props_template/key_formatter.rb
88
76
  - lib/props_template/layout_patch.rb
89
77
  - lib/props_template/railtie.rb
90
78
  - lib/props_template/searcher.rb
79
+ - lib/props_template/version.rb
91
80
  - spec/layout_spec.rb
92
81
  - spec/props_template_spec.rb
93
82
  - spec/searcher_spec.rb
94
- homepage: https://github.com/jho406/breezy/
83
+ homepage: https://github.com/thoughtbot/props_template/
95
84
  licenses:
96
85
  - MIT
97
86
  metadata: {}
98
- post_install_message:
87
+ post_install_message:
99
88
  rdoc_options: []
100
89
  require_paths:
101
90
  - lib
@@ -103,17 +92,17 @@ required_ruby_version: !ruby/object:Gem::Requirement
103
92
  requirements:
104
93
  - - ">="
105
94
  - !ruby/object:Gem::Version
106
- version: '2.3'
95
+ version: '2.5'
107
96
  required_rubygems_version: !ruby/object:Gem::Requirement
108
97
  requirements:
109
98
  - - ">="
110
99
  - !ruby/object:Gem::Version
111
100
  version: '0'
112
101
  requirements: []
113
- rubygems_version: 3.0.3
114
- signing_key:
102
+ rubygems_version: 3.1.6
103
+ signing_key:
115
104
  specification_version: 4
116
- summary: A JSON builder for your React props
105
+ summary: A fast JSON builder
117
106
  test_files:
118
107
  - spec/searcher_spec.rb
119
108
  - spec/layout_spec.rb
@@ -1,33 +0,0 @@
1
- require 'active_support/core_ext/array'
2
-
3
- module Props
4
- class KeyFormatter
5
- def initialize(*args)
6
- @format = {}
7
- @cache = {}
8
-
9
- options = args.extract_options!
10
- args.each do |name|
11
- @format[name] = []
12
- end
13
- options.each do |name, parameters|
14
- @format[name] = parameters
15
- end
16
- end
17
-
18
- def initialize_copy(original)
19
- @cache = {}
20
- end
21
-
22
- def format(key)
23
- @cache[key] ||= @format.inject(key.to_s) do |result, args|
24
- func, args = args
25
- if ::Proc === func
26
- func.call result, *args
27
- else
28
- result.send func, *args
29
- end
30
- end
31
- end
32
- end
33
- end