curlybars 1.1.4 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d425b234907702e06326b091a5dd9bea96d8a5494796e122ee77c56a79dd63ae
4
- data.tar.gz: c23f25a52b8c252ebb5aae349c01faefb7daff9e3e8a112e12f676249ad21f23
3
+ metadata.gz: 92b6cb3082b3fc52f509b72511b93937529f57170d4f8751f989e17727418889
4
+ data.tar.gz: cbe9843e0852ccdb2bd9aabcb4c77e4856de40e60bbc01f6dc10d17e3987754a
5
5
  SHA512:
6
- metadata.gz: 63657e140e3377cea321adfb20ff4ff1445e1bb00901e49e5f9b095f65b25b6d89cd2ef814e425f9abc7b0ed62d4d003c52459f8f7810943359f9160a9c64b7a
7
- data.tar.gz: 077401e926e847f7ee34df7ebce3a0323ae57c9ce6daa8454b67bc6ce6785807cfe96dfcdf406f8a7928f2d2ba992e344d8a7a8581f32c55e3f0459a82dd1f94
6
+ metadata.gz: 8a3cb40974bd6568a73b5640452403da9b40e63da6bc152fadd4ea375961cb8c5de21d561f094f5b181359c791ddf4237a0a58022ae2bd3b38d012748ae9eb6b
7
+ data.tar.gz: 4bd84f5a24ee7fcb3f59fac17844d65638f496f22c263fc1e94c47aac65c84e848718f1695a20421ade814c9e7e98f17b0e10d58b14920fccb33d92987606e6b
@@ -28,12 +28,7 @@ module Curlybars
28
28
  cache_key = ["Curlybars.compile", identifier, Digest::SHA256.hexdigest(source)]
29
29
 
30
30
  cache.fetch(cache_key) do
31
- transformers = Curlybars.configuration.compiler_transformers
32
- transformed_source = transformers.inject(source) do |memo, transformer|
33
- transformer.transform(memo, identifier)
34
- end
35
-
36
- ast(transformed_source, identifier, run_processors: true).compile
31
+ ast(transformed_source(source), identifier, run_processors: true).compile
37
32
  end
38
33
  end
39
34
 
@@ -72,6 +67,16 @@ module Curlybars
72
67
  errors.empty?
73
68
  end
74
69
 
70
+ # Visit nodes in the AST.
71
+ #
72
+ # visitor - An instance of a subclass of `Curlybars::Visitor`.
73
+ # source - The source HBS String used to generate an AST.
74
+ # identifier - The the file name of the template being checked (defaults to `nil`).
75
+ def visit(visitor, source, identifier = nil)
76
+ tree = ast(transformed_source(source), identifier, run_processors: true)
77
+ visitor.accept(tree)
78
+ end
79
+
75
80
  def cache
76
81
  @cache ||= ActiveSupport::Cache::MemoryStore.new
77
82
  end
@@ -80,6 +85,13 @@ module Curlybars
80
85
 
81
86
  private
82
87
 
88
+ def transformed_source(source)
89
+ transformers = Curlybars.configuration.compiler_transformers
90
+ transformers.inject(source) do |memo, transformer|
91
+ transformer.transform(memo, identifier)
92
+ end
93
+ end
94
+
83
95
  def ast(source, identifier, run_processors:)
84
96
  tokens = Curlybars::Lexer.lex(source, identifier)
85
97
 
@@ -118,3 +130,4 @@ require 'curlybars/template_handler'
118
130
  require 'curlybars/railtie' if defined?(Rails)
119
131
  require 'curlybars/presenter'
120
132
  require 'curlybars/method_whitelist'
133
+ require 'curlybars/visitor'
@@ -1,27 +1,57 @@
1
1
  module Curlybars
2
2
  module MethodWhitelist
