xrt 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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