moco 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|