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,224 @@
1
+ require 'spec_helper'
2
+
3
+ describe Massimo::Page do
4
+ context 'with meta data' do
5
+ let(:page) { Massimo::Page.new 'page.erb' }
6
+
7
+ let(:page_content) do
8
+ <<-CONTENT.unindent
9
+ ---
10
+ title: A Page
11
+ created_at: 2010-04-01
12
+ ---
13
+ <%= title %>
14
+ <%= created_at.strftime('%m-%Y') %>
15
+ CONTENT
16
+ end
17
+
18
+ it 'should read the meta data into a #meta_data hash' do
19
+ with_file 'page.erb', page_content do
20
+ page.title.should == 'A Page'
21
+ end
22
+ end
23
+
24
+ it 'should create attributes for each key in the #meta_data' do
25
+ with_file 'page.erb', page_content do
26
+ page.created_at.should_not be_nil
27
+ end
28
+ end
29
+
30
+ it 'should report the #created_at attribute has been set' do
31
+ with_file 'page.erb', page_content do
32
+ page.created_at?.should === true
33
+ end
34
+ end
35
+
36
+ it 'should report the correct line number to Tilt' do
37
+ content = <<-CONTENT.unindent
38
+ ---
39
+ title: A Page
40
+ ---
41
+ <%= raise 'Error' %>
42
+ CONTENT
43
+ with_file 'page.erb', content do
44
+ begin
45
+ page.render
46
+ rescue Exception => error
47
+ error.backtrace.first.should =~ /:4:/
48
+ end
49
+ end
50
+ end
51
+
52
+ it 'should include the meta_data as locals when rendering' do
53
+ with_file 'page.erb', page_content do
54
+ page.render.should include('A Page')
55
+ end
56
+ end
57
+ end
58
+
59
+ context 'without meta data' do
60
+ let(:page) { Massimo::Page.new 'without_meta_data.erb' }
61
+
62
+ it 'should create the #title from the filename' do
63
+ with_file 'without_meta_data.erb' do
64
+ page.title.should == 'Without Meta Data'
65
+ end
66
+ end
67
+
68
+ it "should default the #extension to '.html'" do
69
+ with_file 'without_meta_data.erb' do
70
+ page.extension.should == '.html'
71
+ end
72
+ end
73
+
74
+ it "should default the #layout to 'application'" do
75
+ with_file 'without_meta_data.erb' do
76
+ page.layout.should == 'main'
77
+ end
78
+ end
79
+
80
+ it 'should report the #created_at attribute has not been set' do
81
+ with_file 'without_meta_data.erb' do
82
+ page.created_at?.should === false
83
+ end
84
+ end
85
+ end
86
+
87
+ describe '#render' do
88
+ let(:page) { Massimo::Page.new 'page.erb' }
89
+
90
+ context 'with layouts' do
91
+ it 'should wrap the content in a layout' do
92
+ within_construct do |c|
93
+ c.file 'page.erb'
94
+ c.file 'views/layouts/main.erb', 'Layout'
95
+ page.render.should == 'Layout'
96
+ end
97
+ end
98
+
99
+ it 'should include the content with #yield' do
100
+ within_construct do |c|
101
+ c.file 'page.erb', 'Content'
102
+ c.file 'views/layouts/main.erb', '<%= yield %>'
103
+ page.render.should == 'Content'
104
+ end
105
+ end
106
+
107
+ it 'should include the page as a local' do
108
+ within_construct do |c|
109
+ c.file 'page.erb', "---\ntitle: A Page\n---"
110
+ c.file 'views/layouts/main.erb', "<%= page.title %>"
111
+ page.render.should == 'A Page'
112
+ end
113
+ end
114
+
115
+ it 'should not render the layout if #layout is false' do
116
+ within_construct do |c|
117
+ c.file 'page.erb', "---\nlayout: false\n---\nContent"
118
+ c.file 'views/layouts/main.erb', 'Layout'
119
+
120
+ page.render.should_not include('Layout')
121
+ end
122
+ end
123
+ end
124
+
125
+ it 'should use Site#template_scope as the scope' do
126
+ within_construct do |c|
127
+ c.file 'index.erb', "<%= render 'partial' %>"
128
+ c.file 'views/partial.erb', 'Partial'
129
+ Massimo::Page.new('index.erb').render.should == 'Partial'
130
+ end
131
+ end
132
+
133
+ it 'should include a local that references itself' do
134
+ NewPage = Class.new(Massimo::Page)
135
+ with_file 'new_page.erb', '<%= new_page.title %>' do
136
+ NewPage.new('new_page.erb').render.should == 'New Page'
137
+ end
138
+ end
139
+
140
+ it 'should pass config options for the template' do
141
+ Massimo.config.haml = { :attr_wrapper => %(") }
142
+ with_file 'new_page.haml', '#header Title' do
143
+ Massimo::Page.new('new_page.haml').render.should == %(<div id="header">Title</div>\n)
144
+ end
145
+ end
146
+ end
147
+
148
+ describe '#url' do
149
+ it 'should be a pretty url based on the filename' do
150
+ with_file 'pages/about-us.erb' do
151
+ Massimo::Page.new('pages/about-us.erb').url.should == '/about-us/'
152
+ end
153
+ end
154
+
155
+ context 'with a root directory index' do
156
+ it "should be '/'" do
157
+ with_file 'pages/index.erb' do
158
+ Massimo::Page.new('pages/index.erb').url.should == '/'
159
+ end
160
+ end
161
+ end
162
+
163
+ context 'with a nested directory index' do
164
+ it 'should drop the filename' do
165
+ with_file 'pages/some/url/index.haml' do
166
+ Massimo::Page.new('pages/some/url/index.haml').url.should == '/some/url/'
167
+ end
168
+ end
169
+ end
170
+
171
+ context "when the extension is not '.html'" do
172
+ it 'should not create a pretty url' do
173
+ with_file 'pages/about-us.rss', "---\nextension: .rss\n---" do
174
+ Massimo::Page.new('pages/about-us.rss').url.should == '/about-us.rss'
175
+ end
176
+ end
177
+ end
178
+ end
179
+
180
+ describe '#output_path' do
181
+ it 'should re-add directory index file names' do
182
+ with_file 'pages/some/url/index.html' do
183
+ resource = Massimo::Page.new('pages/some/url/index.html')
184
+ resource.output_path.to_s.should == File.expand_path('public/some/url/index.html')
185
+ end
186
+ end
187
+
188
+ context 'with a root url' do
189
+ it 'should add the directory index' do
190
+ with_file 'pages/index.erb' do
191
+ Massimo::Page.new('pages/index.erb').output_path.to_s.should == File.expand_path('public/index.html')
192
+ end
193
+ end
194
+ end
195
+
196
+ context 'with a pretty url' do
197
+ it 'should create a directory index file' do
198
+ with_file 'pages/about-us.erb' do
199
+ Massimo::Page.new('pages/about-us.erb').output_path.to_s.should == File.expand_path('public/about-us/index.html')
200
+ end
201
+ end
202
+ end
203
+ end
204
+
205
+ context 'as a .yml file' do
206
+ it 'should treat the whole file as front matter' do
207
+ with_file 'pages/data.yml', 'name: Joe' do
208
+ Massimo::Page.new('pages/data.yml').name.should == 'Joe'
209
+ end
210
+ end
211
+
212
+ it 'should default content to an empty string' do
213
+ with_file 'pages/data.yml', 'name: Joe' do
214
+ Massimo::Page.new('pages/data.yml').content.should == ''
215
+ end
216
+ end
217
+
218
+ it 'should set the content with a content attribute' do
219
+ with_file 'pages/data.yml', 'content: Some Content' do
220
+ Massimo::Page.new('pages/data.yml').content.should == 'Some Content'
221
+ end
222
+ end
223
+ end
224
+ end
@@ -0,0 +1,135 @@
1
+ require 'spec_helper'
2
+
3
+ describe Massimo::Reloader do
4
+ def within_load_path
5
+ begin
6
+ dir = create_construct
7
+ path = dir.expand_path.to_s
8
+ $LOAD_PATH << path
9
+ dir.maybe_change_dir(true) do
10
+ yield dir
11
+ end
12
+ ensure
13
+ $LOAD_PATH.delete(path)
14
+ dir.destroy!
15
+ end
16
+ end
17
+
18
+ def feature_path(path)
19
+ if RUBY_VERSION.starts_with?('1.8')
20
+ path.to_s
21
+ else
22
+ File.expand_path(path.to_s)
23
+ end
24
+ end
25
+
26
+ after :each do
27
+ Object.send(:remove_const, :Constant) if Object.const_defined?(:Constant)
28
+ end
29
+
30
+ describe '.load' do
31
+ it 'stores the loaded constants in a cache' do
32
+ cache = Massimo::Reloader.load do
33
+ module Constant; end
34
+ end
35
+ cache[:constants].map(&:to_s).should include('Constant')
36
+ end
37
+
38
+ it 'stores the loaded feature paths in a cache' do
39
+ within_load_path do |d|
40
+ d.file('lib/constant.rb', 'module Constant; end')
41
+ cache = Massimo::Reloader.load do
42
+ require 'lib/constant'
43
+ end
44
+ cache[:constants].map(&:to_s).should include('Constant')
45
+ cache[:features].should include(feature_path('lib/constant.rb'))
46
+ $LOADED_FEATURES.delete(feature_path('lib/constant.rb'))
47
+ end
48
+ end
49
+
50
+ context 'with a cache name' do
51
+ it 'stores a different cache for each given name' do
52
+ default_cache = Massimo::Reloader.load
53
+ cache = Massimo::Reloader.load(:libs) do
54
+ module Constant; end
55
+ end
56
+ default_cache.should_not === cache
57
+ end
58
+ end
59
+ end
60
+
61
+ describe '.unload' do
62
+ context 'with a constant defined in .load' do
63
+ before do
64
+ Massimo::Reloader.load do
65
+ module Constant; end
66
+ end
67
+ end
68
+
69
+ it 'undefines the constant' do
70
+ Massimo::Reloader.unload
71
+ defined?(Constant).should be_false
72
+ end
73
+
74
+ it 'removes the constant from the cache' do
75
+ cache = Massimo::Reloader.unload
76
+ cache[:constants].map(&:to_s).should_not include('Constant')
77
+ end
78
+ end
79
+
80
+ context 'with a required feature' do
81
+ it 'removes the loaded feature' do
82
+ within_load_path do |d|
83
+ d.file('lib/constant.rb', 'module Constant; end')
84
+ Massimo::Reloader.load { require 'lib/constant' }
85
+ $LOADED_FEATURES.should include(feature_path('lib/constant.rb'))
86
+ Massimo::Reloader.unload
87
+ $LOADED_FEATURES.should_not include(feature_path('lib/constant.rb'))
88
+ end
89
+ end
90
+
91
+ it 'removes the feature from the cache' do
92
+ within_load_path do |d|
93
+ d.file('lib/constant.rb', 'module Constant; end')
94
+ Massimo::Reloader.load { require 'lib/constant' }
95
+ cache = Massimo::Reloader.unload
96
+ cache[:features].should_not include(feature_path('lib/constant.rb'))
97
+ end
98
+ end
99
+ end
100
+
101
+ context 'with a cache name' do
102
+ it 'removes the constants for that cache' do
103
+ Massimo::Reloader.load do
104
+ module Constant; end
105
+ end
106
+ Massimo::Reloader.load(:libs) do
107
+ module AnotherConstant; end
108
+ end
109
+ Massimo::Reloader.unload(:libs)
110
+ defined?(Constant).should be_true
111
+ defined?(AnotherConstant).should be_false
112
+ end
113
+ end
114
+ end
115
+
116
+ describe '.reload' do
117
+ context 'with a previously defined constant' do
118
+ it 'removes the constant' do
119
+ Massimo::Reloader.reload do
120
+ module Constant; end
121
+ end
122
+ Massimo::Reloader.reload
123
+ defined?(Constant).should be_false
124
+ end
125
+
126
+ it 'defines new constants' do
127
+ Massimo::Reloader.reload
128
+ Massimo::Reloader.reload do
129
+ module Constant; end
130
+ end
131
+ defined?(Constant).should be_true
132
+ end
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,208 @@
1
+ require 'spec_helper'
2
+
3
+ describe Massimo::Resource do
4
+ let(:resource) { Massimo::Resource.new 'file.txt' }
5
+
6
+ describe '#initialize' do
7
+ it 'should require the #source_path to the file' do
8
+ expect { Massimo::Resource.new }.to raise_error
9
+ end
10
+ end
11
+
12
+ describe '#source_path' do
13
+ context 'when set with a string' do
14
+ it 'should be a Pathname object' do
15
+ with_file 'file.txt' do
16
+ resource.source_path.should be_an_instance_of(Pathname)
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ describe '#url' do
23
+ it 'should be created from the filename' do
24
+ with_file 'a_resource_url.erb' do
25
+ Massimo::Resource.new('a_resource_url.erb').url.should == '/a-resource-url.erb'
26
+ end
27
+ end
28
+
29
+ it "should prepend the Resource's url" do
30
+ with_file 'url.erb' do
31
+ Massimo.config.resources_url = '/resources'
32
+ Massimo::Resource.new('url.erb').url.should == '/resources/url.erb'
33
+ end
34
+ end
35
+
36
+ it 'should replace custom extensions' do
37
+ with_file 'url.erb' do
38
+ resource = Massimo::Resource.new('url.erb')
39
+ stub(resource).extension { '.rss' }
40
+ resource.url.should == '/url.rss'
41
+ end
42
+ end
43
+ end
44
+
45
+ describe '#extension' do
46
+ it 'should be the extension of the file' do
47
+ with_file 'url.erb' do
48
+ Massimo::Resource.new('url.erb').extension.should == '.erb'
49
+ end
50
+ end
51
+ end
52
+
53
+ describe '#filename' do
54
+ it 'should be the filename of the file' do
55
+ with_file 'url.erb' do
56
+ Massimo::Resource.new('url.erb').filename.should == 'url.erb'
57
+ end
58
+ end
59
+ end
60
+
61
+ describe '#output_path' do
62
+ it 'should be a Pathname object' do
63
+ with_file 'file.txt' do
64
+ resource.output_path.should be_an_instance_of(Pathname)
65
+ end
66
+ end
67
+
68
+ it 'should move the #source_path to the sites output dir' do
69
+ with_file 'file.txt' do
70
+ resource.output_path.to_s.should == File.expand_path('public/file.txt')
71
+ end
72
+ end
73
+
74
+ context 'with a custom #base_url' do
75
+ it 'should not include the #base_url' do
76
+ Massimo.config.base_url = '/blog'
77
+ with_file 'file.txt' do
78
+ resource.output_path.to_s.should == File.expand_path('public/file.txt')
79
+ end
80
+ end
81
+ end
82
+ end
83
+
84
+ describe '#content' do
85
+ it "should read the associated file's content" do
86
+ with_file 'file.txt', 'content' do
87
+ resource.content.should == 'content'
88
+ end
89
+ end
90
+ end
91
+
92
+ describe '#render' do
93
+ it "should return the file's content" do
94
+ with_file 'file.txt', 'content' do
95
+ resource.render.should == 'content'
96
+ end
97
+ end
98
+ end
99
+
100
+ describe '#process' do
101
+ it 'should create a file with the rendered content' do
102
+ with_file 'file.txt', 'content' do
103
+ resource.process
104
+ File.read('public/file.txt').should == 'content'
105
+ end
106
+ end
107
+ end
108
+
109
+ describe '.resource_name' do
110
+ context 'for Resource' do
111
+ it "should be 'resource'" do
112
+ Massimo::Resource.resource_name.should == 'resources'
113
+ end
114
+ end
115
+
116
+ context 'for Page' do
117
+ it "should be 'page'" do
118
+ Massimo::Page.resource_name.should == 'pages'
119
+ end
120
+ end
121
+
122
+ context 'for a custom Resource' do
123
+ it 'should be determined based on the class name' do
124
+ NewResource = Class.new(Massimo::Resource)
125
+ NewResource.resource_name.should == 'new_resources'
126
+ end
127
+ end
128
+ end
129
+
130
+ describe '.path' do
131
+ it 'should get the path from the configuration' do
132
+ Massimo::Site.new :pages_path => 'some/path'
133
+ Massimo::Page.path.should == File.expand_path('some/path')
134
+ end
135
+ end
136
+
137
+ describe '.url' do
138
+ it 'should get the url from the configuration' do
139
+ Massimo::Site.new :pages_url => '/pages'
140
+ Massimo::Page.url.should == '/pages'
141
+ end
142
+ end
143
+
144
+ describe '.find' do
145
+ it 'should find resources by their file name' do
146
+ with_file 'views/partials/post.haml', '%h1 Post' do
147
+ Massimo::View.find('partials/post').content.should == '%h1 Post'
148
+ end
149
+ end
150
+ end
151
+
152
+ describe '.all' do
153
+ context 'with no options' do
154
+ it 'should return an array of the resource type' do
155
+ within_construct do |c|
156
+ c.file 'file.txt'
157
+ c.file 'file-2.txt'
158
+ Massimo::Resource.all.map(&:class).should == [ Massimo::Resource, Massimo::Resource ]
159
+ end
160
+ end
161
+
162
+ it 'should find all the files in the resource path' do
163
+ within_construct do |c|
164
+ c.file 'pages/index.haml'
165
+ c.file 'pages/about/us.haml'
166
+ c.file 'pages/contact/us.erb'
167
+ Massimo::Page.all.map(&:filename).should =~ %w( index.haml us.haml us.erb)
168
+ end
169
+ end
170
+
171
+ it 'should skip files with prefixed underscores' do
172
+ within_construct do |c|
173
+ c.file 'stylesheets/main.sass'
174
+ c.file 'stylesheets/_base.sass'
175
+ c.file 'stylesheets/_mixins.sass'
176
+ Massimo::Stylesheet.all.map(&:filename).should =~ %w( main.sass )
177
+ end
178
+ end
179
+ end
180
+ end
181
+
182
+ describe '.processable?' do
183
+ it 'should be true be default' do
184
+ Massimo::Resource.should be_processable
185
+ end
186
+
187
+ context 'when a process in unprocessable' do
188
+ it 'should be false' do
189
+ class Unprocessable < Massimo::Resource
190
+ unprocessable
191
+ end
192
+ Unprocessable.should_not be_processable
193
+ end
194
+ end
195
+ end
196
+
197
+ describe '.unprocessable' do
198
+ it 'should overwrite #process to do nothing' do
199
+ class NewResource < Massimo::Resource
200
+ unprocessable
201
+ end
202
+ with_file 'resource.txt' do
203
+ dont_allow(File).open
204
+ NewResource.new('resource.txt').process
205
+ end
206
+ end
207
+ end
208
+ end
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+
3
+ describe Massimo::Server do
4
+ let(:app) { Massimo::Server.new(Massimo.site) }
5
+
6
+ it 'should serve up static files' do
7
+ with_file 'public/stylesheets/main.css', 'body { font-size: 12px; }' do
8
+ get '/stylesheets/main.css'
9
+ last_response.body.should == 'body { font-size: 12px; }'
10
+ end
11
+ end
12
+
13
+ it 'should serve up directory index files' do
14
+ with_file 'public/about-us/index.html', 'About Us' do
15
+ get '/about-us/'
16
+ last_response.body.should == 'About Us'
17
+ end
18
+ end
19
+
20
+ context 'when files have changed' do
21
+ it 'should process the site on request' do
22
+ with_file 'pages/index.haml' do
23
+ mock(Massimo.site).process
24
+ get '/'
25
+ end
26
+ end
27
+ end
28
+
29
+ context 'when files have not changed' do
30
+ it 'should not process the site on request' do
31
+ dont_allow(Massimo.site).process
32
+ get '/'
33
+ end
34
+ end
35
+ end