nanoc 1.1.3 → 1.2

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.
@@ -0,0 +1,34 @@
1
+ == 1.2
2
+
3
+ * Sites now have an 'assets' directory, whose contents are copied to the 'output' directory when compiling
4
+ * Added support for non-eRuby layouts (Markaby, Haml, Liquid, ...)
5
+ * Added many more filters
6
+ * Improved error reporting
7
+ * Accessing page attributes using instance variables, and not through @page, is no longer possible
8
+ * Page attributes can now be accessed using dot notation, i.e. @page.title as well as @page[:title]
9
+
10
+ == 1.1.3
11
+
12
+ * Fixed bug which would cause pages without layouts to be outputed incorrectly
13
+
14
+ == 1.1.2
15
+
16
+ * Backup files (files ending with a “~”) are now ignored
17
+ * Fixed bug which would cause subpages not to be generated correctly
18
+
19
+ == 1.1
20
+
21
+ * Added support for nested layouts
22
+ * Added coloured logging
23
+ * @page now hold the page that is currently being processed
24
+ * Index files are now called “content” files and are now named after the directory they are in [Colin Barrett]
25
+ * It is now possible to access @page in the page’s content file
26
+
27
+ == 1.0.1
28
+
29
+ * Fixed a bug which would cause a “no such template” error to be displayed when the template existed but compiling it would raise an exception
30
+ * Fixed bug which would cause pages not to be sorted by order before compiling
31
+
32
+ == 1.0
33
+
34
+ * Initial release
data/Rakefile CHANGED
@@ -17,7 +17,8 @@ EMAIL = 'denis.defreyne@stoneship.org'
17
17
 
18
18
  #####
19
19
 
20
- CLEAN.include [ '*.gem', 'pkg', 'tmp', 'test/fixtures/*/output/*' ]
20
+ CLEAN.include [ 'tmp', 'test/fixtures/*/output/*', 'test/fixtures/*/tmp' ]
21
+ CLOBBER.include [ 'pkg' ]
21
22
 
22
23
  spec = Gem::Specification.new do |s|
23
24
  s.name = NAME
@@ -32,7 +33,7 @@ spec = Gem::Specification.new do |s|
32
33
  s.required_ruby_version = '>= 1.8.2'
33
34
 
34
35
  s.has_rdoc = false
35
- s.files = %w( README LICENSE Rakefile ) + Dir['{bin,lib}/**/*']
36
+ s.files = %w( README LICENSE ChangeLog Rakefile ) + Dir['{bin,lib}/**/*']
36
37
  s.executables = [ 'nanoc' ]
37
38
  s.require_path = 'lib'
38
39
  s.bindir = 'bin'
data/bin/nanoc CHANGED
@@ -1,29 +1,32 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- # Available options:
4
- #
5
- # -h, --help:
6
- # prints the help
7
- #
8
- # -t <template>, --template <template>
9
- # Uses the specified template when creating a page
10
- #
11
- # -v, --version:
12
- # prints the version information
3
+ require File.dirname(__FILE__) + '/../lib/nanoc.rb'
13
4
 
14
- require 'rubygems' rescue nil
15
5
  require 'getoptlong'
16
6
 
17
- require File.dirname(__FILE__) + '/../lib/nanoc.rb'
7
+ try_require 'rubygems'
18
8
 
19
9
  # Define some texts
20
10
  version_text = "nanoc #{Nanoc::VERSION} (c) 2007 Denis Defreyne."
11
+ usage_text = "Usage: nanoc [options] [command] [parameters]"
21
12
  help_text = <<EOT
22
- usage: nanoc [-hv]
13
+ Usage: nanoc [-chv]
23
14
  nanoc create_site <name>
24
15
  nanoc create_page <name> [-t template]
25
16
  nanoc create_template <name>
26
17
  nanoc compile
18
+
19
+ Options:
20
+ -h, --help Show this help message and quit.
21
+ -v, --version Show the nanoc version number and quit.
22
+ -t, --template Template that should be used when creating a page.
23
+ (can only be used with create_page)
24
+
25
+ Description:
26
+ The 'nanoc' command is used for creating nanoc-powered sites, as well as
27
+ managing them: creating pages and templates and compiling the site.
28
+
29
+ Read more about nanoc on the site: <http://stoneship.org/software/nanoc/>
27
30
  EOT