3
- def allow_methods(*methods, **methods_with_type)
4
- methods_with_type.each do |(method_name, type)|
5
- if type.is_a?(Array)
6
- if type.size != 1 || !type.first.respond_to?(:dependency_tree)
7
- raise "Invalid allowed method syntax for `#{method_name}`. Collections must be of one presenter class"
3
+ def allow_methods(*methods_without_type, **methods_with_type, &contextual_block)
4
+ methods_with_type_validator = lambda do |methods_to_validate|
5
+ methods_to_validate.each do |(method_name, type)|
6
+ if type.is_a?(Array)
7
+ if type.size != 1 || !type.first.respond_to?(:dependency_tree)
8
+ raise "Invalid allowed method syntax for `#{method_name}`. Collections must be of one presenter class"
9
+ end
8
10
  end
9
11
  end
10
12
  end
11
13
 
14
+ methods_with_type_validator.call(methods_with_type)
15
+
12
16
  define_method(:allowed_methods) do
13
- methods_list = methods + methods_with_type.keys
14
- defined?(super) ? super() + methods_list : methods_list
17
+ @method_whitelist_allowed_methods ||= begin
18
+ methods_list = methods_without_type + methods_with_type.keys
19
+
20
+ # Adds methods to the list of allowed methods
21
+ method_adder = lambda do |*more_methods, **more_methods_with_type|
22
+ methods_with_type_validator.call(more_methods_with_type)
23
+
24
+ methods_list += more_methods
25
+ methods_list += more_methods_with_type.keys
26
+ end
27
+
28
+ contextual_block&.call(self, method_adder)
29
+
30
+ defined?(super) ? super() + methods_list : methods_list
31
+ end
15
32
  end
16
33
 
17
- define_singleton_method(:methods_schema) do |*args|
18
- schema = methods.each_with_object({}) do |method, memo|
34
+ define_singleton_method(:methods_schema) do |context = nil|
35
+ all_methods_without_type = methods_without_type
36
+ all_methods_with_type = methods_with_type
37
+
38
+ # Adds methods to the schema
39
+ schema_adder = lambda do |*more_methods_without_type, **more_methods_with_type|
40
+ methods_with_type_validator.call(more_methods_with_type)
41
+
42
+ all_methods_without_type += more_methods_without_type
43
+ all_methods_with_type = all_methods_with_type.merge(more_methods_with_type)
44
+ end
45
+
46
+ contextual_block&.call(context, schema_adder)
47
+
48
+ schema = all_methods_without_type.each_with_object({}) do |method, memo|
19
49
  memo[method] = nil
20
50
  end
21
51
 
22
- methods_with_type_resolved = methods_with_type.each_with_object({}) do |(method_name, type), memo|
52
+ methods_with_type_resolved = all_methods_with_type.each_with_object({}) do |(method_name, type), memo|
23
53
  memo[method_name] = if type.respond_to?(:call)
24
- type.call(*args)
54
+ type.call(context)
25
55
  else
26
56
  type
27
57
  end
@@ -30,26 +60,26 @@ module Curlybars
30
60
  schema.merge!(methods_with_type_resolved)
31
61
 
32
62
  # Inheritance
33
- schema.merge!(super(*args)) if defined?(super)
63
+ schema.merge!(super(context)) if defined?(super)
34
64
 
35
65
  # Included modules
36
66
  included_modules.each do |mod|
37
67
  next unless mod.respond_to?(:methods_schema)
38
- schema.merge!(mod.methods_schema(*args))
68
+ schema.merge!(mod.methods_schema(context))
39
69
  end
40
70
 
41
71
  schema
42
72
  end
43
73
 
44
- define_singleton_method(:dependency_tree) do |*args|
45
- methods_schema(*args).each_with_object({}) do |method_with_type, memo|
74
+ define_singleton_method(:dependency_tree) do |context = nil|
75
+ methods_schema(context).each_with_object({}) do |method_with_type, memo|
46
76
  method_name = method_with_type.first
47
77
  type = method_with_type.last
48
78
 
49
79
  memo[method_name] = if type.respond_to?(:dependency_tree)
