curlybars 1.16.1.pre.2 → 1.16.1
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/exe/curlybars-indent-lint +2 -0
- data/lib/curlybars/configuration.rb +1 -0
- data/lib/curlybars/method_whitelist.rb +11 -4
- data/lib/curlybars/node/each_else.rb +1 -0
- data/lib/curlybars/node/path.rb +0 -5
- data/lib/curlybars/node/sub_expression.rb +8 -11
- data/lib/curlybars/node/template.rb +0 -5
- data/lib/curlybars/node/with_else.rb +1 -0
- data/lib/curlybars/path_finder.rb +1 -2
- data/lib/curlybars/rendering_support.rb +15 -2
- data/lib/curlybars/serialization_guard.rb +35 -0
- data/lib/curlybars/version.rb +1 -1
- data/lib/curlybars.rb +13 -4
- metadata +3 -4
- data/lib/curlybars/node/boolean.rb +0 -24
- data/lib/curlybars/node/string.rb +0 -24
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 746a0625058bd9a2bca9ebf655c36f7cbd7e11e43000a051f4a76516ce73150b
|
|
4
|
+
data.tar.gz: f631902fba5a77cce57a36472636b99a5c03450dfbe5943e058c92b7c09e1d07
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c0fb5fb5398bf4643f679fdb07403de92892ee84efb70bae6a689297251e1ff9e79311b1c7711a6ba6a247a84eac945d735f72deacd8a555c9cd8ebfd75e9d81
|
|
7
|
+
data.tar.gz: ac273a2a866a2634c6d17ebf4a4bfd32b656c765e896214e2f3d6d2d49069232503e96bdc04a23b698d8f569cf5d368f5ab736699e280e404b9765bb370b4132
|
data/exe/curlybars-indent-lint
CHANGED
|
@@ -47,8 +47,8 @@ module Curlybars
|
|
|
47
47
|
|
|
48
48
|
contextual_block&.call(context, schema_adder)
|
|
49
49
|
|
|
50
|
-
schema = all_methods_without_type.
|
|
51
|
-
|
|
50
|
+
schema = all_methods_without_type.to_h do |method|
|
|
51
|
+
[method, nil]
|
|
52
52
|
end
|
|
53
53
|
|
|
54
54
|
methods_with_type_resolved = all_methods_with_type.transform_values do |type|
|
|
@@ -104,12 +104,19 @@ module Curlybars
|
|
|
104
104
|
define_method(:as_json) do |*args|
|
|
105
105
|
return @__as_json if defined?(@__as_json)
|
|
106
106
|
|
|
107
|
-
|
|
107
|
+
guard = args.first.is_a?(Hash) ? args.first[:curlybars_guard] : nil
|
|
108
|
+
guard&.enter!
|
|
109
|
+
|
|
110
|
+
@__as_json = allowed_methods.each_with_object({}) do |method, hash|
|
|
108
111
|
unless self.method(method).arity > 0
|
|
109
112
|
value = send(method)
|
|
110
|
-
|
|
113
|
+
serialized = value.equal?(self) ? "[circular reference]" : value.as_json(*args)
|
|
114
|
+
guard&.measure(serialized)
|
|
115
|
+
hash[method] = serialized
|
|
111
116
|
end
|
|
112
117
|
end
|
|
118
|
+
ensure
|
|
119
|
+
guard&.leave!
|
|
113
120
|
end
|
|
114
121
|
end
|
|
115
122
|
|
|
@@ -12,6 +12,7 @@ module Curlybars
|
|
|
12
12
|
template_cache_key = '#{each_template.cache_key}'
|
|
13
13
|
|
|
14
14
|
collection = rendering.coerce_to_hash!(collection, #{collection_path.path.inspect}, position)
|
|
15
|
+
rendering.check_nesting_not_too_deep(position)
|
|
15
16
|
collection.each.with_index.map do |key_and_presenter, index|
|
|
16
17
|
rendering.check_timeout!
|
|
17
18
|
rendering.optional_presenter_cache(key_and_presenter[1], template_cache_key, buffer) do |buffer|
|
data/lib/curlybars/node/path.rb
CHANGED
|
@@ -168,11 +168,6 @@ module Curlybars
|
|
|
168
168
|
|
|
169
169
|
message = "`#{path}` cannot resolve to a helper"
|
|
170
170
|
raise Curlybars::Error::Validate.new('not_a_helper', message, position)
|
|
171
|
-
when :not_helper
|
|
172
|
-
return unless helper?(branches)
|
|
173
|
-
|
|
174
|
-
message = "`#{path}` resolves to a helper"
|
|
175
|
-
raise Curlybars::Error::Validate.new('is_a_helper', message, position)
|
|
176
171
|
when :anything
|
|
177
172
|
else
|
|
178
173
|
raise "invalid type `#{check_type}`"
|
|
@@ -63,22 +63,19 @@ module Curlybars
|
|
|
63
63
|
|
|
64
64
|
type = node.resolve_and_check!(branches, check_type: check_type)
|
|
65
65
|
|
|
66
|
-
if
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
66
|
+
if generic_helper?(type)
|
|
67
|
+
is_collection = type.last.is_a?(Array)
|
|
68
|
+
infer_generic_helper_type!(branches, is_collection: is_collection)
|
|
69
|
+
elsif helper?(type)
|
|
70
|
+
type.last
|
|
71
|
+
else
|
|
72
|
+
type
|
|
73
73
|
end
|
|
74
|
-
|
|
75
|
-
type
|
|
76
74
|
end
|
|
77
75
|
|
|
78
76
|
def generic_helper?(type)
|
|
79
|
-
return false unless
|
|
77
|
+
return false unless helper?(type)
|
|
80
78
|
return false unless type.size == 2
|
|
81
|
-
return false unless type.first == :helper
|
|
82
79
|
|
|
83
80
|
type.last == [{}] || type.last == {}
|
|
84
81
|
end
|
|
@@ -9,11 +9,6 @@ module Curlybars
|
|
|
9
9
|
<<-RUBY
|
|
10
10
|
::Module.new do
|
|
11
11
|
def self.exec(contexts, rendering, variables, buffer)
|
|
12
|
-
unless contexts.length < ::Curlybars.configuration.nesting_limit
|
|
13
|
-
message = "Nesting too deep"
|
|
14
|
-
position = rendering.position(#{position.line_number}, #{position.line_offset})
|
|
15
|
-
raise ::Curlybars::Error::Render.new('nesting_too_deep', message, position)
|
|
16
|
-
end
|
|
17
12
|
#{compiled_items}
|
|
18
13
|
end
|
|
19
14
|
end.exec(contexts, rendering, variables, buffer)
|
|
@@ -10,6 +10,7 @@ module Curlybars
|
|
|
10
10
|
if rendering.to_bool(compiled_path)
|
|
11
11
|
position = rendering.position(#{position.line_number}, #{position.line_offset})
|
|
12
12
|
rendering.check_context_is_presenter(compiled_path, #{presenter_path.path.inspect}, position)
|
|
13
|
+
rendering.check_nesting_not_too_deep(position)
|
|
13
14
|
|
|
14
15
|
contexts.push(compiled_path)
|
|
15
16
|
begin
|
|
@@ -31,6 +31,16 @@ module Curlybars
|
|
|
31
31
|
raise ::Curlybars::Error::Render.new('timeout', message, nil)
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
+
# Called before a #with or #each block pushes its context. The contexts
|
|
35
|
+
# stack starts with the root presenter, so its length is one more than
|
|
36
|
+
# the number of enclosing #with and #each blocks.
|
|
37
|
+
def check_nesting_not_too_deep(position)
|
|
38
|
+
return if contexts.length <= Curlybars.configuration.nesting_limit
|
|
39
|
+
|
|
40
|
+
message = "Nesting too deep (> #{Curlybars.configuration.nesting_limit} levels)"
|
|
41
|
+
raise Curlybars::Error::Render.new('nesting_too_deep', message, position)
|
|
42
|
+
end
|
|
43
|
+
|
|
34
44
|
def check_context_is_presenter(context, path, position)
|
|
35
45
|
return if presenter?(context)
|
|
36
46
|
|
|
@@ -205,7 +215,7 @@ module Curlybars
|
|
|
205
215
|
|
|
206
216
|
eval(eval_source) # rubocop:disable Security/Eval -- eval is the established compilation pattern for the whole engine
|
|
207
217
|
rescue Curlybars::Error::Render => e
|
|
208
|
-
raise if e.id == 'render.timeout' || e.id == 'render.output_too_long'
|
|
218
|
+
raise if e.id == 'render.timeout' || e.id == 'render.output_too_long' || e.id == 'render.nesting_too_deep'
|
|
209
219
|
|
|
210
220
|
""
|
|
211
221
|
rescue StandardError
|
|
@@ -257,8 +267,11 @@ module Curlybars
|
|
|
257
267
|
raise Curlybars::Error::Render.new('unallowed_path', message, position)
|
|
258
268
|
end
|
|
259
269
|
|
|
270
|
+
# Counts the dots after the final `/` in the path. Leading `../` segments
|
|
271
|
+
# move up the contexts stack rather than traversing, so they don't count.
|
|
260
272
|
def check_traverse_not_too_deep(traverse, position)
|
|
261
|
-
|
|
273
|
+
dots = traverse.split('/').last.count('.')
|
|
274
|
+
return unless dots > Curlybars.configuration.traversing_limit
|
|
262
275
|
|
|
263
276
|
message = "`#{traverse}` too deep"
|
|
264
277
|
raise Curlybars::Error::Render.new('traverse_too_deep', message, position)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module Curlybars
|
|
2
|
+
class SerializationGuard
|
|
3
|
+
def initialize(budget:, depth_limit:)
|
|
4
|
+
@budget = budget
|
|
5
|
+
@depth_limit = depth_limit
|
|
6
|
+
@accumulated = 0
|
|
7
|
+
@depth = 0
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def enter!
|
|
11
|
+
@depth += 1
|
|
12
|
+
return unless @depth > @depth_limit
|
|
13
|
+
|
|
14
|
+
message = "Serialization nesting too deep (> #{@depth_limit})"
|
|
15
|
+
raise Curlybars::Error::Render.new('nesting_too_deep', message, nil)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def leave!
|
|
19
|
+
@depth -= 1
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def measure(value)
|
|
23
|
+
# Only String leaves are counted: deep-measuring every Hash/Array would
|
|
24
|
+
# mean serializing twice. This under-counts raw hash/array fields; the
|
|
25
|
+
# authoritative ceiling is still SafeBuffer#concat at the output node.
|
|
26
|
+
return unless value.is_a?(String)
|
|
27
|
+
|
|
28
|
+
@accumulated += value.bytesize
|
|
29
|
+
return unless @accumulated > @budget
|
|
30
|
+
|
|
31
|
+
message = "Output too long (> #{@budget} bytes)"
|
|
32
|
+
raise Curlybars::Error::Render.new('output_too_long', message, nil)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
data/lib/curlybars/version.rb
CHANGED
data/lib/curlybars.rb
CHANGED
|
@@ -29,7 +29,7 @@ module Curlybars
|
|
|
29
29
|
cache_key = ["Curlybars.compile", Curlybars::VERSION, identifier, Digest::SHA256.hexdigest(source)]
|
|
30
30
|
|
|
31
31
|
cache.fetch(cache_key) do
|
|
32
|
-
ast(transformed_source(source), identifier, run_processors: true).compile
|
|
32
|
+
ast(transformed_source(source, identifier), identifier, run_processors: true).compile
|
|
33
33
|
end
|
|
34
34
|
end
|
|
35
35
|
|
|
@@ -76,7 +76,7 @@ module Curlybars
|
|
|
76
76
|
# source - The source HBS String used to generate an AST.
|
|
77
77
|
# identifier - The the file name of the template being checked (defaults to `nil`).
|
|
78
78
|
def visit(visitor, source, identifier = nil)
|
|
79
|
-
tree = ast(transformed_source(source), identifier, run_processors: true)
|
|
79
|
+
tree = ast(transformed_source(source, identifier), identifier, run_processors: true)
|
|
80
80
|
visitor.accept(tree)
|
|
81
81
|
end
|
|
82
82
|
|
|
@@ -99,11 +99,19 @@ module Curlybars
|
|
|
99
99
|
#
|
|
100
100
|
# Returns an Array of Curlybars::Node::Path instances that match the target path.
|
|
101
101
|
def find(target_path, source, identifier = nil, role: nil)
|
|
102
|
-
tree = ast(transformed_source(source), identifier, run_processors: true)
|
|
102
|
+
tree = ast(transformed_source(source, identifier), identifier, run_processors: true)
|
|
103
103
|
finder = Curlybars::PathFinder.new(tree)
|
|
104
104
|
finder.find(target_path, role: role)
|
|
105
105
|
end
|
|
106
106
|
|
|
107
|
+
def serialize(value)
|
|
108
|
+
guard = Curlybars::SerializationGuard.new(
|
|
109
|
+
budget: configuration.output_limit,
|
|
110
|
+
depth_limit: configuration.nesting_limit
|
|
111
|
+
)
|
|
112
|
+
value.to_json(curlybars_guard: guard)
|
|
113
|
+
end
|
|
114
|
+
|
|
107
115
|
def global_helpers_dependency_tree
|
|
108
116
|
@global_helpers_dependency_tree ||= begin
|
|
109
117
|
classes = Curlybars.configuration.global_helpers_provider_classes
|
|
@@ -119,7 +127,7 @@ module Curlybars
|
|
|
119
127
|
|
|
120
128
|
private
|
|
121
129
|
|
|
122
|
-
def transformed_source(source)
|
|
130
|
+
def transformed_source(source, identifier)
|
|
123
131
|
transformers = Curlybars.configuration.compiler_transformers
|
|
124
132
|
transformers.inject(source) do |memo, transformer|
|
|
125
133
|
transformer.transform(memo, identifier)
|
|
@@ -147,6 +155,7 @@ module Curlybars
|
|
|
147
155
|
end
|
|
148
156
|
|
|
149
157
|
require 'curlybars/safe_buffer'
|
|
158
|
+
require 'curlybars/serialization_guard'
|
|
150
159
|
require 'curlybars/configuration'
|
|
151
160
|
require 'curlybars/rendering_support'
|
|
152
161
|
require 'curlybars/parser'
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: curlybars
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.16.1
|
|
4
|
+
version: 1.16.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Libo Cannici
|
|
@@ -96,7 +96,6 @@ files:
|
|
|
96
96
|
- lib/curlybars/lexer.rb
|
|
97
97
|
- lib/curlybars/method_whitelist.rb
|
|
98
98
|
- lib/curlybars/node/block_helper_else.rb
|
|
99
|
-
- lib/curlybars/node/boolean.rb
|
|
100
99
|
- lib/curlybars/node/each_else.rb
|
|
101
100
|
- lib/curlybars/node/if_else.rb
|
|
102
101
|
- lib/curlybars/node/item.rb
|
|
@@ -106,7 +105,6 @@ files:
|
|
|
106
105
|
- lib/curlybars/node/partial.rb
|
|
107
106
|
- lib/curlybars/node/path.rb
|
|
108
107
|
- lib/curlybars/node/root.rb
|
|
109
|
-
- lib/curlybars/node/string.rb
|
|
110
108
|
- lib/curlybars/node/sub_expression.rb
|
|
111
109
|
- lib/curlybars/node/template.rb
|
|
112
110
|
- lib/curlybars/node/text.rb
|
|
@@ -123,6 +121,7 @@ files:
|
|
|
123
121
|
- lib/curlybars/railtie.rb
|
|
124
122
|
- lib/curlybars/rendering_support.rb
|
|
125
123
|
- lib/curlybars/safe_buffer.rb
|
|
124
|
+
- lib/curlybars/serialization_guard.rb
|
|
126
125
|
- lib/curlybars/template_handler.rb
|
|
127
126
|
- lib/curlybars/validation_context.rb
|
|
128
127
|
- lib/curlybars/version.rb
|
|
@@ -139,7 +138,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
139
138
|
requirements:
|
|
140
139
|
- - ">="
|
|
141
140
|
- !ruby/object:Gem::Version
|
|
142
|
-
version: '3.
|
|
141
|
+
version: '3.3'
|
|
143
142
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
144
143
|
requirements:
|
|
145
144
|
- - ">="
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
module Curlybars
|
|
2
|
-
module Node
|
|
3
|
-
Boolean = Struct.new(:boolean) do
|
|
4
|
-
def compile
|
|
5
|
-
# NOTE: the following is a heredoc string, representing the ruby code fragment
|
|
6
|
-
# outputted by this node.
|
|
7
|
-
<<-RUBY
|
|
8
|
-
->() { #{boolean} }
|
|
9
|
-
RUBY
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
def validate(branches, context: nil)
|
|
13
|
-
# Nothing to validate here.
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def cache_key
|
|
17
|
-
[
|
|
18
|
-
boolean.to_s,
|
|
19
|
-
self.class.name
|
|
20
|
-
].join("/")
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
end
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
module Curlybars
|
|
2
|
-
module Node
|
|
3
|
-
String = Struct.new(:string) do
|
|
4
|
-
def compile
|
|
5
|
-
# NOTE: the following is a heredoc string, representing the ruby code fragment
|
|
6
|
-
# outputted by this node.
|
|
7
|
-
<<-RUBY
|
|
8
|
-
->() { #{string.inspect} }
|
|
9
|
-
RUBY
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
def validate(branches, context: nil)
|
|
13
|
-
# Nothing to validate here.
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def cache_key
|
|
17
|
-
[
|
|
18
|
-
string,
|
|
19
|
-
self.class.name
|
|
20
|
-
].join("/")
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
end
|