nanoc 2.1.1 → 2.1.2

Sign up to get free protection for your applications and to get access to all the features.
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