nanoc 2.1.1 → 2.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.
data/ChangeLog CHANGED
@@ -1,6 +1,13 @@
1
1
  nanoc Release Notes
2
2
  ===================
3
3
 
4
+ 2.1.2
5
+ -----
6
+
7
+ * Autocompiler now compiles assets as well
8
+ * Sass filter now takes options (just like the Haml filter)
9
+ * Haml/Sass options are now taken from the page *rep* instead of the page
10
+
4
11
  2.1.1
5
12
  -----
6
13
 
@@ -0,0 +1,280 @@
1
+ module Nanoc
2
+
3
+ # A Nanoc::AssetRep is a single representation (rep) of an asset
4
+ # (Nanoc::Asset). An asset can have multiple representations. A
5
+ # representation has its own attributes and its own output file. A single
6
+ # asset can therefore have multiple output files, each run through a
7
+ # different set of filters with a different layout.
8
+ #
9
+ # An asset representation is observable. The following events will be
10
+ # notified:
11
+ #
12
+ # * :compilation_started
13
+ # * :compilation_ended
14
+ # * :filtering_started
15
+ # * :filtering_ended
16
+ #
17
+ # The compilation-related events have one parameters (the page
18
+ # representation); the filtering-related events have two (the page
19
+ # representation, and a symbol containing the filter class name).
20
+ class AssetRep
21
+
22
+ # The asset (Nanoc::Asset) to which this representation belongs.
23
+ attr_reader :asset
24
+
25
+ # A hash containing this asset representation's attributes.
26
+ attr_accessor :attributes
27
+
28
+ # This asset representation's unique name.
29
+ attr_reader :name
30
+
31
+ # Creates a new asset representation for the given asset and with the
32
+ # given attributes.
33
+ #
34
+ # +asset+:: The asset (Nanoc::Asset) to which the new representation will
35
+ # belong.
36
+ #
37
+ # +attributes+:: A hash containing the new asset representation's
38
+ # attributes. This hash must have been run through
39
+ # Hash#clean before using it here.
40
+ #
41
+ # +name+:: The unique name for the new asset representation.
42
+ def initialize(asset, attributes, name)
43
+ # Set primary attributes
44
+ @asset = asset
45
+ @attributes = attributes
46
+ @name = name
47
+
48
+ # Reset flags
49
+ @compiled = false
50
+ @modified = false
51
+ @created = false
52
+
53
+ # Reset stages
54
+ @filtered = false
55
+ end
56
+
57
+ # Returns a proxy (Nanoc::AssetRepProxy) for this asset representation.
58
+ def to_proxy
59
+ @proxy ||= AssetRepProxy.new(self)
60
+ end
61
+
62
+ # Returns true if this asset rep's output file was created during the last
63
+ # compilation session, or false if the output file did already exist.
64
+ def created?
65
+ @created
66
+ end
67
+
68
+ # Returns true if this asset rep's output file was modified during the
69
+ # last compilation session, or false if the output file wasn't changed.
70
+ def modified?
71
+ @modified
72
+ end
73
+
74
+ # Returns true if this page rep has been compiled, false otherwise.
75
+ def compiled?
76
+ @compiled
77
+ end
78
+
79
+ # Returns the path to the output file, including the path to the output
80
+ # directory specified in the site configuration, and including the
81
+ # filename and extension.
82
+ def disk_path
83
+ @disk_path ||= @asset.site.router.disk_path_for(self)
84
+ end
85
+
86
+ # Returns the path to the output file as it would be used in a web
87
+ # browser: starting with a slash (representing the web root), and only
88
+ # including the filename and extension if they cannot be ignored (i.e.
89
+ # they are not in the site configuration's list of index files).
90
+ def web_path
91
+ compile(false, false)
92
+
93
+ @web_path ||= @asset.site.router.web_path_for(self)
94
+ end
95
+
96
+ # Returns true if this asset rep's output file is outdated and must be
97
+ # regenerated, false otherwise.
98
+ def outdated?
99
+ # Outdated if we don't know
100
+ return true if @asset.mtime.nil?
101
+
102
+ # Outdated if compiled file doesn't exist
103
+ return true if !File.file?(disk_path)
104
+
105
+ # Get compiled mtime
106
+ compiled_mtime = File.stat(disk_path).mtime
107
+
108
+ # Outdated if file too old
109
+ return true if @asset.mtime > compiled_mtime
110
+
111
+ # Outdated if asset defaults outdated
112
+ return true if @asset.site.asset_defaults.mtime.nil?
113
+ return true if @asset.site.asset_defaults.mtime > compiled_mtime
114
+
115
+ # Outdated if code outdated
116
+ return true if @asset.site.code.mtime.nil?
117
+ return true if @asset.site.code.mtime > compiled_mtime
118
+
119
+ return false
120
+ end
121
+
122
+ # Returns the attribute with the given name. This method will look in
123
+ # several places for the requested attribute:
124
+ #
125
+ # 1. This asset representation's attributes;
126
+ # 2. The attributes of this asset representation's asset;
127
+ # 3. The asset defaults' representation corresponding to this asset
128
+ # representation;
129
+ # 4. The asset defaults in general;
130
+ # 5. The hardcoded asset defaults, if everything else fails.
131
+ def attribute_named(name)
132
+ # Check in here
133
+ return @attributes[name] if @attributes.has_key?(name)
134
+
135
+ # Check in asset
136
+ return @asset.attributes[name] if @asset.attributes.has_key?(name)
137
+
138
+ # Check in asset defaults' asset rep
139
+ asset_default_reps = @asset.site.asset_defaults.attributes[:reps] || {}
140
+ asset_default_rep = asset_default_reps[@name] || {}
141
+ return asset_default_rep[name] if asset_default_rep.has_key?(name)
142
+
143
+ # Check in site defaults (global)
144
+ asset_defaults_attrs = @asset.site.asset_defaults.attributes
145
+ return asset_defaults_attrs[name] if asset_defaults_attrs.has_key?(name)
146
+
147
+ # Check in hardcoded defaults
148
+ return Nanoc::Asset::DEFAULTS[name]
149
+ end
150
+
151
+ # Compiles the asset representation and writes the result to the disk.
152
+ # This method should not be called directly; please use
153
+ # Nanoc::Compiler#run instead, and pass this asset representation's asset
154
+ # as its first argument.
155
+ #
156
+ # The asset representation will only be compiled if it wasn't compiled
157
+ # before yet. To force recompilation of the asset rep, forgetting any
158
+ # progress, set +from_scratch+ to true.
159
+ #
160
+ # +even_when_not_outdated+:: true if the asset rep should be compiled even
161
+ # if it is not outdated, false if not.
162
+ #
163
+ # +from_scratch+:: true if the asset rep should be filtered again even if
164
+ # it has already been filtered, false otherwise.
165
+ def compile(even_when_not_outdated, from_scratch)
166
+ # Don't compile if already compiled
167
+ return if @compiled and !from_scratch
168
+
169
+ # Skip unless outdated
170
+ unless outdated? or even_when_not_outdated
171
+ Nanoc::NotificationCenter.post(:compilation_started, self)
172
+ Nanoc::NotificationCenter.post(:compilation_ended, self)
173
+ return
174
+ end
175
+
176
+ # Reset flags
177
+ @compiled = false
178
+ @modified = false
179
+ @created = !File.file?(self.disk_path)
180
+
181
+ # Forget progress if requested
182
+ @filtered = false if from_scratch
183
+
184
+ # Start
185
+ @asset.site.compiler.stack.push(self)
186
+ Nanoc::NotificationCenter.post(:compilation_started, self)
187
+
188
+ # Compile
189
+ unless @filtered
190
+ if attribute_named(:binary) == true
191
+ compile_binary
192
+ else
193
+ compile_textual
194
+ end
195
+ end
196
+ @compiled = true
197
+
198
+ # Stop
199
+ @asset.site.compiler.stack.pop
200
+ Nanoc::NotificationCenter.post(:compilation_ended, self)
201
+ end
202
+
203
+ private
204
+
205
+ # Computes and returns the MD5 digest for the given file.
206
+ def digest(filename)
207
+ # Create hash
208
+ incr_digest = Digest::MD5.new()
209
+
210
+ # Collect data
211
+ File.open(filename, 'r') do |file|
212
+ incr_digest << file.read(1000) until file.eof?
213
+ end
214
+
215
+ # Calculate hex hash
216
+ incr_digest.hexdigest
217
+ end
218
+
219
+ # Compiles the asset rep, treating its contents as binary data.
220
+ def compile_binary
221
+ # Calculate digest before
222
+ digest_before = File.file?(disk_path) ? digest(disk_path) : nil
223
+
224
+ # Run filters
225
+ current_file = @asset.file
226
+ attribute_named(:filters).each do |filter_name|
227
+ # Free resources so that this filter won't fail
228
+ GC.start
229
+
230
+ # Create filter
231
+ klass = Nanoc::BinaryFilter.named(filter_name)
232
+ raise Nanoc::Errors::UnknownFilterError.new(filter_name) if klass.nil?
233
+ filter = klass.new(self.to_proxy, @asset.to_proxy, @asset.site)
234
+
235
+ # Run filter
236
+ Nanoc::NotificationCenter.post(:filtering_started, self, klass.identifier)
237
+ old_file = current_file
238
+ current_file = filter.run(current_file)
239
+ old_file.close
240
+ Nanoc::NotificationCenter.post(:filtering_ended, self, klass.identifier)
241
+ end
242
+
243
+ # Write asset
244
+ FileUtils.mkdir_p(File.dirname(self.disk_path))
245
+ FileUtils.cp(current_file.path, disk_path)
246
+
247
+ # Calculate digest after
248
+ digest_after = digest(disk_path)
249
+ @modified = (digest_after != digest_before)
250
+ end
251
+
252
+ # Compiles the asset rep, treating its contents as textual data.
253
+ def compile_textual
254
+ # Get content
255
+ current_content = @asset.file.read
256
+
257
+ # Check modified
258
+ @modified = @created ? true : File.read(self.disk_path) != current_content
259
+
260
+ # Run filters
261
+ attribute_named(:filters).each do |filter_name|
262
+ # Create filter
263
+ klass = Nanoc::Filter.named(filter_name)
264
+ raise Nanoc::Errors::UnknownFilterError.new(filter_name) if klass.nil?
265
+ filter = klass.new(self)
266
+
267
+ # Run filter
268
+ Nanoc::NotificationCenter.post(:filtering_started, self, klass.identifier)
269
+ current_content = filter.run(current_content)
270
+ Nanoc::NotificationCenter.post(:filtering_ended, self, klass.identifier)
271
+ end
272
+
273
+ # Write asset
274
+ FileUtils.mkdir_p(File.dirname(self.disk_path))
275
+ File.open(self.disk_path, 'w') { |io| io.write(current_content) }
276
+ end
277
+
278
+ end
279
+
280
+ end
@@ -0,0 +1,68 @@
1
+ module Nanoc
2
+
3
+ # A Nanoc::Template represents a template, which can be used for creating
4
+ # new pages. Pages don't necessary have to be created using templates, but
5
+ # they can be useful for generating pages where you only have to "fill in
6
+ # the blanks".
7
+ class Template
8
+
9
+ # The Nanoc::Site this template belongs to.
10
+ attr_accessor :site
11
+
12
+ # The name of this template.
13
+ attr_reader :name
14
+
15
+ # The raw content a page created using this template will have.
16
+ attr_reader :page_content
17
+
18
+ # A hash containing the attributes a page created using this template will
19
+ # have.
20
+ attr_reader :page_attributes
21
+
22
+ # Creates a new template.
23
+ #
24
+ # +name+:: The name of this template.
25
+ #
26
+ # +page_content+:: The raw content a page created using this template will
27
+ # have.
28
+ #
29
+ # +page_attributes+:: A hash containing the attributes a page created
30
+ # using this template will have.
31
+ def initialize(page_content, page_attributes, name)
32
+ @page_content = page_content
33
+ @page_attributes = page_attributes.clean
34
+ @name = name
35
+ end
36
+
37
+ def inspect
38
+ "Nanoc::Template(name=#{@name})"
39
+ end
40
+
41
+ # Saves the template in the database, creating it if it doesn't exist yet
42
+ # or updating it if it already exists. Tells the site's data source to
43
+ # save the template.
44
+ def save
45
+ @site.data_source.loading do
46
+ @site.data_source.save_template(self)
47
+ end
48
+ end
49
+
50
+ # Renames the template. Tells the site's data source to rename the
51
+ # template.
52
+ def move_to(new_name)
53
+ @site.data_source.loading do
54
+ @site.data_source.move_template(self, new_name)
55
+ end
56
+ end
57
+
58
+ # Deletes the template. Tells the site's data source to delete the
59
+ # template.
60
+ def delete
61
+ @site.data_source.loading do
62
+ @site.data_source.delete_template(self)
63
+ end
64
+ end
65
+
66
+ end
67
+
68
+ end
@@ -173,12 +173,16 @@ END
173
173
  # Reload site data
