twig_ruby 0.0.1 → 0.0.3

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.
Files changed (168) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +116 -0
  3. data/lib/tasks/twig_parity.rake +278 -0
  4. data/lib/twig/auto_hash.rb +7 -1
  5. data/lib/twig/callable.rb +28 -1
  6. data/lib/twig/compiler.rb +35 -3
  7. data/lib/twig/environment.rb +198 -41
  8. data/lib/twig/error/base.rb +81 -16
  9. data/lib/twig/error/loader.rb +8 -0
  10. data/lib/twig/error/logic.rb +8 -0
  11. data/lib/twig/error/runtime.rb +8 -0
  12. data/lib/twig/expression_parser/base.rb +30 -0
  13. data/lib/twig/expression_parser/expression_parsers.rb +57 -0
  14. data/lib/twig/expression_parser/infix/arrow.rb +31 -0
  15. data/lib/twig/expression_parser/infix/binary.rb +34 -0
  16. data/lib/twig/expression_parser/infix/conditional_ternary.rb +39 -0
  17. data/lib/twig/expression_parser/infix/dot.rb +72 -0
  18. data/lib/twig/expression_parser/infix/filter.rb +43 -0
  19. data/lib/twig/expression_parser/infix/function.rb +67 -0
  20. data/lib/twig/expression_parser/infix/is.rb +53 -0
  21. data/lib/twig/expression_parser/infix/is_not.rb +19 -0
  22. data/lib/twig/expression_parser/infix/parses_arguments.rb +84 -0
  23. data/lib/twig/expression_parser/infix/square_bracket.rb +66 -0
  24. data/lib/twig/expression_parser/infix_expression_parser.rb +34 -0
  25. data/lib/twig/expression_parser/prefix/grouping.rb +60 -0
  26. data/lib/twig/expression_parser/prefix/literal.rb +244 -0
  27. data/lib/twig/expression_parser/prefix/unary.rb +29 -0
  28. data/lib/twig/expression_parser/prefix_expression_parser.rb +18 -0
  29. data/lib/twig/extension/base.rb +26 -4
  30. data/lib/twig/extension/core.rb +1076 -48
  31. data/lib/twig/extension/debug.rb +25 -0
  32. data/lib/twig/extension/escaper.rb +73 -0
  33. data/lib/twig/extension/rails.rb +10 -57
  34. data/lib/twig/extension/string_loader.rb +19 -0
  35. data/lib/twig/extension_set.rb +117 -20
  36. data/lib/twig/file_extension_escaping_strategy.rb +35 -0
  37. data/lib/twig/lexer.rb +225 -81
  38. data/lib/twig/loader/array.rb +25 -8
  39. data/lib/twig/loader/chain.rb +93 -0
  40. data/lib/twig/loader/filesystem.rb +106 -7
  41. data/lib/twig/node/auto_escape.rb +18 -0
  42. data/lib/twig/node/base.rb +58 -2
  43. data/lib/twig/node/block.rb +2 -0
  44. data/lib/twig/node/block_reference.rb +5 -1
  45. data/lib/twig/node/body.rb +7 -0
  46. data/lib/twig/node/cache.rb +50 -0
  47. data/lib/twig/node/capture.rb +22 -0
  48. data/lib/twig/node/deprecated.rb +53 -0
  49. data/lib/twig/node/do.rb +19 -0
  50. data/lib/twig/node/embed.rb +43 -0
  51. data/lib/twig/node/expression/array.rb +29 -20
  52. data/lib/twig/node/expression/arrow_function.rb +55 -0
  53. data/lib/twig/node/expression/assign_name.rb +1 -1
  54. data/lib/twig/node/expression/binary/and.rb +17 -0
  55. data/lib/twig/node/expression/binary/base.rb +6 -4
  56. data/lib/twig/node/expression/binary/boolean.rb +24 -0
  57. data/lib/twig/node/expression/binary/concat.rb +20 -0
  58. data/lib/twig/node/expression/binary/elvis.rb +35 -0
  59. data/lib/twig/node/expression/binary/ends_with.rb +24 -0
  60. data/lib/twig/node/expression/binary/floor_div.rb +21 -0
  61. data/lib/twig/node/expression/binary/has_every.rb +20 -0
  62. data/lib/twig/node/expression/binary/has_some.rb +20 -0
  63. data/lib/twig/node/expression/binary/in.rb +20 -0
  64. data/lib/twig/node/expression/binary/matches.rb +24 -0
  65. data/lib/twig/node/expression/binary/not_in.rb +20 -0
  66. data/lib/twig/node/expression/binary/null_coalesce.rb +49 -0
  67. data/lib/twig/node/expression/binary/or.rb +15 -0
  68. data/lib/twig/node/expression/binary/starts_with.rb +24 -0
  69. data/lib/twig/node/expression/binary/xor.rb +17 -0
  70. data/lib/twig/node/expression/block_reference.rb +62 -0
  71. data/lib/twig/node/expression/call.rb +126 -6
  72. data/lib/twig/node/expression/constant.rb +3 -1
  73. data/lib/twig/node/expression/filter/default.rb +37 -0
  74. data/lib/twig/node/expression/filter/raw.rb +31 -0
  75. data/lib/twig/node/expression/filter.rb +2 -2
  76. data/lib/twig/node/expression/function.rb +37 -0
  77. data/lib/twig/node/expression/get_attribute.rb +51 -7
  78. data/lib/twig/node/expression/hash.rb +75 -0
  79. data/lib/twig/node/expression/helper_method.rb +6 -18
  80. data/lib/twig/node/expression/macro_reference.rb +43 -0
  81. data/lib/twig/node/expression/name.rb +42 -8
  82. data/lib/twig/node/expression/operator_escape.rb +13 -0
  83. data/lib/twig/node/expression/parent.rb +28 -0
  84. data/lib/twig/node/expression/support_defined_test.rb +23 -0
  85. data/lib/twig/node/expression/ternary.rb +7 -1
  86. data/lib/twig/node/expression/test/base.rb +26 -0
  87. data/lib/twig/node/expression/test/constant.rb +35 -0
  88. data/lib/twig/node/expression/test/defined.rb +33 -0
  89. data/lib/twig/node/expression/test/divisible_by.rb +23 -0
  90. data/lib/twig/node/expression/test/even.rb +21 -0
  91. data/lib/twig/node/expression/test/iterable.rb +21 -0
  92. data/lib/twig/node/expression/test/mapping.rb +21 -0
  93. data/lib/twig/node/expression/test/null.rb +21 -0
  94. data/lib/twig/node/expression/test/odd.rb +21 -0
  95. data/lib/twig/node/expression/test/same_as.rb +23 -0
  96. data/lib/twig/node/expression/test/sequence.rb +21 -0
  97. data/lib/twig/node/expression/unary/base.rb +3 -1
  98. data/lib/twig/node/expression/unary/not.rb +18 -0
  99. data/lib/twig/node/expression/unary/spread.rb +18 -0
  100. data/lib/twig/node/expression/unary/string_cast.rb +18 -0
  101. data/lib/twig/node/expression/variable/assign_template.rb +35 -0
  102. data/lib/twig/node/expression/variable/local.rb +35 -0
  103. data/lib/twig/node/expression/variable/template.rb +54 -0
  104. data/lib/twig/node/for.rb +38 -8
  105. data/lib/twig/node/for_loop.rb +0 -22
  106. data/lib/twig/node/if.rb +4 -1
  107. data/lib/twig/node/import.rb +32 -0
  108. data/lib/twig/node/include.rb +38 -8
  109. data/lib/twig/node/macro.rb +79 -0
  110. data/lib/twig/node/module.rb +278 -23
  111. data/lib/twig/node/output.rb +7 -0
  112. data/lib/twig/node/print.rb +4 -1
  113. data/lib/twig/node/set.rb +72 -0
  114. data/lib/twig/node/text.rb +4 -1
  115. data/lib/twig/node/with.rb +50 -0
  116. data/lib/twig/node/yield.rb +6 -1
  117. data/lib/twig/node_traverser.rb +50 -0
  118. data/lib/twig/node_visitor/base.rb +30 -0
  119. data/lib/twig/node_visitor/escaper.rb +165 -0
  120. data/lib/twig/node_visitor/safe_analysis.rb +127 -0
  121. data/lib/twig/node_visitor/spreader.rb +39 -0
  122. data/lib/twig/output_buffer.rb +14 -12
  123. data/lib/twig/parser.rb +281 -8
  124. data/lib/twig/rails/config.rb +33 -0
  125. data/lib/twig/rails/engine.rb +44 -0
  126. data/lib/twig/rails/renderer.rb +41 -0
  127. data/lib/twig/runtime/argument_spreader.rb +46 -0
  128. data/lib/twig/runtime/context.rb +154 -0
  129. data/lib/twig/runtime/enumerable_hash.rb +51 -0
  130. data/lib/twig/runtime/escaper.rb +155 -0
  131. data/lib/twig/runtime/loop_context.rb +81 -0
  132. data/lib/twig/runtime/loop_iterator.rb +60 -0
  133. data/lib/twig/runtime/spread.rb +21 -0
  134. data/lib/twig/runtime_loader/base.rb +12 -0
  135. data/lib/twig/runtime_loader/factory.rb +23 -0
  136. data/lib/twig/template.rb +267 -14
  137. data/lib/twig/template_wrapper.rb +42 -0
  138. data/lib/twig/token.rb +28 -2
  139. data/lib/twig/token_parser/apply.rb +48 -0
  140. data/lib/twig/token_parser/auto_escape.rb +45 -0
  141. data/lib/twig/token_parser/base.rb +26 -0
  142. data/lib/twig/token_parser/block.rb +4 -4
  143. data/lib/twig/token_parser/cache.rb +31 -0
  144. data/lib/twig/token_parser/deprecated.rb +40 -0
  145. data/lib/twig/token_parser/do.rb +19 -0
  146. data/lib/twig/token_parser/embed.rb +62 -0
  147. data/lib/twig/token_parser/extends.rb +4 -3
  148. data/lib/twig/token_parser/for.rb +14 -9
  149. data/lib/twig/token_parser/from.rb +57 -0
  150. data/lib/twig/token_parser/guard.rb +65 -0
  151. data/lib/twig/token_parser/if.rb +9 -9
  152. data/lib/twig/token_parser/import.rb +29 -0
  153. data/lib/twig/token_parser/include.rb +2 -2
  154. data/lib/twig/token_parser/macro.rb +109 -0
  155. data/lib/twig/token_parser/set.rb +76 -0
  156. data/lib/twig/token_parser/use.rb +54 -0
  157. data/lib/twig/token_parser/with.rb +36 -0
  158. data/lib/twig/token_parser/yield.rb +7 -7
  159. data/lib/twig/token_stream.rb +23 -3
  160. data/lib/twig/twig_filter.rb +20 -0
  161. data/lib/twig/twig_function.rb +37 -0
  162. data/lib/twig/twig_test.rb +31 -0
  163. data/lib/twig/util/callable_arguments_extractor.rb +227 -0
  164. data/lib/twig_ruby.rb +21 -2
  165. metadata +148 -6
  166. data/lib/twig/context.rb +0 -64
  167. data/lib/twig/expression_parser.rb +0 -517
  168. data/lib/twig/railtie.rb +0 -60
