ruby-handlebars 0.0.6 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: ae92539214ecdf59a678a44935f42fdb2f2264a6
4
- data.tar.gz: 6e0da52049ff769f168933b3fd8b0f0832c42e8a
2
+ SHA256:
3
+ metadata.gz: 5bb7ada122dcffa528888da4c6a4b1e2822cbecd5626d14e559081a8881670b8
4
+ data.tar.gz: aeed1a35e4a57bee75e9fa0f67ddfd0c26a07fd33c613608974492f258eb6c06
5
5
  SHA512:
6
- metadata.gz: 8931ee0570230133d9f0f09fb38595e5362a01412890c4ce43d35be5b9335d5dc15ba7c0c09e465c50bdf2e46dedbbe758c1fbd6ce169cb31cb182224ee788f5
7
- data.tar.gz: fe7e970a5d0626e90044268ccaf4e5c1aa8380a77bb2f5f9782d422e129d9c3ad098b66db0f3638157db620bb52b885e7def81ca02a4e441c4a5ecb5d19407dc
6
+ metadata.gz: 6505f37bb31fbdd0d344d5404c1bc5b5a6259e97120039037dc9ae2aa45635cfa2f7b2c40be1c4b5faced929dab3404665a451e495465c94e446c66dbb0362bf
7
+ data.tar.gz: ca528f8a90911381216fa5af4fe7aba5d36ca83c84dd88f084023860fe3dc6067970f5b5155f9a296ee2baf814024ff32b5fcdc14edc120f75b4257ef6220e0b
data/README.md CHANGED
@@ -105,9 +105,7 @@ Limitations and roadmap
105
105
 
106
106
  This gem does not reuse the real Handlebars code (the JS one) and not everything is handled yet (but it will be someday ;) ):
107
107
 
108
- - there is no escaping, all strings are considered as safe (so ``{{{ my_var }}}`` and ``{{ my_var }}``) will output the same thing
109
108
  - the parser is not fully tested yet, it may complain with spaces ...
110
- - curly bracket are __not__ usable in the template content yet. one workaround is to create simple helpers to generate them
111
109
  - parsing errors are, well, not helpful at all
112
110
 
113
111
  Aknowledgements
@@ -115,3 +113,5 @@ Aknowledgements
115
113
 
116
114
  This gem would simply not exist if the handlebars team was not here. Thanks a lot for this awesome templating system.
117
115
  Thanks a lot to @cowboyd for the [handlebars.rb](https://github.com/cowboyd/handlebars.rb) gem. We used it for a while and it's great (and as told at the beginning of the README, if you do not need any Windows support, use handlebars.rb instead ;) )
116
+
117
+ Thanks a lot to the contributors @mvz and @schuetzm for making it a way better Handlebars renderer :)
@@ -2,15 +2,18 @@ require_relative 'ruby-handlebars/parser'
2
2
  require_relative 'ruby-handlebars/tree'
3
3
  require_relative 'ruby-handlebars/template'
4
4
  require_relative 'ruby-handlebars/helper'
5
+ require_relative 'ruby-handlebars/escapers/html_escaper'
5
6
 
6
7
  module Handlebars
7
8
  class Handlebars
8
9
  include Context
10
+ attr_reader :escaper
9
11
 
10
- def initialize
12
+ def initialize()
11
13
  @helpers = {}
12
14
  @partials = {}
13
15
  register_default_helpers
16
+ set_escaper
14
17
  end
15
18
 
16
19
  def compile(template)
@@ -37,6 +40,10 @@ module Handlebars
37
40
  @data = ctx
38
41
  end
39
42
 
43
+ def set_escaper(escaper = nil)
44
+ @escaper = escaper || Escapers::HTMLEscaper
45
+ end
46
+
40
47
  private
41
48
 
42
49
  PARSER = Parser.new
@@ -67,20 +74,18 @@ module Handlebars
67
74
 
68
75
  def register_each_helper
69
76
  register_helper('each') do |context, items, block, else_block|
70
- current_this = context.get('this')
71
-
72
77
  if (items.nil? || items.empty?)