174
174
  @site.load_data(true)
175
175
 
176
- # Get page or file
177
- page_reps = @site.pages.map { |p| p.reps }.flatten
178
- page_rep = page_reps.find { |p| p.web_path == path.cleaned_path }
176
+ # Get paths
177
+ rep_path = path.cleaned_path
179
178
  file_path = @site.config[:output_dir] + path
180
179
 
181
- if page_rep.nil?
180
+ # Find rep
181
+ objs = @site.pages + @site.assets
182
+ reps = objs.map { |o| o.reps }.flatten
183
+ rep = reps.find { |r| r.web_path == rep_path }
184
+
185
+ if rep.nil?
182
186
  # Serve file
183
187
  if File.file?(file_path)
184
188
  serve_file(file_path)
@@ -186,8 +190,8 @@ END
186
190
  serve_404(path)
187
191
  end
188
192
  else
189
- # Serve page rep
190
- serve_page_rep(page_rep)
193
+ # Serve rep
194
+ serve_rep(rep)
191
195
  end
192
196
  end
193
197
  end
@@ -244,19 +248,22 @@ END
244
248
  ]
245
249
  end
246
250
 
247
- def serve_page_rep(page_rep)
248
- # Recompile page rep
251
+ def serve_rep(rep)
252
+ # Recompile rep
249
253
  begin
