massimo 0.7.5 → 0.8.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.
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