28
31
 
29
32
  # Parse options
@@ -47,13 +50,13 @@ begin
47
50
  end
48
51
  end
49
52
  rescue GetoptLong::InvalidOption
50
- $stderr.puts help_text
53
+ $stderr.puts usage_text
51
54
  exit
52
55
  end
53
56
 
54
57
  # Make sure we have at least one argument
55
58
  if ARGV.size == 0
56
- $stderr.puts help_text
59
+ $stderr.puts usage_text
57
60
  exit
58
61
  end
59
62
 
@@ -2,51 +2,157 @@ module Nanoc
2
2
  class Compiler
3
3
 
4
4
  DEFAULT_CONFIG = { :output_dir => 'output' }
5
- DEFAULT_PAGE = { :filters => [], :extension => 'html', :order => 0, :layout => "default" }
5
+
6
+ FILE_TYPES = {
7
+ '.erb' => :eruby,
8
+ '.rhtml' => :eruby,
9
+ '.haml' => :haml,
10
+ '.mab' => :markaby,
11
+ '.liquid' => :liquid
12
+ }
13
+
14
+ PAGE_DEFAULTS = {
15
+ :custom_path => nil,
16
+ :extension => 'html',
17
+ :filters => [],
18
+ :is_draft => false,
19
+ :layout => 'default',
20
+ :order => 0
21
+ }
6
22
 
7
23
  def initialize
8
24
  Nanoc.ensure_in_site
9
25
 
10
26
  @config = DEFAULT_CONFIG.merge(YAML.load_file_and_clean('config.yaml'))
11
- @global_page = DEFAULT_PAGE.merge(YAML.load_file_and_clean('meta.yaml'))
27
+ @global_page = PAGE_DEFAULTS.merge(YAML.load_file_and_clean('meta.yaml'))
12
28
  end
13
29
 
14
30
  def run
31
+ # Require all Ruby source files in lib/
15
32
  Dir['lib/*.rb'].each { |f| require f }
16
33
 
34
+ # Copy assets to output directory
35
+ copy_assets
36
+
37
+ # Compile all pages
17
38
  pages = compile_pages(uncompiled_pages)
39
+
40
+ # Put pages in their layouts
18
41
  pages.each do |page|
19
- content = (page[:layout].nil? ? "<%= @page[:content] %>" : File.read("layouts/#{page[:layout]}.erb")).eruby(page.merge({ :page => page, :pages => pages })) # fallback for nanoc 1.0
42
+ # Prepare layout content
43
+ content = nil
44
+ context = { :page => page.merge(:_content_filename => nil), :pages => pages }
45
+ layout = layout_for_page(page)
46
+ begin
47
+ case layout[:type]
48
+ when :eruby
49
+ content = layout[:content].eruby(context)
50
+ when :haml
51
+ content = layout[:content].haml(context)
52
+ when :markaby
53
+ content = layout[:content].markaby(context)
54
+ when :liquid
55
+ content = layout[:content].liquid(context)
56
+ end
57
+ rescue => exception
58
+ $stderr.puts "Exception occured while layouting page" +
59
+ "'#{page[:_content_filename]}' in layout '#{page[:layout]}':" unless $quiet
60
+ $stderr.puts exception unless $quiet
61
+ $stderr.puts exception.backtrace.join("\n") unless $quiet
62
+ exit
63
+ end
64
+
65
+ # Write page with layout
20
66
  FileManager.create_file(path_for_page(page)) { content }
21
67
  end
22
68
  end
23
69
 
24
70
  private
25
71
 
72
+ # Copies the contents of the assets directory into the output directory
73
+ def copy_assets
74
+ Dir['assets/*'].each { |f| FileUtils.remove_entry_secure(f.sub('assets/', 'output/'), true) }
75
+ FileUtils.cp_r(Dir['assets/*'], 'output') if File.directory?('assets') and !Dir['assets/*'].empty?
76
+ end
77
+
78
+ # Returns a list of uncompiled pages
26
79
  def uncompiled_pages