250
- @site.compiler.run([ page_rep.page ], :even_when_not_outdated => @include_outdated)
254
+ @site.compiler.run(
255
+ [ rep.respond_to?(:page) ? rep.page : rep.asset ],
256
+ :even_when_not_outdated => @include_outdated
257
+ )
251
258
  rescue Exception => exception
252
- return serve_500(page_rep.web_path, exception)
259
+ return serve_500(rep.web_path, exception)
253
260
  end
254
261
 
255
262
  # Build response
256
263
  [
257
264
  200,
258
- { 'Content-Type' => mime_type_of(page_rep.disk_path, 'text/html') },
259
- [ page_rep.content(:post) ]
265
+ { 'Content-Type' => mime_type_of(rep.disk_path, 'text/html') },
266
+ [ rep.content(:post) ]
260
267
  ]
261
268
  end
262
269
 
@@ -8,7 +8,7 @@ module Nanoc::Filters
8
8
  require 'haml'
9
9
 
10
10
  # Get options
11
- options = @page.attribute_named(:haml_options) || {}
11
+ options = @page_rep.attribute_named(:haml_options) || {}
12
12
 
13
13
  # Create context
14
14
  context = ::Nanoc::Extra::Context.new(assigns)
@@ -6,7 +6,11 @@ module Nanoc::Filters
6
6
  def run(content)