73
78
  if else_block
74
79
  result = else_block.fn(context)
75
80
  end
76
81
  else
77
- result = items.map do |item|
78
- context.add_item(:this, item)
79
- block.fn(context)
80
- end.join('')
82
+ context.with_temporary_context(:this => nil, :@index => 0, :@first => false, :@last => false) do
83
+ result = items.each_with_index.map do |item, index|
84
+ context.add_items(:this => item, :@index => index, :@first => (index == 0), :@last => (index == items.length - 1))
85
+ block.fn(context)
86
+ end.join('')
87
+ end
81
88
  end
82
-
83
- context.add_item(:this, current_this)
84
89
  result
85
90
  end
86
91
  end
@@ -2,8 +2,12 @@ module Handlebars
2
2
  module Context
3
3
  def get(path)
4
4
  items = path.split('.'.freeze)
5
+ if locals.key? items.first.to_sym
6
+ current = locals
7
+ else
8
+ current = @data
9
+ end
5
10
 
6
- current = @data
7
11
  until items.empty?
8
12
  current = get_attribute(current, items.shift)
9
13
  end
@@ -12,16 +16,32 @@ module Handlebars
12
16
  end
13
17
 
14
18
  def add_item(key, value)
15
- @data[key] = value
19
+ locals[key.to_sym] = value
20
+ end
21
+
22
+ def add_items(hash)
23
+ hash.map { |k, v| add_item(k, v) }
24
+ end
25
+
26
+ def with_temporary_context(args = {})
27
+ saved = args.keys.collect { |key| [key, get(key.to_s)] }.to_h
28
+
29
+ add_items(args)
30
+ yield
31
+ locals.merge!(saved)
16
32
  end
17
33
 
18
34
  private
19
35
 
36
+ def locals
37
+ @locals ||= {}
38
+ end
39
+
20
40
  def get_attribute(item, attribute)
21
41
  sym_attr = attribute.to_sym
22
42
  str_attr = attribute.to_s
23
43
 
24
- if item.respond_to?(:[])
44
+ if item.respond_to?(:[]) && item.respond_to?(:has_key?)
25
45
  if item.has_key?(sym_attr)
26
46
  return item[sym_attr]
27
47
  elsif item.has_key?(str_attr)
@@ -29,10 +49,6 @@ module Handlebars
29
49
  end
30
50
  end
31
51
 
32
- if item.instance_variables.include?("@#{attribute}")
33
- return item.instance_variable_get("@#{attribute}")
34
- end
35
-
36
52
  if item.respond_to?(sym_attr)
37
53
  return item.send(sym_attr)
38
54
  end
@@ -0,0 +1,9 @@
1
+ module Handlebars
2
+ module Escapers
3
+ class DummyEscaper
4
+ def self.escape(value)
5
+ value
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Handlebars
2
+ module Escapers
3
+ class HTMLEscaper
4
+ def self.escape(value)
5
+ CGI::escapeHTML(value)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -13,24 +13,37 @@ module Handlebars
13
13
 
14
14
  rule(:docurly) { ocurly >> ocurly }
15
15
  rule(:dccurly) { ccurly >> ccurly }
16
+ rule(:tocurly) { ocurly >> ocurly >> ocurly }
17
+ rule(:tccurly) { ccurly >> ccurly >> ccurly }
16
18
 
17
- rule(:identifier) { match['a-zA-Z0-9_\?'].repeat(1) }
19
+ rule(:identifier) { match['@a-zA-Z0-9_\?'].repeat(1) }
18
20
  rule(:path) { identifier >> (dot >> identifier).repeat }
19
21
 
20
- rule(:template_content) { match('[^{}]').repeat(1).as(:template_content) }
22
+ rule(:nocurly) { match('[^{}]') }
23
+ rule(:eof) { any.absent? }
24
+ rule(:template_content) {
25
+ (
26
+ ocurly >> nocurly | # Opening curly that doesn't start a {{}}
27
+ ocurly >> eof | # Opening curly that doesn't start a {{}} because it's the end
28
+ ccurly | # Closing curly that is not inside a {{}}
29
+ nocurly
30
+ ).repeat(1).as(:template_content) }
21
31
 
