xrt 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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4f297398d1b9757376b533d456353e069ad6c852
4
+ data.tar.gz: a953d05e896c259e4c46fa2414cff70d66407969
5
+ SHA512:
6
+ metadata.gz: d0ed3a59d292aa21d82f91d7c6277a19793d0f0fd8f9c7c1c7008a557f7408628d7e6355b922d0e5ff2fb4cd4642039ee2032aa8ffd77e88e20ca99f17f738fa
7
+ data.tar.gz: 4a39005f378374d91efcda18bf016744873bb07778c5e1579b90f00e06516796e40b38a4484b9c71322c189653f0ae5d7dfce52dcc1710c0bdb0bc1cc9f80c52
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2
4
+ - 2.0
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in xrt.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2016 hitode909
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,85 @@
1
+ [![Build Status](https://travis-ci.org/hitode909/xrt.svg?branch=master)](https://travis-ci.org/hitode909/xrt)
2
+
3
+ # XRT
4
+
5
+ Refactoring Tool for [Text::Xslate](https://metacpan.org/pod/Text::Xslate).
6
+ Currently aim to refactor [Syntax::TTerse](https://metacpan.org/pod/Text::Xslate::Syntax::TTerse) templates.
7
+
8
+ ## Installation
9
+
10
+ ```
11
+ $ gem install xrt
12
+ ```
13
+
14
+ ## Commands
15
+
16
+ `XRT` provides `xrt` command.
17
+
18
+ ### Dump
19
+
20
+ `xrt dump` command annotates the template with nested levels of control statements.
21
+
22
+ ```
23
+ $ xrt dump TARGET_TEMPLATE
24
+ ```
25
+
26
+ #### Example
27
+
28
+ `FOR` locates on nested level 1, and inner `IF` locates on nested level is 2.
29
+ Valid templates must end with nested level is 0.
30
+
31
+ ```
32
+ % xrt dump templates/sample.html
33
+ Dumping templates/sample.html
34
+ <html>
35
+ <body>
36
+ 0[% FOR item IN items %]1
37
+ 1[% IF item.title %]2
38
+ <h1>2[% title %]2</h1>
39
+ 2[% END %]1
40
+ 1[% END %]0
41
+ </body>
42
+ </html>
43
+ 0
44
+ ```
45
+
46
+ ### Extract
47
+
48
+ `xrt extract` extracts the specified block from target template to another new template.
49
+
50
+ ```
51
+ $ xrt extract TARGET_TEMPLATE TARGET_BLOCK TEMPLATES_DIRECTORY NEW_TEMPLATE_NAME
52
+ ```
53
+
54
+ #### Example
55
+
56
+ When you want to extract `FOR` block to new template `templates/_items.tt`,
57
+
58
+ - `TARGET_TEMPLATE` is the path of original template.
59
+ - Set `TARGET_BLOCK` like `[% FOR item IN items %]`, `[% FOR item IN` or `[% FOR`. This must match with a beginning of only one block.
60
+ - `TEMPLATES_DIRECTORY` is the directory which the template is located. Usually it may be `templates/` or `views/`.
61
+ - `NEW_TEMPLATE_NAME` is the new template. This parameter is used to generate new `[% INCLUDE %]` directive. New template will stored in `TEMPLATES_DIRECTORY`.
62
+
63
+ ```
64
+ % xrt extract templates/sample.html '[% FOR item IN items %]' templates/ _items.tt
65
+ ```
66
+
67
+ The result is below.
68
+
69
+ ```
70
+ # tempaltes/sample.html
71
+ <html>
72
+ <body>
73
+ [% INCLUDE "_items.tt" %]
74
+ </body>
75
+ </html>
76
+ ```
77
+
78
+ ```
79
+ # templates/_items.tt
80
+ [% FOR item IN items %]
81
+ [% IF item.title %]
82
+ <h1>[% title %]</h1>
83
+ [% END %]
84
+ [% END %]
85
+ ```
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ task :default => :test
4
+
5
+ desc "Run tests"
6
+ task :test do
7
+ ruby("test/run-test.rb")
8
+ end
data/bin/xrt ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ base_dir = File.expand_path(File.join(File.dirname(__FILE__), ".."))
4
+ lib_dir = File.join(base_dir, "lib")
5
+ $LOAD_PATH.unshift(lib_dir)
6
+
7
+ require 'xrt/cli'
8
+
9
+ cli = XRT::CLI.new
10
+ cli.execute ARGV
data/lib/xrt/cli.rb ADDED
@@ -0,0 +1,22 @@
1
+ require 'xrt/commands'
2
+
3
+ module XRT
4
+ class CLI
5
+ def execute(args)
6
+ command_name = args.shift
7
+
8
+ case command_name
9
+ when 'dump'
10
+ success = XRT::Command::Dump.new.execute(args)
11
+ exit success ? 0 : 1
12
+ when 'extract'
13
+ success = XRT::Command::Extract.new.execute(*args)
14
+ exit success ? 0 : 1
15
+ else
16
+ warn "command not found"
17
+ exit 1
18
+ end
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,70 @@
1
+ require 'pathname'
2
+ require 'xrt/depth_checker'
3
+
4
+ module XRT
5
+ module Command
6
+ class Dump
7
+ def execute(files)
8
+ files.each{|file|
9
+ dump_file file
10
+ }
11
+ end
12
+
13
+ def dump_file target_file
14
+ warn "Dumping #{target_file}"
15
+ checker = XRT::DepthChecker.new
16
+ parsed, annotated_source = checker.check open(target_file).read
17
+ puts annotated_source
18
+ unless parsed
19
+ raise "Failed to parser #{target_file} (#{index}/#{target_files.length})"
20
+ end
21
+ end
22
+ end
23
+
24
+ class Extract
25
+ # xrt extract templates/blogs/index.html '[% IF pager' 'templates/' 'blogs/_pager.tt'
26
+ def execute(from_file, target_block, templates_directory, to_file_name)
27
+ from_source = open(from_file).read
28
+ parser = XRT::Parser.new(from_source)
29
+ from_doc = parser.document
30
+
31
+ found_blocks = from_doc.find_blocks.select{|block|
32
+ block.content.index(target_block) == 0
33
+ }
34
+
35
+ if found_blocks.length == 0
36
+ raise "target_block not found"
37
+ end
38
+
39
+ if found_blocks.length > 1
40
+ raise "ambiguous target_block"
41
+ end
42
+
43
+ found_block = found_blocks.first
44
+
45
+ replace_to_node = XRT::Parser.new(%Q{[% INCLUDE "#{to_file_name}" %]}).document
46
+
47
+ from_doc.replace_child(replace_to_node, found_block)
48
+
49
+ content_to_overwrite = from_doc.content
50
+ content_for_new_file = found_block.auto_indent
51
+
52
+ open(from_file, 'w'){|f|
53
+ f.write content_to_overwrite
54
+ }
55
+
56
+ new_file = Pathname(templates_directory).join(to_file_name)
57
+
58
+ if new_file.exist?
59
+ raise 'TO_FILE_NAME exists.'
60
+ end
61
+
62
+ open(new_file, 'w'){|f|
63
+ f.puts content_for_new_file
64
+ }
65
+
66
+ true
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,51 @@
1
+ require 'xrt/parser'
2
+ require 'xrt/syntax'
3
+
4
+ module XRT
5
+ class DepthChecker
6
+ def check source
7
+ annotated_source = ''
8
+ parser = XRT::Parser.new(source)
9
+ syntax = XRT::Syntax.new
10
+
11
+ current_level = 0
12
+ parser.tokens.each{|statement|
13
+ diff = syntax.block_level statement
14
+ current_level += diff
15
+ annotated_source += statement
16
+
17
+ color = 44 + diff
18
+ if STDOUT.tty?
19
+ annotated_source += "\e[#{color}m#{current_level}\e[0m"
20
+ else
21
+ annotated_source += current_level.to_s
22
+ end
23
+ }
24
+ if current_level == 0
25
+ return true, annotated_source
26
+ else
27
+ return false, annotated_source
28
+ end
29
+ end
30
+
31
+ def max_depth source
32
+ parser = XRT::Parser.new(source)
33
+ syntax = XRT::Syntax.new
34
+
35
+ max_level = 0
36
+ current_level = 0
37
+
38
+ parser.tokens.each{|statement|
39
+ diff = syntax.block_level statement
40
+ current_level += diff
41
+ max_level = [ current_level, max_level].max
42
+ }
43
+
44
+ if current_level != 0
45
+ raise 'failed to parse'
46
+ end
47
+
48
+ max_level
49
+ end
50
+ end
51
+ end
data/lib/xrt/parser.rb ADDED
@@ -0,0 +1,79 @@
1
+ require 'xrt/statement'
2
+
3
+ module XRT
4
+ class Parser
5
+ def initialize(source='')
6
+ @source = source
7
+ end
8
+
9
+ def document
10
+ doc = XRT::Statement::Document.new
11
+
12
+ tokenized = self.tokens
13
+
14
+ parse_contents(tokenized, doc)
15
+ doc
16
+ end
17
+
18
+ def parse_contents(tokenized, node)
19
+ while tokenized.length > 0
20
+ statement = XRT::Statement::Factory.new_from_content(tokenized.shift)
21
+ case statement
22
+ when XRT::Statement::Block
23
+ parse_contents(tokenized, statement)
24
+ node << statement
25
+ when XRT::Statement::End
26
+ node << statement
27
+ break
28
+ when XRT::Statement::Text
29
+ node << statement
30
+ when XRT::Statement::Directive
31
+ node << statement
32
+ end
33
+ end
34
+
35
+ node
36
+ end
37
+
38
+ def tokens
39
+ reading = @source.dup
40
+ result = []
41
+
42
+ while reading.length > 0
43
+ got = read_directive(reading) || read_text(reading)
44
+ unless got
45
+ raise "failed to parse #{@source}"
46
+ end
47
+ result << got
48
+ end
49
+
50
+ result
51
+ end
52
+
53
+ def read_directive source
54
+ return nil unless source[0...2] == '[%'
55
+
56
+ buffer = ''
57
+ while source[0...2] != '%]'
58
+ buffer << source.slice!(0)
59
+ end
60
+ buffer << source.slice!(0, 2)
61
+ buffer
62
+ end
63
+
64
+ def read_text source
65
+ return nil if source[0...2] == '[%'
66
+
67
+ buffer = ''
68
+ while true
69
+ return buffer if source[0...2] == '[%'
70
+ break if source.length < 2
71
+ buffer << source.slice!(0)
72
+ end
73
+
74
+ buffer << source.slice!(0, source.length)
75
+
76
+ buffer
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,145 @@
1
+ require 'xrt/syntax'
2
+
3
+ module XRT
4
+ class Statement
5
+ def initialize(content)
6
+ @content = content
7
+ end
8
+
9
+ def content
10
+ @content
11
+ end
12
+
13
+ def == other
14
+ self.class == other.class && self.content == other.content
15
+ end
16
+
17
+ def inspect
18
+ "<#{self.class}:#{self.content}>"
19
+ end
20
+
21
+ def children
22
+ []
23
+ end
24
+
25
+ def replace_child(new_child, old_child)
26
+ children.each_with_index{|child, index|
27
+ if child.equal? old_child
28
+ children[index] = new_child
29
+ return old_child
30
+ elsif child.replace_child(new_child, old_child)
31
+ return old_child
32
+ end
33
+ }
34
+ nil
35
+ end
36
+
37
+ def depth(target)
38
+ children.each{|child|
39
+ return 0 if child.equal? target
40
+ d = child.depth(target)
41
+ if d
42
+ return d + 1
43
+ end
44
+ }
45
+ nil
46
+ end
47
+
48
+ def find_blocks
49
+ children.select{|child|
50
+ child.kind_of? XRT::Statement::Block
51
+ }.concat(children.map{|child| child.find_blocks }.flatten)
52
+ end
53
+
54
+ def auto_indent
55
+ lines = content.split(/\n/)[1..-1]
56
+ whitespaces = lines.map{|line| line.scan(/^\s+/).first }.compact
57
+ indent = whitespaces.sort_by{|whitespace| whitespace.length }.first
58
+ content.gsub(/^#{indent}/, '')
59
+ end
60
+ end
61
+
62
+ class Statement
63
+ module Factory
64
+ def self.new_from_content content
65
+ syntax = XRT::Syntax.new
66
+
67
+ block_level = syntax.block_level content
68
+
69
+ if block_level == 1
70
+ XRT::Statement::Block.new content
71
+ elsif block_level == -1
72
+ XRT::Statement::End.new content
73
+ elsif syntax.block? content
74
+ XRT::Statement::Directive.new content
75
+ else
76
+ XRT::Statement::Text.new content
77
+ end
78
+ end
79
+
80
+ end
81
+
82
+ class Document < Statement
83
+ def initialize
84
+ @children = []
85
+ end
86
+
87
+ def << statement
88
+ @children << statement
89
+ end
90
+
91
+ def children
92
+ @children
93
+ end
94
+
95
+ def content
96
+ children.map{|c| c.content }.join
97
+ end
98
+
99
+ def == other
100
+ self.content == other.content && self.children.zip(other.children).all{|a, b| p [a, b]; a == b }
101
+ end
102
+
103
+ def inspect
104
+ "<#{self.class}:#{self.children}>"
105
+ end
106
+ end
107
+
108
+ class Text < Statement
109
+ end
110
+
111
+ class Directive < Statement
112
+ end
113
+
114
+ class End < Directive
115
+ end
116
+
117
+ class Block < Directive
118
+ def initialize(content)
119
+ super
120
+ @children = []
121
+ end
122
+
123
+ def closed?
124
+ children.last.kind_of? End
125
+ end
126
+
127
+ def << statement
128
+ raise 'trying to push_child to closed block' if closed?
129
+ @children << statement
130
+ end
131
+
132
+ def children
133
+ @children
134
+ end
135
+
136
+ def content
137
+ @content + children.map{|c| c.content }.join
138
+ end
139
+
140
+ def inspect
141
+ "<#{self.class}:#{@content},#{self.children}>"
142
+ end
143
+ end
144
+ end
145
+ end
data/lib/xrt/syntax.rb ADDED
@@ -0,0 +1,40 @@
1
+ module XRT
2
+ class Syntax
3
+ def block? statement
4
+ statement =~ /\A\[%.+%\]\Z/m
5
+ end
6
+
7
+ def beginning_block? statement
8
+ without_comment = remove_comment statement
9
+ without_comment =~ beginning_block_regexp
10
+ end
11
+
12
+ def end_block? statement
13
+ return unless block? statement
14
+ without_comment = remove_comment statement
15
+ without_comment =~ end_block_regexp
16
+ end
17
+
18
+ def beginning_block_regexp
19
+ keywords = %w(IF UNLESS FOR FOREACH WHILE SWITCH MACRO BLOCK WRAPPER FILTER)
20
+ /\[%.?\s*\b(#{keywords.join('|')})\b/i
21
+ end
22
+
23
+ def end_block_regexp
24
+ /\bEND\b/i
25
+ end
26
+
27
+ def remove_comment(statement)
28
+ statement.gsub(/\s*#.*$/, '')
29
+ end
30
+
31
+ def block_level(statement)
32
+ stripped = statement.strip
33
+ return 0 unless block? stripped
34
+ level = 0
35
+ level += 1 if beginning_block? stripped
36
+ level -= 1 if end_block? stripped
37
+ level
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,3 @@
1
+ module Xrt
2
+ VERSION = "0.0.1"
3
+ end
data/lib/xrt.rb ADDED
@@ -0,0 +1,5 @@
1
+ require "xrt/version"
2
+
3
+ module Xrt
4
+ # Your code goes here...
5
+ end
@@ -0,0 +1,22 @@
1
+ base_dir = File.expand_path(File.join(File.dirname(__FILE__), ".."))
2
+ lib_dir = File.join(base_dir, "lib")
3
+ $LOAD_PATH.unshift(lib_dir)
4
+
5
+ require 'xrt/parser'
6
+ require 'pp'
7
+
8
+ target_files = ARGV
9
+
10
+ target_files.each_with_index{|target_file, index|
11
+ warn "Dumping #{target_file}"
12
+ source = open(target_file).read
13
+ parser = XRT::Parser.new(source)
14
+ doc = parser.document
15
+ doc.find_blocks.each{|block|
16
+ p "depth: #{doc.depth(block)}"
17
+
18
+ puts block.content
19
+ }
20
+ }
21
+
22
+ exit 0
@@ -0,0 +1,16 @@
1
+ base_dir = File.expand_path(File.join(File.dirname(__FILE__), ".."))
2
+ lib_dir = File.join(base_dir, "lib")
3
+ $LOAD_PATH.unshift(lib_dir)
4
+
5
+ require 'xrt/depth_checker'
6
+
7
+ target_files = ARGV
8
+
9
+ checker = XRT::DepthChecker.new
10
+
11
+ target_files.each{|target_file|
12
+ level = checker.max_depth open(target_file).read
13
+ puts [level, target_file].join("\t")
14
+ }
15
+
16
+ exit 0
@@ -0,0 +1,17 @@
1
+ base_dir = File.expand_path(File.join(File.dirname(__FILE__), ".."))
2
+ lib_dir = File.join(base_dir, "lib")
3
+ $LOAD_PATH.unshift(lib_dir)
4
+
5
+ require 'xrt/parser'
6
+ require 'pp'
7
+
8
+ target_files = ARGV
9
+
10
+ target_files.each_with_index{|target_file, index|
11
+ warn "Dumping #{target_file}"
12
+ source = open(target_file).read
13
+ parser = XRT::Parser.new(source)
14
+ pp parser.document
15
+ }
16
+
17
+ exit 0
@@ -0,0 +1,24 @@
1
+ base_dir = File.expand_path(File.join(File.dirname(__FILE__), ".."))
2
+ lib_dir = File.join(base_dir, "lib")
3
+ $LOAD_PATH.unshift(lib_dir)
4
+
5
+ require 'xrt/parser'
6
+
7
+ target_file, target_block = *ARGV
8
+
9
+ warn "target_file: #{target_file}"
10
+ warn "target_block: #{target_block}"
11
+
12
+ source = open(target_file).read
13
+ parser = XRT::Parser.new(source)
14
+ doc = parser.document
15
+ found = doc.find_blocks.select{|block|
16
+ block.content.index(target_block) == 0
17
+ }.first
18
+
19
+ if found
20
+ puts found.auto_indent
21
+ else
22
+ warn 'not found'
23
+ exit 1
24
+ end
@@ -0,0 +1,31 @@
1
+ base_dir = File.expand_path(File.join(File.dirname(__FILE__), ".."))
2
+ lib_dir = File.join(base_dir, "lib")
3
+ $LOAD_PATH.unshift(lib_dir)
4
+
5
+ require 'xrt/parser'
6
+
7
+ target_file, target_block, replace_to = *ARGV
8
+
9
+ warn "target_file: #{target_file}"
10
+ warn "target_block: #{target_block}"
11
+ warn "replace_to: #{replace_to}"
12
+
13
+ source = open(target_file).read
14
+ parser = XRT::Parser.new(source)
15
+ doc = parser.document
16
+ found = doc.find_blocks.select{|block|
17
+ block.content.index(target_block) == 0
18
+ }.first
19
+
20
+ if found
21
+ puts found.content
22
+ else
23
+ warn 'not found'
24
+ exit 1
25
+ end
26
+
27
+ replace_to_node = XRT::Parser.new(replace_to).document
28
+
29
+ doc.replace_child(replace_to_node, found)
30
+
31
+ puts doc.content
data/test/run-test.rb ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $VERBOSE = true
4
+
5
+ base_dir = File.expand_path(File.join(File.dirname(__FILE__), ".."))
6
+ lib_dir = File.join(base_dir, "lib")
7
+ test_dir = File.join(base_dir, "test")
8
+
9
+ require "test-unit"
10
+
11
+ $LOAD_PATH.unshift(lib_dir)
12
+ $LOAD_PATH.unshift(test_dir)
13
+
14
+ ENV["TEST_UNIT_MAX_DIFF_TARGET_STRING_SIZE"] ||= "5000"
15
+
16
+ exit Test::Unit::AutoRunner.run(true, test_dir)
@@ -0,0 +1,25 @@
1
+ require 'test/unit'
2
+ require 'xrt/depth_checker'
3
+
4
+ class TestDepthChecker < Test::Unit::TestCase
5
+ def setup
6
+ @checker = XRT::DepthChecker.new
7
+ end
8
+
9
+ def test_check
10
+ assert_equal true, @checker.check(%q{[% IF a %]1[% END %]})[0]
11
+ assert_equal true, @checker.check(%q{hi})[0]
12
+ assert_equal false, @checker.check(%q{[% IF a %]})[0]
13
+
14
+ assert_equal true, @checker.check(%q{[% IF a %]1[% END %]})[1].kind_of?(String)
15
+ assert_equal true, @checker.check(%q{[% IF a %]})[1].kind_of?(String)
16
+ end
17
+
18
+ def test_max_depth
19
+ assert_equal 0, @checker.max_depth(%q{foo})
20
+ assert_equal 0, @checker.max_depth(%q{[% foo %]})
21
+ assert_equal 1, @checker.max_depth(%q{[% IF a %]1[% END %]})
22
+
23
+ assert_raises { @checker.max_depth(%q{[% IF a %]}) }
24
+ end
25
+ end
@@ -0,0 +1,89 @@
1
+ require 'test/unit'
2
+ require 'xrt/parser'
3
+
4
+ class TestParser < Test::Unit::TestCase
5
+ def setup
6
+ @parser = XRT::Parser.new
7
+ end
8
+
9
+ def test_document_empty
10
+ parser = XRT::Parser.new('')
11
+ doc = parser.document
12
+ assert doc.kind_of? XRT::Statement::Document
13
+ assert_equal [], doc.children
14
+ end
15
+
16
+ def test_document_text_directive
17
+ parser = XRT::Parser.new('1[% 2 %]3')
18
+ doc = parser.document
19
+ assert doc.kind_of? XRT::Statement::Document
20
+ assert_equal [
21
+ XRT::Statement::Text.new('1'),
22
+ XRT::Statement::Directive.new('[% 2 %]'),
23
+ XRT::Statement::Text.new('3'),
24
+ ], doc.children
25
+ end
26
+
27
+ def test_document_block
28
+ parser = XRT::Parser.new('[% IF a %]1[% END %]')
29
+ doc = parser.document
30
+ assert doc.kind_of? XRT::Statement::Document
31
+ if_block = XRT::Statement::Block.new('[% IF a %]')
32
+ if_block << XRT::Statement::Text.new('1')
33
+ if_block << XRT::Statement::Text.new('[% END %]')
34
+ assert_equal [
35
+ if_block
36
+ ], doc.children
37
+ end
38
+
39
+ def test_document_nested_block
40
+ parser = XRT::Parser.new('[% IF a %][% IF b %]1[% END %][% END %]')
41
+ doc = parser.document
42
+ assert doc.kind_of? XRT::Statement::Document
43
+ if_block1 = XRT::Statement::Block.new('[% IF a %]')
44
+ if_block2 = XRT::Statement::Block.new('[% IF b %]')
45
+ if_block2 << XRT::Statement::Text.new('1')
46
+ if_block2 << XRT::Statement::Text.new('[% END %]')
47
+ if_block1 << if_block2
48
+ if_block1 << XRT::Statement::Text.new('[% END %]')
49
+ assert_equal [
50
+ if_block1
51
+ ], doc.children
52
+ end
53
+
54
+ def test_read_directive
55
+ assert_equal '[% %]', @parser.read_directive('[% %]')
56
+ assert_equal '[% [ ] %]', @parser.read_directive('[% [ ] %]')
57
+ assert_nil @parser.read_directive('hi')
58
+ end
59
+
60
+ def test_read_text
61
+ assert_equal 'hi', @parser.read_text('hi')
62
+ assert_equal 'hi[', @parser.read_text('hi[')
63
+ assert_equal 'hi', @parser.read_text('hi[%')
64
+ assert_nil @parser.read_text('[% %]')
65
+ end
66
+
67
+ def test_tokens
68
+ test_cases = [
69
+ ['<html>', ['<html>']],
70
+ ['a [% b %] c', ['a ', '[% b %]', ' c']],
71
+ ['[% a %] [% b %] [% c %]', ['[% a %]', ' ', '[% b %]', ' ', '[% c %]']],
72
+ ['[% FOR k IN [1, 2, 3] %]', ['[% FOR k IN [1, 2, 3] %]']],
73
+ [
74
+ %q([% WRAPPER "wrapper.tt" WITH args = [1,2,3] %]<div></div>[% END %]),
75
+ [
76
+ %q([% WRAPPER "wrapper.tt" WITH args = [1,2,3] %]),
77
+ %q(<div></div>),
78
+ %q([% END %]),
79
+ ]
80
+ ],
81
+ ]
82
+
83
+ test_cases.each{|test_case|
84
+ input, expected = *test_case
85
+ parser = XRT::Parser.new(input)
86
+ assert_equal expected, parser.tokens
87
+ }
88
+ end
89
+ end
@@ -0,0 +1,157 @@
1
+ require 'test/unit'
2
+ require 'xrt/statement'
3
+
4
+ class TestStatementFactory < Test::Unit::TestCase
5
+ def test_new_from_string
6
+ assert XRT::Statement::Factory.new_from_content('hi').kind_of? XRT::Statement::Text
7
+ assert XRT::Statement::Factory.new_from_content('[% foo %]').kind_of? XRT::Statement::Directive
8
+ assert XRT::Statement::Factory.new_from_content('[% IF 1 %]').kind_of? XRT::Statement::Block
9
+ assert XRT::Statement::Factory.new_from_content('[% foo IF 1 %]').kind_of? XRT::Statement::Directive
10
+ end
11
+ end
12
+
13
+ class TestStatement < Test::Unit::TestCase
14
+ def test_text
15
+ text = XRT::Statement::Text.new('hi')
16
+ assert text.kind_of? XRT::Statement
17
+ assert_equal text.content, 'hi'
18
+ assert_equal text.children, []
19
+ end
20
+
21
+ def test_text_auto_indent
22
+ text = XRT::Statement::Text.new(<<'HTML')
23
+ <html>
24
+ <body>
25
+
26
+ </body>
27
+ </html>
28
+ HTML
29
+
30
+ assert_equal text.auto_indent, <<'HTML'
31
+ <html>
32
+ <body>
33
+
34
+ </body>
35
+ </html>
36
+ HTML
37
+ end
38
+
39
+ def test_directive
40
+ text = XRT::Statement::End.new('[% foo() %]')
41
+ assert text.kind_of? XRT::Statement
42
+ assert_equal text.content, '[% foo() %]'
43
+ assert_equal text.children, []
44
+ end
45
+
46
+ def test_end
47
+ text = XRT::Statement::End.new('[% END %]')
48
+ assert text.kind_of? XRT::Statement
49
+ assert_equal text.content, '[% END %]'
50
+ assert_equal text.children, []
51
+ end
52
+
53
+ def test_block
54
+ text = XRT::Statement::Block.new('[% IF 1 %]')
55
+ assert text.kind_of? XRT::Statement
56
+ assert_equal text.content, '[% IF 1 %]'
57
+ assert_equal text.children, []
58
+ end
59
+
60
+ def test_document
61
+ document = XRT::Statement::Document.new
62
+ s1 = XRT::Statement::Text.new('ok')
63
+ s2 = XRT::Statement::Directive.new('[% foo %]')
64
+ document << s1
65
+ document << s2
66
+
67
+ assert_equal document.children, [s1, s2]
68
+
69
+ assert_equal document.content, %q{ok[% foo %]}
70
+ end
71
+
72
+ def test_replace_child
73
+ document = XRT::Statement::Document.new
74
+ s1 = XRT::Statement::Text.new('1')
75
+ s2 = XRT::Statement::Text.new('2')
76
+ s3 = XRT::Statement::Text.new('3')
77
+ document << s1
78
+
79
+ assert_nil document.replace_child(s2, s3),'when not found'
80
+ assert_equal '1', document.content, 'not changed'
81
+
82
+ replaced = document.replace_child(s2, s1)
83
+ assert_same replaced, s1
84
+ assert_equal '2', document.content, 'replaced'
85
+ end
86
+
87
+ def test_replace_child_for_descendant
88
+ document = XRT::Statement::Document.new
89
+ if_block = XRT::Statement::Block.new('[% IF a %]')
90
+ if_block_inner_text = XRT::Statement::Text.new('1')
91
+ new_if_block_inner_text = XRT::Statement::Text.new('2')
92
+ if_block << if_block_inner_text
93
+ if_block << XRT::Statement::Text.new('[% END %]')
94
+ document << if_block
95
+
96
+ assert document.replace_child(new_if_block_inner_text, if_block_inner_text)
97
+ assert_equal '[% IF a %]2[% END %]', document.content
98
+ end
99
+
100
+ def test_depth
101
+ document = XRT::Statement::Document.new
102
+ s1 = XRT::Statement::Text.new('1')
103
+ if_block = XRT::Statement::Block.new('[% IF a %]')
104
+ if_block_inner_text = XRT::Statement::Text.new('1')
105
+ if_block << if_block_inner_text
106
+ if_block << XRT::Statement::Text.new('[% END %]')
107
+ not_child = XRT::Statement::Text.new('not_child')
108
+ document << s1
109
+ document << if_block
110
+
111
+ assert_equal 0, document.depth(s1)
112
+ assert_equal 0, document.depth(if_block)
113
+ assert_equal 1, document.depth(if_block_inner_text)
114
+ assert_equal nil, document.depth(not_child)
115
+ end
116
+
117
+ def test_find_blocks
118
+ document = XRT::Statement::Document.new
119
+ assert_equal [], document.find_blocks, 'when there is no block'
120
+
121
+ document << XRT::Statement::Text.new('1')
122
+ if_block = XRT::Statement::Block.new('[% IF a %]')
123
+ document << if_block
124
+
125
+ assert_equal [ if_block ], document.find_blocks, 'returns block'
126
+ end
127
+ end
128
+
129
+ class TestBlock < Test::Unit::TestCase
130
+ def test_push_child
131
+ block = XRT::Statement::Block.new('[% IF 1 %]')
132
+ assert_equal false, block.closed?
133
+
134
+ statement_ok = XRT::Statement::Text.new('ok')
135
+ statement_end = XRT::Statement::End.new('[% END %]')
136
+
137
+ block << statement_ok
138
+ assert_equal false, block.closed?
139
+
140
+ block << statement_end
141
+ assert_equal true, block.closed?
142
+
143
+ assert_equal block.children, [statement_ok, statement_end]
144
+
145
+ assert_raises {
146
+ block << statement_ok
147
+ }
148
+ end
149
+
150
+ def test_content
151
+ block = XRT::Statement::Block.new('[% IF 1 %]')
152
+ block << XRT::Statement::Text.new('ok')
153
+ block << XRT::Statement::End.new('[% END %]')
154
+
155
+ assert_equal block.content, %q{[% IF 1 %]ok[% END %]}
156
+ end
157
+ end
@@ -0,0 +1,47 @@
1
+ require 'test/unit'
2
+ require 'xrt/syntax'
3
+
4
+ class TestSyntax < Test::Unit::TestCase
5
+ def setup
6
+ @syntax = XRT::Syntax.new
7
+ end
8
+
9
+ def test_remove_comment
10
+ assert_equal 'foo', @syntax.remove_comment('foo # bar')
11
+ end
12
+
13
+ def test_beginning_block?
14
+ assert @syntax.beginning_block? '[% IF 1 %]'
15
+ assert @syntax.beginning_block? '[% if 1 %]'
16
+ assert @syntax.beginning_block? '[% WHILE 1 %]'
17
+ assert_nil @syntax.beginning_block? '[% END # WRAPPER "wrapper.tt" WITH %]'
18
+ assert_nil @syntax.beginning_block? '[% "true" UNLESS false %]'
19
+ end
20
+
21
+ def test_block?
22
+ assert @syntax.beginning_block? '[% IF 1 %]'
23
+ assert @syntax.beginning_block? '[%- IF 1 -%]'
24
+ assert @syntax.beginning_block? "[% IF 1\nfoo\nEND %]"
25
+ assert_nil @syntax.beginning_block? "hi"
26
+ assert_nil @syntax.beginning_block? "[%"
27
+ end
28
+
29
+ def test_end_block?
30
+ assert @syntax.end_block? '[% END %]'
31
+ assert @syntax.end_block? '[% end %]'
32
+ assert @syntax.end_block? '[% END # WRAPPER "wrapper.tt" WITH %]'
33
+ assert_nil @syntax.end_block? ' END '
34
+ end
35
+
36
+ def test_block_level
37
+ assert_equal(0, @syntax.block_level('[% foo %]'))
38
+ assert_equal(1, @syntax.block_level('[% IF 1 %]'))
39
+ assert_equal(1, @syntax.block_level('[%- IF 1 -%]'))
40
+ assert_equal(0, @syntax.block_level('[% IF a THEN 1 END %]'))
41
+ assert_equal(0, @syntax.block_level('[% IF a THEN 1 ELSE 0 END %]'))
42
+ assert_equal(0, @syntax.block_level('[% a ? 1 : 0 %]'))
43
+ assert_equal(-1, @syntax.block_level('[% END # WRAPPER "wrapper.tt" WITH %]'))
44
+ assert_equal(0, @syntax.block_level('[% 2 UNLESS a %]'))
45
+ assert_equal(0, @syntax.block_level('[% 1 IF a %]'))
46
+ end
47
+ end
data/xrt.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'xrt/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "xrt"
8
+ spec.version = Xrt::VERSION
9
+ spec.authors = ["hitode909"]
10
+ spec.email = ["hitode909@gmail.com"]
11
+ spec.summary = %q{Refactoring Tool for Text::Xslate}
12
+ spec.homepage = 'https://github.com/hitode909/xrt'
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_development_dependency "bundler", "~> 1.7"
21
+ spec.add_development_dependency "rake", "~> 10.0"
22
+
23
+ spec.add_development_dependency("test-unit")
24
+ end
metadata ADDED
@@ -0,0 +1,118 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: xrt
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - hitode909
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-11-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: test-unit
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description:
56
+ email:
57
+ - hitode909@gmail.com
58
+ executables:
59
+ - xrt
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - ".gitignore"
64
+ - ".travis.yml"
65
+ - Gemfile
66
+ - LICENSE.txt
67
+ - README.md
68
+ - Rakefile
69
+ - bin/xrt
70
+ - lib/xrt.rb
71
+ - lib/xrt/cli.rb
72
+ - lib/xrt/commands.rb
73
+ - lib/xrt/depth_checker.rb
74
+ - lib/xrt/parser.rb
75
+ - lib/xrt/statement.rb
76
+ - lib/xrt/syntax.rb
77
+ - lib/xrt/version.rb
78
+ - sketch/dump_blocks.rb
79
+ - sketch/dump_max_depth.rb
80
+ - sketch/dump_tree.rb
81
+ - sketch/find_block.rb
82
+ - sketch/replace_block.rb
83
+ - test/run-test.rb
84
+ - test/test-depth_checker.rb
85
+ - test/test-parser.rb
86
+ - test/test-statement.rb
87
+ - test/test-syntax.rb
88
+ - xrt.gemspec
89
+ homepage: https://github.com/hitode909/xrt
90
+ licenses:
91
+ - MIT
92
+ metadata: {}
93
+ post_install_message:
94
+ rdoc_options: []
95
+ require_paths:
96
+ - lib
97
+ required_ruby_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ required_rubygems_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ requirements: []
108
+ rubyforge_project:
109
+ rubygems_version: 2.4.5.1
110
+ signing_key:
111
+ specification_version: 4
112
+ summary: Refactoring Tool for Text::Xslate
113
+ test_files:
114
+ - test/run-test.rb
115
+ - test/test-depth_checker.rb
116
+ - test/test-parser.rb
117
+ - test/test-statement.rb
118
+ - test/test-syntax.rb