7
7
  require 'sass'
8
8
 
9
- ::Sass::Engine.new(content).render
9
+ # Get options
10
+ options = @page_rep.attribute_named(:sass_options) || {}
11
+
12
+ # Get result
13
+ ::Sass::Engine.new(content, options).render
10
14
  end
11
15
 
12
16
  end
@@ -0,0 +1,73 @@
1
+ module Nanoc::Helpers
2
+
3
+ # Nanoc::Helpers::LinkTo contains functions for linking to pages.
4
+ module LinkTo
5
+
6
+ include Nanoc::Helpers::HTMLEscape
7
+
8
+ # Creates a HTML link to the given path or page/asset representation, and
9
+ # with the given text.
10
+ #
11
+ # +path_or_rep+:: the URL or path (a String) that should be linked to, or
12
+ # the page or asset representation that should be linked
13
+ # to.
14
+ #
15
+ # +text+:: the visible link text.
16
+ #
17
+ # +attributes+:: a hash containing HTML attributes that will be added to
18
+ # the link.
19
+ #
20
+ # Examples:
21
+ #
22
+ # link_to('Blog', '/blog/')
23
+ # # => '<a href="/blog/">Blog</a>'
24
+ #
25
+ # page_rep = @pages.find { |p| p.page_id == 'special' }.reps(:default)
26
+ # link_to('Special Page', page_rep)
27
+ # # => '<a href="/special_page/">Special Page</a>'
28
+ #
29
+ # link_to('Blog', '/blog/', :title => 'My super cool blog')
30
+ # # => '<a href="/blog/" title="My super cool blog">Blog</a>
31
+ def link_to(text, path_or_rep, attributes={})
32
+ # Find path
33
+ if path_or_rep.is_a?(String)
34
+ path = path_or_rep
35
+ else
36
+ path = (@config[:base_dir] || '') + path_or_rep.path
37
+ end
38
+
39
+ # Join attributes
40
+ attributes = attributes.inject('') do |memo, (key, value)|
41
+ memo + key.to_s + '="' + h(value) + '" '
42
+ end
43
+
44
+ # Create link
45
+ "<a #{attributes}href=\"#{path}\">#{text}</a>"
46
+ end
47
+
48
+ # Creates a HTML link using link_to, except when the linked page is the
49
+ # current one. In this case, a span element with class "active" and with
50
+ # the given text will be returned.
51
+ #
52
+ # Examples:
53
+ #
54
+ # link_to('Blog', '/blog/')
55
+ # # => '<a href="/blog/">Blog</a>'
56
+ #
57
+ # link_to('This Page', @page_rep)
58
+ # # => '<span class="active">This Page</span>'
59
+ def link_to_unless_current(text, path_or_rep, attributes={})
60
+ # Find path
61
+ path = path_or_rep.is_a?(String) ? path_or_rep : path_or_rep.path
62
+
63
+ if @page_rep and @page_rep.path == path
64
+ # Create message
65
+ "<span class=\"active\" title=\"You're here.\">#{text}</span>"
66
+ else
67
+ link_to(text, path_or_rep, attributes)
68
+ end
69
+ end
70
+
71
+ end
72
+
73
+ end
data/lib/nanoc.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  module Nanoc
2
2
 