@@ -4,25 +4,69 @@ module Twig
4
4
  module Node
5
5
  module Expression
6
6
  class GetAttribute < Expression::Base
7
+ include SupportDefinedTest
8
+
7
9
  def initialize(node, attribute, arguments, type, lineno)
8
10
  nodes = { node:, attribute: }
9
11
  nodes[:arguments] = arguments if arguments
10
12
 
11
- super(nodes, { type: }, lineno)
13
+ super(nodes, { type:, ignore_strict_check: false }, lineno)
14
+ end
15
+
16
+ def enable_defined_test
17
+ super
18
+ change_ignore_strict_check(self)
12
19
  end
13
20
 
14
21
  def compile(compiler)
15
- var = compiler.var_name
22
+ compiler.
23
+ raw('::Twig::Extension::Core.get_attribute(env, source_context, ')
24
+
25
+ if attributes[:ignore_strict_check]
26
+ nodes[:node].attributes[:ignore_strict_check] = true
27
+ end
16
28
 
17
29
  compiler.
18
- raw("(#{var} = ").
19
30
  subcompile(nodes[:node]).
20
- raw("\n").
21
- write("::Twig::Extension::Core.get_attribute(#{var}, ").
31
+ raw(', ').
22
32
  subcompile(nodes[:attribute]).