50
- type.dependency_tree(*args)
80
+ type.dependency_tree(context)
51
81
  elsif type.is_a?(Array)
52
- [type.first.dependency_tree(*args)]
82
+ [type.first.dependency_tree(context)]
53
83
  else
54
84
  type
55
85
  end
@@ -15,7 +15,7 @@ module Curlybars
15
15
 
16
16
  def validate(branches)
17
17
  [
18
- expression.validate(branches, check_type: :not_helper),
18
+ expression.validate(branches),
19
19
  if_template.validate(branches),
20
20
  else_template.validate(branches)
21
21
  ]
@@ -15,9 +15,9 @@ module Curlybars
15
15
  end
16
16
 
17
17
  def validate(branches)
18
- catch(:skip_item_validation) do
19
- item.validate(branches)
20
- end
18
+ item.validate(branches)
19
+ rescue Curlybars::Error::Validate => path_error
20
+ path_error
21
21
  end
22
22
 
23
23
  def cache_key
@@ -60,10 +60,10 @@ module Curlybars
60
60
  path_split_by_slashes = path.split('/')
61
61
  backward_steps_on_branches = path_split_by_slashes.count - 1
62
62
  base_tree_position = branches.length - backward_steps_on_branches
63
+ base_tree_index = base_tree_position - 1
63
64
 
64
- throw :skip_item_validation unless base_tree_position > 0
65
+ raise Curlybars::Error::Validate.new('unallowed_path', "'#{path}' goes out of scope", position) if base_tree_index < 0
65
66
 
66
- base_tree_index = base_tree_position - 1
67
67
  base_tree = branches[base_tree_index]
68
68
 
69
69
  dotted_path_side = path_split_by_slashes.last
@@ -15,7 +15,7 @@ module Curlybars
15
15
 
16
16
  def validate(branches)
17
17
  [
18
- expression.validate(branches, check_type: :not_helper),
18
+ expression.validate(branches),
19
19
  unless_template.validate(branches),
20
20
  else_template.validate(branches)
21
21
  ]
@@ -101,7 +101,7 @@ module Curlybars
101
101
 
102
102
  def cached_call(meth)
103
103
  return cached_calls[meth] if cached_calls.key? meth
104
- instrument(meth) { cached_calls[meth] = meth.call }
104
+ instrument(meth) { cached_calls[meth] = meth.call(*arguments_for_signature(meth, [], {})) }
105
105
  end
106
106
 
107
107
  def call(helper, helper_path, helper_position, arguments, options, &block)
@@ -1,3 +1,3 @@
1
1
  module Curlybars
2
- VERSION = '1.1.4'.freeze
2
+ VERSION = '1.4.0'.freeze
3
3
  end
