massimo 0.7.5 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/.document +5 -0
  2. data/.gitignore +6 -0
  3. data/Gemfile +2 -0
  4. data/Gemfile.lock +88 -0
  5. data/README.md +3 -1
  6. data/Rakefile +8 -0
  7. data/bin/massimo +1 -17
  8. data/lib/massimo/cli.rb +63 -0
  9. data/lib/massimo/config.rb +8 -5
  10. data/lib/massimo/javascript.rb +10 -2
  11. data/lib/massimo/reloader.rb +37 -0
  12. data/lib/massimo/site.rb +24 -21
  13. data/lib/massimo/templates/site/config.rb +18 -0
  14. data/lib/massimo/templates/site/javascripts/main.js +0 -0
  15. data/lib/massimo/templates/site/pages/index.erb +5 -0
  16. data/lib/massimo/templates/site/public/.empty_directory +0 -0
  17. data/lib/massimo/templates/site/stylesheets/main.scss +0 -0
  18. data/lib/massimo/templates/site/views/layouts/main.erb +12 -0
  19. data/lib/massimo/ui.rb +2 -2
  20. data/lib/massimo/version.rb +1 -1
  21. data/lib/massimo/watcher.rb +22 -7
  22. data/lib/massimo.rb +3 -11
  23. data/massimo.gemspec +40 -0
  24. data/spec/massimo/cli_spec.rb +254 -0
  25. data/spec/massimo/config_spec.rb +97 -0
  26. data/spec/massimo/helpers_spec.rb +26 -0
  27. data/spec/massimo/javascript_spec.rb +120 -0
  28. data/spec/massimo/massimo_spec.rb +23 -0
  29. data/spec/massimo/page_spec.rb +224 -0
  30. data/spec/massimo/reloader_spec.rb +135 -0
  31. data/spec/massimo/resource_spec.rb +208 -0
  32. data/spec/massimo/server_spec.rb +35 -0
  33. data/spec/massimo/site_spec.rb +299 -0
  34. data/spec/massimo/stylesheet_spec.rb +77 -0
  35. data/spec/massimo/ui_spec.rb +109 -0
  36. data/spec/massimo/view_spec.rb +46 -0
  37. data/spec/massimo/watcher_spec.rb +122 -0
  38. data/spec/spec_helper.rb +65 -0
  39. data/spec/support/matchers/be_a_directory_matcher.rb +5 -0
  40. data/spec/support/matchers/be_a_file_matcher.rb +26 -0
  41. metadata +74 -20
  42. data/lib/massimo/commands/base.rb +0 -85
  43. data/lib/massimo/commands/build.rb +0 -19
  44. data/lib/massimo/commands/generate.rb +0 -62
  45. data/lib/massimo/commands/help.rb +0 -34
  46. data/lib/massimo/commands/server.rb +0 -24
  47. data/lib/massimo/commands/version.rb +0 -9
  48. data/lib/massimo/commands/watch.rb +0 -16