23
33
  raw(', ').
24
- repr(attributes[:type]).
25
- raw('))')
34
+ repr(attributes[:type])
35
+
36
+ if nodes.key?(:arguments)
37
+ compiler.
38
+ raw(', arguments: ').
39
+ subcompile(nodes[:arguments])
40
+ end
41
+
42
+ if define_test_enabled?
43
+ compiler.
44
+ raw(', defined_test: true')
45
+ end
46
+
47
+ if attributes[:ignore_strict_check]
48
+ compiler.
49
+ raw(', ignore_strict_check: true')
50
+ end
51
+
52
+ compiler.
53
+ raw(', lineno: ').
54
+ repr(lineno).
55
+ raw(')')
56
+ end
57
+
58
+ private
59
+
60
+ # @param [GetAttribute] node]
61
+ def change_ignore_strict_check(node)
62
+ node.attributes[:optimizable] = false
63
+ node.attributes[:ignore_strict_check] = true
64
+
65
+ object_node = node.nodes[:node]
66
+
67
+ if object_node.is_a?(GetAttribute)
68
+ change_ignore_strict_check(object_node)
69
+ end
26
70
  end
27
71
  end
28
72
  end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Twig
4
+ module Node
5
+ module Expression
6
+ class Hash < Expression::Base
7
+ include Expression::SupportDefinedTest
8
+
9
+ def initialize(elements, lineno)
10
+ super(elements, {}, lineno)
11
+
12
+ @index = -1
13
+ end
14
+
15
+ # @param [Expression::Base] value
16
+ # @param [Expression::Base|nil] key
17
+ def add_element(value, key = nil)
18
+ if key.nil?
19
+ @index += 1
20
+ key = Constant.new(@index, value.lineno)
21
+ end
22
+
23
+ nodes.add(key, value)
24
+ end
25
+
26
+ def compile(compiler)
27
+ if define_test_enabled?
28
+ return compiler.repr(true)
29
+ end
30
+
31
+ compiler.
32
+ raw('{').
33
+ indent
34
+
35
+ first = true
36
+
37
+ key_value_pairs.each do |key, value|
38
+ unless first
39
+ compiler.raw(', ')
40
+ end
41
+
42
+ first = false
43
+
44
+ unless value.is_a?(Expression::Unary::HashSpread)
45
+ case key
46
+ when Variable::Context
47
+ key = Unary::StringCast.new(key, key.lineno)
48
+ when Variable::Local
49
+ key_value = key.attributes[:name]
50
+ key = Constant.new(key_value, key.lineno)
51
+ when Constant
52
+ key.attributes[:value]
53
+ end
54
+
55
+ compiler.
56
+ subcompile(key).
57
+ raw(' => ')
58
+ end
59
+
60
+ compiler.
61
+ subcompile(value)
62
+ end
63
+
64
+ compiler.
65
+ outdent.
66
+ raw('}')
67
+ end
68
+
69
+ def key_value_pairs
70
+ nodes.each_value.each_slice(2)
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -3,27 +3,15 @@
3
3
  module Twig