27
- Dir['content/**/meta.yaml'].collect do |filename|
80
+ # Read all meta files
81
+ pages = Dir['content/**/meta.yaml'].collect do |filename|
82
+ # Read the meta file
28
83
  page = @global_page.merge(YAML.load_file_and_clean(filename))
84
+
85
+ # Fix the path
29
86
  page[:path] = filename.sub(/^content/, '').sub('meta.yaml', '')
30
87
 
88
+ # Get the content filename
31
89
  content_filenames = Dir[filename.sub('meta.yaml', File.basename(File.dirname(filename)) + '.*')].reject { |f| f =~ /~$/ }
32
90
  content_filenames += Dir["#{File.dirname(filename)}/index.*"] # fallback for nanoc 1.0
33
91
  content_filenames.ensure_single('content files', File.dirname(filename))
34
92
  page[:_content_filename] = content_filenames[0]
35
93
 
36
94
  page
37
- end.compact.reject { |page| page[:is_draft] }.sort do |x,y|
38
- x[:order].to_i == y[:order].to_i ? x[:path] <=> y[:path] : x[:order].to_i <=> y[:order].to_i
95
+ end
96
+
97
+ # Ignore drafts
98
+ pages.reject! { |page| page[:is_draft] }
99
+
100
+ # Sort pages by order and by path
101
+ pages.sort! do |x,y|
102
+ if x[:order].to_i == y[:order].to_i
103
+ x[:path] <=> y[:path]
104
+ else
105
+ x[:order].to_i <=> y[:order].to_i
106
+ end
107
+ end
108
+
109
+ pages
110
+ end
111
+
112
+ # Returns the layout for the given page
113
+ def layout_for_page(a_page)
114
+ if a_page[:layout].nil?
115
+ { :type => :eruby, :content => "<%= @page[:content] %>" }
116
+ else
117
+ filenames = Dir["layouts/#{a_page[:layout]}.*"]
118
+ filenames.ensure_single('layout files', a_page[:layout])
119
+ filename = filenames[0]
120
+
121
+ { :type => FILE_TYPES[File.extname(filename)], :content => File.read(filename) }
39
122
  end
40
123
  end
41
124
 
125
+ # Returns the path for the given page
42
126
  def path_for_page(a_page)
43
- @config[:output_dir] + ( a_page[:custom_path].nil? ? a_page[:path] + 'index.' + a_page[:extension] : a_page[:custom_path] )
127
+ if a_page[:custom_path].nil?
128
+ @config[:output_dir] + a_page[:path] + 'index.' + a_page[:extension]
129
+ else
130
+ @config[:output_dir] + a_page[:custom_path]
131
+ end
44
132
  end
45
133
 
134
+ # Compiles the given pages and returns the compiled pages
46
135
  def compile_pages(a_pages)
47
136
  a_pages.inject([]) do |pages, page|
48
- content = File.read(page[:_content_filename]).filter(page[:filters], :eruby_context => { :page => page, :pages => pages })
49
- pages + [ page.merge( { :content => content, :_content_filename => nil }) ]
137
+ # Read page
138
+ content = File.read(page[:_content_filename])
139
+
140
+ # Filter page
141
+ begin
142
+ content = content.filter(page[:filters], :assigns => { :page => Page.new(page), :pages => pages })
143
+ rescue Exception => exception
144
+ $stderr.puts "Exception occured while compiling page" +
145
+ "'#{page[:_content_filename]}':" unless $quiet
146
+ $stderr.puts exception unless $quiet
147
+ $stderr.puts exception.backtrace.join("\n") unless $quiet
148
+ exit
149
+ end
150
+
151
+ # Create compiled page
152
+ compiled_page = page.merge( :content => content )
153
+
154
+ # Remember page
155
+ pages + [ Page.new(compiled_page) ]
50
156
  end
51
157
  end
52
158
 
@@ -4,6 +4,7 @@ module Nanoc
4
4
 
5
5
  def self.create_site(a_sitename)
6
6
  FileManager.create_dir a_sitename do
7
+ FileManager.create_dir 'assets'
7
8
  FileManager.create_dir 'output'
