curlybars 1.1.4 → 1.4.0

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: 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