@@ -0,0 +1,299 @@
1
+ require 'spec_helper'
2
+
3
+ describe Massimo::Site do
4
+ describe '#initialize' do
5
+ context 'with an options hash' do
6
+ it 'configures the site' do
7
+ site = Massimo::Site.new :source_path => 'source/dir'
8
+ site.config.source_path.should == File.expand_path('source/dir')
9
+ end
10
+ end
11
+
12
+ context 'with a block' do
13
+ it 'configures the site' do
14
+ site = Massimo::Site.new do
15
+ config.source_path = 'source/dir'
16
+ end
17
+ site.config.source_path.should == File.expand_path('source/dir')
18
+ end
19
+ end
20
+
21
+ context 'with a config file' do
22
+ it 'evals the config file' do
23
+ with_file 'config.rb', 'config.output_path = "output/dir"' do
24
+ site = Massimo::Site.new
25
+ site.config.output_path.should == File.expand_path('output/dir')
26
+ end
27
+ end
28
+
29
+ context 'with a specified path' do
30
+ it 'evals the config file' do
31
+ with_file 'config/site.rb', 'config.output_path = "output/dir"' do
32
+ site = Massimo::Site.new :config_path => 'config/site.rb'
33
+ site.config.output_path.should == File.expand_path('output/dir')
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ describe 'reload' do
41
+ it 'resets the resources array' do
42
+ Post = Class.new(Massimo::Resource)
43
+ site = Massimo::Site.new
44
+ site.resource Post
45
+ site.reload
46
+ site.resources.map(&:to_s).should_not include('Post')
47
+ Object.send(:remove_const, :Post)
48
+ end
49
+
50
+ it 'uses original options' do
51
+ within_construct do |c|
52
+ c.file 'config.rb', 'config.output_path = "output/dir"'
53
+ site = Massimo::Site.new :output_path => 'output'
54
+ c.file 'config.rb', 'config.source_path = "source/dir"'
55
+ site.reload
56
+ site.config.output_path.should == File.expand_path('output')
57
+ site.config.source_path.should == File.expand_path('source/dir')
58
+ end
59
+ end
60
+
61
+ context 'with a config file' do
62
+ it 'reloads the config file' do
63
+ within_construct do |c|
64
+ c.file 'config.rb', 'config.output_path = "output/dir"'
65
+ site = Massimo::Site.new
66
+ c.file 'config.rb', 'config.output_path = "output"'
67
+ site.reload
68
+ site.config.output_path.should == File.expand_path('output')
69
+ end
70
+ end
71
+
72
+ context 'with a defined resource' do
73
+ it 'removes old resource constants' do
74
+ within_construct do |c|
75
+ c.file 'config.rb', 'resource :post'
76
+ site = Massimo::Site.new
77
+ c.file 'config.rb', 'resource :comment'
78
+ site.reload
79
+ defined?(Post).should be_false
80
+ Object.send(:remove_const, :Comment)
81
+ end
82
+ end
83
+ end
84
+
85
+ context 'with a required gem' do
86
+ it 'does not remove the gem constant' do
87
+ within_construct do |c|
88
+ c.file 'active_record.rb', 'module ActiveRecord; end'
89
+ c.file 'config.rb', '$: << File.expand_path("..", __FILE__); require "active_record"'
90
+ site = Massimo::Site.new
91
+ site.reload
92
+ defined?(ActiveRecord).should be_true
93
+ end
94
+ end
95
+ end
96
+
97
+ context 'with a helper method' do
98
+ it 'adds the new helper method' do
99
+ within_construct do |c|
100
+ c.file 'config.rb', 'helpers { }'
101
+ site = Massimo::Site.new
102
+ c.file 'config.rb', 'helpers { def hello; "hello"; end }'
103
+ site.reload
104
+ site.template_scope.hello.should == 'hello'
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
110
+
111
+ describe '#resources' do
112
+ it 'is an array of the default resources' do
113
+ Massimo::Site.new.resources.should =~ [ Massimo::Page, Massimo::Javascript, Massimo::Stylesheet, Massimo::View ]
114
+ end
115
+ end
116
+
117
+ describe '#resource' do
118
+ context 'with a Class constant' do
119
+ it "adds a resource to the site's resources" do
120
+ Post = Class.new(Massimo::Resource)
121
+ site = Massimo::Site.new
122
+ site.resource Post
123
+ site.resources.should include(Post)
124
+ Object.send(:remove_const, :Post)
125
+ end
126
+ end
127
+
128
+ context 'with a Class body' do
129
+ before do
130
+ @site = Massimo::Site.new
131
+ @site.resource :comment do
132
+ def spam?
133
+ true
134
+ end
135
+ end
136
+ end
137
+
138
+ after do
139
+ Object.send(:remove_const, :Comment)
140
+ end
141
+
142
+ it 'creates a class that inherits from Page' do
143
+ Comment.superclass.should == Massimo::Page
144
+ end
145
+
146
+ it "adds the class to the site's resources" do
147
+ @site.resources.should include(Comment)
148
+ end
149
+
150
+ it 'adds the methods in the class body' do
151
+ with_file 'comment.txt' do
152
+ Comment.new('comment.txt').should be_spam
153
+ end
154
+ end
155
+ end
156
+ end
157
+
158
+ describe '#template_scope' do
159
+ it 'returns an object with the Helpers methods included' do
160
+ Massimo.site.template_scope.methods.map(&:to_s).should include('render')
161
+ end
162
+ end
163
+
164
+ describe '#helpers' do
165
+ context 'with a block' do
166
+ it 'adds the defined methods to the template scope' do
167
+ Massimo.site.helpers do
168
+ def hello
169
+ 'world'
170
+ end
171
+ end
172
+ Massimo.site.template_scope.hello.should == 'world'
173
+ end
174
+ end
175
+
176
+ context 'with a Module' do
177
+ it 'extends the template_scope with the given Module' do
178
+ module CycleHelper
179
+ def cycle
180
+ 'even'
181
+ end
182
+ end
183
+ Massimo.site.helpers CycleHelper
184
+ Massimo.site.template_scope.cycle.should == 'even'
185
+ end
186
+ end
187
+ end
188
+
189
+ describe '#process' do
190
+ let(:processed_files) { Dir.glob('public/**/*.*') }
191
+
192
+ it 'processes each resource' do
193
+ within_construct do |c|
194
+ c.file 'pages/index.html'
195
+ c.file 'pages/about-us.html'
196
+ c.file 'javascripts/main.js'
197
+ c.file 'stylesheets/main.css'
198
+ Massimo.site.process
199
+ processed_files.should =~ [
200
+ 'public/index.html',
201
+ 'public/about-us/index.html',
202
+ 'public/javascripts/main.js',
203
+ 'public/stylesheets/main.css'
204
+ ]
205
+ end
206
+ end
207
+
208
+ it 'does not process views' do
209
+ with_file 'views/partial.haml' do
210
+ Massimo.site.process
211
+ processed_files.should be_empty
212
+ end
213
+ end
214
+
215
+ context 'with a custom resource' do
216
+ it 'processes the resource' do
217
+ with_file 'videos/keyboard-cat.html' do
218
+ Massimo.site.resource :video
219
+ Massimo.site.process
220
+ processed_files.should =~ %w( public/keyboard-cat/index.html )
221
+ end
222
+ end
223
+ end
224
+
225
+ context 'with lib files' do
226
+ it 'loads them' do
227
+ content = <<-CONTENT.unindent
228
+ class Massimo::Site
229
+ def test
230
+ true
231
+ end
232
+ end
233
+ CONTENT
234
+ with_file 'lib/site.rb', content do
235
+ Massimo.site.process
236
+ Massimo.site.test.should === true
237
+ end
238
+ end
239
+
240
+ it 'reloads them' do
241
+ content = <<-CONTENT.unindent
242
+ class Massimo::Site
243
+ def test
244
+ false
245
+ end
246
+ end
247
+ CONTENT
248
+ with_file 'lib/site.rb', content do
249
+ expect {
250
+ Massimo.site.process
251
+ }.to change(Massimo.site, :test).to(false)
252
+ end
253
+ end
254
+
255
+ it 'removes previously loaded libs' do
256
+ with_file 'lib/some_constant.rb', 'module SomeConstant; end' do
257
+ Massimo.site.process
258
+ File.delete 'lib/some_constant.rb'
259
+ Massimo.site.process
260
+ defined?(SomeConstant).should be_false
261
+ end
262
+ end
263
+ end
264
+
265
+ context 'with helper files' do
266
+ it 'extends the template scope' do
267
+ content = <<-CONTENT.unindent
268
+ module SomeHelper
269
+ def helper_method
270
+ 'working'
271
+ end
272
+ end
273
+ CONTENT
274
+ with_file 'helpers/some_helper.rb', content do
275
+ Massimo.site.process
276
+ Massimo.site.template_scope.helper_method.should == 'working'
277
+ end
278
+ end
279
+
280
+ it 'reloads the methods in the template scope' do
281
+ content = <<-CONTENT.unindent
282
+ module Helper
283
+ def testing
284
+ 'working'
285
+ end
286
+ end
287
+ CONTENT
288
+ with_file 'helpers/helper.rb', content do
289
+ Massimo.site.process
290
+ File.delete 'helpers/helper.rb'
291
+ Massimo.site.process
292
+ expect {
293
+ Massimo.site.template_scope.testing
294
+ }.to raise_error
295
+ end
296
+ end
297
+ end
298
+ end
299
+ end
@@ -0,0 +1,77 @@
1
+ require 'spec_helper'
2
+
3
+ describe Massimo::Stylesheet do
4
+ context 'with normal .css files' do
5
+ let(:stylesheet) { Massimo::Stylesheet.new('stylesheets/main.css') }
6
+
7
+ it 'should render using Sass' do
8
+ within_construct do |c|
9
+ c.file 'stylesheets/main.css', '#header { font-size: 36px }'
10
+ stylesheet.render.should == '#header { font-size: 36px }'
11
+ end
12
+ end
13
+ end
14
+
15
+ context 'with .sass styleheets' do
16
+ let(:stylesheet) { Massimo::Stylesheet.new('stylesheets/main.sass') }
17
+
18
+ it 'should render using Sass' do
19
+ within_construct do |c|
20
+ c.file 'stylesheets/main.sass', "#header\n font-size: 36px"
21
+ stylesheet.render.should == "#header {\n font-size: 36px; }\n"
22
+ end
23
+ end
24
+
25
+ it 'should import other .sass files' do
26
+ within_construct do |c|
27
+ c.file 'stylesheets/main.sass', "@import _base.sass"
28
+ c.file 'stylesheets/_base.sass', "#header\n font-size: 36px"
29
+ stylesheet.render.should == "#header {\n font-size: 36px; }\n"
30
+ end
31
+ end
32
+
33
+ it 'should output .css files' do
34
+ within_construct do |c|
35
+ c.file 'stylesheets/main.sass'
36
+ stylesheet.output_path.extname.should == '.css'
37
+ end
38
+ end
39
+
40
+ it 'should use Sass options from config' do
41
+ Massimo.config.sass = { :style => :compressed }
42
+ within_construct do |c|
43
+ c.file 'stylesheets/main.sass', "#header\n font-size: 36px"
44
+ stylesheet.render.should == "#header{font-size:36px}\n"
45
+ end
46
+ end
47
+ end
48
+
49
+ context 'with .scss styleheets' do
50
+ let(:stylesheet) { Massimo::Stylesheet.new('stylesheets/main.scss') }
51
+
52
+ it 'should render using Sass' do
53
+ within_construct do |c|
54
+ c.file 'stylesheets/main.scss', "$size: 36px;\n#header { font-size: $size; }\n"
55
+ stylesheet.render.should == "#header {\n font-size: 36px; }\n"
56
+ end
57
+ end
58
+ end
59
+
60
+ context 'with .less stylesheets' do
61
+ let(:stylesheet) { Massimo::Stylesheet.new('stylesheets/main.less') }
62
+
63
+ it 'should render using Less' do
64
+ within_construct do |c|
65
+ c.file 'stylesheets/main.less', "@color: #000000;\n#header { color: @color; }"
66
+ stylesheet.render.should == "#header { color: #000000; }\n"
67
+ end
68
+ end
69
+
70
+ it 'should output .css files' do
71
+ within_construct do |c|
72
+ c.file 'stylesheets/main.less'
73
+ stylesheet.output_path.extname.should == '.css'
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,109 @@
1
+ require 'spec_helper'
2
+
3
+ describe Massimo::UI do
4
+ describe '.say' do
5
+ it 'prints out the given message to stdout' do
6
+ mock($stdout).puts 'message'
7
+ Massimo::UI.say 'message'
8
+ end
9
+
10
+ it 'does not send a Growl notification' do
11
+ dont_allow(Growl).notify
12
+ Massimo::UI.say 'message'
13
+ end
14
+
15
+ context 'with a color' do
16
+ it 'prints out the message with the correct color code' do
17
+ mock($stdout).puts "\e[31mmessage\e[0m"
18
+ Massimo::UI.say 'message', :red
19
+ end
20
+ end
21
+
22
+ context 'with :growl => true' do
23
+ it 'sends a Growl Notification' do
24
+ mock(Growl).notify('message', anything)
25
+ Massimo::UI.say 'message', :growl => true
26
+ end
27
+
28
+ context 'and a color' do
29
+ it 'sends an uncolored Growl Notification' do
30
+ mock(Growl).notify('message', anything)
31
+ Massimo::UI.say 'message', :red, :growl => true
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ describe '.color' do
38
+ it 'wraps the given message with the correct color code' do
39
+ Massimo::UI.color('message', :red).should == "\e[31mmessage\e[0m"
40
+ end
41
+ end
42
+
43
+ describe '.indent' do
44
+ context 'within the block' do
45
+ it 'indents messages' do
46
+ Massimo::UI.indent do
47
+ mock($stdout).puts ' message'
48
+ Massimo::UI.say 'message'
49
+ end
50
+ end
51
+
52
+ context 'with another .indent' do
53
+ it 'further indents the messages' do
54
+ Massimo::UI.indent do
55
+ Massimo::UI.indent do
56
+ mock($stdout).puts ' message'
57
+ Massimo::UI.say 'message'
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ context 'after the block' do
65
+ it 'returns to the original indent' do
66
+ Massimo::UI.indent {}
67
+ mock($stdout).puts 'message'
68
+ Massimo::UI.say 'message'
69
+ end
70
+ end
71
+ end
72
+
73
+ describe '.report_errors' do
74
+ context 'without an error' do
75
+ it 'returns true' do
76
+ Massimo::UI.report_errors do
77
+ # no error
78
+ end.should be_true
79
+ end
80
+ end
81
+
82
+ context 'with an error' do
83
+ it 'swallows the error' do
84
+ expect {
85
+ Massimo::UI.report_errors do
86
+ raise ArgumentError
87
+ end
88
+ }.to_not raise_error
89
+ end
90
+
91
+ it 'reports the error' do
92
+ mock($stdout) do |expect|
93
+ expect.puts(/massimo/i)
94
+ expect.puts(/ArgumentError/)
95
+ expect.puts(/:\d+/)
96
+ end
97
+ Massimo::UI.report_errors do
98
+ raise ArgumentError
99
+ end
100
+ end
101
+
102
+ it 'returns false' do
103
+ Massimo::UI.report_errors do
104
+ raise ArgumentError
105
+ end.should be_false
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+
3
+ describe Massimo::View do
4
+ describe '#render' do
5
+ it 'should use Tilt to render the templates' do
6
+ with_file 'index.erb' do
7
+ stub(template = Object.new).render
8
+ mock(Tilt).new(anything, anything, anything) { template }
9
+ Massimo::View.new('index.erb').render
10
+ end
11
+ end
12
+
13
+ context 'with a locals hash' do
14
+ it 'should use the locals when rendering' do
15
+ with_file 'index.haml', '%h1= local' do
16
+ view = Massimo::View.new 'index.haml'
17
+ view.render(:local => 'Local').should == "<h1>Local</h1>\n"
18
+ end
19
+ end
20
+ end
21
+
22
+ context 'with a content block' do
23
+ it 'should yield the content' do
24
+ with_file 'index.erb', '<%= yield %>' do
25
+ view = Massimo::View.new 'index.erb'
26
+ view.render { 'Content' }.should == 'Content'
27
+ end
28
+ end
29
+ end
30
+
31
+ it 'should use Site#template_scope as the scope' do
32
+ within_construct do |c|
33
+ c.file 'index.erb', "<%= render 'partial' %>"
34
+ c.file 'views/partial.erb', 'Partial'
35
+ Massimo::View.new('index.erb').render.should == 'Partial'
36
+ end
37
+ end
38
+
39
+ it 'should pass config options for the template' do
40
+ Massimo.config.haml = { :attr_wrapper => %(") }
41
+ with_file 'view.haml', '#header Title' do
42
+ Massimo::View.new('view.haml').render.should == %(<div id="header">Title</div>\n)
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,122 @@
1
+ require 'spec_helper'
2
+
3
+ describe Massimo::Watcher do
4
+ let(:site) { Massimo::Site.new(:lib_path => 'libs') }
5
+ let(:watcher) { Massimo::Watcher.new(site) }
6
+
7
+ def with_files(check_once = true)
8
+ within_construct do |construct|
9
+ construct.file 'pages/index.haml'
10
+ watcher.changed? if check_once
11
+ yield construct
12
+ end
13
+ end
14
+
15
+ describe '#changed?' do
16
+ it 'returns false' do
17
+ watcher.should_not be_changed
18
+ end
19
+
20
+ context 'when a file is added' do
21
+ it 'returns true' do
22
+ with_files(false) do |c|
23
+ watcher.should be_changed
24
+ end
25
+ end
26
+
27
+ context 'then checked without updates' do
28
+ it 'returns false' do
29
+ with_files do |c|
30
+ watcher.should_not be_changed
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ context 'when a file is removed' do
37
+ it 'returns true' do
38
+ with_files do |c|
39
+ File.delete 'pages/index.haml'
40
+ watcher.should be_changed
41
+ end
42
+ end
43
+ end
44
+
45
+ context 'when a file is updated' do
46
+ it 'returns true' do
47
+ with_files do |c|
48
+ sleep 1
49
+ File.open('pages/index.haml', 'w+') { |file| file.write('change') }
50
+ watcher.should be_changed
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ describe '#config_changed?' do
57
+ it 'returns false' do
58
+ watcher.should_not be_config_changed
59
+ end
60
+
61
+ context 'when a file is updated' do
62
+ it 'returns false' do
63
+ with_files do |c|
64
+ sleep 1
65
+ c.file 'pages/index.haml'
66
+ watcher.should_not be_config_changed
67
+ end
68
+ end
69
+ end
70
+
71
+ context 'when the config file is updated' do
72
+ it 'returns true' do
73
+ within_construct do |c|
74
+ c.file 'config.rb'
75
+ watcher.config_changed?
76
+ sleep 1
77
+ c.file 'config.rb', 'config.output_path = "output"'
78
+ watcher.should be_config_changed
79
+ end
80
+ end
81
+ end
82
+ end
83
+
84
+ describe '#process' do
85
+ context 'with changes' do
86
+ it 'calls site#process' do
87
+ mock(Massimo::Site.new).process
88
+ watcher = Massimo::Watcher.new(Massimo.site)
89
+ mock(watcher).changed? { true }
90
+ watcher.process
91
+ end
92
+ end
93
+
94
+ context 'with config changes' do
95
+ it 'calls site#reload' do
96
+ site = Massimo::Site.new
97
+ mock(site).reload
98
+ mock(site).process
99
+ watcher = Massimo::Watcher.new(site)
100
+ mock(watcher).config_changed? { true }
101
+ watcher.process
102
+ end
103
+ end
104
+
105
+ context 'without changes' do
106
+ it 'does not call site#process' do
107
+ dont_allow(Massimo::Site.new).process
108
+ watcher = Massimo::Watcher.new(Massimo.site)
109
+ mock(watcher).changed? { false }
110
+ watcher.process
111
+ end
112
+ end
113
+ end
114
+
115
+ describe '.start' do
116
+ it 'runs a new Watcher' do
117
+ mock(watcher = Object.new).run
118
+ mock(Massimo::Watcher).new(Massimo.site) { watcher }
119
+ Massimo::Watcher.start(Massimo.site)
120
+ end
121
+ end
122
+ end