moco 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +20 -0
  3. data/README.txt +67 -0
  4. data/Rakefile +15 -0
  5. data/bin/moco +6 -0
  6. data/lib/moco/ansi_escape.rb +37 -0
  7. data/lib/moco/application.rb +109 -0
  8. data/lib/moco/browser.rb +64 -0
  9. data/lib/moco/browser_error.rb +105 -0
  10. data/lib/moco/compile_error.rb +66 -0
  11. data/lib/moco/compiler.rb +151 -0
  12. data/lib/moco/compiler_option.rb +60 -0
  13. data/lib/moco/compiler_register.rb +29 -0
  14. data/lib/moco/compilers/coffee_compiler.rb +70 -0
  15. data/lib/moco/compilers/haml_compiler.rb +21 -0
  16. data/lib/moco/compilers/less_compiler.rb +15 -0
  17. data/lib/moco/compilers/markdown_compiler.rb +139 -0
  18. data/lib/moco/compilers/sass_compiler.rb +25 -0
  19. data/lib/moco/file_util.rb +54 -0
  20. data/lib/moco/log.rb +108 -0
  21. data/lib/moco/monitor.rb +83 -0
  22. data/lib/moco/options.rb +336 -0
  23. data/lib/moco/source_map.rb +22 -0
  24. data/lib/moco/support/error/error.css +22 -0
  25. data/lib/moco/support/error/error.html +7 -0
  26. data/lib/moco/support/error/error.js +58 -0
  27. data/lib/moco/support/reload.scpt +0 -0
  28. data/lib/moco.rb +44 -0
  29. data/moco.gemspec +35 -0
  30. data/moco.rb +35 -0
  31. data/src/error/error.coffee +49 -0
  32. data/src/reload.applescript +135 -0
  33. data/test/ansi_escape_test.rb +52 -0
  34. data/test/application_test.rb +40 -0
  35. data/test/browser_error_test.rb +101 -0
  36. data/test/browser_test.rb +29 -0
  37. data/test/compile_error_test.rb +82 -0
  38. data/test/compiler_option_test.rb +121 -0
  39. data/test/compiler_register_test.rb +41 -0
  40. data/test/compiler_test.rb +243 -0
  41. data/test/compilers/coffee_compiler_test.rb +117 -0
  42. data/test/compilers/haml_compiler_test.rb +86 -0
  43. data/test/compilers/less_compiler_test.rb +72 -0
  44. data/test/compilers/markdown_compiler_test.rb +211 -0
  45. data/test/compilers/sass_compiler_test.rb +84 -0
  46. data/test/file_util_test.rb +37 -0
  47. data/test/fixtures/_color.scss +1 -0
  48. data/test/fixtures/color.less +1 -0
  49. data/test/fixtures/css_lib.rb +2 -0
  50. data/test/fixtures/html_lib.rb +2 -0
  51. data/test/fixtures/js_lib.rb +2 -0
  52. data/test/fixtures/layout.html +13 -0
  53. data/test/fixtures/moco.rb +8 -0
  54. data/test/fixtures/options_lib.rb +2 -0
  55. data/test/fixtures/source.txt +1 -0
  56. data/test/monitor_test.rb +68 -0
  57. data/test/options_test.rb +177 -0
  58. data/test/test_helper.rb +57 -0
  59. metadata +270 -0