8
9
 
9
10
  FileManager.create_file 'config.yaml' do
@@ -1,18 +1,24 @@
1
1
  def try_require(s) ; begin ; require s ; rescue LoadError ; end ; end
2
2
 
3
3
  try_require 'rubygems'
4
- try_require 'bluecloth'
5
- try_require 'rubypants'
6
4
 
7
5
  require 'erubis'
8
6
  require 'fileutils'
9
7
  require 'yaml'
10
8
 
9
+ try_require 'bluecloth'
10
+ try_require 'rubypants'
11
+ try_require 'markaby'
12
+ try_require 'liquid'
13
+ try_require 'haml'
14
+ try_require 'rdoc/markup/simple_markup'
15
+ try_require 'rdoc/markup/simple_markup/to_html'
16
+
11
17
  class Array
12
18
  # Ensures that the array contains only one element
13
19
  def ensure_single(a_noun, a_context)
14
20
  if self.size != 1
15
- $stderr.puts "ERROR: expected 1 #{a_noun}, found #{self.size} (#{a_context})" unless $quiet == true
21
+ $stderr.puts "ERROR: expected 1 #{a_noun}, found #{self.size} (#{a_context})" unless $quiet
16
22
  exit
17
23
  end
18
24
  end
@@ -46,6 +52,13 @@ class Hash
46
52
  hash
47
53
  end
48
54
  end
55
+
56
+ def stringify_keys
57
+ inject({}) do |hash, (key, value)|
58
+ hash[key.to_s] = value
59
+ hash
60
+ end
61
+ end
49
62
  end
50
63
 
51
64
  class String
@@ -54,47 +67,134 @@ class String
54
67
  def filter(a_filters, a_params={})
55
68
  a_filters.inject(self) do |result, filter|
56
69
  case filter
70
+ when 'eruby'
71
+ result.replace(result.eruby(a_params[:assigns]))
72
+ when 'haml'
73
+ result.replace(result.haml(a_params[:assigns]))
74
+ when 'liquid'
75
+ result.replace(result.liquid(a_params[:assigns]))
76
+ when 'markaby'
77
+ result.replace(result.markaby(a_params[:assigns]))
57
78
  when 'markdown'
58
79
  result.replace(result.markdown)
80
+ when 'rdoc'
81
+ result.replace(result.rdoc)
59
82
  when 'smartypants'
60
83
  result.replace(result.smartypants)
61
- when 'eruby'
62
- result.replace(result.eruby(a_params[:eruby_context]))
63
84
  end
64
85
  end
65
86
  end
66
87
 
67
- # Converts the string to HTML using Markdown.
88
+ # Converts the string using eRuby.
89
+ def eruby(a_assigns={})
90
+ Erubis::Eruby.new(self).evaluate(a_assigns)
91
+ end
92
+
93
+ # Converts the string using Haml
94
+ def haml(a_assigns={})
95
+ Haml::Engine.new(self, :locals => a_assigns).to_html
96
+ rescue NameError
97
+ $stderr.puts 'ERROR: String#haml failed (Haml not installed?)' unless $quiet
98
+ exit
99
+ end
100
+
101
+ # Converts the string using Liquid
102
+ def liquid(a_assigns={})
103
+ Liquid::Template.parse(self).render(a_assigns.stringify_keys)
104
+ rescue NameError
105
+ $stderr.puts 'ERROR: String#liquid failed (Liquid not installed?)' unless $quiet
106
+ exit
107
+ end
108
+
109
+ # Converts the string using Markaby
110
+ # TODO perhaps add support for helpers
111
+ def markaby(a_assigns={})
112
+ Markaby::Builder.new(a_assigns).instance_eval(self).to_s
113
+ rescue NameError
114
+ $stderr.puts 'ERROR: String#markaby failed (Markaby not installed?)' unless $quiet
115
+ exit
116
+ end
117
+
118
+ # Converts the string to HTML using BlueCloth/Markdown.
68
119
  def markdown
69
- BlueCloth::new(self).to_html
120
+ BlueCloth.new(self).to_html
70
121
  rescue NameError