@@ -0,0 +1,100 @@
1
+ require 'active_support/core_ext/string/inflections'
2
+
3
+ module Curlybars
4
+ class Visitor
5
+ attr_accessor :context
6
+
7
+ def initialize(context)
8
+ @context = context
9
+ end
10
+
11
+ def accept(node)
12
+ visit(node)
13
+ context
14
+ end
15
+
16
+ private
17
+
18
+ def visit(node)
19
+ class_name = node.class.name.to_s
20
+ return unless class_name.start_with?('Curlybars::Node')
21
+
22
+ method_name = class_name.demodulize.underscore
23
+ send("visit_#{method_name}", node)
24
+ end
25
+
26
+ def visit_block_helper_else(node)
27
+ node.arguments.each { |arg| visit(arg) }
28
+ node.options.each { |opt| visit(opt) }
29
+ visit(node.helper)
30
+ visit(node.helper_template)
31
+ visit(node.else_template)
32
+ end
33
+
34
+ def visit_boolean(_node)
35
+ end
36
+
37
+ def visit_each_else(node)
38
+ visit(node.path)
39
+ visit(node.each_template)
40
+ visit(node.else_template)
41
+ end
42
+
43
+ def visit_if_else(node)
44
+ visit(node.expression)
45
+ visit(node.if_template)
46
+ visit(node.else_template)
47
+ end
48
+
49
+ def visit_item(node)
50
+ visit(node.item)
51
+ end
52
+
53
+ def visit_literal(_node)
54
+ end
55
+
56
+ def visit_option(node)
57
+ visit(node.expression)
58
+ end
59
+
60
+ def visit_output(node)
61
+ visit(node.value)
62
+ end
63
+
64
+ def visit_partial(node)
65
+ visit(node.path)
66
+ end
67
+
68
+ def visit_path(_node)
69
+ end
70
+
71
+ def visit_root(node)
72
+ visit(node.template)
73
+ end
74
+
75
+ def visit_string(_node)
76
+ end
77
+
78
+ def visit_template(node)
79
+ node.items.each { |item| visit(item) }
80
+ end
81
+
82
+ def visit_text(_node)
83
+ end
84
+
85
+ def visit_unless_else(node)
86
+ visit(node.expression)
87
+ visit(node.unless_template)
88
+ visit(node.else_template)
89
+ end
90
+
91
+ def visit_variable(_node)
92
+ end
93
+
94
+ def visit_with_else(node)
95
+ visit(node.path)
96
+ visit(node.with_template)
97
+ visit(node.else_template)
98
+ end
99
+ end
100
+ end
@@ -1,5 +1,32 @@
1
1
  describe Curlybars::MethodWhitelist do
2
- let(:dummy_class) { Class.new { extend Curlybars::MethodWhitelist } }
2
+ let(:dummy_class) do
3
+ Class.new do
4
+ extend Curlybars::MethodWhitelist
5
+
6
+ # A method available in the context
7
+ def foo?
8
+ true
9
+ end
10
+
11
+ def qux?
12
+ false
13
+ end
14
+ end
15
+ end
16
+
17
+ let(:validation_context_class) do
18
+ Class.new do
19
+ attr_accessor :invocation_count
20
+
21
+ def foo?
22
+ true
23
+ end
24
+
25
+ def qux?
26
+ false
27
+ end
28
+ end
29
+ end
3
30
 
4
31
  describe "#allowed_methods" do
5
32
  it "returns an empty array as default" do
@@ -21,6 +48,25 @@ describe Curlybars::MethodWhitelist do
21
48
  expect(dummy_class.new.allowed_methods).to eq([:cook, :link, :article])
22
49
  end
23
50
 
51
+ it "supports adding more methods for validation" do
52
+ dummy_class.class_eval do
53
+ allow_methods do |context, allow_method|
54
+ if context.foo?
55
+ allow_method.call(:bar)
56
+ end
57
+
58
+ if context.qux?
59
+ allow_method.call(:quux)
60
+ end
61
+ end
62
+ end
63
+
64
+ aggregate_failures "test both allowed_methods and allows_method?" do
65
+ expect(dummy_class.new.allowed_methods).to eq([:bar])
66
+ expect(dummy_class.new.allows_method?(:bar)).to eq(true)
67
+ end
68
+ end
69
+
24
70
  it "raises when collection is not of presenters" do
25
71
  expect do
26
72
  dummy_class.class_eval { allow_methods :cook, links: ["foobar"] }
@@ -79,6 +125,81 @@ describe Curlybars::MethodWhitelist do
79
125
  wave: nil
80
126
  )
81
127
  end