3
3
  # The current nanoc version.
4
- VERSION = '2.1.1'
4
+ VERSION = '2.1.2'
5
5
 
6
6
  # Generic error. Superclass for all nanoc-specific errors.
7
7
  class Error < RuntimeError ; end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nanoc
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.1
4
+ version: 2.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Denis Defreyne
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-08-18 00:00:00 +02:00
12
+ date: 2008-09-08 00:00:00 +02:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -32,6 +32,7 @@ files:
32
32
  - lib/nanoc/base/asset.rb
33
33
  - lib/nanoc/base/asset_defaults.rb
34
34
  - lib/nanoc/base/asset_rep.rb
35
+ - lib/nanoc/base/asset_rep.rb.orig
35
36
  - lib/nanoc/base/binary_filter.rb
36
37
  - lib/nanoc/base/code.rb
37
38
  - lib/nanoc/base/compiler.rb
@@ -58,6 +59,7 @@ files:
58
59
  - lib/nanoc/base/router.rb
59
60
  - lib/nanoc/base/site.rb
60
61
  - lib/nanoc/base/template.rb
62
+ - lib/nanoc/base/template.rb.orig
61
63
  - lib/nanoc/binary_filters
62
64
  - lib/nanoc/binary_filters/image_science_thumbnail.rb
63
65
  - lib/nanoc/cli
@@ -114,6 +116,7 @@ files:
114
116
  - lib/nanoc/helpers/capturing.rb
115
117
  - lib/nanoc/helpers/html_escape.rb
116
118
  - lib/nanoc/helpers/link_to.rb
119
+ - lib/nanoc/helpers/link_to.rb.orig
117
120
  - lib/nanoc/helpers/render.rb
118
121
  - lib/nanoc/helpers/tagging.rb
119
122
  - lib/nanoc/helpers/xml_sitemap.rb