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 +4 -4
 - data/lib/curlybars.rb +19 -6
 - data/lib/curlybars/method_whitelist.rb +47 -17
 - data/lib/curlybars/node/if_else.rb +1 -1
 - data/lib/curlybars/node/item.rb +3 -3
 - data/lib/curlybars/node/path.rb +2 -2
 - data/lib/curlybars/node/unless_else.rb +1 -1
 - data/lib/curlybars/rendering_support.rb +1 -1
 - data/lib/curlybars/version.rb +1 -1
 - data/lib/curlybars/visitor.rb +100 -0
 - data/spec/curlybars/method_whitelist_spec.rb +134 -7
 - data/spec/integration/node/helper_spec.rb +12 -0
 - data/spec/integration/node/if_else_spec.rb +31 -0
 - data/spec/integration/node/if_spec.rb +2 -2
 - data/spec/integration/node/path_spec.rb +14 -2
 - data/spec/integration/node/unless_else_spec.rb +2 -2
 - data/spec/integration/visitor_spec.rb +146 -0
 - metadata +8 -5
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 92b6cb3082b3fc52f509b72511b93937529f57170d4f8751f989e17727418889
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: cbe9843e0852ccdb2bd9aabcb4c77e4856de40e60bbc01f6dc10d17e3987754a
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 8a3cb40974bd6568a73b5640452403da9b40e63da6bc152fadd4ea375961cb8c5de21d561f094f5b181359c791ddf4237a0a58022ae2bd3b38d012748ae9eb6b
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 4bd84f5a24ee7fcb3f59fac17844d65638f496f22c263fc1e94c47aac65c84e848718f1695a20421ade814c9e7e98f17b0e10d58b14920fccb33d92987606e6b
         
     | 
    
        data/lib/curlybars.rb
    CHANGED
    
    | 
         @@ -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 
     | 
    
         
            -
                     
     | 
| 
       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(* 
     | 
| 
       4 
     | 
    
         
            -
                   
     | 
| 
       5 
     | 
    
         
            -
                     
     | 
| 
       6 
     | 
    
         
            -
                      if type. 
     | 
| 
       7 
     | 
    
         
            -
                         
     | 
| 
      
 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 
     | 
    
         
            -
                     
     | 
| 
       14 
     | 
    
         
            -
             
     | 
| 
      
 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  
     | 
| 
       18 
     | 
    
         
            -
                     
     | 
| 
      
 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 =  
     | 
| 
      
 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( 
     | 
| 
      
 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( 
     | 
| 
      
 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( 
     | 
| 
      
 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  
     | 
| 
       45 
     | 
    
         
            -
                    methods_schema( 
     | 
| 
      
 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( 
     | 
| 
      
 80 
     | 
    
         
            +
                        type.dependency_tree(context)
         
     | 
| 
       51 
81 
     | 
    
         
             
                      elsif type.is_a?(Array)
         
     | 
| 
       52 
     | 
    
         
            -
                        [type.first.dependency_tree( 
     | 
| 
      
 82 
     | 
    
         
            +
                        [type.first.dependency_tree(context)]
         
     | 
| 
       53 
83 
     | 
    
         
             
                      else
         
     | 
| 
       54 
84 
     | 
    
         
             
                        type
         
     | 
| 
       55 
85 
     | 
    
         
             
                      end
         
     | 
    
        data/lib/curlybars/node/item.rb
    CHANGED
    
    
    
        data/lib/curlybars/node/path.rb
    CHANGED
    
    | 
         @@ -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 
     | 
    
         
            -
                       
     | 
| 
      
 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
         
     | 
| 
         @@ -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)
         
     | 
    
        data/lib/curlybars/version.rb
    CHANGED
    
    
| 
         @@ -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)  
     | 
| 
      
 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: -> {  
     | 
| 
      
 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: {  
     | 
| 
      
 237 
     | 
    
         
            +
                  expect(dummy_class.methods_schema(validation_context_class.new)).to eq(settings: { background_color: nil })
         
     | 
| 
       117 
238 
     | 
    
         
             
                end
         
     | 
| 
       118 
239 
     | 
    
         | 
| 
       119 
     | 
    
         
            -
                it "supports  
     | 
| 
       120 
     | 
    
         
            -
                  dummy_class.class_eval  
     | 
| 
      
 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( 
     | 
| 
      
 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  
     | 
| 
      
 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). 
     | 
| 
      
 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 " 
     | 
| 
      
 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). 
     | 
| 
      
 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  
     | 
| 
      
 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). 
     | 
| 
      
 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. 
     | 
| 
      
 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:  
     | 
| 
      
 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:  
     | 
| 
      
 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 
     | 
    
         
            -
             
     | 
| 
       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
         
     |