22
- rule(:replacement) { docurly >> space? >> path.as(:replaced_item) >> space? >> dccurly}
23
- rule(:safe_replacement) { ocurly >> replacement >> ccurly }
32
+ rule(:unsafe_replacement) { docurly >> space? >> path.as(:replaced_unsafe_item) >> space? >> dccurly }
33
+ rule(:safe_replacement) { tocurly >> space? >> path.as(:replaced_safe_item) >> space? >> tccurly }
24
34
 
25
- rule(:sq_string) { match("'") >> match("[^']").repeat(1).as(:str_content) >> match("'") }
26
- rule(:dq_string) { match('"') >> match('[^"]').repeat(1).as(:str_content) >> match('"') }
35
+ rule(:sq_string) { match("'") >> match("[^']").repeat.maybe.as(:str_content) >> match("'") }
36
+ rule(:dq_string) { match('"') >> match('[^"]').repeat.maybe.as(:str_content) >> match('"') }
27
37
  rule(:string) { sq_string | dq_string }
28
38
 
29
- rule(:parameter) { (path | string).as(:parameter_name) }
39
+ rule(:parameter) {
40
+ (path | string).as(:parameter_name) |
41
+ (str('(') >> space? >> identifier.as(:safe_helper_name) >> (space? >> parameters.as(:parameters)).maybe >> space? >> str(')'))
42
+ }
30
43
  rule(:parameters) { parameter >> (space >> parameter).repeat }
31
44
 
32
- rule(:unsafe_helper) { docurly >> space? >> identifier.as(:helper_name) >> (space? >> parameters.as(:parameters)).maybe >> space? >> dccurly}
33
- rule(:safe_helper) { ocurly >> helper >> ccurly }
45
+ rule(:unsafe_helper) { docurly >> space? >> identifier.as(:unsafe_helper_name) >> (space? >> parameters.as(:parameters)).maybe >> space? >> dccurly }
46
+ rule(:safe_helper) { tocurly >> space? >> identifier.as(:safe_helper_name) >> (space? >> parameters.as(:parameters)).maybe >> space? >> tccurly }
34
47
 
35
48
  rule(:helper) { unsafe_helper | safe_helper }
36
49
 
@@ -58,8 +71,8 @@ module Handlebars
58
71
  dccurly
59
72
  }
60
73
 
61
- rule(:block) { (template_content | replacement | safe_replacement | helper | partial | block_helper ).repeat.as(:block_items) }
74
+ rule(:block) { (template_content | unsafe_replacement | safe_replacement | helper | partial | block_helper ).repeat.as(:block_items) }
62
75
 
63
76
  root :block
64
77
  end
65
- end
78
+ end
@@ -7,12 +7,12 @@ module Handlebars
7
7
  @ast = ast
8
8
  end
9
9
 
10
- def call(args)
11
- if args.is_a? Hash
10
+ def call(args = nil)
11
+ if args
12
12
  @hbs.set_context(args)
13
13
  end
14
14
 
15
15
  @ast.eval(@hbs)
16
16
  end
17
17
  end
18
- end
18
+ end
@@ -1,5 +1,3 @@
1
- require 'colorize'
2
-
3
1
  module Handlebars
4
2
  module Tree
5
3
  class TreeItem < Struct
@@ -28,6 +26,12 @@ module Handlebars
28
26
  end
29
27
  end
30
28
 
29
+ class EscapedReplacement < Replacement
30
+ def _eval(context)
31
+ context.escaper.escape(super(context).to_s)
32
+ end
33
+ end
34
+
31
35
  class String < TreeItem.new(:content)
32
36
  def _eval(context)
33
37
  return content
@@ -50,9 +54,15 @@ module Handlebars
50
54
  end
51
55
  end
52
56
 
57
+ class EscapedHelper < Helper
58
+ def _eval(context)
59
+ context.escaper.escape(super(context).to_s)
60
+ end
61
+ end
62
+
53
63
  class Partial < TreeItem.new(:partial_name)