71
- $stderr.puts 'ERROR: String#markdown failed: BlueCloth not installed' unless $quiet == true
122
+ $stderr.puts 'ERROR: String#markdown failed: BlueCloth not installed' unless $quiet
72
123
  exit
73
124
  end
74
125
 
75
- # Styles the string as HTML by converting quotes, dashes, ... using RubyPants
126
+ # Converts the string using RDoc
127
+ def rdoc
128
+ SM::SimpleMarkup.new.convert(self, SM::ToHtml.new)
129
+ end
130
+
131
+ # Converts the string using RedCloth/Textile
132
+ def textile
133
+ RedCloth.new(self).to_html
134
+ rescue NameError
135
+ $stderr.puts 'ERROR: String#textile failed (RedCloth not installed?)' unless $quiet
136
+ exit
137
+ end
138
+
139
+ # Converts the string using RubyPants/SmartyPants
76
140
  def smartypants
77
- RubyPants::new(self).to_html
141
+ RubyPants.new(self).to_html
78
142
  rescue NameError
79
- $stderr.puts 'ERROR: String#smartypants failed: RubyPants not installed' unless $quiet == true
143
+ $stderr.puts 'ERROR: String#smartypants failed (RubyPants not installed?)' unless $quiet
80
144
  exit
81
145
  end
146
+ end
147
+
148
+ class FileLogger
149
+ COLORS = {
150
+ :reset => "\e[0m",
151
+
152
+ :bold => "\e[1m",
153
+
154
+ :black => "\e[30m",
155
+ :red => "\e[31m",
156
+ :green => "\e[32m",
157
+ :yellow => "\e[33m",
158
+ :blue => "\e[34m",
159
+ :magenta => "\e[35m",
160
+ :cyan => "\e[36m",
161
+ :white => "\e[37m"
162
+ }
163
+
164
+ ACTION_COLORS = {
165
+ :create => COLORS[:bold] + COLORS[:green],
166
+ :update => COLORS[:bold] + COLORS[:yellow],
167
+ :move => COLORS[:bold] + COLORS[:blue],
168
+ :identical => COLORS[:bold]
169
+ }
170
+
171
+ attr_reader :out
172
+
173
+ def initialize(a_out = $stdout)
174
+ @out = a_out
175
+ end
82
176
 
83
- # Converts the string using eRuby.
84
- def eruby(a_context={})
85
- Erubis::Eruby.new(self).evaluate(a_context)
177
+ def log(a_action, a_path)
178
+ @out.puts('%s%12s%s %s' % [ACTION_COLORS[a_action.to_sym], a_action, COLORS[:reset], a_path]) unless $quiet
179
+ end
180
+
181
+ private
182
+
183
+ def method_missing(a_method, *a_args)
184
+ log(a_method.to_s, a_args.first)
86
185
  end
87
186
  end
88
187
 
89
188
  class FileManager
90
189
  @@stack = []
190
+ @@logger = FileLogger.new
91
191
 
92
192
  def self.create_dir(a_name)
93
193
  @@stack.push(a_name)
94
194
  path = File.join(@@stack)
95
195
  unless File.directory?(path)
96
196
  FileUtils.mkdir_p(path)
97
- log('create', path)
197
+ @@logger.create(path)
98
198
  end
99
199
  yield if block_given?
100
200
  @@stack.pop
@@ -104,35 +204,19 @@ class FileManager
104
204
  path = File.join(@@stack + [ a_name ])
105
205
  FileManager.create_dir(path.sub(/\/[^\/]+$/, '')) if @@stack.empty?
106
206
  content = block_given? ? yield : nil
107
- File.exist?(path) ? ( block_given? and File.read(path) == content ? log('identical', path) : log('update', path) ) : log('create', path)
207
+ if File.exist?(path)
208
+ if block_given? and File.read(path) == content
209
+ @@logger.identical(path)
210
+ else
211
+ @@logger.update(path)
212
+ end
213
+ else
214
+ @@logger.create(path)
215
+ end
108
216
  open(path, 'w') { |io| io.write(content) unless content.nil? }
109
217
  end
110
218
  end
111
219
 
