massimo 0.3.9 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -8,6 +8,13 @@ Massimo is a full static website generator. While making [Rails](http://rubyonra
8
8
  * It concats javascripts using [Sprockets](http://getsprockets.org/)
9
9
  and then minifies them using [JSMin](http://github.com/rgrove/jsmin)
10
10
  * It renders stylesheets using either [Sass](http://sass-lang.com/) or [Less](http://lesscss.org/)
11
+
12
+
13
+ ## Installation
14
+
15
+ Massimo is hosted on [Gemcutter](http://gemcutter.org/) at http://gemcutter.org/gems/massimo, so installation is simply:
16
+
17
+ sudo gem install massimo
11
18
 
12
19
 
13
20
  ## Basic Usage
@@ -17,7 +24,8 @@ Massimo is a full static website generator. While making [Rails](http://rubyonra
17
24
  3. Run you Site locally to see how it looks
18
25
  4. Deploy your Site
19
26
 
20
- ### Structure
27
+
28
+ ## Structure
21
29
 
22
30
  A basic Massimo Site looks something like this, though each directory's path can be customized:
23
31
 
@@ -78,9 +86,23 @@ This is where you put the working copies of your stylesheets. If they are Sass o
78
86
 
79
87
  This is where you put partials and layouts (like Rails). You can render partials from your pages by calling `render("partial_name")`.
80
88
 
81
- ### Running Massimo
89
+
90
+ ## Running Massimo
91
+
92
+ ### Using the Command Line
82
93
 
83
- Usually this is done through the massimo executable, which is installed with the gem. In order to get a server up and running with your Massimo site, run `massimo --server` and then browse to http://localhost:1984/. Or you could simply run `massimo --watch` to watch for changes and regenerate the site.
94
+ Usually this is done through the massimo executable, which is installed with the gem. In order to get a server up and running with your Massimo site, run `massimo --server` and then browse to http://localhost:1984/. Or you could simply run `massimo --watch` to watch for changes and regenerate the site. For full command line options run `massimo --help`.
95
+
96
+ ### Using the Ruby lib
97
+
98
+ Massimo is designed to also work well with straight Ruby code. In order to create and process the Site you would do the following:
99
+
100
+ require "rubygems"
101
+ require "massimo"
102
+
103
+ site = Massimo::Site(:source => "./source", :output => "./output") # Create a site with the given configuration options
104
+ site.pages # An array of all the pages found in the pages directory
105
+ site.process! # Processes all the source files and generates the output files.
84
106
 
85
107
 
86
108
  ## YAML Front Matter
@@ -92,6 +114,49 @@ Pages can contain YAML front matter blocks for either predefined configuration o
92
114
  layout: false
93
115
  ---
94
116
 
117
+ ### Options
118
+
119
+ The options available are:
120
+
121
+ #### title
122
+
123
+ This is the title of the Page. By default it will be generated from the basename of the Page file. For Example: `my-first-post.haml` would become `"My First Post"`.
124
+
125
+ #### extension
126
+
127
+ This would be the extension of the file generated from the Page file. This defaults to `".html"`.
128
+
129
+ #### url
130
+
131
+ This is the URL that will be used to determine the output file's location and name. This defaults to the same location (relative to the pages directory) of the Page and its filename without the extension (and dasherized). For Example: The Page: `posts/10_best_rubygems.haml` would default to the URL: `"posts/10-best-rubygems/"`.
132
+
133
+ #### layout
134
+
135
+ This is the name of the layout used for the Page. Settings this value to `false` will process the Page without a layout. This defaults to `"application"`.
136
+
137
+ ### Custom Variables
138
+
139
+ Any other variables will be available as methods in your pages. For instance:
140
+
141
+ ---
142
+ title: It's Christmas!
143
+ date: 2009-12-25
144
+ ---
145
+ <h1><%= title %></h1>
146
+ <p><%= date.strftime("%m, %e, %Y") %></p>
147
+
148
+ The page object will also be available in your layout as `page`, and the same methods can be called on it:
149
+
150
+ <html>
151
+ <head>
152
+ <title><%= page.title %></title>
153
+ </head>
154
+ <body>
155
+ <%= yield %>
156
+ </body>
157
+ </html>
158
+
159
+
95
160
  ## Note on Patches/Pull Requests
96
161
 
97
162
  * Fork the project.
@@ -103,6 +168,7 @@ Pages can contain YAML front matter blocks for either predefined configuration o
103
168
  bump version in a commit by itself I can ignore when I pull)
104
169
  * Send me a pull request. Bonus points for topic branches.
105
170
 
171
+
106
172
  ## Copyright
107
173
 
108
- Copyright (c) 2009 [Peter Browne](http://peterbrowne.net). See LICENSE for details.
174
+ Copyright (c) 2009 [Peter Browne](http://petebrowne.com). See LICENSE for details.
data/Rakefile CHANGED
@@ -7,13 +7,13 @@ begin
7
7
  gem.name = "massimo"
8
8
  gem.summary = %{Massimo is a static website builder.}
9
9
  gem.description = %{Massimo builds HTML, Javascript, and CSS Files from your source.}
10
- gem.email = "peter@peterbrowne.net"
10
+ gem.email = "me@petebrowne.com"
11
11
  gem.homepage = "http://github.com/peterbrowne/massimo"
12
12
  gem.authors = [ "Peter Browne" ]
13
13
  gem.add_development_dependency "shoulda", ">= 2.10.2"
14
14
  gem.add_development_dependency "yard", ">= 0.5.2"
15
15
  gem.add_dependency "activesupport", ">= 2.3.5"
16
- gem.add_dependency "sinatra_more", ">= 0.3.26"
16
+ gem.add_dependency "sinatra_more", ">= 0.3.29"
17
17
  gem.add_dependency "directory_watcher", ">= 1.3.1"
18
18
  gem.add_dependency "sprockets", ">= 1.0.2"
19
19
  gem.add_dependency "jsmin", ">= 1.0.1"
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.9
1
+ 0.4.0
@@ -1,7 +1,7 @@
1
1
  require "optparse"
2
2
  require "active_support"
3
3
  require "yaml"
4
- begin require "growl"; rescue ::LoadError; end
4
+ begin require "growl"; rescue LoadError; end
5
5
 
6
6
  module Massimo
7
7
  class Command
@@ -9,7 +9,7 @@ module Massimo
9
9
 
10
10
  # Default options. Overriden by values in config.yml or command-line opts.
11
11
  DEFAULT_OPTIONS = {
12
- :config_path => ::File.join(".", "config.yml")
12
+ :config_path => File.join(".", "config.yml")
13
13
  }.freeze
14
14
 
15
15
  #
@@ -20,17 +20,17 @@ module Massimo
20
20
  self.parse!
21
21
 
22
22
  # Load the options from the config file
23
- config = ::YAML.load_file(self.options[:config_path]) if ::File.exist?(self.options[:config_path])
24
- self.options.merge!(config.symbolize_keys) if config.is_a?(::Hash)
23
+ config = YAML.load_file(self.options[:config_path]) if File.exist?(self.options[:config_path])
24
+ self.options.merge!(config.symbolize_keys) if config.is_a?(Hash)
25
25
 
26
26
  # Initialize the Site
27
- self.site = ::Massimo::Site(self.options)
27
+ self.site = Massimo::Site(self.options)
28
28
  self.options = self.site.options
29
29
  self.source = self.options[:source]
30
30
  self.output = self.options[:output]
31
31
 
32
32
  # Setup Backtrace Cleaner
33
- @cleaner = ::ActiveSupport::BacktraceCleaner.new
33
+ @cleaner = ActiveSupport::BacktraceCleaner.new
34
34
  @cleaner.add_silencer { |line| line =~ /^(\/|\\)/ } # Remove full File path traces
35
35
  end
36
36
 
@@ -45,10 +45,10 @@ module Massimo
45
45
  end
46
46
  run_server! if server?
47
47
  return 0
48
- rescue ::Interrupt
48
+ rescue Interrupt
49
49
  message "Massimo is done watching you.", :newline => true
50
50
  return 0
51
- rescue ::Exception => e
51
+ rescue Exception => e
52
52
  report_error(e)
53
53
  return 1
54
54
  end
@@ -60,11 +60,11 @@ module Massimo
60
60
  require "fileutils"
61
61
  message "Massimo is generating the default site layout"
62
62
  [ site.source_dir, site.all_source_dirs, site.output_dir ].flatten.each do |dir|
63
- full_dir = ::File.expand_path(dir)
64
- if ::File.exists?(full_dir)
63
+ full_dir = File.expand_path(dir)
64
+ if File.exists?(full_dir)
65
65
  puts indent_body("exists: #{full_dir}")
66
66
  else
67
- ::FileUtils.mkdir_p(full_dir)
67
+ FileUtils.mkdir_p(full_dir)
68
68
  puts indent_body("created: #{full_dir}")
69
69
  end
70
70
  end
@@ -76,19 +76,19 @@ module Massimo
76
76
 
77
77
  message %{Massimo is watching "#{source}" for changes. Press Ctrl-C to Stop.}
78
78
 
79
- watcher = ::DirectoryWatcher.new(
79
+ watcher = DirectoryWatcher.new(
80
80
  ".",
81
81
  :interval => 1,
82
- :glob => site.all_source_dirs.collect { |dir| ::File.join(dir, *%w{** *}) }
82
+ :glob => site.all_source_dirs.collect { |dir| File.join(dir, *%w{** *}) }
83
83
  )
84
84
 
85
85
  watcher.add_observer do |*args|
86
86
  begin
87
87
  site.process!
88
- time = ::Time.now.strftime("%H:%M")
88
+ time = Time.now.strftime("%l:%M:%S").strip
89
89
  change = args.size == 1 ? "1 file" : "#{args.size} files"
90
90
  message "Massimo has rebuilt your site. #{change} changed. (#{time})"
91
- rescue ::Exception => e
91
+ rescue Exception => e
92
92
  report_error(e)
93
93
  end
94
94
  end
@@ -111,9 +111,9 @@ module Massimo
111
111
  require "webrick"
112
112
 
113
113
  # Make sure the output dir exists
114
- ::FileUtils.mkdir_p(output)
114
+ FileUtils.mkdir_p(output)
115
115
 
116
- server = ::WEBrick::HTTPServer.new(
116
+ server = WEBrick::HTTPServer.new(
117
117
  :Port => options[:server_port],
118
118
  :DocumentRoot => output
119
119
  )
@@ -148,7 +148,7 @@ module Massimo
148
148
  options.reverse_merge!(:growl => true)
149
149
  puts "\n" if options[:newline]
150
150
  puts "== #{string}"
151
- ::Growl.notify(string, :title => "Massimo") if options[:growl] && defined?(::Growl)
151
+ Growl.notify(string, :title => "Massimo") if options[:growl] && defined?(Growl)
152
152
  end
153
153
 
154
154
  # Report the given error. This could eventually log the backtrace.
@@ -169,7 +169,7 @@ module Massimo
169
169
  puts "\n"
170
170
 
171
171
  # Format the message differently for growl
172
- ::Growl.notify(error.message, :title => "Massimo Error") if defined?(::Growl)
172
+ Growl.notify(error.message, :title => "Massimo Error") if defined?(Growl)
173
173
  end
174
174
 
175
175
  # Returns the string with each line indented.
@@ -179,16 +179,16 @@ module Massimo
179
179
 
180
180
  # Parse the options
181
181
  def parse!
182
- opts = ::OptionParser.new do |opts|
182
+ opts = OptionParser.new do |opts|
183
183
  opts.banner = <<-HELP
184
184
  Massimo is a static website builder.
185
185
 
186
186
  Basic Command Line Usage:
187
- massimo # . -> ./public
188
- massimo <path to write generated site> # . -> <path>
189
- massimo <path to source> <path to write generated site> # <path> -> <path>
187
+ massimo # . -> ./public
188
+ massimo <path to output> # . -> <path>
189
+ massimo <path to source> <path to output> # <path> -> <path>
190
190
 
191
- Configuration is read from "<source>/config.yml" but can be overriden
191
+ Configuration is read from "./config.yml" but can be overriden
192
192
  using the following options:
193
193
 
194
194
  HELP
@@ -197,7 +197,7 @@ HELP
197
197
  options[:config_path] = path
198
198
  end
199
199
 
200
- opts.on("--generate", "Generate the default layout of the site.") do
200
+ opts.on("--generate", "Generate the default layout of the site. This will create all the necessary directories needed to generate websites using Massimo.") do
201
201
  options[:generate] = true
202
202
  end
203
203
 
@@ -218,7 +218,7 @@ HELP
218
218
  end
219
219
 
220
220
  opts.on("--version", "-V", "Display current version") do
221
- puts "Massimo #{::Massimo::VERSION}"
221
+ puts "Massimo #{Massimo::VERSION}"
222
222
  exit 0
223
223
  end
224
224
  end
@@ -1,26 +1,26 @@
1
1
  module Massimo
2
2
  class Helpers
3
- if defined? ::SinatraMore
4
- include ::SinatraMore::OutputHelpers
5
- include ::SinatraMore::TagHelpers
6
- include ::SinatraMore::AssetTagHelpers
7
- include ::SinatraMore::FormHelpers
8
- include ::SinatraMore::FormatHelpers
3
+ if defined? SinatraMore
4
+ include SinatraMore::OutputHelpers
5
+ include SinatraMore::TagHelpers
6
+ include SinatraMore::AssetTagHelpers
7
+ include SinatraMore::FormHelpers
8
+ include SinatraMore::FormatHelpers
9
9
  end
10
10
 
11
11
  #
12
12
  def initialize(modules = nil)
13
- self.extend(*modules) unless modules.nil? || modules.empty?
13
+ extend(*modules) unless modules.nil? || modules.empty?
14
14
  end
15
15
 
16
16
  # Gets the site instance
17
17
  def site
18
- ::Massimo::Site()
18
+ Massimo::Site()
19
19
  end
20
20
 
21
21
  #
22
22
  def render(name, locals = {}, &block)
23
- self.site.render_view(name, locals, &block)
23
+ site.render_view(name, locals, &block)
24
24
  end
25
25
  end
26
26
  end
@@ -1,38 +1,20 @@
1
1
  module Massimo
2
- class Javascript < Resource
2
+ class Javascript < Massimo::Resource::Base
3
+ processable!
4
+
3
5
  # Concat the Javascript using Sprockets, then minify using JSmin
4
6
  def render
5
- secretary = ::Sprockets::Secretary.new(
6
- :assert_root => self.site.output_dir,
7
+ secretary = Sprockets::Secretary.new(
8
+ :assert_root => site.output_dir,
7
9
  :source_files => [ @source_path.to_s ]
8
10
  )
9
11
  # install assets if necessary
10
12
  secretary.install_assets
11
13
 
12
- if self.site.production? or self.site.options[:minify]
13
- # minify the concatenated javascript
14
- ::JSMin.minify(secretary.concatenation.to_s)
15
- else
16
- secretary.concatenation.to_s
17
- end
18
- end
19
-
20
- # Writes the rendered js to the output file.
21
- def process!
22
- # Make the full path to the directory of the output file
23
- ::FileUtils.mkdir_p(self.output_path.dirname)
24
- # write the filtered data to the output file
25
- self.output_path.open("w") do |file|
26
- file.write self.render
27
- end
14
+ # Concatenate the scripts and minify if necessary
15
+ output = secretary.concatenation.to_s
16
+ output = JSMin.minify(output) if site.production? or site.options[:minify]
17
+ output
28
18
  end
29
-
30
- protected
31
-
32
- # Determine the output file path
33
- def output_path
34
- @output_path ||= ::Pathname.new(@source_path.to_s.
35
- sub(self.site.source_dir, self.site.output_dir)) # move to output dir
36
- end
37
19
  end
38
20
  end
data/lib/massimo/page.rb CHANGED
@@ -1,50 +1,37 @@
1
1
  module Massimo
2
- class Page < View
2
+ class Page < Massimo::View
3
3
  META_SEP = %r/\A---\s*(?:\r\n|\n)?\z/ # :nodoc:
4
4
 
5
+ processable!
6
+
5
7
  # Creates a new page associated with the given file path.
6
8
  def initialize(source_path)
7
- @source_path = ::Pathname.new(source_path)
8
- @meta_data = {
9
- :title => @source_path.basename.to_s.gsub(@source_path.extname, "").titleize,
9
+ super(source_path, {
10
+ :title => File.basename(source_path).gsub(File.extname(source_path), "").titleize,
10
11
  :extension => ".html",
11
- :url => @source_path.to_s.gsub(self.site.pages_dir, "").gsub(@source_path.extname, "").dasherize,
12
+ :url => source_path.gsub(self.class.dir, "").gsub(File.extname(source_path), "").dasherize,
12
13
  :layout => "application"
13
- }
14
- # read and parse the source file
15
- self.read_source!
14
+ })
16
15
  end
17
16
 
18
17
  # Override render to wrap the result in the layout
19
- def render(with_layout = true)
20
- if with_layout && layout = self.find_layout
21
- layout.render(:page => self) { self.render(false) }
22
- else
23
- super()
24
- end
25
- end
26
-
27
- # Writes the filtered data to the output file.
28
- def process!
29
- refresh_layout
30
- path = self.output_path
31
- # Make the full path to the directory of the output file
32
- ::FileUtils.mkdir_p(path.dirname)
33
- # write the filtered data to the output file
34
- path.open("w") do |file|
35
- file.write self.render(self.layout?)
18
+ def render(use_layout = true)
19
+ output = super()
20
+ if use_layout and found_layout = find_layout
21
+ output = found_layout.render(:page => self) { output }
36
22
  end
23
+ output
37
24
  end
38
25
 
39
- # Override to_s so that the layout can include the page with <%= page %>
26
+ # Override to_s so that the layout can include the page with `<%= page %>`
40
27
  def to_s
41
- self.render(false)
28
+ render(false)
42
29
  end
43
30
 
44
31
  protected
45
32
 
46
- # Reads the source page file, and populates the meta_data and
47
- # body attributes.
33
+ # Reads the source page file, and populates the `@meta_data` and
34
+ # `@body` attributes.
48
35
  def read_source!
49
36
  # read the source file and setup some values for the loop
50
37
  source = super()
@@ -69,20 +56,22 @@ module Massimo
69
56
  end
70
57
 
71
58
  # finally get the meta_data as a hash and set the body
72
- meta_data = ::YAML.load(meta_data)
59
+ meta_data = YAML.load(meta_data)
73
60
  @meta_data.merge!(meta_data.symbolize_keys) if meta_data
74
61
  @body = body
75
62
  end
76
63
 
77
64
  # Determine the output file path
78
65
  def output_path
79
- path = self.site.output_dir(self.url)
80
- path << if index? or not html?
81
- self.extension unless path.match(/#{self.extension}$/)
82
- else
83
- "/index.html" unless path.match(/\/index\.html$/)
84
- end
85
- ::Pathname.new(path)
66
+ @output_path ||= begin
67
+ path = site.output_dir(url)
68
+ path << if index? or not html?
69
+ extension unless path.match(/#{extension}$/)
70
+ else
71
+ "/index.html" unless path.match(/\/index\.html$/)
72
+ end
73
+ Pathname.new(path)
74
+ end
86
75
  end
87
76
 
88
77
  # Determines if this is an index page
@@ -92,22 +81,12 @@ module Massimo
92
81
 
93
82
  # Determines if this an HTML page.
94
83
  def html?
95
- self.extension =~ /(html|php)$/
96
- end
97
-
98
- # The next time `find_layout` is called, the layout will be reloaded.
99
- def refresh_layout
100
- @layout_view = nil
101
- end
102
-
103
- # Determines if there's a layout associated with this page.
104
- def layout?
105
- self.layout != false && !self.find_layout.nil?
84
+ extension =~ /(html|php)$/
106
85
  end
107
86
 
108
87
  # Finds the Layout View if it exists
109
88
  def find_layout
110
- @layout_view ||= self.site.find_view("layouts/#{self.layout}") unless self.layout == false
89
+ site.find_view("layouts/#{layout}") unless layout == false
111
90
  end
112
91
  end
113
92
  end
@@ -0,0 +1,71 @@
1
+ require File.join(File.dirname(__FILE__), "processing")
2
+ require File.join(File.dirname(__FILE__), "collection")
3
+
4
+ module Massimo
5
+ module Resource
6
+ class Base
7
+ include Massimo::Resource::Processing
8
+ extend Massimo::Resource::Collection
9
+
10
+ attr_reader :source_path, :body
11
+
12
+ # The name of this Resource type.
13
+ def self.name
14
+ self.to_s.underscore.gsub(/.*\//, "")
15
+ end
16
+
17
+ # The plural name of this Resource type.
18
+ def self.collection_name
19
+ name.pluralize
20
+ end
21
+
22
+ # Gets the site instance
23
+ def self.site
24
+ Massimo::Site()
25
+ end
26
+
27
+ # Get the directory to this Resource type.
28
+ def self.dir(*path)
29
+ site.dir_for(self.collection_name, *path)
30
+ end
31
+
32
+ # Hook for adding Resource types.
33
+ def self.inherited(subclass)
34
+ Massimo.resources << subclass
35
+ end
36
+
37
+ # Creates a new page associated with the given file path.
38
+ def initialize(source_path)
39
+ @source_path = Pathname.new(source_path)
40
+ read_source!
41
+ end
42
+
43
+ # Gets the resource's file name.
44
+ def file_name
45
+ @source_path.basename.to_s
46
+ end
47
+
48
+ # Gets the resource type, based on the file's extension
49
+ def resource_type
50
+ @source_path.extname.to_s[1..-1]
51
+ end
52
+
53
+ # Gets the site instance
54
+ def site
55
+ self.class.site
56
+ end
57
+
58
+ # Renders the page using the registered filters.
59
+ def render(locals = {})
60
+ @body
61
+ end
62
+
63
+ protected
64
+
65
+ # Get the options from the Site's config for the current resource type.
66
+ def options_for_resource_type
67
+ site.options[resource_type.to_sym]
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,56 @@
1
+ module Massimo
2
+ module Resource
3
+ module Collection
4
+ # Find all the the Resources in this Resource type's directory.
5
+ def all(reload = false)
6
+ return @resources if defined?(@resources) && !reload
7
+ @resources = find_resource_files.collect { |file| self.new(file) }
8
+ end
9
+
10
+ protected
11
+
12
+ # Returns only the files listed in the options or all the files in this
13
+ # Resource type's directory, with certain files filtered out.
14
+ def find_resource_files
15
+ files = site.options[collection_name.to_sym]
16
+ if files && files.is_a?(Array)
17
+ files = files.dup
18
+ add_full_path!(files)
19
+ else
20
+ files = Dir.glob(File.join(dir, "**", "*"))
21
+ reject_partials_and_directories!(files)
22
+ reject_skipped_files!(files)
23
+ end
24
+ files
25
+ end
26
+
27
+ # Reject all files that begin with "_" (like partials) and directories
28
+ def reject_partials_and_directories!(files)
29
+ files.reject! { |file| File.basename(file) =~ /^_/ || File.directory?(file) }
30
+ end
31
+
32
+ # Reject the files in the skip_files option, which can either be an Array of files to skip
33
+ # or a Proc that returns true if the file should be skipped.
34
+ def reject_skipped_files!(files)
35
+ if skip_files = site.options["skip_#{collection_name}".to_sym]
36
+ files.reject! do |file|
37
+ test_file = file.sub("#{dir}/", "")
38
+ case skip_files
39
+ when Array
40
+ skip_files.include?(test_file)
41
+ when Proc
42
+ skip_files.call(test_file)
43
+ else
44
+ false
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ # Add the full path to each file.
51
+ def add_full_path!(files)
52
+ files.collect! { |file| dir(file) }
53
+ end
54
+ end
55
+ end
56
+ end