128
+
129
+ context "with context dependent methods" do
130
+ let(:base_presenter) do
131
+ stub_const("LinkPresenter", Class.new)
132
+
133
+ Class.new do
134
+ extend Curlybars::MethodWhitelist
135
+ attr_accessor :invocation_count
136
+
137
+ allow_methods :cook, link: LinkPresenter do |context, allow_method|
138
+ if context.foo?
139
+ allow_method.call(:bar)
140
+ end
141
+
142
+ context.invocation_count ||= 0
143
+ context.invocation_count += 1
144
+ end
145
+
146
+ def foo?
147
+ true
148
+ end
149
+ end
150
+ end
151
+
152
+ let(:helpers) do
153
+ Module.new do
154
+ extend Curlybars::MethodWhitelist
155
+ allow_methods :form do |context, allow_method|
156
+ if context.foo?
157
+ allow_method.call(foo_bar: :helper)
158
+ end
159
+ end
160
+
161
+ def foo?
162
+ true
163
+ end
164
+ end
165
+ end
166
+
167
+ let(:post_presenter) do
168
+ Class.new(base_presenter) do
169
+ extend Curlybars::MethodWhitelist
170
+ include Helpers
171
+ allow_methods :wave
172
+ end
173
+ end
174
+
175
+ before do
176
+ stub_const("Helpers", helpers)
177
+ end
178
+
179
+ it "allows context methods from inheritance and composition" do
180
+ expect(post_presenter.new.allowed_methods).to eq([:cook, :link, :bar, :form, :foo_bar, :wave])
181
+ end
182
+
183
+ it "only invokes the context block once" do
184
+ presenter = post_presenter.new
185
+
186
+ 10.times { presenter.allowed_methods }
187
+
188
+ expect(presenter.invocation_count).to eq(1)
189
+ end
190
+
191
+ it "returns a dependency_tree with inheritance and composition with context" do
192
+ expect(post_presenter.dependency_tree(validation_context_class.new)).
193
+ to eq(
194
+ cook: nil,
195
+ link: LinkPresenter,
196
+ form: nil,
197
+ wave: nil,
198
+ bar: nil,
199
+ foo_bar: :helper
200
+ )
201
+ end
202
+ end
82
203
  end
83
204
 
84
205
  describe ".methods_schema" do
@@ -110,16 +231,22 @@ describe Curlybars::MethodWhitelist do
110
231
  expect(dummy_class.methods_schema).to eq(links: [LinkPresenter])
111
232
  end
112
233
 
113
- it "supports procs in schema" do
114
- dummy_class.class_eval { allow_methods settings: -> { { color_1: nil } } }
234
+ it "supports procs with context in schema" do
235
+ dummy_class.class_eval { allow_methods settings: ->(context) { context.foo? ? Hash[:background_color, nil] : nil } }
115
236
 
116
- expect(dummy_class.methods_schema).to eq(settings: { color_1: nil })
237
+ expect(dummy_class.methods_schema(validation_context_class.new)).to eq(settings: { background_color: nil })
117
238
  end
118
239
 
119
- it "supports procs with arguments in schema" do
120
- dummy_class.class_eval { allow_methods settings: ->(name) { Hash[name, nil] } }
240
+ it "supports context methods" do
241
+ dummy_class.class_eval do
242
+ allow_methods do |context, allow_method|
243
+ if context.foo?
244
+ allow_method.call(:bar)
245
+ end
246
+ end
247
+ end
121
248
 
122
- expect(dummy_class.methods_schema(:background_color)).to eq(settings: { background_color: nil })
249
+ expect(dummy_class.methods_schema(validation_context_class.new)).to eq(bar: nil)
123
250
  end
124
251
  end
125
252
 
@@ -15,6 +15,18 @@ describe "{{helper context key=value}}" do
15
15
  HTML
16
16
  end
17
17
 