4
4
  module Node
5
5
  module Expression
6
- class HelperMethod < Expression::Base
7
- def initialize(name, args, lineno)
8
- super({ args: }, { name: }, lineno)
6
+ class HelperMethod < Call
7
+ def initialize(name, arguments, lineno)
8
+ super({ arguments: }, { name: }, lineno)
9
9
  end
10
10
 
11
- def compile(compiler)
12
- compiler.
13
- raw("@call_context.#{attributes[:name]}(")
11
+ private
14
12
 
15
- nodes[:args].nodes.each do |key, value|
16
- unless key.is_a?(Integer)
17
- compiler.raw("#{key}: ")
18
- end
19
-
20
- compiler.
21
- subcompile(value).
22
- raw(',')
23
- end
24
-
25
- compiler.
26
- raw(')')
13
+ def callable_method
14
+ "context.call_context.method(:#{attributes[:name]})"
27
15
  end
28
16
  end
29
17
  end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Twig
4
+ module Node
5
+ module Expression
6
+ # Represents a macro call node.
7
+ class MacroReference < Expression::Base
8
+ include SupportDefinedTest
9
+
10
+ # @param [Variable::Template] template
11
+ # @param [String] name
12
+ # @param [Expression::Base] arguments
13
+ # @param [Integer] lineno
14
+ def initialize(template, name, arguments, lineno)
15
+ super({ template:, arguments: }, { name: }, lineno)
16
+ end
17
+
18
+ # @param [Compiler] compiler
19
+ def compile(compiler)
20
+ if define_test_enabled?
21
+ compiler.
22
+ subcompile(nodes[:template]).
23
+ raw('.macro?(').
24
+ repr(attributes[:name]).
25
+ raw(', context').
26
+ raw(')')
27
+ return
28
+ end
29
+
30
+ compiler.
31
+ subcompile(nodes[:template]).
32
+ raw('.render_macro(').
33
+ repr(attributes[:name]).
34
+ raw(', context, ').
35
+ subcompile(nodes[:arguments]).
36
+ raw(', ').
37
+ repr(lineno).
38
+ raw(', source_context)')
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -1,11 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'support_defined_test'
4
+
3
5
  module Twig
