curly-templates 0.9.1 → 0.10.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.
- data/CHANGELOG.md +10 -0
- data/curly-templates.gemspec +4 -2
- data/lib/curly.rb +1 -1
- data/lib/curly/compiler.rb +72 -44
- data/lib/curly/scanner.rb +111 -0
- data/spec/compiler_spec.rb +16 -0
- data/spec/scanner_spec.rb +55 -0
- metadata +6 -3
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
### Unreleased
|
2
2
|
|
3
|
+
### Curly 0.10.0 (July 11, 2013)
|
4
|
+
|
5
|
+
* Allow comments in Curly templates using the `{{! ... }}` syntax:
|
6
|
+
|
7
|
+
```
|
8
|
+
{{! This is a comment }}
|
9
|
+
```
|
10
|
+
|
11
|
+
*Daniel Schierbeck*
|
12
|
+
|
3
13
|
### Curly 0.9.1 (June 20, 2013)
|
4
14
|
|
5
15
|
* Better error handling. If a presenter class cannot be found, we not raise a
|
data/curly-templates.gemspec
CHANGED
@@ -4,8 +4,8 @@ Gem::Specification.new do |s|
|
|
4
4
|
s.rubygems_version = '1.3.5'
|
5
5
|
|
6
6
|
s.name = 'curly-templates'
|
7
|
-
s.version = '0.
|
8
|
-
s.date = '2013-
|
7
|
+
s.version = '0.10.0'
|
8
|
+
s.date = '2013-07-11'
|
9
9
|
|
10
10
|
s.summary = "Free your views!"
|
11
11
|
s.description = "A view layer for your Rails apps that separates structure and logic."
|
@@ -41,6 +41,7 @@ Gem::Specification.new do |s|
|
|
41
41
|
lib/curly/invalid_reference.rb
|
42
42
|
lib/curly/presenter.rb
|
43
43
|
lib/curly/railtie.rb
|
44
|
+
lib/curly/scanner.rb
|
44
45
|
lib/curly/template_handler.rb
|
45
46
|
lib/generators/curly/controller/controller_generator.rb
|
46
47
|
lib/generators/curly/controller/templates/presenter.rb.erb
|
@@ -49,6 +50,7 @@ Gem::Specification.new do |s|
|
|
49
50
|
spec/compiler_spec.rb
|
50
51
|
spec/generators/controller_generator_spec.rb
|
51
52
|
spec/presenter_spec.rb
|
53
|
+
spec/scanner_spec.rb
|
52
54
|
spec/spec_helper.rb
|
53
55
|
spec/template_handler_spec.rb
|
54
56
|
]
|
data/lib/curly.rb
CHANGED
data/lib/curly/compiler.rb
CHANGED
@@ -1,64 +1,92 @@
|
|
1
|
+
require 'curly/scanner'
|
1
2
|
require 'curly/invalid_reference'
|
2
3
|
|
3
4
|
module Curly
|
5
|
+
|
6
|
+
# Compiles Curly templates into executable Ruby code.
|
7
|
+
#
|
8
|
+
# A template must be accompanied by a presenter class. This class defines the
|
9
|
+
# references that are valid within the template.
|
10
|
+
#
|
4
11
|
class Compiler
|
5
|
-
|
12
|
+
# Compiles a Curly template to Ruby code.
|
13
|
+
#
|
14
|
+
# template - The template String that should be compiled.
|
15
|
+
# presenter_class - The presenter Class.
|
16
|
+
#
|
17
|
+
# Returns a String containing the Ruby code.
|
18
|
+
def self.compile(template, presenter_class)
|
19
|
+
new(template, presenter_class).compile
|
20
|
+
end
|
6
21
|
|
7
|
-
|
22
|
+
# Whether the Curly template is valid. This includes whether all
|
23
|
+
# references are available on the presenter class.
|
24
|
+
#
|
25
|
+
# template - The template String that should be validated.
|
26
|
+
# presenter_class - The presenter Class.
|
27
|
+
#
|
28
|
+
# Returns true if the template is valid, false otherwise.
|
29
|
+
def self.valid?(template, presenter_class)
|
30
|
+
compile(template, presenter_class)
|
31
|
+
|
32
|
+
true
|
33
|
+
rescue InvalidReference
|
34
|
+
false
|
35
|
+
end
|
8
36
|
|
9
|
-
|
10
|
-
#
|
11
|
-
# template - The template String that should be compiled.
|
12
|
-
#
|
13
|
-
# Returns a String containing the Ruby code.
|
14
|
-
def compile(template, presenter_class)
|
15
|
-
if presenter_class.nil?
|
16
|
-
raise ArgumentError, "presenter class cannot be nil"
|
17
|
-
end
|
37
|
+
attr_reader :template, :presenter_class
|
18
38
|
|
19
|
-
|
20
|
-
|
39
|
+
def initialize(template, presenter_class)
|
40
|
+
@template, @presenter_class = template, presenter_class
|
41
|
+
end
|
21
42
|
|
22
|
-
|
43
|
+
def compile
|
44
|
+
if presenter_class.nil?
|
45
|
+
raise ArgumentError, "presenter class cannot be nil"
|
23
46
|
end
|
24
47
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
# presenter_class - The presenter Class.
|
30
|
-
#
|
31
|
-
# Returns true if the template is valid, false otherwise.
|
32
|
-
def valid?(template, presenter_class)
|
33
|
-
compile(template, presenter_class)
|
34
|
-
|
35
|
-
true
|
36
|
-
rescue InvalidReference
|
37
|
-
false
|
48
|
+
tokens = Scanner.scan(template)
|
49
|
+
|
50
|
+
parts = tokens.map do |type, value|
|
51
|
+
send("compile_#{type}", value)
|
38
52
|
end
|
39
53
|
|
40
|
-
|
54
|
+
parts.join(" << ")
|
55
|
+
end
|
41
56
|
|
42
|
-
|
43
|
-
method, argument = reference.split(".", 2)
|
57
|
+
private
|
44
58
|
|
45
|
-
|
46
|
-
|
47
|
-
end
|
59
|
+
def compile_reference(reference)
|
60
|
+
method, argument = reference.split(".", 2)
|
48
61
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
presenter.#{method}(#{argument.inspect}) {|*args| yield(*args) }
|
53
|
-
RUBY
|
54
|
-
else
|
55
|
-
code = <<-RUBY
|
56
|
-
presenter.#{method} {|*args| yield(*args) }
|
57
|
-
RUBY
|
58
|
-
end
|
62
|
+
unless presenter_class.method_available?(method.to_sym)
|
63
|
+
raise Curly::InvalidReference.new(method.to_sym)
|
64
|
+
end
|
59
65
|
|
60
|
-
|
66
|
+
if presenter_class.instance_method(method).arity == 1
|
67
|
+
# The method accepts a single argument -- pass it in.
|
68
|
+
code = <<-RUBY
|
69
|
+
presenter.#{method}(#{argument.inspect}) {|*args| yield(*args) }
|
70
|
+
RUBY
|
71
|
+
else
|
72
|
+
code = <<-RUBY
|
73
|
+
presenter.#{method} {|*args| yield(*args) }
|
74
|
+
RUBY
|
61
75
|
end
|
76
|
+
|
77
|
+
'ERB::Util.html_escape(%s)' % code.strip
|
78
|
+
end
|
79
|
+
|
80
|
+
def compile_text(text)
|
81
|
+
text.inspect
|
82
|
+
end
|
83
|
+
|
84
|
+
def compile_comment_line(comment)
|
85
|
+
"''" # Replace the content with an empty string.
|
86
|
+
end
|
87
|
+
|
88
|
+
def compile_comment(comment)
|
89
|
+
"''" # Replace the content with an empty string.
|
62
90
|
end
|
63
91
|
end
|
64
92
|
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'strscan'
|
2
|
+
|
3
|
+
module Curly
|
4
|
+
|
5
|
+
# Scans Curly templates for tokens.
|
6
|
+
#
|
7
|
+
# The Scanner goes through the template piece by piece, extracting tokens
|
8
|
+
# until the end of the template is reached.
|
9
|
+
#
|
10
|
+
class Scanner
|
11
|
+
REFERENCE_REGEX = %r(\{\{[\w\.]+\}\})
|
12
|
+
COMMENT_REGEX = %r(\{\{!.*\}\})
|
13
|
+
COMMENT_LINE_REGEX = %r(\s*#{COMMENT_REGEX}\s*\n)
|
14
|
+
|
15
|
+
# Scans a Curly template for tokens.
|
16
|
+
#
|
17
|
+
# source - The String source of the template.
|
18
|
+
#
|
19
|
+
# Example
|
20
|
+
#
|
21
|
+
# Curly::Scanner.scan("hello {{name}}!")
|
22
|
+
# #=> [[:text, "hello "], [:reference, "name"], [:text, "!"]]
|
23
|
+
#
|
24
|
+
# Returns an Array of type/value pairs representing the tokens in the
|
25
|
+
# template.
|
26
|
+
def self.scan(source)
|
27
|
+
new(source).scan
|
28
|
+
end
|
29
|
+
|
30
|
+
def initialize(source)
|
31
|
+
@scanner = StringScanner.new(source)
|
32
|
+
end
|
33
|
+
|
34
|
+
def scan
|
35
|
+
tokens = []
|
36
|
+
tokens << scan_token until @scanner.eos?
|
37
|
+
tokens
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
# Scans the next token in the template.
|
43
|
+
#
|
44
|
+
# Returns a two-element Array, the first element being the Symbol type of
|
45
|
+
# the token and the second being the String value.
|
46
|
+
def scan_token
|
47
|
+
scan_reference ||
|
48
|
+
scan_comment_line ||
|
49
|
+
scan_comment ||
|
50
|
+
scan_text ||
|
51
|
+
scan_remainder
|
52
|
+
end
|
53
|
+
|
54
|
+
# Scans a reference token, if a reference is the next token in the template.
|
55
|
+
#
|
56
|
+
# Returns an Array representing the token, or nil if no reference token can
|
57
|
+
# be found at the current position.
|
58
|
+
def scan_reference
|
59
|
+
if value = @scanner.scan(REFERENCE_REGEX)
|
60
|
+
# Return the reference name excluding "{{" and "}}".
|
61
|
+
[:reference, value[2..-3]]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Scans a comment line token, if a comment line is the next token in the
|
66
|
+
# template.
|
67
|
+
#
|
68
|
+
# Returns an Array representing the token, or nil if no comment line token
|
69
|
+
# can be found at the current position.
|
70
|
+
def scan_comment_line
|
71
|
+
if value = @scanner.scan(COMMENT_LINE_REGEX)
|
72
|
+
# Returns the comment excluding "{{!" and "}}".
|
73
|
+
[:comment_line, value[3..-4]]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Scans a comment token, if a comment is the next token in the template.
|
78
|
+
#
|
79
|
+
# Returns an Array representing the token, or nil if no comment token can
|
80
|
+
# be found at the current position.
|
81
|
+
def scan_comment
|
82
|
+
if value = @scanner.scan(COMMENT_REGEX)
|
83
|
+
# Returns the comment excluding "{{!" and "}}".
|
84
|
+
[:comment, value[3..-3]]
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Scans a text token, if a text is the next token in the template.
|
89
|
+
#
|
90
|
+
# Returns an Array representing the token, or nil if no text token can
|
91
|
+
# be found at the current position.
|
92
|
+
def scan_text
|
93
|
+
if value = @scanner.scan_until(/\{\{/)
|
94
|
+
# Rewind the scanner until before the "{{"
|
95
|
+
@scanner.pos -= 2
|
96
|
+
|
97
|
+
# Return the text up until "{{".
|
98
|
+
[:text, value[0..-3]]
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Scans the remainder of the template and treats it as a text token.
|
103
|
+
#
|
104
|
+
# Returns an Array representing the token, or nil if no text is remaining.
|
105
|
+
def scan_remainder
|
106
|
+
if value = @scanner.scan(/.+/m)
|
107
|
+
[:text, value]
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
data/spec/compiler_spec.rb
CHANGED
@@ -94,6 +94,22 @@ describe Curly::Compiler do
|
|
94
94
|
presenter.stub(:dirty) { "<p>dirty</p>".html_safe }
|
95
95
|
evaluate("{{dirty}}").should == "<p>dirty</p>"
|
96
96
|
end
|
97
|
+
|
98
|
+
it "removes comments from the output" do
|
99
|
+
evaluate("HELO{{! I'm a comment, yo }}WORLD").should == "HELOWORLD"
|
100
|
+
end
|
101
|
+
|
102
|
+
it "removes comment lines from the output" do
|
103
|
+
evaluate(<<-CURLY.strip_heredoc).should == "HELO\nWORLD\n"
|
104
|
+
HELO
|
105
|
+
{{! I'm a comment }}
|
106
|
+
WORLD
|
107
|
+
CURLY
|
108
|
+
end
|
109
|
+
|
110
|
+
it "does not execute arbitrary Ruby code" do
|
111
|
+
evaluate('#{foo}').should == '#{foo}'
|
112
|
+
end
|
97
113
|
end
|
98
114
|
|
99
115
|
describe ".valid?" do
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Curly::Scanner, ".scan" do
|
4
|
+
it "returns the tokens in the source" do
|
5
|
+
scan("foo {{bar}} baz").should == [
|
6
|
+
[:text, "foo "],
|
7
|
+
[:reference, "bar"],
|
8
|
+
[:text, " baz"]
|
9
|
+
]
|
10
|
+
end
|
11
|
+
|
12
|
+
it "scans parameterized references" do
|
13
|
+
scan("{{foo.bar}}").should == [
|
14
|
+
[:reference, "foo.bar"]
|
15
|
+
]
|
16
|
+
end
|
17
|
+
|
18
|
+
it "scans comments in the source" do
|
19
|
+
scan("foo {{!bar}} baz").should == [
|
20
|
+
[:text, "foo "],
|
21
|
+
[:comment, "bar"],
|
22
|
+
[:text, " baz"]
|
23
|
+
]
|
24
|
+
end
|
25
|
+
|
26
|
+
it "scans comment lines in the source" do
|
27
|
+
scan("foo\n{{!bar}}\nbaz").should == [
|
28
|
+
[:text, "foo\n"],
|
29
|
+
[:comment_line, "bar"],
|
30
|
+
[:text, "baz"]
|
31
|
+
]
|
32
|
+
end
|
33
|
+
|
34
|
+
it "scans to the end of the source" do
|
35
|
+
scan("foo\n").should == [
|
36
|
+
[:text, "foo\n"]
|
37
|
+
]
|
38
|
+
end
|
39
|
+
|
40
|
+
it "treats quotes as text" do
|
41
|
+
scan('"').should == [
|
42
|
+
[:text, '"']
|
43
|
+
]
|
44
|
+
end
|
45
|
+
|
46
|
+
it "treats Ruby interpolation as text" do
|
47
|
+
scan('#{foo}').should == [
|
48
|
+
[:text, '#{foo}']
|
49
|
+
]
|
50
|
+
end
|
51
|
+
|
52
|
+
def scan(source)
|
53
|
+
Curly::Scanner.scan(source)
|
54
|
+
end
|
55
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: curly-templates
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.10.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-07-11 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: actionpack
|
@@ -122,6 +122,7 @@ files:
|
|
122
122
|
- lib/curly/invalid_reference.rb
|
123
123
|
- lib/curly/presenter.rb
|
124
124
|
- lib/curly/railtie.rb
|
125
|
+
- lib/curly/scanner.rb
|
125
126
|
- lib/curly/template_handler.rb
|
126
127
|
- lib/generators/curly/controller/controller_generator.rb
|
127
128
|
- lib/generators/curly/controller/templates/presenter.rb.erb
|
@@ -130,6 +131,7 @@ files:
|
|
130
131
|
- spec/compiler_spec.rb
|
131
132
|
- spec/generators/controller_generator_spec.rb
|
132
133
|
- spec/presenter_spec.rb
|
134
|
+
- spec/scanner_spec.rb
|
133
135
|
- spec/spec_helper.rb
|
134
136
|
- spec/template_handler_spec.rb
|
135
137
|
homepage: https://github.com/zendesk/curly
|
@@ -148,7 +150,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
148
150
|
version: '0'
|
149
151
|
segments:
|
150
152
|
- 0
|
151
|
-
hash:
|
153
|
+
hash: 4015061898851795676
|
152
154
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
153
155
|
none: false
|
154
156
|
requirements:
|
@@ -165,5 +167,6 @@ test_files:
|
|
165
167
|
- spec/compiler_spec.rb
|
166
168
|
- spec/generators/controller_generator_spec.rb
|
167
169
|
- spec/presenter_spec.rb
|
170
|
+
- spec/scanner_spec.rb
|
168
171
|
- spec/template_handler_spec.rb
|
169
172
|
has_rdoc:
|