moco 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +20 -0
- data/README.txt +67 -0
- data/Rakefile +15 -0
- data/bin/moco +6 -0
- data/lib/moco/ansi_escape.rb +37 -0
- data/lib/moco/application.rb +109 -0
- data/lib/moco/browser.rb +64 -0
- data/lib/moco/browser_error.rb +105 -0
- data/lib/moco/compile_error.rb +66 -0
- data/lib/moco/compiler.rb +151 -0
- data/lib/moco/compiler_option.rb +60 -0
- data/lib/moco/compiler_register.rb +29 -0
- data/lib/moco/compilers/coffee_compiler.rb +70 -0
- data/lib/moco/compilers/haml_compiler.rb +21 -0
- data/lib/moco/compilers/less_compiler.rb +15 -0
- data/lib/moco/compilers/markdown_compiler.rb +139 -0
- data/lib/moco/compilers/sass_compiler.rb +25 -0
- data/lib/moco/file_util.rb +54 -0
- data/lib/moco/log.rb +108 -0
- data/lib/moco/monitor.rb +83 -0
- data/lib/moco/options.rb +336 -0
- data/lib/moco/source_map.rb +22 -0
- data/lib/moco/support/error/error.css +22 -0
- data/lib/moco/support/error/error.html +7 -0
- data/lib/moco/support/error/error.js +58 -0
- data/lib/moco/support/reload.scpt +0 -0
- data/lib/moco.rb +44 -0
- data/moco.gemspec +35 -0
- data/moco.rb +35 -0
- data/src/error/error.coffee +49 -0
- data/src/reload.applescript +135 -0
- data/test/ansi_escape_test.rb +52 -0
- data/test/application_test.rb +40 -0
- data/test/browser_error_test.rb +101 -0
- data/test/browser_test.rb +29 -0
- data/test/compile_error_test.rb +82 -0
- data/test/compiler_option_test.rb +121 -0
- data/test/compiler_register_test.rb +41 -0
- data/test/compiler_test.rb +243 -0
- data/test/compilers/coffee_compiler_test.rb +117 -0
- data/test/compilers/haml_compiler_test.rb +86 -0
- data/test/compilers/less_compiler_test.rb +72 -0
- data/test/compilers/markdown_compiler_test.rb +211 -0
- data/test/compilers/sass_compiler_test.rb +84 -0
- data/test/file_util_test.rb +37 -0
- data/test/fixtures/_color.scss +1 -0
- data/test/fixtures/color.less +1 -0
- data/test/fixtures/css_lib.rb +2 -0
- data/test/fixtures/html_lib.rb +2 -0
- data/test/fixtures/js_lib.rb +2 -0
- data/test/fixtures/layout.html +13 -0
- data/test/fixtures/moco.rb +8 -0
- data/test/fixtures/options_lib.rb +2 -0
- data/test/fixtures/source.txt +1 -0
- data/test/monitor_test.rb +68 -0
- data/test/options_test.rb +177 -0
- data/test/test_helper.rb +57 -0
- metadata +270 -0
@@ -0,0 +1,135 @@
|
|
1
|
+
on run argv
|
2
|
+
local browsers, urls
|
3
|
+
set {browsers, urls} to parse(argv)
|
4
|
+
if browsers contains "Canary" then reloadCanary(urls)
|
5
|
+
if browsers contains "Chrome" then reloadChrome(urls)
|
6
|
+
if browsers contains "Firefox" then reloadFirefox(urls)
|
7
|
+
if browsers contains "Opera" then reloadOpera(urls)
|
8
|
+
if browsers contains "Safari" then reloadSafari(urls)
|
9
|
+
if browsers contains "WebKit" then reloadWebKit(urls)
|
10
|
+
return
|
11
|
+
end
|
12
|
+
|
13
|
+
on parse(argv)
|
14
|
+
set urls to {}
|
15
|
+
set browsers to {}
|
16
|
+
set allBrowsers to {"Canary", "Chrome", "Firefox", "Opera", "Safari", "WebKit"}
|
17
|
+
|
18
|
+
repeat with arg in argv
|
19
|
+
set arg to arg as string
|
20
|
+
if allBrowsers contains arg then
|
21
|
+
set browsers to browsers & arg
|
22
|
+
else
|
23
|
+
set urls to urls & arg
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
if browsers = {} then set browsers to allBrowsers
|
28
|
+
|
29
|
+
return {browsers, urls}
|
30
|
+
end
|
31
|
+
|
32
|
+
on shouldReload(_url, urls)
|
33
|
+
if _url = "" then return false
|
34
|
+
if urls = {} then return true
|
35
|
+
repeat with u in urls
|
36
|
+
if _url starts with u then return true
|
37
|
+
end
|
38
|
+
return false
|
39
|
+
end
|
40
|
+
|
41
|
+
on reloadSafari(urls)
|
42
|
+
reloadSafariWebKit("Safari", urls)
|
43
|
+
end
|
44
|
+
|
45
|
+
on reloadWebKit(urls)
|
46
|
+
reloadSafariWebKit("WebKit", urls)
|
47
|
+
end
|
48
|
+
|
49
|
+
on reloadSafariWebKit(browser, urls)
|
50
|
+
using terms from application "Safari"
|
51
|
+
tell application browser
|
52
|
+
if it is not running then return
|
53
|
+
if (windows where visible is true) = {} then return
|
54
|
+
if not (front document exists) then return
|
55
|
+
|
56
|
+
tell front document
|
57
|
+
if my shouldReload(URL as string, urls) then
|
58
|
+
do JavaScript "location.reload()"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
on reloadChrome(urls)
|
66
|
+
reloadGoogleChrome("Google Chrome", urls)
|
67
|
+
end
|
68
|
+
|
69
|
+
on reloadCanary(urls)
|
70
|
+
reloadGoogleChrome("Google Chrome Canary", urls)
|
71
|
+
end
|
72
|
+
|
73
|
+
on reloadGoogleChrome(browser, urls)
|
74
|
+
using terms from application "Google Chrome"
|
75
|
+
tell application browser
|
76
|
+
if it is not running then return
|
77
|
+
if (windows where visible is true) = {} then return
|
78
|
+
|
79
|
+
tell active tab of front window
|
80
|
+
if my shouldReload(URL, urls) then reload
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
on reloadOpera(urls)
|
87
|
+
tell application "Opera"
|
88
|
+
if it is not running then return
|
89
|
+
|
90
|
+
if my shouldReload(URL of front document as string, urls) then
|
91
|
+
set URL of front document to "javascript:location.reload()"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
on reloadFirefox(urls)
|
97
|
+
tell application "Firefox"
|
98
|
+
if it is not running then return
|
99
|
+
if (windows where visible is true) = {} then return
|
100
|
+
|
101
|
+
set frontApp to my findFrontApp()
|
102
|
+
activate
|
103
|
+
if my shouldReload(my copyUrl(), urls) then my doReload()
|
104
|
+
my resetFrontApp(frontApp)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
on copyUrl()
|
109
|
+
set the clipboard to ""
|
110
|
+
tell application "System Events"
|
111
|
+
delay 0.01
|
112
|
+
keystroke "l" using {command down}
|
113
|
+
delay 0.01
|
114
|
+
keystroke "c" using {command down}
|
115
|
+
delay 0.1
|
116
|
+
end
|
117
|
+
return the clipboard as string
|
118
|
+
end
|
119
|
+
|
120
|
+
on doReload()
|
121
|
+
tell application "System Events"
|
122
|
+
delay 0.01
|
123
|
+
keystroke "r" using {command down}
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
on findFrontApp()
|
128
|
+
tell application "System Events"
|
129
|
+
return first process where frontmost is true
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
on resetFrontApp(frontApp)
|
134
|
+
tell frontApp to set frontmost to true
|
135
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module MoCo
|
4
|
+
|
5
|
+
describe AnsiEscape do
|
6
|
+
|
7
|
+
def stdout_tty(tty, &block)
|
8
|
+
$stdout = $stdout.dup
|
9
|
+
$stdout.define_singleton_method(:tty?) { tty }
|
10
|
+
yield
|
11
|
+
ensure
|
12
|
+
$stdout = STDOUT
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'escapes the message when stdout points to a terminal' do
|
16
|
+
stdout_tty(true) do
|
17
|
+
assert_equal "\e[1mHello\e[0m", AnsiEscape.bold('Hello')
|
18
|
+
assert_equal "\e[1;31mHello\e[0m", AnsiEscape.bold_red('Hello')
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'leaves the message alone if stdout is redirected to a file' do
|
23
|
+
stdout_tty(false) do
|
24
|
+
assert_equal 'Hello', AnsiEscape.bold('Hello')
|
25
|
+
assert_equal 'Hello', AnsiEscape.bold_red('Hello')
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe 'unescape' do
|
30
|
+
|
31
|
+
let(:hello) { "\e[1mHello\e[0m \e[1;31mWorld\e[00m!" }
|
32
|
+
|
33
|
+
it 'removes ANSI escape sequences' do
|
34
|
+
assert_equal 'Hello World!', AnsiEscape.unescape(hello)
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'can replace ANSI escape sequences' do
|
38
|
+
unescaped = AnsiEscape.unescape(hello) { |msg| "<b>#{msg}</b>" }
|
39
|
+
assert_equal '<b>Hello</b> <b>World</b>!', unescaped
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'does not support nested escape sequences' do
|
43
|
+
nested = "\e[;31mNested \e[1mEscape\e[m\e[0m"
|
44
|
+
refute_equal 'Nested Escape', AnsiEscape.unescape(nested)
|
45
|
+
assert_equal "Nested \e[1mEscape\e[0m", AnsiEscape.unescape(nested)
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module MoCo
|
4
|
+
|
5
|
+
describe Application do
|
6
|
+
|
7
|
+
describe 'the compiled directory' do
|
8
|
+
|
9
|
+
def compiled_dir(file)
|
10
|
+
compiled_dirs = {
|
11
|
+
'/one' => '/1',
|
12
|
+
'/one/two' => nil,
|
13
|
+
'/one/two/three' => '/3',
|
14
|
+
}
|
15
|
+
Application.new(:compiled_dirs => compiled_dirs).compiled_dir(file)
|
16
|
+
end
|
17
|
+
|
18
|
+
before { Application.send(:public, :compiled_dir) }
|
19
|
+
after { Application.send(:private, :compiled_dir) }
|
20
|
+
|
21
|
+
it 'returns the directory that closest matches the filename' do
|
22
|
+
assert_equal '/1', compiled_dir('/one/file')
|
23
|
+
assert_equal '/3', compiled_dir('/one/two/three/file')
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'keeps the nested directory structure' do
|
27
|
+
assert_equal '/1/nested', compiled_dir('/one/nested/file')
|
28
|
+
assert_equal '/3/nested', compiled_dir('/one/two/three/nested/file')
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'returns nil when the compiled directory is unspecified' do
|
32
|
+
assert_nil compiled_dir('/one/two/file')
|
33
|
+
assert_nil compiled_dir('/one/two/nested/file')
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module MoCo
|
4
|
+
|
5
|
+
describe BrowserError do
|
6
|
+
|
7
|
+
class BrowserError
|
8
|
+
define_method(:txmt_url_scheme?) { true }
|
9
|
+
end
|
10
|
+
|
11
|
+
let(:error) do
|
12
|
+
error = StandardError.new("\e[1;31m eval \e[0m error \\n&")
|
13
|
+
error.define_singleton_method(:line) { 2 }
|
14
|
+
error.define_singleton_method(:column) { 7 }
|
15
|
+
CompileError.new(error, File.expand_path('~') + '/file.txt')
|
16
|
+
end
|
17
|
+
|
18
|
+
describe 'the browser error message' do
|
19
|
+
|
20
|
+
let(:msg) { BrowserError.message(error) }
|
21
|
+
|
22
|
+
it 'contains the line number and filename' do
|
23
|
+
assert_match "\n\nLine: 2\nFile: file.txt", msg
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'shortens the filename by replacing the home directory with ~' do
|
27
|
+
assert_match '>~/file.txt</a>', msg
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
describe 'the CSS error message' do
|
33
|
+
|
34
|
+
let(:msg) { CssError.message(error) }
|
35
|
+
|
36
|
+
it 'is CSS' do
|
37
|
+
assert_match 'body:before {', msg
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'is escaped for CSS' do
|
41
|
+
assert_match 'error \\\\n&\\a \\a Line: 2', msg
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'removes ansi color codes completely' do
|
45
|
+
refute_match '[1;31m', msg
|
46
|
+
refute_match '<span>', msg
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'has no link' do
|
50
|
+
refute_match '<a href', msg
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
describe 'the JavaScript error message' do
|
56
|
+
|
57
|
+
let(:msg) { JsError.message(error) }
|
58
|
+
|
59
|
+
it 'is JavaScript' do
|
60
|
+
assert_match 'function() {', msg
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'is escaped for Html' do
|
64
|
+
assert_match 'error \\\\n&<br><br>Line: 2', msg
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'replaces ansi color codes with a span element' do
|
68
|
+
refute_match '[1;31m', msg
|
69
|
+
assert_match '<span> eval </span>', msg
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'has a TextMate link to the exact error location' do
|
73
|
+
link = "<a href='txmt://open/?url=file://~/file.txt&line=2&column=7'>"
|
74
|
+
assert_match link, msg
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'has no link if the TextMate url scheme is unsupported' do
|
78
|
+
js_error = JsError.new(error)
|
79
|
+
js_error.define_singleton_method(:txmt_url_scheme?) { false }
|
80
|
+
refute_match '<a href', js_error.message
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
describe 'the HTML error message' do
|
86
|
+
|
87
|
+
let(:msg) { HtmlError.message(error) }
|
88
|
+
|
89
|
+
it 'is Html' do
|
90
|
+
assert_match '<!DOCTYPE html>', msg
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'contains the JavaScript error message' do
|
94
|
+
assert_match JsError.message(error), msg
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module MoCo
|
4
|
+
|
5
|
+
describe Browser do
|
6
|
+
|
7
|
+
describe 'urls' do
|
8
|
+
|
9
|
+
def urls(*urls)
|
10
|
+
Browser.new([], [], urls).instance_variable_get(:@args)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "expands 'localhost' into all the localhost urls" do
|
14
|
+
assert_equal Browser.localhost, urls('localhost')
|
15
|
+
end
|
16
|
+
|
17
|
+
it "empties the urls if it contains 'all'" do
|
18
|
+
assert_empty urls('localhost', 'all')
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'removes duplicates' do
|
22
|
+
assert_equal 1, urls('file:///', 'file:///').size
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module MoCo
|
4
|
+
|
5
|
+
describe CompileError do
|
6
|
+
|
7
|
+
describe 'the error message' do
|
8
|
+
|
9
|
+
let(:error) do
|
10
|
+
message = 'script.coffee:4:8: Error: syntax error'
|
11
|
+
CompileError.new(StandardError.new(message), 'script.coffee')
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'removes the filename and line number' do
|
15
|
+
refute_match 'script.coffee', error.message
|
16
|
+
refute_match /\d/, error.message
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'removes error: from the start of the message' do
|
20
|
+
refute_match 'Error:', error.message
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'capitalizes the first letter' do
|
24
|
+
assert_equal 'Syntax error', error.message
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
describe 'the line number' do
|
30
|
+
|
31
|
+
def line_from(options)
|
32
|
+
error = StandardError.new(options[:message])
|
33
|
+
error.define_singleton_method(:line) { options[:line_method] }
|
34
|
+
error.set_backtrace(options[:backtrace])
|
35
|
+
CompileError.new(error, 'index.haml').line
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'uses the line method of the original error' do
|
39
|
+
assert_equal 2, line_from(:line_method => 2)
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'finds the line number in the error message' do
|
43
|
+
assert_equal 42, line_from(:message => 'index.haml:42 ...')
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'finds the line number in the backtrace' do
|
47
|
+
assert_equal 4, line_from(:backtrace => 'index.haml:4:2: ...')
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'ignores line numbers from other files' do
|
51
|
+
assert_nil line_from(:backtrace => 'about.haml:2: ...')
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'works with filenames containing special characters' do
|
55
|
+
file = 'index( |*).haml'
|
56
|
+
error = CompileError.new(StandardError.new(file + ':7:'), file)
|
57
|
+
assert_equal 7, error.line
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
describe 'the column' do
|
63
|
+
|
64
|
+
def column_from(options)
|
65
|
+
error = StandardError.new(options[:message])
|
66
|
+
error.define_singleton_method(:column) { options[:column_method] }
|
67
|
+
CompileError.new(error, 'index.haml').column
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'uses the column method of the original error' do
|
71
|
+
assert_equal 4, column_from(:column_method => 4)
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'finds the column in the error message' do
|
75
|
+
assert_equal 2, column_from(:message => 'index.haml:4:2: ...')
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module MoCo
|
4
|
+
|
5
|
+
describe CompilerOption do
|
6
|
+
|
7
|
+
def convert(option)
|
8
|
+
value = option.split(':', 3)[2]
|
9
|
+
CompilerOption.convert(value)
|
10
|
+
end
|
11
|
+
|
12
|
+
specify 'convert booleans' do
|
13
|
+
assert_equal true, convert('ext:key:true')
|
14
|
+
assert_equal false, convert('ext:key:false')
|
15
|
+
end
|
16
|
+
|
17
|
+
specify 'the default option value is true' do
|
18
|
+
assert_equal true, convert('ext:key')
|
19
|
+
end
|
20
|
+
|
21
|
+
specify 'convert integers' do
|
22
|
+
assert_equal 100, convert('ext:key:100')
|
23
|
+
assert_equal -10, convert('ext:key:-10')
|
24
|
+
assert_equal 7, convert('ext:key:+007')
|
25
|
+
end
|
26
|
+
|
27
|
+
specify 'convert floats' do
|
28
|
+
assert_equal 0.5, convert('ext:key:.5')
|
29
|
+
assert_equal 0.5, convert('ext:key:+0.5')
|
30
|
+
assert_equal -0.5, convert('ext:key:-.5')
|
31
|
+
end
|
32
|
+
|
33
|
+
specify 'exponential notation is unsupported' do
|
34
|
+
assert_equal '1e2', convert('ext:key:1e2')
|
35
|
+
end
|
36
|
+
|
37
|
+
describe 'convert symbols' do
|
38
|
+
|
39
|
+
specify do
|
40
|
+
assert_equal :html5, convert('ext:key::html5')
|
41
|
+
assert_equal :false, convert('ext:key::false')
|
42
|
+
assert_equal :'007', convert('ext:key::007')
|
43
|
+
end
|
44
|
+
|
45
|
+
specify 'do not quote symbols' do
|
46
|
+
assert_equal :'"dir/file"', convert('ext:key::"dir/file"')
|
47
|
+
assert_equal :'dir/file', convert('ext:key::dir/file')
|
48
|
+
end
|
49
|
+
|
50
|
+
specify 'symbols with colons are unsupported' do
|
51
|
+
assert_equal [:'"foo', 'bar"'], convert('ext:key::"foo:bar"')
|
52
|
+
assert_equal [:'foo\\', 'bar'], convert('ext:key::foo\\:bar')
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
describe 'convert strings' do
|
58
|
+
|
59
|
+
specify do
|
60
|
+
assert_equal 'value', convert('ext:key:value')
|
61
|
+
assert_equal 'v a l', convert('ext:key:v a l')
|
62
|
+
assert_equal "cat's", convert("ext:key:cat's")
|
63
|
+
end
|
64
|
+
|
65
|
+
specify 'quoted strings' do
|
66
|
+
assert_equal '007', convert('ext:key:"007"')
|
67
|
+
assert_equal 'true', convert("ext:key:'true'")
|
68
|
+
assert_equal 'a:b:c', convert('ext:key:"a:b:c"')
|
69
|
+
end
|
70
|
+
|
71
|
+
specify 'double quoted strings can include single quotes and vice versa' do
|
72
|
+
assert_equal ":cat's", convert(%(ext:key:":cat's"))
|
73
|
+
assert_equal "'cat'", convert(%(ext:key:"'cat'"))
|
74
|
+
assert_equal '"cat"', convert(%(ext:key:'"cat"'))
|
75
|
+
end
|
76
|
+
|
77
|
+
specify 'quoted strings cannot include the same quote character' do
|
78
|
+
assert_equal ["'", "cat's'"], convert(%(ext:key:':cat's'))
|
79
|
+
assert_equal "''cat''", convert(%(ext:key:''cat''))
|
80
|
+
assert_equal '""cat""', convert(%(ext:key:""cat""))
|
81
|
+
end
|
82
|
+
|
83
|
+
specify 'single and double quotes cannot be mixed' do
|
84
|
+
assert_equal ["'not", 'quoted"'], convert(%(ext:key:'not:quoted"))
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
specify 'convert empty strings and arrays' do
|
90
|
+
assert_equal '', convert('ext:key:')
|
91
|
+
assert_equal [], convert('ext:key::')
|
92
|
+
assert_equal [''], convert('ext:key:"":')
|
93
|
+
end
|
94
|
+
|
95
|
+
describe 'convert arrays' do
|
96
|
+
|
97
|
+
specify 'string arrays' do
|
98
|
+
assert_equal ['one', 'two'], convert('ext:key:one:two')
|
99
|
+
assert_equal ['one'], convert('ext:key:one:')
|
100
|
+
end
|
101
|
+
|
102
|
+
specify 'symbol arrays' do
|
103
|
+
assert_equal [:one, :two], convert('ext:key::one::two')
|
104
|
+
assert_equal [:one], convert('ext:key::one:')
|
105
|
+
end
|
106
|
+
|
107
|
+
specify 'array with mixed types' do
|
108
|
+
assert_equal ["Cat's toy", true, :html5, -0.1, "007"],
|
109
|
+
convert('ext:key:"Cat\'s toy":true::html5:-.1:"007"')
|
110
|
+
end
|
111
|
+
|
112
|
+
specify 'array values cannot include colons' do
|
113
|
+
assert_equal ['"Title', ' MoCo"'], convert('ext:key:"Title: MoCo":')
|
114
|
+
assert_equal ["'", "not_symbol'"], convert("ext:key:':not_symbol':")
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module MoCo
|
4
|
+
|
5
|
+
describe CompilerRegister do
|
6
|
+
|
7
|
+
before { Singleton.__init__(CompilerRegister) }
|
8
|
+
after { reset_register }
|
9
|
+
|
10
|
+
it 'registers the compiler class for the source extension' do
|
11
|
+
HtmlCompiler.register('haml')
|
12
|
+
assert_equal HtmlCompiler, MoCo.compiler_for('haml')
|
13
|
+
end
|
14
|
+
|
15
|
+
describe 'compiler lookup' do
|
16
|
+
|
17
|
+
before do
|
18
|
+
MoCo.register(CssCompiler, 'sass')
|
19
|
+
MoCo.register(CssCompiler, 'scss')
|
20
|
+
MoCo.register(HtmlCompiler, 'md')
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'works when more than one compiler is registered' do
|
24
|
+
assert_equal CssCompiler, MoCo.compiler_for('scss')
|
25
|
+
assert_equal HtmlCompiler, MoCo.compiler_for('md')
|
26
|
+
assert_equal CssCompiler, MoCo.compiler_for('sass')
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'accepts a filename' do
|
30
|
+
assert_equal CssCompiler, MoCo.compiler_for('/dir/a style.css.sass')
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'returns nil when the extension is unregistered' do
|
34
|
+
assert_nil MoCo.compiler_for('dummy')
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|