simple_templates 0.8.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +6 -0
- data/Gemfile +5 -0
- data/Guardfile +30 -0
- data/LICENSE +21 -0
- data/README.md +62 -0
- data/Rakefile +20 -0
- data/bin/simple-template +13 -0
- data/lib/simple_templates.rb +38 -0
- data/lib/simple_templates/AST/node.rb +59 -0
- data/lib/simple_templates/AST/placeholder.rb +25 -0
- data/lib/simple_templates/AST/text.rb +28 -0
- data/lib/simple_templates/delimiter.rb +6 -0
- data/lib/simple_templates/lexer.rb +73 -0
- data/lib/simple_templates/parser.rb +111 -0
- data/lib/simple_templates/parser/error.rb +6 -0
- data/lib/simple_templates/parser/node_parser.rb +45 -0
- data/lib/simple_templates/parser/placeholder.rb +83 -0
- data/lib/simple_templates/parser/text.rb +52 -0
- data/lib/simple_templates/template.rb +65 -0
- data/lib/simple_templates/unescapes.rb +5 -0
- data/lib/simple_templates/version.rb +3 -0
- data/simple_templates.gemspec +23 -0
- data/test/simple_templates/AST/node_test.rb +50 -0
- data/test/simple_templates/AST/placeholder_test.rb +28 -0
- data/test/simple_templates/AST/text_test.rb +23 -0
- data/test/simple_templates/lexer_test.rb +145 -0
- data/test/simple_templates/parser/node_parser_test.rb +38 -0
- data/test/simple_templates/parser/placeholder_test.rb +105 -0
- data/test/simple_templates/parser/text_test.rb +118 -0
- data/test/simple_templates/parser_test.rb +184 -0
- data/test/simple_templates/template_test.rb +58 -0
- data/test/test_helper.rb +21 -0
- metadata +192 -0
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module SimpleTemplates
|
4
|
+
class Parser
|
5
|
+
# A Base class for the Placeholders and Text parsers
|
6
|
+
class NodeParser
|
7
|
+
|
8
|
+
# The Base class doesn't accept any tokens for parsing, since it isn't
|
9
|
+
# supposed to be instantiated.
|
10
|
+
# @return [Set<Object>]
|
11
|
+
STARTING_TOKENS = Set[]
|
12
|
+
|
13
|
+
# Checks if the class is applicable for the first token in the list
|
14
|
+
# @param tokens <Array[SimpleTemplates::Lexer::Token]> a list of tokens
|
15
|
+
def self.applicable?(tokens)
|
16
|
+
tokens.any? && self::STARTING_TOKENS.include?(tokens.first.type)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Initializes a new NodeParser. Please note that this class is not
|
20
|
+
# supposed to be instantiated.
|
21
|
+
# Raises an error if it is not applicable.
|
22
|
+
# @param unescapes [SimpleTemplates::Unescapes] a Unescapes object
|
23
|
+
# @param tokens <Array[SimpleTemplates::Lexer::Token]> a list of tokens
|
24
|
+
# @param allowed_placeholders <Array[String]> a list of allowed placeholders
|
25
|
+
def initialize(unescapes, tokens, allowed_placeholders)
|
26
|
+
raise ArgumentError, "Invalid Parser for String!" unless self.class.applicable?(tokens)
|
27
|
+
|
28
|
+
@unescapes = unescapes.to_h.clone.freeze
|
29
|
+
@tokens = tokens.clone.freeze
|
30
|
+
|
31
|
+
# Placeholders to match are mapped to strings for our validity checks.
|
32
|
+
# This is because if we go the other way and convert all possible
|
33
|
+
# placeholder names to symbols before comparing to the whitelist, we
|
34
|
+
# could cause a memory leak by allocating an infinite amount of symbols
|
35
|
+
# that won't be garbage-collected.
|
36
|
+
@allowed_placeholders = allowed_placeholders &&
|
37
|
+
allowed_placeholders.map(&:to_s).freeze
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
attr_reader :tokens, :allowed_placeholders, :unescapes
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
require 'simple_templates/parser/node_parser'
|
4
|
+
require 'simple_templates/AST/placeholder'
|
5
|
+
|
6
|
+
module SimpleTemplates
|
7
|
+
class Parser
|
8
|
+
# Recognizes a set of input tokens as a Placeholder.
|
9
|
+
class Placeholder < NodeParser
|
10
|
+
|
11
|
+
# The expected tag order for a valid placeholder.
|
12
|
+
EXPECTED_TAG_ORDER = [:ph_start, :ph_name, :ph_end]
|
13
|
+
|
14
|
+
# The starting token that the input must have
|
15
|
+
STARTING_TOKENS = Set[:ph_start]
|
16
|
+
|
17
|
+
# If this stream starts with a placeholder token, parse out the
|
18
|
+
# Placeholder, or a Result with errors indicating the syntax problem.
|
19
|
+
# @return <Array <Array[SimpleTemplates::AST::Placeholder]>,
|
20
|
+
# <Array[SimpleTemplates::Parser::Error]>,
|
21
|
+
# <Array[SimpleTemplates::Lexer::Token]>> an +Array+ with the
|
22
|
+
# AST::Placeholder as first element, a list of parser errors and a list
|
23
|
+
# of the remaining tokens.
|
24
|
+
def parse
|
25
|
+
errors = check_placeholder_syntax
|
26
|
+
|
27
|
+
remaining_tokens = []
|
28
|
+
|
29
|
+
placeholder_ast = if errors.empty?
|
30
|
+
remaining_tokens = tokens[3..-1] || []
|
31
|
+
|
32
|
+
allowed = allowed_placeholders.nil? ||
|
33
|
+
allowed_placeholders.include?(tag_name.content)
|
34
|
+
|
35
|
+
[AST::Placeholder.new(tag_name.content, tag_start.pos, allowed)]
|
36
|
+
else
|
37
|
+
[] # we don't have an AST portion to return if we encountered errors
|
38
|
+
end
|
39
|
+
|
40
|
+
[placeholder_ast, errors, remaining_tokens]
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def check_placeholder_syntax
|
46
|
+
expected_order_with_found_tokens = EXPECTED_TAG_ORDER.zip(tag_tokens)
|
47
|
+
|
48
|
+
errors = expected_order_with_found_tokens.
|
49
|
+
reduce([]) do |errs, (expected_type, found_tag)|
|
50
|
+
|
51
|
+
if found_tag.nil?
|
52
|
+
break errs << Parser::Error.new(
|
53
|
+
"Expected #{FRIENDLY_TAG_NAMES.fetch(expected_type)} token, but" +
|
54
|
+
" reached end of input.")
|
55
|
+
|
56
|
+
elsif expected_type != found_tag.type
|
57
|
+
break errs << Parser::Error.new(
|
58
|
+
"Expected #{FRIENDLY_TAG_NAMES.fetch(expected_type)} token at " +
|
59
|
+
"character position #{found_tag.pos}, but found a " +
|
60
|
+
"#{FRIENDLY_TAG_NAMES.fetch(found_tag.type)} token instead.")
|
61
|
+
|
62
|
+
else
|
63
|
+
# This token was expected at this point in the placeholder sequence,
|
64
|
+
# no need to add errors.
|
65
|
+
errs
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def tag_tokens
|
71
|
+
tokens[0..2].compact
|
72
|
+
end
|
73
|
+
|
74
|
+
def tag_start
|
75
|
+
tokens[0]
|
76
|
+
end
|
77
|
+
|
78
|
+
def tag_name
|
79
|
+
tokens[1]
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
require 'simple_templates/parser/node_parser'
|
4
|
+
require 'simple_templates/AST/text'
|
5
|
+
|
6
|
+
module SimpleTemplates
|
7
|
+
class Parser
|
8
|
+
#Recognizes a set of input tokens as a Text
|
9
|
+
class Text < NodeParser
|
10
|
+
|
11
|
+
# The starting tokens that the input can have
|
12
|
+
# @return [Set<Symbol>]
|
13
|
+
STARTING_TOKENS = Set[:quoted_ph_start, :quoted_ph_end, :text]
|
14
|
+
|
15
|
+
# A hash containing the method for a quoted placeholder start or end
|
16
|
+
# @return [Hash{ Symbol => Symbol }]
|
17
|
+
UNESCAPE_METHODS = {
|
18
|
+
quoted_ph_start: :start,
|
19
|
+
quoted_ph_end: :end
|
20
|
+
}
|
21
|
+
|
22
|
+
# It parses the stream, if it starts with a text node then it parses out
|
23
|
+
# the text until it is not applicable for the input anymore.
|
24
|
+
# @return <Array <Array[SimpleTemplates::AST::Text]>,
|
25
|
+
# <Array>,
|
26
|
+
# <Array[SimpleTemplates::Lexer::Token]>> an +Array+ with a list of
|
27
|
+
# AST::Text as first element, always an Empty list of Errors and the
|
28
|
+
# remaining unparsed tokens.
|
29
|
+
def parse
|
30
|
+
txt_node = nil
|
31
|
+
toks = tokens.dup
|
32
|
+
|
33
|
+
while self.class.applicable?(toks)
|
34
|
+
next_txt_token = toks.shift
|
35
|
+
|
36
|
+
this_txt_node =
|
37
|
+
AST::Text.new(unescape(next_txt_token), next_txt_token.pos, true)
|
38
|
+
|
39
|
+
txt_node = txt_node.nil? ? this_txt_node : txt_node + this_txt_node
|
40
|
+
end
|
41
|
+
|
42
|
+
[[txt_node], [], toks || []]
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def unescape(token)
|
48
|
+
unescapes[UNESCAPE_METHODS[token.type]] || token.content
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
require 'simple_templates/AST/placeholder'
|
4
|
+
|
5
|
+
module SimpleTemplates
|
6
|
+
#
|
7
|
+
# A +Template+ is a renderable collection of SimpleTemplates::AST nodes.
|
8
|
+
#
|
9
|
+
# @!attribute [r] ast
|
10
|
+
# @return <Array[SimpleTemplates::AST::Node]> a list of renderable nodes
|
11
|
+
# @!attribute [r] errors
|
12
|
+
# @return <Array[SimpleTemplates::Parser::Error]> a list of errors found
|
13
|
+
# during parsing
|
14
|
+
# @!attribute [r] remaining_tokens
|
15
|
+
# @return <Array[SimpleTemplates::Lexer::Token]> a list of the remaining
|
16
|
+
# not parsed tokens
|
17
|
+
#
|
18
|
+
class Template
|
19
|
+
|
20
|
+
attr_reader :ast, :errors, :remaining_tokens
|
21
|
+
|
22
|
+
# Initializes a new Template
|
23
|
+
# @param ast <Array[SimpleTemplates::AST::Node]> list of AST nodes
|
24
|
+
# @param errors <Array[SimpleTemplates::Parser::Error]> a list of errors
|
25
|
+
# found during parsing
|
26
|
+
# @param remaining_tokens <Array[SimpleTemplates::Lexer::Token]> list of
|
27
|
+
# unparsed tokens from the input
|
28
|
+
def initialize(ast = [], errors = [], remaining_tokens = [])
|
29
|
+
@ast = ast.clone.freeze
|
30
|
+
@errors = errors.clone.freeze
|
31
|
+
@remaining_tokens = remaining_tokens.clone.freeze
|
32
|
+
end
|
33
|
+
|
34
|
+
# Returns all placeholder names used in the template.
|
35
|
+
# @return [Set<String>] Placeholders content
|
36
|
+
def placeholder_names
|
37
|
+
placeholders.map(&:contents).to_set
|
38
|
+
end
|
39
|
+
|
40
|
+
# Accepts a hash with the placeholder names as keys and the values for
|
41
|
+
# substitution
|
42
|
+
# @param substitutions [Hash{Symbol => String}] a hash with the placeholder
|
43
|
+
# name as the key and the substitution for that placeholder as value
|
44
|
+
# @return [String] The concatenated result of rendering the substitutions
|
45
|
+
def render(substitutions)
|
46
|
+
raise errors.map(&:message).join(", ") unless errors.empty?
|
47
|
+
ast.map { |node| node.render(substitutions) }.join
|
48
|
+
end
|
49
|
+
|
50
|
+
# Returns all the +SimpleTemplates::AST::Placeholder+ nodes in the +ast+
|
51
|
+
# list of the instance
|
52
|
+
# @return [Set<SimpleTemplates::AST::Placeholder>]
|
53
|
+
def placeholders
|
54
|
+
ast.select{ |node| SimpleTemplates::AST::Placeholder === node }.to_set
|
55
|
+
end
|
56
|
+
|
57
|
+
# Compares a +Template+ with another by comparing the +ast+, +errors+
|
58
|
+
# and the +remaining_tokens+ of each one
|
59
|
+
def ==(other)
|
60
|
+
ast == other.ast &&
|
61
|
+
errors == other.errors &&
|
62
|
+
remaining_tokens == other.remaining_tokens
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- encoding: utf-8 -*-
|
3
|
+
|
4
|
+
require File.expand_path("../lib/simple_templates/version.rb", __FILE__)
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "simple_templates"
|
8
|
+
s.version = SimpleTemplates::VERSION
|
9
|
+
s.authors = ["Michal Papis"]
|
10
|
+
s.email = ["support@stackbuilders.com"]
|
11
|
+
s.homepage = "https://github.com/stackbuilders/simple_templates"
|
12
|
+
s.summary = "Minimalistic templates engine"
|
13
|
+
s.license = "MIT"
|
14
|
+
s.files = `git ls-files`.split("\n")
|
15
|
+
s.executables << "simple-template"
|
16
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
17
|
+
s.required_ruby_version = ">= 2.0.0"
|
18
|
+
%w{rake minitest simplecov coveralls guard-minitest yard}.each do |name|
|
19
|
+
s.add_development_dependency(name)
|
20
|
+
end
|
21
|
+
s.add_development_dependency("guard", ">=2.12.8", "<3")
|
22
|
+
# s.add_development_dependency("smf-gem")
|
23
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require_relative "../../test_helper"
|
2
|
+
|
3
|
+
module SimpleTemplates
|
4
|
+
module AST
|
5
|
+
class TestNodeImpl < Node
|
6
|
+
def render(context)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe SimpleTemplates::AST::Node do
|
13
|
+
let(:target) {SimpleTemplates::AST::Node}
|
14
|
+
let(:allowed) {target.new("a", 0, true)}
|
15
|
+
let(:impl) {SimpleTemplates::AST::TestNodeImpl.new("a", 0, true)}
|
16
|
+
|
17
|
+
describe "==" do
|
18
|
+
it "is equal" do
|
19
|
+
allowed.must_equal target.new("a", 0, true)
|
20
|
+
end
|
21
|
+
it "isn't equal" do
|
22
|
+
allowed.wont_equal target.new("b", 0, true)
|
23
|
+
allowed.wont_equal target.new("a", 1, true)
|
24
|
+
allowed.wont_equal target.new("a", 0, false)
|
25
|
+
allowed.wont_equal target.new("b", 1, false)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "allowed?" do
|
30
|
+
it "is allowed?" do
|
31
|
+
allowed.allowed?.must_equal true
|
32
|
+
end
|
33
|
+
it "isn't allowed?" do
|
34
|
+
target.new("a", 0, false).allowed?.must_equal false
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "render" do
|
39
|
+
let(:context) { {a: 1} }
|
40
|
+
it "fails" do
|
41
|
+
->(){
|
42
|
+
allowed.render(context)
|
43
|
+
}.must_raise NotImplementedError
|
44
|
+
end
|
45
|
+
it "succeeds" do
|
46
|
+
impl.render(context).must_be_nil
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require_relative "../../test_helper"
|
2
|
+
|
3
|
+
describe SimpleTemplates::AST::Placeholder do
|
4
|
+
let(:target) {SimpleTemplates::AST::Placeholder}
|
5
|
+
|
6
|
+
describe "render" do
|
7
|
+
|
8
|
+
let(:substitutions) { { my_placeholder: 'result1' } }
|
9
|
+
|
10
|
+
it "fails when the placeholder name is not in the given Hash" do
|
11
|
+
->(){
|
12
|
+
target.new('not_in_substitutions', 0, true).render(substitutions)
|
13
|
+
}.must_raise KeyError
|
14
|
+
end
|
15
|
+
|
16
|
+
it "fails when the placeholder is marked as invalid" do
|
17
|
+
->(){
|
18
|
+
target.new('my_placeholder', 0, false).render(substitutions)
|
19
|
+
}.must_raise RuntimeError
|
20
|
+
end
|
21
|
+
|
22
|
+
it "succeeds" do
|
23
|
+
target.new('my_placeholder', 0, true).
|
24
|
+
render(substitutions).must_equal("result1")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require_relative "../../test_helper"
|
2
|
+
|
3
|
+
describe SimpleTemplates::AST::Text do
|
4
|
+
let(:target) {SimpleTemplates::AST::Text}
|
5
|
+
let(:valid) {target.new("a", 0, true)}
|
6
|
+
let(:context) {Struct.new(:a).new("result1")}
|
7
|
+
|
8
|
+
describe "render" do
|
9
|
+
it "succeeds" do
|
10
|
+
valid.render(context).must_equal("a")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "+" do
|
15
|
+
it "adds valid" do
|
16
|
+
valid.+(target.new("b", 1, true)).must_equal target.new("ab", 0, true)
|
17
|
+
end
|
18
|
+
it "adds invalid" do
|
19
|
+
valid.+(target.new("b", 1, false)).must_equal target.new("ab", 0, false)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
require_relative "../test_helper"
|
2
|
+
|
3
|
+
describe SimpleTemplates::Lexer do
|
4
|
+
let(:delimiter) { SimpleTemplates::Delimiter.new(/\\</, /\\>/, /\</, /\>/) }
|
5
|
+
let(:token) { SimpleTemplates::Lexer::Token }
|
6
|
+
|
7
|
+
describe '#tokenize' do
|
8
|
+
it 'tokenizes a string with no placeholders' do
|
9
|
+
raw_input = 'string with no placeholders'
|
10
|
+
tokens = SimpleTemplates::Lexer.new(
|
11
|
+
delimiter,
|
12
|
+
raw_input
|
13
|
+
).tokenize
|
14
|
+
|
15
|
+
tokens.must_equal [
|
16
|
+
token.new(:text, 'string with no placeholders', 0)
|
17
|
+
]
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'tokenizes a string with placeholders' do
|
21
|
+
raw_input = 'string with <placeholder>'
|
22
|
+
tokens = SimpleTemplates::Lexer.new(
|
23
|
+
delimiter,
|
24
|
+
raw_input
|
25
|
+
).tokenize
|
26
|
+
|
27
|
+
tokens.must_equal [
|
28
|
+
token.new(:text, 'string with ', 0),
|
29
|
+
token.new(:ph_start, '<', 12),
|
30
|
+
token.new(:ph_name, 'placeholder', 13),
|
31
|
+
token.new(:ph_end, '>', 24)
|
32
|
+
]
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'tokenizes a string with invalid placeholders, containing a new line character' do
|
36
|
+
raw_input = "string with <foo\nbar> text"
|
37
|
+
tokens = SimpleTemplates::Lexer.new(
|
38
|
+
delimiter,
|
39
|
+
raw_input
|
40
|
+
).tokenize
|
41
|
+
|
42
|
+
tokens.must_equal [
|
43
|
+
token.new(:text, 'string with ', 0),
|
44
|
+
token.new(:ph_start, '<', 12),
|
45
|
+
token.new(:ph_name, 'foo', 13),
|
46
|
+
token.new(:text, "\nbar", 16),
|
47
|
+
token.new(:ph_end, '>', 20),
|
48
|
+
token.new(:text, ' text', 21)
|
49
|
+
]
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'tokenizes a string with invalid placeholders, contains leading and trailing space' do
|
53
|
+
raw_input = "string with < foobar > text"
|
54
|
+
tokens = SimpleTemplates::Lexer.new(
|
55
|
+
delimiter,
|
56
|
+
raw_input
|
57
|
+
).tokenize
|
58
|
+
|
59
|
+
tokens.must_equal [
|
60
|
+
token.new(:text, 'string with ', 0),
|
61
|
+
token.new(:ph_start, '<', 12),
|
62
|
+
token.new(:text, ' foobar ', 13),
|
63
|
+
token.new(:ph_end, '>', 21),
|
64
|
+
token.new(:text, ' text', 22)
|
65
|
+
]
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'tokenizes a string with placeholders and a newline character' do
|
69
|
+
raw_input = "string with <placeholder>\n Something else"
|
70
|
+
tokens = SimpleTemplates::Lexer.new(
|
71
|
+
delimiter,
|
72
|
+
raw_input
|
73
|
+
).tokenize
|
74
|
+
|
75
|
+
tokens.must_equal [
|
76
|
+
token.new(:text, 'string with ', 0),
|
77
|
+
token.new(:ph_start, '<', 12),
|
78
|
+
token.new(:ph_name, 'placeholder', 13),
|
79
|
+
token.new(:ph_end, '>', 24),
|
80
|
+
token.new(:text, "\n Something else", 25)
|
81
|
+
]
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'tokenizes a string with invalid placeholder and an empty placeholder name' do
|
85
|
+
raw_input = "string with <> text"
|
86
|
+
tokens = SimpleTemplates::Lexer.new(
|
87
|
+
delimiter,
|
88
|
+
raw_input
|
89
|
+
).tokenize
|
90
|
+
|
91
|
+
tokens.must_equal [
|
92
|
+
token.new(:text, 'string with ', 0),
|
93
|
+
token.new(:ph_start, '<', 12),
|
94
|
+
token.new(:ph_end, '>', 13),
|
95
|
+
token.new(:text, ' text', 14)
|
96
|
+
]
|
97
|
+
end
|
98
|
+
it 'tokenizes a string with placeholders having a new line character in the placeholder name' do
|
99
|
+
raw_input = "string with <placeholder\n> Something else"
|
100
|
+
tokens = SimpleTemplates::Lexer.new(
|
101
|
+
delimiter,
|
102
|
+
raw_input
|
103
|
+
).tokenize
|
104
|
+
|
105
|
+
tokens.must_equal [
|
106
|
+
token.new(:text, 'string with ', 0),
|
107
|
+
token.new(:ph_start, '<', 12),
|
108
|
+
token.new(:ph_name, 'placeholder', 13),
|
109
|
+
token.new(:text, "\n", 24),
|
110
|
+
token.new(:ph_end, '>', 25),
|
111
|
+
token.new(:text, " Something else", 26)
|
112
|
+
]
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'tokenizes a string with a placeholder containing numbers' do
|
116
|
+
raw_input = 'string with <1placeholder1>'
|
117
|
+
tokens = SimpleTemplates::Lexer.new(
|
118
|
+
delimiter,
|
119
|
+
raw_input
|
120
|
+
).tokenize
|
121
|
+
|
122
|
+
tokens.must_equal [
|
123
|
+
token.new(:text, 'string with ', 0),
|
124
|
+
token.new(:ph_start, '<', 12),
|
125
|
+
token.new(:ph_name, '1placeholder1', 13),
|
126
|
+
token.new(:ph_end, '>', 26),
|
127
|
+
]
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'tokenizes a string with a placeholder containing underscores' do
|
131
|
+
raw_input = 'string with <_place_holder_>'
|
132
|
+
tokens = SimpleTemplates::Lexer.new(
|
133
|
+
delimiter,
|
134
|
+
raw_input
|
135
|
+
).tokenize
|
136
|
+
|
137
|
+
tokens.must_equal [
|
138
|
+
token.new(:text, 'string with ', 0),
|
139
|
+
token.new(:ph_start, '<', 12),
|
140
|
+
token.new(:ph_name, '_place_holder_', 13),
|
141
|
+
token.new(:ph_end, '>', 27),
|
142
|
+
]
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|