4
6
  module Node
5
7
  module Expression
6
8
  class Name < Expression::Base
9
+ include Expression::SupportDefinedTest
10
+
7
11
  SPECIAL_VARS = {
8
- '_self' => 'get_template_name',
12
+ '_self' => 'template_name',
9
13
  '_context' => 'context',
10
14
  '_charset' => 'env.charset',
11
15
  }.freeze
@@ -17,19 +21,49 @@ module Twig
17
21
  name:,
18
22
  is_defined_test: false,
19
23
  ignore_strict_check: false,
20
- alwways_defined: false,
24
+ always_defined: false,
21
25
  }, lineno)
22
26
  end
23
27
 
24
28
  def compile(compiler)
25
29
  name = attributes[:name]
26
30
 
27
- compiler.
28
- raw("(context.key?(:#{name})").
29
- raw(" ? context[:#{name}]").
30
- raw(' : raise("#{').
31
- string(name).
32
- raw('} does not exist"))')
31
+ compiler.add_debug_info(self)
32
+
33
+ if attributes[:name][0] == '@'
34
+ check = "context.call_context.instance_variable_defined?('#{name}')"
35
+ get = "context.call_context.instance_variable_get('#{name}')"
36
+ else
37
+ check = "context.key?(:#{name})"
38
+ get = "context[:#{name}]"
39
+ end
40
+
41
+ if define_test_enabled?
42
+ if attributes[:always_defined] || SPECIAL_VARS.key?(name)
43
+ compiler.repr(true)
44
+ else
45
+ compiler.raw(check)
46
+ end
47
+ elsif SPECIAL_VARS.key?(name)
48
+ compiler.raw(SPECIAL_VARS[name])
49
+ elsif attributes[:always_defined]
50
+ compiler.
51
+ raw(get)
52
+ elsif attributes[:ignore_strict_check] || !compiler.environment.strict_variables?
53
+ compiler.
54
+ raw('(').
55
+ raw(get).
56
+ raw(' || nil)')
57
+ else
58
+ compiler.
59
+ raw("(#{check}").
60
+ raw(" ? #{get}").
61
+ raw(' : raise(::Twig::Error::Runtime.new("Variable \"#{').
62
+ string(name).
63
+ raw('}\" does not exist.", ').
64
+ repr(lineno).
65
+ raw(', source_context)))')
66
+ end
33
67
  end