112
- COLORS = {
113
- :reset => "\e[0m",
114
-
115
- :bold => "\e[1m",
116
-
117
- :black => "\e[30m",
118
- :red => "\e[31m",
119
- :green => "\e[32m",
120
- :yellow => "\e[33m",
121
- :blue => "\e[34m",
122
- :magenta => "\e[35m",
123
- :cyan => "\e[36m",
124
- :white => "\e[37m"
125
- }
126
- ACTION_COLORS = {
127
- :create => COLORS[:bold] + COLORS[:green],
128
- :update => COLORS[:bold] + COLORS[:yellow],
129
- :identical => COLORS[:bold]
130
- }
131
-
132
- def log(a_action, a_path)
133
- puts format('%s%12s%s %s', ACTION_COLORS[a_action.to_sym], a_action, COLORS[:reset], a_path) unless $quiet == true
134
- end
135
-
136
220
  def render(a_name, a_context={})
137
- File.read('layouts/' + a_name + '.erb').eruby(a_context.merge({ :page => @page, :pages => @pages }))
221
+ File.read('layouts/' + a_name.to_s + '.erb').eruby(a_context.merge({ :page => @page, :pages => @pages }))
138
222
  end
@@ -1,9 +1,10 @@
1
1
  module Nanoc
2
- VERSION = '1.1.3'
2
+ VERSION = '1.2'
3
3
 
4
4
  def self.ensure_in_site
5
5
  unless in_site?
6
- $stderr.puts 'ERROR: The current working directory does not seem to be a valid/complete nanoc site directory; aborting.' unless $quiet
6
+ $stderr.puts 'ERROR: The current working directory does not seem to ' +
7
+ 'be a valid/complete nanoc site directory; aborting.' unless $quiet
7
8
  exit
8
9
  end
9
10
  end
@@ -17,7 +18,6 @@ module Nanoc
17
18
  return false unless File.directory?('output')
18
19
  return false unless File.directory?('tasks')
19
20
  return false unless File.directory?('templates')
20
-
21
21
  return false unless File.exist?('config.yaml')
22
22
  return false unless File.exist?('meta.yaml')
23
23
  return false unless File.exist?('Rakefile')
@@ -26,6 +26,7 @@ module Nanoc
26
26
  end
27
27
  end
28
28
 
29
+ require File.dirname(__FILE__) + '/page.rb'
29
30
  require File.dirname(__FILE__) + '/creator.rb'
30
31
  require File.dirname(__FILE__) + '/compiler.rb'
31
32
  require File.dirname(__FILE__) + '/enhancements.rb'
@@ -0,0 +1,30 @@
1
+ module Nanoc
2
+ class Page
3
+
4
+ def initialize(a_hash={})
5
+ @attributes = a_hash
6
+ end
7
+
8
+ def [](a_key)
9
+ @attributes[a_key]
10
+ end
11
+
12
+ def []=(a_key, a_value)
13
+ @attributes[a_key] = a_value
14
+ end
15
+
16
+ def merge(a_other)
17
+ @attributes.merge(a_other.to_hash)
18
+ self
19
+ end
20
+
21
+ def to_hash
22
+ @attributes
23
+ end
24
+
25
+ def method_missing(a_method, *a_args)
26
+ @attributes[a_method.to_sym]
27
+ end
28
+
29
+ end
30
+ end
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.2
3
3
  specification_version: 1
4
4
  name: nanoc
5
5
  version: !ruby/object:Gem::Version
6
- version: 1.1.3
7
- date: 2007-05-18 00:00:00 +02:00
6
+ version: "1.2"
7
+ date: 2007-06-05 00:00:00 +02:00
8
8
  summary: a CMS that doesn't run on your server
9
9
  require_paths:
10
10
  - lib
@@ -31,12 +31,14 @@ authors: []
31
31
  files:
32
32
  - README
33
33
  - LICENSE
34
+ - ChangeLog
34
35
  - Rakefile
35
36
  - bin/nanoc
36
37
  - lib/compiler.rb
37
38
  - lib/creator.rb
38
39
  - lib/enhancements.rb
39
40
  - lib/nanoc.rb
41
+ - lib/page.rb
40
42
  test_files: []
41
43
 
42
44
  rdoc_options: []