54
64
  def _eval(context)
55
- context.get_partial(partial_name.to_s).call(context)
65
+ context.get_partial(partial_name.to_s).call
56
66
  end
57
67
  end
58
68
 
@@ -70,12 +80,19 @@ module Handlebars
70
80
 
71
81
  class Transform < Parslet::Transform
72
82
  rule(template_content: simple(:content)) {Tree::TemplateContent.new(content)}
73
- rule(replaced_item: simple(:item)) {Tree::Replacement.new(item)}
83
+ rule(replaced_unsafe_item: simple(:item)) {Tree::EscapedReplacement.new(item)}
84
+ rule(replaced_safe_item: simple(:item)) {Tree::Replacement.new(item)}
74
85
  rule(str_content: simple(:content)) {Tree::String.new(content)}
75
86
  rule(parameter_name: simple(:name)) {Tree::Parameter.new(name)}
76
87
 
77
88
  rule(
78
- helper_name: simple(:name),
89
+ unsafe_helper_name: simple(:name),
90
+ parameters: subtree(:parameters)
91
+ ) {
92
+ Tree::EscapedHelper.new(name, parameters)
93
+ }
94
+ rule(
95
+ safe_helper_name: simple(:name),
79
96
  parameters: subtree(:parameters)
80
97
  ) {
81
98
  Tree::Helper.new(name, parameters)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-handlebars
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vincent Pretre
@@ -9,88 +9,68 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2018-02-12 00:00:00.000000000 Z
12
+ date: 2019-06-26 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: parslet
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  requirements:
18
- - - ">="
19
- - !ruby/object:Gem::Version
20
- version: 1.6.2
21
18
  - - "~>"
22
19
  - !ruby/object:Gem::Version
23
20
  version: '1.6'
24
- type: :runtime
25
- prerelease: false
26
- version_requirements: !ruby/object:Gem::Requirement
27
- requirements:
28
21
  - - ">="
29
22
  - !ruby/object:Gem::Version
30
23
  version: 1.6.2
31
- - - "~>"
32
- - !ruby/object:Gem::Version
33
- version: '1.6'
34
- - !ruby/object:Gem::Dependency
35
- name: colorize
36
- requirement: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- version: 0.7.5
41
- - - "~>"
42
- - !ruby/object:Gem::Version
43
- version: '0.7'
44
24
  type: :runtime
45
25
  prerelease: false
46
26
  version_requirements: !ruby/object:Gem::Requirement
47
27
  requirements:
48
- - - ">="
49
- - !ruby/object:Gem::Version
50
- version: 0.7.5
51
28
  - - "~>"
52
29
  - !ruby/object:Gem::Version
53
- version: '0.7'
30
+ version: '1.6'
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.6.2
54
34
  - !ruby/object:Gem::Dependency
55
35
  name: pry
56
36
  requirement: !ruby/object:Gem::Requirement
57
37
  requirements:
58
- - - ">="
59
- - !ruby/object:Gem::Version
60
- version: 0.10.1
61
38
  - - "~>"
62
39
  - !ruby/object:Gem::Version
63
40
  version: '0.10'
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 0.10.1
64
44
  type: :development
65
45
  prerelease: false
66
46
  version_requirements: !ruby/object:Gem::Requirement
67
47
  requirements:
68
- - - ">="
69
- - !ruby/object:Gem::Version
70
- version: 0.10.1
71
48
  - - "~>"
72
49
  - !ruby/object:Gem::Version
73
50
  version: '0.10'
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: 0.10.1
74
54
  - !ruby/object:Gem::Dependency
75
55
  name: pry-stack_explorer
76
56
  requirement: !ruby/object:Gem::Requirement
77
57
  requirements:
78
- - - ">="
79
- - !ruby/object:Gem::Version
80
- version: 0.4.9.1
81
58
  - - "~>"
82
59
  - !ruby/object:Gem::Version
83
60
  version: '0.4'
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: 0.4.9.1
84
64
  type: :development
85
65
  prerelease: false
86
66
  version_requirements: !ruby/object:Gem::Requirement
87
67
  requirements:
88
- - - ">="
89
- - !ruby/object:Gem::Version
90
- version: 0.4.9.1
91
68
  - - "~>"
92
69
  - !ruby/object:Gem::Version
93
70
  version: '0.4'
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: 0.4.9.1
94
74
  - !ruby/object:Gem::Dependency
95
75
  name: rspec
96
76
  requirement: !ruby/object:Gem::Requirement
@@ -115,62 +95,62 @@ dependencies:
115
95
  name: rspec-mocks
116
96
  requirement: !ruby/object:Gem::Requirement
117
97
  requirements:
118
- - - ">="
119
- - !ruby/object:Gem::Version
120
- version: 3.1.3
121
98
  - - "~>"
122
99
  - !ruby/object:Gem::Version
123
100
  version: '3.1'
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: 3.1.3
124
104
  type: :development
125
105
  prerelease: false
126
106
  version_requirements: !ruby/object:Gem::Requirement
127
107
  requirements:
128
- - - ">="
129
- - !ruby/object:Gem::Version
130
- version: 3.1.3
131
108
  - - "~>"
132
109
  - !ruby/object:Gem::Version
133
110
  version: '3.1'
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: 3.1.3
134
114
  - !ruby/object:Gem::Dependency
135
115
  name: codeclimate-test-reporter
136
116
  requirement: !ruby/object:Gem::Requirement
137
117
  requirements:
138
- - - ">="
139
- - !ruby/object:Gem::Version
140
- version: 0.4.6
141
118
  - - "~>"
142
119
  - !ruby/object:Gem::Version
143
120
  version: '0.4'
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: 0.4.6
144
124
  type: :development
145
125
  prerelease: false
146
126
  version_requirements: !ruby/object:Gem::Requirement
147
127
  requirements:
148
- - - ">="
149
- - !ruby/object:Gem::Version
150
- version: 0.4.6
151
128
  - - "~>"
152
129
  - !ruby/object:Gem::Version
153
130
  version: '0.4'
131
+ - - ">="
132
+ - !ruby/object:Gem::Version
133
+ version: 0.4.6
154
134
  - !ruby/object:Gem::Dependency
155
135
  name: jeweler
156
136
  requirement: !ruby/object:Gem::Requirement
157
137
  requirements:
158
- - - ">="
159
- - !ruby/object:Gem::Version
160
- version: 2.0.1
161
138
  - - "~>"
162
139
  - !ruby/object:Gem::Version
163
140
  version: '2.0'
141
+ - - ">="
142
+ - !ruby/object:Gem::Version
143
+ version: 2.0.1
164
144
  type: :development
165
145
  prerelease: false
166
146
  version_requirements: !ruby/object:Gem::Requirement
167
147
  requirements:
168
- - - ">="
169
- - !ruby/object:Gem::Version
170
- version: 2.0.1
171
148
  - - "~>"
172
149
  - !ruby/object:Gem::Version
173
150
  version: '2.0'
151
+ - - ">="
152
+ - !ruby/object:Gem::Version
153
+ version: 2.0.1
174
154
  description:
175
155
  email: v.pretre@hiptest.net
176
156
  executables: []
@@ -183,6 +163,8 @@ files:
183
163
  - README.md
184
164
  - lib/ruby-handlebars.rb
185
165
  - lib/ruby-handlebars/context.rb
166
+ - lib/ruby-handlebars/escapers/dummy_escaper.rb
167
+ - lib/ruby-handlebars/escapers/html_escaper.rb
186
168
  - lib/ruby-handlebars/helper.rb
187
169
  - lib/ruby-handlebars/parser.rb
188
170
  - lib/ruby-handlebars/template.rb
@@ -205,8 +187,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
205
187
  - !ruby/object:Gem::Version
206
188
  version: '0'
207
189
  requirements: []
208
- rubyforge_project:
209
- rubygems_version: 2.6.10
190
+ rubygems_version: 3.0.4
210
191
  signing_key:
211
192
  specification_version: 4
212
193
  summary: Pure Ruby library for Handlebars templates