@@ -0,0 +1,243 @@
1
+ require 'test_helper'
2
+
3
+ module MoCo
4
+
5
+ describe Compiler do
6
+
7
+ describe 'require libraries' do
8
+
9
+ $LOAD_PATH.unshift(fixtures_path)
10
+
11
+ it 'will not require the library at the time of registration' do
12
+ JsCompiler.require_library('js_lib')
13
+ refute defined? JsLib
14
+ end
15
+
16
+ it 'requires the library when the compiler is instantiated' do
17
+ CssCompiler.require_library('css_lib')
18
+ refute defined? CssLib
19
+ CssCompiler.new('')
20
+ assert defined? CssLib
21
+ end
22
+
23
+ it 'requires the superclass libraries' do
24
+ HtmlCompiler.require_library('html_lib')
25
+ refute defined? HtmlLib
26
+ Class.new(Class.new(HtmlCompiler)).new('')
27
+ assert defined? HtmlLib
28
+ end
29
+
30
+ end
31
+
32
+ describe 'require uninstalled libraries' do
33
+
34
+ class CorruptCompiler < HtmlCompiler
35
+ require_library 'not_installed', 'not-installed-gem', '~> 2'
36
+ end
37
+
38
+ it 'raises a LoadError with installation instructions' do
39
+ e = assert_raises(Gem::LoadError) { CorruptCompiler.new('') }
40
+ assert_match "gem install not-installed-gem -v '~> 2'", e.message
41
+ end
42
+
43
+ end
44
+
45
+ describe 'compiler options' do
46
+
47
+ after { reset_options(HtmlCompiler) }
48
+
49
+ it 'sets the option' do
50
+ HtmlCompiler.set_option(:format, :html5)
51
+ assert_equal :html5, HtmlCompiler.options[:format]
52
+ assert_equal :html5, HtmlCompiler.new('').options[:format]
53
+ end
54
+
55
+ specify 'the default value is true' do
56
+ HtmlCompiler.set_option(:ugly)
57
+ assert_equal true, HtmlCompiler.options[:ugly]
58
+ end
59
+
60
+ specify 'updating the options hash directly has no effect' do
61
+ HtmlCompiler.options[:format] = :xhtml
62
+ compiler = HtmlCompiler.new('')
63
+ compiler.options[:format] = :xhtml
64
+ assert_empty HtmlCompiler.options
65
+ assert_empty compiler.options
66
+ end
67
+
68
+ end
69
+
70
+ describe 'the compiled filename' do
71
+
72
+ it 'cannot be equal to the source filename' do
73
+ e = assert_raises(Error) do
74
+ HtmlCompiler.new('index.html', Dir.pwd + '/index.html')
75
+ end
76
+ assert_match 'validate_filenames', e.backtrace.first
77
+ end
78
+
79
+ it 'will keep the compiled filename unchanged, if provided' do
80
+ compiler = HtmlCompiler.new('source.md', 'compiled.xml', 'dir')
81
+ assert_equal 'compiled.xml', compiler.compiled_file
82
+ end
83
+
84
+ end
85
+
86
+ describe 'the compiled filename extension' do
87
+
88
+ def compiled_file(source_file)
89
+ JsCompiler.new(source_file).compiled_file
90
+ end
91
+
92
+ it 'has the compiled extension' do
93
+ assert_equal 'script.js', compiled_file('script.coffee')
94
+ end
95
+
96
+ specify 'the compiler must implement compiled_extension' do
97
+ e = assert_raises(NotImplementedError) { Compiler.new('') }
98
+ assert_match 'compiled_extension', e.backtrace.first
99
+ end
100
+
101
+ describe 'nested source extension' do
102
+
103
+ it 'will not duplicate the compiled extension' do
104
+ assert_equal 'script.js', compiled_file('script.js.coffee')
105
+ end
106
+
107
+ it 'will keep other extensions' do
108
+ assert_equal 'script.min.js', compiled_file('script.min.coffee')
109
+ end
110
+
111
+ specify 'not duplicate extension' do
112
+ assert_equal 'about_js.js', compiled_file('about_js.coffee')
113
+ end
114
+
115
+ end
116
+
117
+ describe 'nested compiled extension' do
118
+
119
+ class Minify < JsCompiler
120
+ define_singleton_method(:compiled_extension) { '.min.js' }
121
+ end
122
+
123
+ specify do
124
+ assert_equal 'script.min.js', Minify.new('script.js').compiled_file
125
+ end
126
+
127
+ end
128
+
129
+ describe 'no extensions' do
130
+
131
+ class Readme < Compiler
132
+ define_singleton_method(:compiled_extension) { '' }
133
+ end
134
+
135
+ specify 'the compiled extension is empty' do
136
+ assert_equal 'README', Readme.new('README.txt').compiled_file
137
+ end
138
+
139
+ specify 'the source file has no extension' do
140
+ assert_equal 'README.html', HtmlCompiler.new('README').compiled_file
141
+ end
142
+
143
+ end
144
+
145
+ end
146
+
147
+ describe 'the compiled filename directory' do
148
+
149
+ def compiled_file(source_file, compiled_dir = nil)
150
+ CssCompiler.new(source_file, nil, compiled_dir).compiled_file
151
+ end
152
+
153
+ it 'has the same directory as the source file by default' do
154
+ assert_equal 'css/style.css', compiled_file('css/style.sass')
155
+ end
156
+
157
+ it 'replaces the directory if one is provided' do
158
+ assert_equal 'css/style.css', compiled_file('style.scss', 'css')
159
+ assert_equal 'css/style.css', compiled_file('/sass/style.scss', 'css/')
160
+ end
161
+
162
+ it 'removes the source directory if compiled_dir is empty' do
163
+ assert_equal 'style.css', compiled_file('templates/style.scss', '')
164
+ end
165
+
166
+ end
167
+
168
+ describe 'compile' do
169
+
170
+ class GoodCompiler < Compiler
171
+ define_method(:compiled_text) { 'The compiled text' }
172
+ end
173
+
174
+ let(:compiler) do
175
+ GoodCompiler.new(fixtures_path('source.txt'), tmp_dir + '/dir/out.txt')
176
+ end
177
+
178
+ it 'reads the source file' do
179
+ assert_equal 'The source text', compiler.source_text
180
+ end
181
+
182
+ it 'makes the necessary directories for the compiled file' do
183
+ compiler.compile
184
+ assert File.directory?(File.dirname(compiler.compiled_file))
185
+ end
186
+
187
+ it 'writes the compiled text to the compiled file' do
188
+ compiler.compile
189
+ assert_equal 'The compiled text', File.read(compiler.compiled_file)
190
+ end
191
+
192
+ end
193
+
194
+ describe 'failed compilation' do
195
+
196
+ class BadCompiler < Compiler
197
+ define_method(:compiled_text) { raise 'The error text' }
198
+ end
199
+
200
+ let(:compiler) { BadCompiler.new('', tmp_dir + '/dir/out.txt') }
201
+
202
+ it 'raises a CompileError' do
203
+ assert_raises(CompileError) { compiler.compile }
204
+ end
205
+
206
+ it 'writes the error message to the compiled file' do
207
+ compiler.compile rescue
208
+ assert_equal 'The error text', File.read(compiler.compiled_file)
209
+ end
210
+
211
+ specify 'the compiler must implement compiled_text' do
212
+ e = assert_raises(NotImplementedError) { JsCompiler.new('').compile }
213
+ assert_match 'compiled_text', e.backtrace.first
214
+ end
215
+
216
+ end
217
+
218
+ describe 'should_compile?' do
219
+
220
+ let(:compiler) { JsCompiler.new(tmp_dir + '/script.coffee') }
221
+
222
+ it 'is true if the compiled file does not exist' do
223
+ touch(compiler.source_file)
224
+ assert compiler.should_compile?
225
+ end
226
+
227
+ it 'is true when the source file is updated' do
228
+ touch(compiler.compiled_file, :mtime => 0)
229
+ touch(compiler.source_file, :mtime => 1)
230
+ assert compiler.should_compile?
231
+ end
232
+
233
+ it 'is false if the compiled file is up to date' do
234
+ touch(compiler.source_file)
235
+ touch(compiler.compiled_file)
236
+ refute compiler.should_compile?
237
+ end
238
+
239
+ end
240
+
241
+ end
242
+
243
+ end
@@ -0,0 +1,117 @@
1
+ require 'test_helper'
2
+
3
+ module MoCo
4
+
5
+ describe CoffeeCompiler do
6
+
7
+ let(:compiler) { mock_compiler(CoffeeCompiler, 'alert yes') }
8
+ let(:compiled_text) { compiler.compiled_text }
9
+
10
+ it 'is registered for coffee files' do
11
+ assert_equal CoffeeCompiler, MoCo.compiler_for('coffee')
12
+ end
13
+
14
+ it 'is a JavaScript compiler' do
15
+ assert CoffeeCompiler < JsCompiler
16
+ assert_equal 'js', CoffeeCompiler.compiled_extension
17
+ end
18
+
19
+ it 'compiles CoffeeScript into JavaScript' do
20
+ assert_match 'alert(true);', compiled_text
21
+ end
22
+
23
+ describe 'options' do
24
+
25
+ after { reset_options(CoffeeCompiler) }
26
+
27
+ it 'has a function wrapper by default' do
28
+ assert compiled_text.start_with?('(function() {')
29
+ assert compiled_text.strip.end_with?('}).call(this);')
30
+ end
31
+
32
+ specify 'the bare option disables the function wrapper' do
33
+ CoffeeCompiler.set_option(:bare)
34
+ assert_equal 'alert(true);', compiled_text.strip
35
+ end
36
+
37
+ specify 'the header includes the version number' do
38
+ CoffeeCompiler.set_option(:header)
39
+ version = CoffeeCompiler.context.run('return CoffeeScript.VERSION;')
40
+ assert_match "// Generated by CoffeeScript #{version}", compiled_text
41
+ end
42
+
43
+ end
44
+
45
+ describe 'source maps' do
46
+
47
+ before { CoffeeCompiler.set_option(:sourceMap) }
48
+ after { reset_options(CoffeeCompiler) }
49
+
50
+ let(:compiler) do
51
+ mock_compiler(CoffeeCompiler, 'alert', 'app.coffee', 'dir/the app.js')
52
+ end
53
+
54
+ it 'produces a source map with relative paths' do
55
+ compiler.compile
56
+ map = JSON.load(compiler.source_map_text)
57
+ assert_equal 3, map['version']
58
+ assert_equal 'the app.js', map['file']
59
+ assert_equal '../app.coffee', map['sources'].first
60
+ assert_match 'AAA', map['mappings']
61
+ end
62
+
63
+ it 'adds a comment, with the source map URI, to the compiled text' do
64
+ assert_match '//@ sourceMappingURL=the%20app.js.map', compiled_text
65
+ end
66
+
67
+ it 'will be saved to the same directory as the compiled file' do
68
+ assert_equal File.dirname(compiler.source_map_file),
69
+ File.dirname(compiler.compiled_file)
70
+ end
71
+
72
+ it 'writes the source map file' do
73
+ compiler = CoffeeCompiler.new('/script.coffee', nil, tmp_dir)
74
+ compiler.define_singleton_method(:source_text) { 'alert' }
75
+ compiler.compile
76
+ assert_equal compiler.source_map_text, File.read(compiler.source_map_file)
77
+ end
78
+
79
+ end
80
+
81
+ describe 'compile error' do
82
+
83
+ let(:compiler) { mock_compiler(CoffeeCompiler, 'evil = (eval) ->') }
84
+ let(:error) { compiler.compile rescue $! }
85
+
86
+ it 'raises a CompileError' do
87
+ assert_raises(CompileError) { compiler.compile }
88
+ end
89
+
90
+ it 'captures the error location' do
91
+ assert_equal 1, error.line
92
+ assert_equal 9, error.column
93
+ end
94
+
95
+ describe 'the error message' do
96
+
97
+ it 'removes the filename and location from the original message' do
98
+ message = error.error.message
99
+ assert message.start_with?('mock.coffee:1:9: error: parameter name')
100
+
101
+ message = error.message
102
+ assert message.start_with?('Parameter name')
103
+ end
104
+
105
+ it 'marks the erroneous code with red (using ANSI escape codes)' do
106
+ message = error.message.split("\n")
107
+ assert_equal "evil = (\e[1;31meval\e[0m) ->", message[-2]
108
+ assert_equal "\e[1;31m ^^^^\e[0m" , message[-1]
109
+ end
110
+
111
+ end
112
+
113
+ end
114
+
115
+ end if run_compiler_tests?(CoffeeCompiler)
116
+
117
+ end
@@ -0,0 +1,86 @@
1
+ require 'test_helper'
2
+
3
+ module MoCo
4
+
5
+ describe HamlCompiler do
6
+
7
+ let(:compiler) { mock_compiler(HamlCompiler, '#haml= 2 + 2') }
8
+
9
+ it 'is registered for haml files' do
10
+ assert_equal HamlCompiler, MoCo.compiler_for('haml')
11
+ end
12
+
13
+ it 'is a Html compiler' do
14
+ assert HamlCompiler < HtmlCompiler
15
+ assert_equal 'html', HamlCompiler.compiled_extension
16
+ end
17
+
18
+ it 'compiles haml into html' do
19
+ assert_equal "<div id='haml'>4</div>", compiler.compiled_text.strip
20
+ end
21
+
22
+ specify 'options are passed on to the haml compiler' do
23
+ HamlCompiler.set_option(:attr_wrapper, '@')
24
+ assert_match '<div id=@haml@>', compiler.compiled_text
25
+ reset_options(HamlCompiler)
26
+ end
27
+
28
+ describe 'compile error' do
29
+
30
+ def compile(haml)
31
+ mock_compiler(HamlCompiler, haml, 'mock.haml').compile
32
+ rescue CompileError => error
33
+ error
34
+ end
35
+
36
+ it 'removes the filename and line number from the error message' do
37
+ error = compile('.haml= [}')
38
+ refute_match 'mock.haml', error.message
39
+ refute_match /[:\d]/, error.message
40
+
41
+ # The original error
42
+ assert_match 'mock.haml:1:', error.error.message
43
+ assert_match 'mock.haml:2:', error.error.message
44
+ end
45
+
46
+ describe 'the line number' do
47
+
48
+ specify 'line numbers fetched from @line start at 1' do
49
+ error = compile(' %p')
50
+ assert_equal 1, error.line
51
+
52
+ # The original error
53
+ assert error.error.line
54
+ end
55
+
56
+ it 'finds the line number in the backtrace' do
57
+ error = compile("\n.time= Date.tomorrow")
58
+ assert_equal 2, error.line
59
+
60
+ # The original error
61
+ refute defined? error.error.line
62
+ assert_match 'mock.haml:2:', error.error.backtrace.first
63
+ end
64
+
65
+ it 'finds the line number in the error message' do
66
+ error = compile("\n\n.unbalanced= [}")
67
+ assert_equal 3, error.line
68
+
69
+ # The original error
70
+ assert_nil error.error.line
71
+ refute_match ':3:', error.error.backtrace.to_s
72
+ assert_match 'mock.haml:3:', error.error.message
73
+ end
74
+
75
+ specify 'line can be nil' do
76
+ error = compile("%p= eval '2 *** 10'")
77
+ assert_nil error.line
78
+ end
79
+
80
+ end
81
+
82
+ end
83
+
84
+ end if run_compiler_tests?(HamlCompiler)
85
+
86
+ end
@@ -0,0 +1,72 @@
1
+ require 'test_helper'
2
+
3
+ module MoCo
4
+
5
+ describe LessCompiler do
6
+
7
+ it 'is registered for less files' do
8
+ assert_equal LessCompiler, MoCo.compiler_for('less')
9
+ end
10
+
11
+ it 'is a CSS compiler' do
12
+ assert LessCompiler < CssCompiler
13
+ assert_equal 'css', LessCompiler.compiled_extension
14
+ end
15
+
16
+ describe 'compile' do
17
+
18
+ css = <<-EOF
19
+ .less {
20
+ color: #d9eef2;
21
+ }
22
+ EOF
23
+
24
+ less = <<-EOF
25
+ @blue: #D9EEF2;
26
+ .less {
27
+ color: @blue;
28
+ }
29
+ EOF
30
+
31
+ import = <<-EOF
32
+ @import 'color';
33
+ .less {
34
+ color: @blue;
35
+ }
36
+ EOF
37
+
38
+ [css, less, import].each { |style| style.gsub!(/^ {8}/, '') }
39
+
40
+ it 'compiles less into css' do
41
+ compiler = mock_compiler(LessCompiler, less)
42
+ assert_equal css, compiler.compiled_text
43
+ end
44
+
45
+ it 'handles the @import directive' do
46
+ LessCompiler.set_option(:paths, [fixtures_path])
47
+ compiler = mock_compiler(LessCompiler, import)
48
+ assert_equal css, compiler.compiled_text
49
+ reset_options(LessCompiler)
50
+ end
51
+
52
+ end
53
+
54
+ describe 'compile error' do
55
+
56
+ let(:compiler) do
57
+ mock_compiler(LessCompiler, '.less { color: @pink; }')
58
+ end
59
+
60
+ it 'raises a CompileError' do
61
+ assert_raises(CompileError) { compiler.compile }
62
+ end
63
+
64
+ it 'has a line number' do
65
+ assert_equal 1, (compiler.compile rescue $!).line
66
+ end
67
+
68
+ end
69
+
70
+ end if run_compiler_tests?(LessCompiler)
71
+
72
+ end