34
68
  end
35
69
  end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Twig
4
+ module Node
5
+ module Expression
6
+ module OperatorEscape
7
+ def operand_names_to_escape
8
+ raise NotImplementedError
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Twig
4
+ module Node
5
+ module Expression
6
+ # Represents a parent node
7
+ class Parent < Expression::Base
8
+ # @param [String] name
9
+ # @param [Integer] lineno
10
+ def initialize(name, lineno)
11
+ super({}, { output: false, name: }, lineno)
12
+ end
13
+
14
+ def compile(compiler)
15
+ if attributes[:output]
16
+ compiler.add_debug_info(self)
17
+ raise NotImplementedError
18
+ else
19
+ compiler.
20
+ raw('render_parent_block(').
21
+ string(attributes[:name]).
22
+ raw(', context, blocks)')
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Twig
4
+ module Node
5
+ module Expression
6
+ module SupportDefinedTest
7
+ def enable_defined_test
8
+ self.defined_test = true
9
+ end
10
+
11
+ def define_test_enabled?
12
+ defined_test
13
+ end
14
+
15
+ private
16
+
17
+ # @param [Boolean] defined_test
18
+ # @return [Boolean]
19
+ attr_accessor :defined_test
20
+ end
21
+ end
22
+ end
23
+ end
@@ -4,6 +4,8 @@ module Twig
4
4
  module Node
5
5
  module Expression
6
6
  class Ternary < Expression::Base
7
+ include OperatorEscape
8
+
7
9
  def initialize(test, left, right, lineno)
