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.
- checksums.yaml +4 -4
- data/README.md +205 -140
- data/lib/props_template.rb +3 -9
- data/lib/props_template/base.rb +33 -24
- data/lib/props_template/base_with_extensions.rb +18 -19
- data/lib/props_template/debug_writer.rb +55 -0
- data/lib/props_template/extension_manager.rb +27 -31
- data/lib/props_template/extensions/cache.rb +2 -21
- data/lib/props_template/extensions/deferment.rb +11 -3
- data/lib/props_template/extensions/fragment.rb +4 -28
- data/lib/props_template/extensions/partial_renderer.rb +79 -33
- data/lib/props_template/railtie.rb +0 -8
- data/lib/props_template/searcher.rb +0 -3
- data/lib/props_template/version.rb +3 -0
- data/spec/layout_spec.rb +18 -10
- metadata +15 -26
- data/lib/props_template/key_formatter.rb +0 -33
@@ -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
|
-
|
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
|
-
|
31
|
-
|
32
|
-
|
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
|
-
|
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] =
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
165
|
-
|
166
|
-
|
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
|
data/spec/layout_spec.rb
CHANGED
@@ -1,16 +1,24 @@
|
|
1
|
-
require_relative
|
2
|
-
require_relative
|
3
|
-
require
|
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
|
6
|
-
|
7
|
-
|
8
|
-
|
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.
|
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.
|
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:
|
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
|
-
-
|
56
|
-
|
57
|
-
|
58
|
-
|
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/
|
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.
|
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.
|
114
|
-
signing_key:
|
102
|
+
rubygems_version: 3.1.6
|
103
|
+
signing_key:
|
115
104
|
specification_version: 4
|
116
|
-
summary: A JSON builder
|
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
|