twig_ruby 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. checksums.yaml +7 -0
  2. data/lib/twig/auto_hash.rb +17 -0
  3. data/lib/twig/cache/base.rb +31 -0
  4. data/lib/twig/cache/filesystem.rb +47 -0
  5. data/lib/twig/cache/nil.rb +19 -0
  6. data/lib/twig/callable.rb +21 -0
  7. data/lib/twig/compiler.rb +123 -0
  8. data/lib/twig/context.rb +64 -0
  9. data/lib/twig/environment.rb +161 -0
  10. data/lib/twig/error/base.rb +37 -0
  11. data/lib/twig/error/syntax.rb +8 -0
  12. data/lib/twig/expression_parser.rb +517 -0
  13. data/lib/twig/extension/base.rb +23 -0
  14. data/lib/twig/extension/core.rb +89 -0
  15. data/lib/twig/extension/rails.rb +70 -0
  16. data/lib/twig/extension_set.rb +69 -0
  17. data/lib/twig/lexer.rb +372 -0
  18. data/lib/twig/loader/array.rb +39 -0
  19. data/lib/twig/loader/base.rb +32 -0
  20. data/lib/twig/loader/filesystem.rb +45 -0
  21. data/lib/twig/node/base.rb +61 -0
  22. data/lib/twig/node/block.rb +20 -0
  23. data/lib/twig/node/block_reference.rb +17 -0
  24. data/lib/twig/node/empty.rb +11 -0
  25. data/lib/twig/node/expression/array.rb +50 -0
  26. data/lib/twig/node/expression/assign_name.rb +28 -0
  27. data/lib/twig/node/expression/base.rb +20 -0
  28. data/lib/twig/node/expression/binary/base.rb +63 -0
  29. data/lib/twig/node/expression/call.rb +28 -0
  30. data/lib/twig/node/expression/constant.rb +17 -0
  31. data/lib/twig/node/expression/filter.rb +52 -0
  32. data/lib/twig/node/expression/get_attribute.rb +30 -0
  33. data/lib/twig/node/expression/helper_method.rb +31 -0
  34. data/lib/twig/node/expression/name.rb +37 -0
  35. data/lib/twig/node/expression/ternary.rb +28 -0
  36. data/lib/twig/node/expression/unary/base.rb +52 -0
  37. data/lib/twig/node/expression/variable/assign_context.rb +11 -0
  38. data/lib/twig/node/expression/variable/context.rb +11 -0
  39. data/lib/twig/node/for.rb +64 -0
  40. data/lib/twig/node/for_loop.rb +39 -0
  41. data/lib/twig/node/if.rb +50 -0
  42. data/lib/twig/node/include.rb +71 -0
  43. data/lib/twig/node/module.rb +74 -0
  44. data/lib/twig/node/nodes.rb +13 -0
  45. data/lib/twig/node/print.rb +18 -0
  46. data/lib/twig/node/text.rb +20 -0
  47. data/lib/twig/node/yield.rb +54 -0
  48. data/lib/twig/output_buffer.rb +29 -0
  49. data/lib/twig/parser.rb +131 -0
  50. data/lib/twig/railtie.rb +60 -0
  51. data/lib/twig/source.rb +13 -0
  52. data/lib/twig/template.rb +50 -0
  53. data/lib/twig/token.rb +48 -0
  54. data/lib/twig/token_parser/base.rb +20 -0
  55. data/lib/twig/token_parser/block.rb +54 -0
  56. data/lib/twig/token_parser/extends.rb +25 -0
  57. data/lib/twig/token_parser/for.rb +64 -0
  58. data/lib/twig/token_parser/if.rb +64 -0
  59. data/lib/twig/token_parser/include.rb +51 -0
  60. data/lib/twig/token_parser/yield.rb +44 -0
  61. data/lib/twig/token_stream.rb +73 -0
  62. data/lib/twig/twig_filter.rb +21 -0
  63. data/lib/twig_ruby.rb +36 -0
  64. metadata +103 -0
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Twig
4
+ module TokenParser
5
+ # Marks a section of a template as being reusable.
6
+ #
7
+ # {% if condition %}
8
+ # Something
9
+ # {% else %}
10
+ # Something else
11
+ # {% endif %}
12
+ class If < Base
13
+ def parse(token)
14
+ lineno = token.lineno
15
+ expr = parser.expression_parser.parse_expression
16
+ stream = parser.stream
17
+ stream.expect(Token::BLOCK_END_TYPE)
18
+ body = parser.subparse(decide_if_fork)
19
+ tests = [expr, body]
20
+ else_node = nil
21
+
22
+ if_ended = false
23
+ until if_ended
24
+ case stream.next.value
25
+ when 'else'
26
+ stream.expect(Token::BLOCK_END_TYPE)
27
+ else_node = parser.subparse(decide_if_end)
28
+ when 'elsif', 'elseif'
29
+ expr = parser.expression_parser.parse_expression
30
+ stream.expect(Token::BLOCK_END_TYPE)
31
+ body = parser.subparse(decide_if_fork)
32
+ tests.push(expr, body)
33
+ when 'endif'
34
+ if_ended = true
35
+ else
36
+ raise 'Unexpected end of template, expect else, elseif, or endif'
37
+ end
38
+ end
39
+
40
+ stream.expect(Token::BLOCK_END_TYPE)
41
+
42
+ tests_hash = tests.
43
+ each_with_index.
44
+ to_h { |t, i| [i, t] }
45
+
46
+ Node::If.new(Node::Nodes.new(tests_hash), else_node, lineno)
47
+ end
48
+
49
+ def tag
50
+ 'if'
51
+ end
52
+
53
+ private
54
+
55
+ def decide_if_end
56
+ ->(token) { token.test(%w[endif]) }
57
+ end
58
+
59
+ def decide_if_fork
60
+ ->(token) { token.test(%w[elseif elsif else endif]) }
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Twig
4
+ module TokenParser
5
+ class Include < TokenParser::Base
6
+ def parse(token)
7
+ expr = parser.expression_parser.parse_expression
8
+ variables, only, ignore_missing = parse_arguments
9
+
10
+ Node::Include.new(
11
+ expr,
12
+ variables,
13
+ only,
14
+ ignore_missing,
15
+ token.lineno
16
+ )
17
+ end
18
+
19
+ def tag
20
+ 'include'
21
+ end
22
+
23
+ private
24
+
25
+ def parse_arguments
26
+ stream = parser.stream
27
+ ignore_missing = false
28
+
29
+ if stream.next_if(Token::NAME_TYPE, 'ignore')
30
+ stream.expect(Token::NAME_TYPE, 'missing')
31
+
32
+ ignore_missing = true
33
+ end
34
+
35
+ variables = nil
36
+ if stream.next_if(Token::NAME_TYPE, 'with')
37
+ variables = parser.expression_parser.parse_expression
38
+ end
39
+
40
+ only = false
41
+ if stream.next_if(Token::NAME_TYPE, 'only')
42
+ only = true
43
+ end
44
+
45
+ stream.expect(Token::BLOCK_END_TYPE)
46
+
47
+ [variables, only, ignore_missing]
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Twig
4
+ module TokenParser
5
+ class Yield < TokenParser::Base
6
+ def parse(token)
7
+ stream = parser.stream
8
+ lineno = token.lineno
9
+ expr = parser.expression_parser.parse_expression
10
+ arguments = []
11
+
12
+ stream.expect(Token::NAME_TYPE, 'do')
13
+
14
+ if stream.next_if(Token::PUNCTUATION_TYPE, '|')
15
+ until stream.test(Token::PUNCTUATION_TYPE, '|')
16
+ unless arguments.empty?
17
+ stream.expect(Token::PUNCTUATION_TYPE, ',', 'Arguments must be separated by a comma')
18
+ end
19
+
20
+ arguments.push(stream.expect(Token::NAME_TYPE).value)
21
+ end
22
+
23
+ stream.expect(Token::PUNCTUATION_TYPE, '|')
24
+ end
25
+
26
+ stream.expect(Token::BLOCK_END_TYPE)
27
+ body = parser.subparse(decide_yield_end, drop_needle: true)
28
+ stream.expect(Token::BLOCK_END_TYPE)
29
+
30
+ Node::Yield.new(expr, body, arguments, lineno)
31
+ end
32
+
33
+ def tag
34
+ 'yield'
35
+ end
36
+
37
+ private
38
+
39
+ def decide_yield_end
40
+ ->(token) { token.test('endyield') }
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Twig
4
+ # @!attribute [r] source
5
+ # @return [Source]
6
+ class TokenStream
7
+ # @return [Array<Token>]
8
+ attr_reader :tokens
9
+
10
+ # @return [Source]
11
+ attr_reader :source
12
+
13
+ # @param [Array<Token>] tokens
14
+ # @param [Source] source
15
+ def initialize(tokens, source)
16
+ @tokens = tokens
17
+ @source = source
18
+ @current = 0
19
+ end
20
+
21
+ # @return [Token]
22
+ def next
23
+ @current += 1
24
+
25
+ unless tokens[@current]
26
+ raise Error::Syntax.new('Unexpected end of template.', tokens[@current - 1].lineno, source)
27
+ end
28
+
29
+ tokens[@current - 1]
30
+ end
31
+
32
+ # @return [Token]
33
+ def current
34
+ tokens[@current]
35
+ end
36
+
37
+ # @return [Token]
38
+ def expect(type, value = nil, message = nil)
39
+ token = current
40
+
41
+ unless token.test(type, value)
42
+ raise Error::Syntax.new(
43
+ "Expected #{type}(#{value}) but got #{token.type}(#{token.value}) #{message}".rstrip,
44
+ token.lineno,
45
+ source
46
+ )
47
+ end
48
+
49
+ self.next
50
+
51
+ token
52
+ end
53
+
54
+ def test(primary, secondary = nil)
55
+ current.test(primary, secondary)
56
+ end
57
+
58
+ def next_if(primary, secondary = nil)
59
+ current.test(primary, secondary) ? self.next : nil
60
+ end
61
+
62
+ def eof?
63
+ current.type == Token::EOF_TYPE
64
+ end
65
+
66
+ def debug
67
+ tokens.
68
+ map(&:debug).
69
+ map { |type, value| "#{type}(#{value})" }.
70
+ join("\n")
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Twig
4
+ class TwigFilter < Callable
5
+ def initialize(name, callable = nil, options = {})
6
+ super
7
+
8
+ @options = {
9
+ is_safe: nil,
10
+ is_safe_callback: nil,
11
+ pre_escape: nil,
12
+ preserves_safety: nil,
13
+ node_class: Node::Expression::Filter,
14
+ }.merge(@options)
15
+ end
16
+
17
+ def node_class
18
+ @options[:node_class]
19
+ end
20
+ end
21
+ end
data/lib/twig_ruby.rb ADDED
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/core_ext/string/output_safety'
4
+ require 'digest'
5
+ require 'cgi'
6
+
7
+ %w[
8
+ .
9
+ cache
10
+ error
11
+ extension
12
+ loader
13
+ node
14
+ node/expression
15
+ node/expression/binary
16
+ node/expression/unary
17
+ node/expression/variable
18
+ token_parser
19
+ ].each do |directory|
20
+ directory = __dir__ + "/twig/#{directory}/"
21
+ require "#{directory}base.rb" if File.file?("#{directory}base.rb")
22
+
23
+ Dir["#{directory}*.rb"].each do |file|
24
+ next if %w[base.rb railtie.rb].include?(File.basename(file))
25
+
26
+ require file
27
+ end
28
+ end
29
+
30
+ # Railtie
31
+ require 'twig/railtie' if defined?(Rails::Railtie)
32
+
33
+ module Twig
34
+ module Compiled
35
+ end
36
+ end
metadata ADDED
@@ -0,0 +1,103 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: twig_ruby
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Craig Blanchette
8
+ - Fabian Potencier
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2025-01-20 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: ''
14
+ email: craig.blanchette@gmail.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - lib/twig/auto_hash.rb
20
+ - lib/twig/cache/base.rb
21
+ - lib/twig/cache/filesystem.rb
22
+ - lib/twig/cache/nil.rb
23
+ - lib/twig/callable.rb
24
+ - lib/twig/compiler.rb
25
+ - lib/twig/context.rb
26
+ - lib/twig/environment.rb
27
+ - lib/twig/error/base.rb
28
+ - lib/twig/error/syntax.rb
29
+ - lib/twig/expression_parser.rb
30
+ - lib/twig/extension/base.rb
31
+ - lib/twig/extension/core.rb
32
+ - lib/twig/extension/rails.rb
33
+ - lib/twig/extension_set.rb
34
+ - lib/twig/lexer.rb
35
+ - lib/twig/loader/array.rb
36
+ - lib/twig/loader/base.rb
37
+ - lib/twig/loader/filesystem.rb
38
+ - lib/twig/node/base.rb
39
+ - lib/twig/node/block.rb
40
+ - lib/twig/node/block_reference.rb
41
+ - lib/twig/node/empty.rb
42
+ - lib/twig/node/expression/array.rb
43
+ - lib/twig/node/expression/assign_name.rb
44
+ - lib/twig/node/expression/base.rb
45
+ - lib/twig/node/expression/binary/base.rb
46
+ - lib/twig/node/expression/call.rb
47
+ - lib/twig/node/expression/constant.rb
48
+ - lib/twig/node/expression/filter.rb
49
+ - lib/twig/node/expression/get_attribute.rb
50
+ - lib/twig/node/expression/helper_method.rb
51
+ - lib/twig/node/expression/name.rb
52
+ - lib/twig/node/expression/ternary.rb
53
+ - lib/twig/node/expression/unary/base.rb
54
+ - lib/twig/node/expression/variable/assign_context.rb
55
+ - lib/twig/node/expression/variable/context.rb
56
+ - lib/twig/node/for.rb
57
+ - lib/twig/node/for_loop.rb
58
+ - lib/twig/node/if.rb
59
+ - lib/twig/node/include.rb
60
+ - lib/twig/node/module.rb
61
+ - lib/twig/node/nodes.rb
62
+ - lib/twig/node/print.rb
63
+ - lib/twig/node/text.rb
64
+ - lib/twig/node/yield.rb
65
+ - lib/twig/output_buffer.rb
66
+ - lib/twig/parser.rb
67
+ - lib/twig/railtie.rb
68
+ - lib/twig/source.rb
69
+ - lib/twig/template.rb
70
+ - lib/twig/token.rb
71
+ - lib/twig/token_parser/base.rb
72
+ - lib/twig/token_parser/block.rb
73
+ - lib/twig/token_parser/extends.rb
74
+ - lib/twig/token_parser/for.rb
75
+ - lib/twig/token_parser/if.rb
76
+ - lib/twig/token_parser/include.rb
77
+ - lib/twig/token_parser/yield.rb
78
+ - lib/twig/token_stream.rb
79
+ - lib/twig/twig_filter.rb
80
+ - lib/twig_ruby.rb
81
+ homepage: https://rubygems.org/gems/twig-ruby
82
+ licenses:
83
+ - MIT
84
+ metadata:
85
+ rubygems_mfa_required: 'true'
86
+ rdoc_options: []
87
+ require_paths:
88
+ - lib
89
+ required_ruby_version: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ version: '3.4'
94
+ required_rubygems_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ requirements: []
100
+ rubygems_version: 3.6.2
101
+ specification_version: 4
102
+ summary: Twig Templating for Ruby
103
+ test_files: []