8
10
  super({
9
11
  test:,
@@ -14,7 +16,7 @@ module Twig
14
16
 
15
17
  def compile(compiler)
16
18
  compiler.
17
- raw('((').
19
+ raw('(::Twig::Extension::Core.bool(').
18
20
  subcompile(nodes[:test]).
19
21
  raw(') ? (').
20
22
  subcompile(nodes[:left]).
@@ -22,6 +24,10 @@ module Twig
22
24
  subcompile(nodes[:right]).
23
25
  raw('))')
24
26
  end
27
+
28
+ def operand_names_to_escape
29
+ %i[left right]
30
+ end
25
31
  end
26
32
  end
27
33
  end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Twig
4
+ module Node
5
+ module Expression
6
+ module Test
7
+ class Base < Call
8
+ # @param [Node::Expression::Base] node
9
+ # @param [TwigTest] test
10
+ # @param [Node::Base] arguments
11
+ # @param [Integer] lineno
12
+ def initialize(node, test, arguments, lineno)
13
+ super({
14
+ node:,
15
+ arguments: arguments || Node::Empty.new,
16
+ }, {
17
+ name: test.name,
18
+ type: :test,
19
+ twig_callable: test,
20
+ }, lineno)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Twig
4
+ module Node
5
+ module Expression
6
+ module Test
7
+ # Checks if a variable is the exact same value as a constant.
8
+ #
9
+ # {% if post.status is constant('Post::PUBLISHED') %}
10
+ # the status attribute is exactly the same as Post::PUBLISHED
11
+ # {% endif %}
12
+ #
13
+ class Constant < Test::Base
14
+ def compile(compiler)
15
+ compiler.
16
+ raw('(').
17
+ subcompile(nodes[:node]).
18
+ raw(' == ::Twig::Extension::Core.constant(')
19
+
20
+ compiler.
21
+ subcompile(nodes[:arguments].nodes[0])
22
+
23
+ if nodes[:arguments].nodes.key?(1)
24
+ compiler.
25
+ raw(', ').
26
+ subcompile(nodes[:arguments].nodes[1])
27
+ end
28
+
29
+ compiler.raw('))')
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Twig
4
+ module Node
5
+ module Expression
6
+ module Test
7
+ # Checks if an expression is defined
8
+ #
9
+ # {{ var is defined }}
10
+ class Defined < Test::Base
11
+ # @param [Node::Expression::Base] node
12
+ # @param [TwigTest] name
13
+ # @param [Node::Base] arguments
14
+ # @param [Integer] lineno
15
+ def initialize(node, name, arguments, lineno)
16
+ unless node.is_a?(SupportDefinedTest)
17
+ raise Error::Syntax.new('The "defined" test only works with simple variables.', lineno)
18
+ end
19
+
20
+ node.enable_defined_test
21
+
22
+ super
23
+ end
24
+
25
+ def compile(compiler)
26
+ compiler.
27
+ subcompile(nodes[:node])
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Twig
4
+ module Node
5
+ module Expression
6
+ module Test
7
+ # Checks if a number is divisible by another.
8
+ #
9
+ # {{ var is divisible by(3) }}
10
+ class DivisibleBy < Test::Base
11
+ def compile(compiler)
12
+ compiler.
13
+ raw('(').
14
+ subcompile(nodes[:node]).
15
+ raw(' % ').
16
+ subcompile(nodes[:arguments].nodes[0]).
17
+ raw(' == 0)')
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Twig
4
+ module Node
5
+ module Expression
6
+ module Test
7
+ # Checks if a number is even.
8
+ #
9
+ # {{ var is even }}
10
+ class Even < Test::Base
11
+ def compile(compiler)
12
+ compiler.
13
+ raw('(').
14
+ subcompile(nodes[:node]).
15
+ raw(' % 2 == 0)')
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Twig
4
+ module Node
5
+ module Expression
6
+ module Test
7
+ # Checks if a variable is iterable
8
+ #
9
+ # {{ var is iterable }}
10
+ class Iterable < Test::Base
11
+ def compile(compiler)
12
+ compiler.
13
+ raw('(').
14
+ subcompile(nodes[:node]).
15
+ raw(').respond_to?(:each)')
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Twig
4
+ module Node
5
+ module Expression
6
+ module Test
7
+ # Checks if a variable is a mapping (Hash)
8
+ #
9
+ # {{ var is mapping }}
10
+ class Mapping < Test::Base
11
+ def compile(compiler)
12
+ compiler.
13
+ raw('(').
14
+ subcompile(nodes[:node]).
15
+ raw(').is_a?(Hash)')
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Twig
4
+ module Node
5
+ module Expression
6
+ module Test
7
+ # Checks if a variable is null.
8
+ #
9
+ # {{ var is null }}
10
+ class Null < Test::Base
11
+ def compile(compiler)
12
+ compiler.
13
+ raw('(').
14
+ subcompile(nodes[:node]).
15
+ raw(' == nil)')
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Twig
4
+ module Node
5
+ module Expression
6
+ module Test
7
+ # Checks if a number is odd.
8
+ #
9
+ # {{ var is odd }}
10
+ class Odd < Test::Base
11
+ def compile(compiler)
12
+ compiler.
13
+ raw('(').
14
+ subcompile(nodes[:node]).
15
+ raw(' % 2 != 0)')
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Twig
4
+ module Node
5
+ module Expression
6
+ module Test
7
+ # Checks if a variable is the same as another.
8
+ #
9
+ # {{ var is same as(other) }}
10
+ class SameAs < Test::Base
11
+ def compile(compiler)
12
+ compiler.
13
+ raw('(').
14
+ subcompile(nodes[:node]).
15
+ raw(' == ').
16
+ subcompile(nodes[:arguments].nodes[0]).
17
+ raw(')')
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Twig
4
+ module Node
5
+ module Expression
6
+ module Test
7
+ # Checks if a variable is a sequence (array)
8
+ #
9
+ # {{ var is sequence }}
10
+ class Sequence < Test::Base
11
+ def compile(compiler)
12
+ compiler.
13
+ raw('(').
14
+ subcompile(nodes[:node]).
15
+ raw(').is_a?(Array)')
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -33,9 +33,11 @@ module Twig
33
33
  end
34
34
 
35
35
  OPERATORS = {
36
- Not: '!',
37
36
  Neg: '-',
38
37
  Pos: '+',
38
+ Spread: '(not rendered)',
39
+ ArraySpread: '*',
40
+ HashSpread: '**',
39
41
  }.freeze
40
42
 
41
43
  # Lots of simple operator classes can just be generated dynamically