18
+ it "calls a helper without arguments in an if statement" do
19
+ template = Curlybars.compile(<<-HBS)
20
+ {{#if print_args_and_options}}
21
+ {{print_args_and_options 'first' 'second'}}
22
+ {{/if}}
23
+ HBS
24
+
25
+ expect(eval(template)).to resemble(<<-HTML)
26
+ first, second, key=
27
+ HTML
28
+ end
29
+
18
30
  it "passes two arguments and options" do
19
31
  template = Curlybars.compile(<<-HBS)
20
32
  {{print_args_and_options 'first' 'second' key='value'}}
@@ -137,5 +137,36 @@ describe "{{#if}}...{{else}}...{{/if}}" do
137
137
 
138
138
  expect(errors).not_to be_empty
139
139
  end
140
+
141
+ it "validates errors the nested else_template when out of context" do
142
+ dependency_tree = { condition: nil }
143
+
144
+ source = <<-HBS
145
+ {{#if ../condition}}
146
+ {{else}}
147
+ {{unallowed_ELSE_method}}
148
+ {{/if}}
149
+ HBS
150
+
151
+ errors = Curlybars.validate(dependency_tree, source)
152
+
153
+ expect(errors.count).to eq(2)
154
+ end
155
+
156
+ it "gives all possible errors found in validation" do
157
+ dependency_tree = { condition: nil }
158
+
159
+ source = <<-HBS
160
+ {{#if ../condition}}
161
+ {{unallowed_IF_method}}
162
+ {{else}}
163
+ {{unallowed_ELSE_method}}
164
+ {{/if}}
165
+ HBS
166
+
167
+ errors = Curlybars.validate(dependency_tree, source)
168
+
169
+ expect(errors.count).to eq(3)
170
+ end
140
171
  end
141
172
  end
@@ -140,7 +140,7 @@ describe "{{#if}}...{{/if}}" do
140
140
  expect(errors).not_to be_empty
141
141
  end
142
142
 
143
- it "validates with errors the helper as condition" do
143
+ it "validates without errors the helper as condition" do
144
144
  dependency_tree = { helper: :helper }
145
145
 
146
146
  source = <<-HBS
@@ -149,7 +149,7 @@ describe "{{#if}}...{{/if}}" do
149
149
 
150
150
  errors = Curlybars.validate(dependency_tree, source)
151
151
 
152
- expect(errors).not_to be_empty
152
+ expect(errors).to be_empty
153
153
  end
154
154
  end
155
155
  end
@@ -139,7 +139,7 @@ describe "{{path}}" do
139
139
  expect(errors).to be_empty
140
140
  end
141
141
 
142
- it "without errors when it goes out of context" do
142
+ it "gives errors errors when it goes out of context" do
143
143
  dependency_tree = {}
144
144
 
145
145
  source = <<-HBS
@@ -148,7 +148,7 @@ describe "{{path}}" do
148
148
 
149
149
  errors = Curlybars.validate(dependency_tree, source)
150
150
 
151
- expect(errors).to be_empty
151
+ expect(errors).not_to be_empty
152
152
  end
153
153
 
154
154
  it "without errors using `this`" do
@@ -257,6 +257,18 @@ describe "{{path}}" do
257
257
  end
258
258
  end
259
259
 
260
+ it "with errors when going outside of scope" do
261
+ dependency_tree = { ok: { ok: { ok: nil } } }
262
+
263
+ source = <<~HBS
264
+ {{../ok.ok.ok}}
265
+ HBS
266
+
267
+ errors = Curlybars.validate(dependency_tree, source)
268
+
269
+ expect(errors.first.message).to eq("'../ok.ok.ok' goes out of scope")
270
+ end
271
+
260
272
  describe "raises exact location of unallowed steps" do
261
273
  let(:dependency_tree) { { ok: { allowed: { ok: nil } } } }
262
274
 
@@ -96,7 +96,7 @@ describe "{{#unless}}...{{else}}...{{/unless}}" do
96
96
  expect(errors).not_to be_empty
97
97
  end
98
98
 
99
- it "validates with errors the helper as condition" do
99
+ it "validates without errors when using a helper in the condition" do
100
100
  dependency_tree = { helper: :helper }
101
101
 
102
102
  source = <<-HBS
@@ -105,7 +105,7 @@ describe "{{#unless}}...{{else}}...{{/unless}}" do
105
105
 
106
106
  errors = Curlybars.validate(dependency_tree, source)
107
107
 
108
- expect(errors).not_to be_empty
108
+ expect(errors).to be_empty
109
109
  end
110
110
 
111
111
  it "validates with errors the nested unless_template" do
@@ -0,0 +1,146 @@
1
+ describe "visitor" do
2
+ let(:source) do
3
+ <<-HBS
4
+ {{#print_args_and_options 'first' 'second' key='value'}}
5
+ {{/print_args_and_options}}
6
+
7
+ {{#render_inverse}}
8
+ fn
9
+ {{else}}
10
+ inverse
11
+ {{@variable}}
12
+ {{/render_inverse}}
13
+
14
+ {{#each foo}}
15
+ top
16
+ {{#each bar}}
17
+ middle
18
+ {{#each baz}}
19
+ inner
20
+ {{else}}
21
+ inner inverse
22
+ {{/each}}
23
+ {{/each}}
24
+ {{/each}}
25
+
26
+ {{#if valid}}
27
+ if_template
28
+ {{#if bar}}
29
+ foo
30
+ {{else}}
31
+ qux
32
+ {{/if}}
33
+ {{/if}}
34
+
35
+ {{#if baz}}
36
+ qux
37
+ {{/if}}
38
+
39
+ {{> partial}}
40
+
41
+ {{user.avatar.url}}
42
+ {{#with this}}
43
+ {{user.avatar.url}}
44
+ {{/with}}
45
+
46
+ {{#unless things}}
47
+ hi
48
+ {{/unless}}
49
+ HBS
50
+ end
51
+
52
+ describe ".visit" do
53
+ it "visits BlockHelperElse nodes" do
54
+ visitor = counting_visitor_for(Curlybars::Node::BlockHelperElse)
55
+ output = Curlybars.visit(visitor, source)
56
+ expect(output).to eq(4)
57
+ end
58
+
59
+ it "visits EachElse nodes" do
60
+ visitor = counting_visitor_for(Curlybars::Node::EachElse)
61
+ output = Curlybars.visit(visitor, source)
62
+ expect(output).to eq(3)
63
+ end
64
+
65
+ it "visits IfElse nodes" do
66
+ visitor = counting_visitor_for(Curlybars::Node::IfElse)
67
+ output = Curlybars.visit(visitor, source)
68
+ expect(output).to eq(3)
69
+ end
70
+
71
+ it "visits Item nodes" do
72
+ visitor = counting_visitor_for(Curlybars::Node::Item)
73
+ output = Curlybars.visit(visitor, source)
74
+ expect(output).to eq(42)
75
+ end
76
+
77
+ it "visits Literal nodes" do
78
+ visitor = counting_visitor_for(Curlybars::Node::Literal)
79
+ output = Curlybars.visit(visitor, source)
80
+ expect(output).to eq(3)
81
+ end
82
+
83
+ it "visits Option nodes" do
84
+ visitor = counting_visitor_for(Curlybars::Node::Option)
85
+ output = Curlybars.visit(visitor, source)
86
+ expect(output).to eq(1)
87
+ end
88
+
89
+ it "visits Partial nodes" do
90
+ visitor = counting_visitor_for(Curlybars::Node::Partial)
91
+ output = Curlybars.visit(visitor, source)
92
+ expect(output).to eq(1)
93
+ end
94
+
95
+ it "visits Path nodes" do
96
+ visitor = counting_visitor_for(Curlybars::Node::Path)
97
+ output = Curlybars.visit(visitor, source)
98
+ expect(output).to eq(13)
99
+ end
100
+
101
+ it "visits Root nodes" do
102
+ visitor = counting_visitor_for(Curlybars::Node::Root)
103
+ output = Curlybars.visit(visitor, source)
104
+ expect(output).to eq(1)
105
+ end
106
+
107
+ it "visits Template nodes" do
108
+ visitor = counting_visitor_for(Curlybars::Node::Template)
109
+ output = Curlybars.visit(visitor, source)
110
+ expect(output).to eq(14)
111
+ end
112
+
113
+ it "visits Text nodes" do
114
+ visitor = counting_visitor_for(Curlybars::Node::Text)
115
+ output = Curlybars.visit(visitor, source)
116
+ expect(output).to eq(28)
117
+ end
118
+
119
+ it "visits UnlessElse nodes" do
120
+ visitor = counting_visitor_for(Curlybars::Node::UnlessElse)
121
+ output = Curlybars.visit(visitor, source)
122
+ expect(output).to eq(1)
123
+ end
124
+
125
+ it "visits Variable nodes" do
126
+ visitor = counting_visitor_for(Curlybars::Node::Variable)
127
+ output = Curlybars.visit(visitor, source)
128
+ expect(output).to eq(1)
129
+ end
130
+
131
+ it "visits WithElse nodes" do
132
+ visitor = counting_visitor_for(Curlybars::Node::WithElse)
133
+ output = Curlybars.visit(visitor, source)
134
+ expect(output).to eq(1)
135
+ end
136
+ end
137
+
138
+ def counting_visitor_for(klass)
139
+ Class.new(Curlybars::Visitor) do
140
+ define_method "visit_#{klass.name.demodulize.underscore}" do |node|
141
+ self.context += 1
142
+ super(node)
143
+ end
144
+ end.new(0)
145
+ end
146
+ end
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.1.4
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Libo Cannici
@@ -10,10 +10,11 @@ authors:
10
10
  - Mauro Codella
11
11
  - Luís Almeida
12
12
  - Andreas Garnæs
13
+ - Augusto Silva
13
14
  autorequire:
14
15
  bindir: bin
15
16
  cert_chain: []
16
- date: 2018-10-25 00:00:00.000000000 Z
17
+ date: 2020-09-29 00:00:00.000000000 Z
17
18
  dependencies:
18
19
  - !ruby/object:Gem::Dependency
19
20
  name: actionpack
@@ -190,7 +191,7 @@ dependencies:
190
191
  description: |-
191
192
  A view layer for your Rails apps that separates structure and logic, using Handlebars templates.
192
193
  Strongly inspired by Curly Template gem by Daniel Schierbeck.
193
- email: libo@zendesk.com
194
+ email: vikings@zendesk.com
194
195
  executables: []
195
196
  extensions: []
196
197
  extra_rdoc_files: []
@@ -234,6 +235,7 @@ files:
234
235
  - lib/curlybars/safe_buffer.rb
235
236
  - lib/curlybars/template_handler.rb
236
237
  - lib/curlybars/version.rb
238
+ - lib/curlybars/visitor.rb
237
239
  - spec/acceptance/application_layout_spec.rb
238
240
  - spec/acceptance/collection_blocks_spec.rb
239
241
  - spec/acceptance/global_helper_spec.rb
@@ -270,6 +272,7 @@ files:
270
272
  - spec/integration/node/with_spec.rb
271
273
  - spec/integration/processor/tilde_spec.rb
272
274
  - spec/integration/processors_spec.rb
275
+ - spec/integration/visitor_spec.rb
273
276
  homepage: https://github.com/zendesk/curlybars
274
277
  licenses:
275
278
  - Apache-2.0
@@ -290,8 +293,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
290
293
  - !ruby/object:Gem::Version
291
294
  version: '0'
292
295
  requirements: []
293
- rubyforge_project:
294
- rubygems_version: 2.7.3
296
+ rubygems_version: 3.0.3
295
297
  signing_key:
296
298
  specification_version: 4
297
299
  summary: Create your views using Handlebars templates!
@@ -312,6 +314,7 @@ test_files:
312
314
  - spec/integration/processor/tilde_spec.rb
313
315
  - spec/integration/exception_spec.rb
314
316
  - spec/integration/processors_spec.rb
317
+ - spec/integration/visitor_spec.rb
315
318
  - spec/integration/node/escape_spec.rb
316
319
  - spec/integration/node/unless_else_spec.rb
317
320
  - spec/integration/node/output_spec.rb