nanoc3 3.0.6 → 3.0.7

Sign up to get free protection for your applications and to get access to all the features.
data/NEWS.rdoc CHANGED
@@ -1,5 +1,9 @@
1
1
  = nanoc News
2
2
 
3
+ == 3.0.7
4
+
5
+ * Fixed bug which could cause layout rules not be matched in order
6
+
3
7
  == 3.0.6
4
8
 
5
9
  * Error checking in `filesystem_combined` has been improved [Brian Candler]
@@ -30,7 +30,7 @@ module Nanoc3
30
30
 
31
31
  @item_compilation_rules = []
32
32
  @item_routing_rules = []
33
- @layout_filter_mapping = {}
33
+ @layout_filter_mapping = OrderedHash.new
34
34
  end
35
35
 
36
36
  # Compiles (part of) the site and writes out the compiled item
@@ -0,0 +1,200 @@
1
+
2
+ # AUTHOR
3
+ # jan molic /mig/at/1984/dot/cz/
4
+ #
5
+ # DESCRIPTION
6
+ # Hash with preserved order and some array-like extensions
7
+ # Public domain.
8
+ #
9
+ # THANKS
10
+ # Andrew Johnson for his suggestions and fixes of Hash[],
11
+ # merge, to_a, inspect and shift
12
+ class OrderedHash < ::Hash
13
+ attr_accessor :order
14
+
15
+ class << self
16
+ def [] *args
17
+ hsh = OrderedHash.new
18
+ if Hash === args[0]
19
+ hsh.replace args[0]
20
+ elsif (args.size % 2) != 0
21
+ raise ArgumentError, "odd number of elements for Hash"
22
+ else
23
+ 0.step(args.size - 1, 2) do |a|
24
+ b = a + 1
25
+ hsh[args[a]] = args[b]
26
+ end
27
+ end
28
+ hsh
29
+ end
30
+ end
31
+ def initialize(*a, &b)
32
+ super
33
+ @order = []
34
+ end
35
+ def store_only a,b
36
+ store a,b
37
+ end
38
+ alias orig_store store
39
+ def store a,b
40
+ @order.push a unless has_key? a
41
+ super a,b
42
+ end
43
+ alias []= store
44
+ def == hsh2
45
+ return false if @order != hsh2.order
46
+ super hsh2
47
+ end
48
+ def clear
49
+ @order = []
50
+ super
51
+ end
52
+ def delete key
53
+ @order.delete key
54
+ super
55
+ end
56
+ def each_key
57
+ @order.each { |k| yield k }
58
+ self
59
+ end
60
+ def each_value
61
+ @order.each { |k| yield self[k] }
62
+ self
63
+ end
64
+ def each
65
+ @order.each { |k| yield k,self[k] }
66
+ self
67
+ end
68
+ alias each_pair each
69
+ def delete_if
70
+ @order.clone.each { |k|
71
+ delete k if yield(k)
72
+ }
73
+ self
74
+ end
75
+ def values
76
+ ary = []
77
+ @order.each { |k| ary.push self[k] }
78
+ ary
79
+ end
80
+ def keys
81
+ @order
82
+ end
83
+ def first
84
+ {@order.first => self[@order.first]}
85
+ end
86
+ def last
87
+ {@order.last => self[@order.last]}
88
+ end
89
+ def invert
90
+ hsh2 = Hash.new
91
+ @order.each { |k| hsh2[self[k]] = k }
92
+ hsh2
93
+ end
94
+ def reject &block
95
+ self.dup.delete_if &block
96
+ end
97
+ def reject! &block
98
+ hsh2 = reject &block
99
+ self == hsh2 ? nil : hsh2
100
+ end
101
+ def replace hsh2
102
+ @order = hsh2.keys
103
+ super hsh2
104
+ end
105
+ def shift
106
+ key = @order.first
107
+ key ? [key,delete(key)] : super
108
+ end
109
+ def unshift k,v
110
+ unless self.include? k
111
+ @order.unshift k
112
+ orig_store(k,v)
113
+ true
114
+ else
115
+ false
116
+ end
117
+ end
118
+ def push k,v
119
+ unless self.include? k
120
+ @order.push k
121
+ orig_store(k,v)
122
+ true
123
+ else
124
+ false
125
+ end
126
+ end
127
+ def pop
128
+ key = @order.last
129
+ key ? [key,delete(key)] : nil
130
+ end
131
+ def to_a
132
+ ary = []
133
+ each { |k,v| ary << [k,v] }
134
+ ary
135
+ end
136
+ def to_s
137
+ self.to_a.to_s
138
+ end
139
+ def inspect
140
+ ary = []
141
+ each {|k,v| ary << k.inspect + "=>" + v.inspect}
142
+ '{' + ary.join(", ") + '}'
143
+ end
144
+ def update hsh2
145
+ hsh2.each { |k,v| self[k] = v }
146
+ self
147
+ end
148
+ alias :merge! update
149
+ def merge hsh2
150
+ self.dup update(hsh2)
151
+ end
152
+ def select
153
+ ary = []
154
+ each { |k,v| ary << [k,v] if yield k,v }
155
+ ary
156
+ end
157
+ def class
158
+ Hash
159
+ end
160
+ def __class__
161
+ OrderedHash
162
+ end
163
+
164
+ attr_accessor "to_yaml_style"
165
+ def yaml_inline= bool
166
+ if respond_to?("to_yaml_style")
167
+ self.to_yaml_style = :inline
168
+ else
169
+ unless defined? @__yaml_inline_meth
170
+ @__yaml_inline_meth =
171
+ lambda {|opts|
172
+ YAML::quick_emit(object_id, opts) {|emitter|
173
+ emitter << '{ ' << map{|kv| kv.join ': '}.join(', ') << ' }'
174
+ }
175
+ }
176
+ class << self
177
+ def to_yaml opts = {}
178
+ begin
179
+ @__yaml_inline ? @__yaml_inline_meth[ opts ] : super
180
+ rescue
181
+ @to_yaml_style = :inline
182
+ super
183
+ end
184
+ end
185
+ end
186
+ end
187
+ end
188
+ @__yaml_inline = bool
189
+ end
190
+ def yaml_inline!() self.yaml_inline = true end
191
+
192
+ def each_with_index
193
+ @order.each_with_index { |k, index| yield k, self[k], index }
194
+ self
195
+ end
196
+ end # class OrderedHash
197
+
198
+ def OrderedHash(*a, &b)
199
+ OrderedHash.new(*a, &b)
200
+ end
data/lib/nanoc3/base.rb CHANGED
@@ -3,6 +3,7 @@
3
3
  module Nanoc3
4
4
 
5
5
  require 'nanoc3/base/core_ext'
6
+ require 'nanoc3/base/ordered_hash'
6
7
 
7
8
  autoload 'CodeSnippet', 'nanoc3/base/code_snippet'
8
9
  autoload 'Compiler', 'nanoc3/base/compiler'
data/lib/nanoc3.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  module Nanoc3
4
4
 
5
5
  # The current nanoc version.
6
- VERSION = '3.0.6'
6
+ VERSION = '3.0.7'
7
7
 
8
8
  end
9
9
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nanoc3
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.6
4
+ version: 3.0.7
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: 2010-01-18 00:00:00 +01:00
12
+ date: 2010-01-30 00:00:00 +01:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -35,7 +35,6 @@ files:
35
35
  - bin/nanoc3
36
36
  - lib/nanoc3/base/code_snippet.rb
37
37
  - lib/nanoc3/base/compiler.rb
38
- - lib/nanoc3/base/compiler.rb.orig
39
38
  - lib/nanoc3/base/compiler_dsl.rb
40
39
  - lib/nanoc3/base/core_ext/array.rb
41
40
  - lib/nanoc3/base/core_ext/hash.rb
@@ -49,6 +48,7 @@ files:
49
48
  - lib/nanoc3/base/item_rep.rb
50
49
  - lib/nanoc3/base/layout.rb
51
50
  - lib/nanoc3/base/notification_center.rb
51
+ - lib/nanoc3/base/ordered_hash.rb
52
52
  - lib/nanoc3/base/plugin.rb
53
53
  - lib/nanoc3/base/preprocessor_context.rb
54
54
  - lib/nanoc3/base/rule.rb
@@ -73,7 +73,6 @@ files:
73
73
  - lib/nanoc3/data_sources/filesystem_common.rb
74
74
  - lib/nanoc3/data_sources/filesystem_compact.rb
75
75
  - lib/nanoc3/data_sources/last_fm.rb
76
- - lib/nanoc3/data_sources/last_fm.rb.orig
77
76
  - lib/nanoc3/data_sources/twitter.rb
78
77
  - lib/nanoc3/data_sources.rb
79
78
  - lib/nanoc3/extra/auto_compiler.rb
@@ -1,250 +0,0 @@
1
- # encoding: utf-8
2
-
3
- module Nanoc3
4
-
5
- # Nanoc3::Compiler is responsible for compiling a site's item
6
- # representations.
7
- class Compiler
8
-
9
- # The compilation stack. When the compiler begins compiling a rep or a
10
- # layout, it will be placed on the stack; when it is done compiling the
11
- # rep or layout, it will be removed from the stack.
12
- attr_reader :stack
13
-
14
- # The list of compilation rules that will be used to compile items. This
15
- # array will be filled by Nanoc3::Site#load_data.
16
- attr_reader :item_compilation_rules
17
-
18
- # The list of routing rules that will be used to give all items a path.
19
- # This array will be filled by Nanoc3::Site#load_data.
20
- attr_reader :item_routing_rules
21
-
22
- # The hash containing layout-to-filter mapping rules.
23
- attr_reader :layout_filter_mapping
24
-
25
- # Creates a new compiler for the given site.
26
- def initialize(site)
27
- @site = site
28
-
29
- @stack = []
30
-
31
- @item_compilation_rules = []
32
- @item_routing_rules = []
33
- @layout_filter_mapping = {}
34
- end
35
-
36
- # Compiles (part of) the site and writes out the compiled item
37
- # representations.
38
- #
39
- # +items+:: The items that should be compiled, along with their
40
- # dependencies. Pass +nil+ if the entire site should be
41
- # compiled.
42
- #
43
- # This method also accepts a few optional parameters:
44
- #
45
- # +:force+:: true if the rep should be compiled even if it is not
46
- # outdated, false if not. Defaults to false.
47
- def run(item=nil, params={})
48
- # Create output directory if necessary
49
- FileUtils.mkdir_p(@site.config[:output_dir])
50
-
51
- # Load dependencies
52
- dependency_tracker.load_graph
53
- print_dependency_graph if $DEBUG
54
-
55
- # Get items and reps to compile
56
- if item
57
- items = [ item ] + dependency_tracker.all_inverse_dependencies_for(item)
58
- items.uniq!
59
- else
60
- items = @site.items
61
- end
62
- reps = items.map { |i| i.reps }.flatten
63
-
64
- # Prepare dependencies
65
- remember_old_dependencies
66
- @old_dep_graph = dependency_tracker.instance_eval { @graph }.dup
67
- mark_outdated_items(reps, params.has_key?(:force) && params[:force])
68
- forget_dependencies_if_outdated(items)
69
-
70
- # Compile reps
71
- dependency_tracker.start
72
- compile_reps(reps)
73
- dependency_tracker.stop
74
-
75
- # Store dependencies
76
- dependency_tracker.store_graph
77
- end
78
-
79
- # Returns the first matching compilation rule for the given rep.
80
- def compilation_rule_for(rep)
81
- @item_compilation_rules.find do |rule|
82
- rule.applicable_to?(rep.item) && rule.rep_name == rep.name
83
- end
84
- end
85
-
86
- # Returns the first matching routing rule for the given rep.
87
- def routing_rule_for(rep)
88
- @item_routing_rules.find do |rule|
89
- rule.applicable_to?(rep.item) && rule.rep_name == rep.name
90
- end
91
- end
92
-
93
- # Returns a tuple containing the filter name and the filter arguments for
94
- # the given layout.
95
- def filter_for_layout(layout)
96
- @layout_filter_mapping.each_pair do |layout_identifier, filter_name_and_args|
97
- return filter_name_and_args if layout.identifier =~ layout_identifier
98
- end
99
- nil
100
- end
101
-
102
- private
103
-
104
- # Compiles all item representations in the site.
105
- def compile_reps(reps)
106
- active_reps, skipped_reps = reps.partition { |rep| rep.outdated? || rep.item.dependencies_outdated? }
107
- inactive_reps = []
108
- compiled_reps = []
109
-
110
- # Repeat as long as something is successfully compiled...
111
- changed = true
112
- until !changed
113
- changed = false
114
-
115
- # Sort by increasing number of inverse dependencies to speed up compilation
116
- #active_reps = active_reps.sort_by { |r| dependency_tracker.all_dependencies_for(r.item).size }
117
- # 10.times { puts '=' * 80 }
118
- # active_reps.each do |r|
119
- # puts r.item.identifier + ' => ' + dependency_tracker.all_dependencies_for(r.item).size.to_s
120
- # end
121
- # 10.times { puts '=' * 80 }
122
-
123
- # Attempt to compile all active reps
124
- until active_reps.empty?
125
- @stack.clear
126
- begin
127
- rep = active_reps.shift
128
- puts "*** Attempting to compile #{rep.inspect}" if $DEBUG
129
-
130
- @stack.push(rep)
131
- compile_rep(rep)
132
- rescue Nanoc3::Errors::UnmetDependency => e
133
- puts "*** Attempt failed due to unmet dependency on #{e.rep.inspect}" if $DEBUG
134
-
135
- # Save rep to compile it later
136
- inactive_reps << rep
137
-
138
- # Add dependency to list of items to compile
139
- unless active_reps.include?(e.rep) || inactive_reps.include?(e.rep)
140
- changed = true
141
- skipped_reps.delete(e.rep)
142
- inactive_reps.unshift(e.rep)
143
- end
144
-
145
- # Assume that this page has more dependencies and add those
146
- @old_dependencies[rep.item].each do |i|
147
- i.reps.each do |r|
148
- next if active_reps.include?(r)
149
- next if compiled_reps.include?(r)
150
- next if inactive_reps.include?(r)
151
-
152
- inactive_reps.unshift(r)
153
- end
154
- end
155
- else
156
- puts "*** Attempt succeeded" if $DEBUG
157
-
158
- changed = true
159
- compiled_reps << rep
160
- end
161
- puts if $DEBUG
162
- end
163
-
164
- # Retry
165
- puts "*** No active reps left; activating all (#{inactive_reps.size}) inactive reps" if $DEBUG
166
- puts if $DEBUG
167
- active_reps = inactive_reps
168
- inactive_reps = []
169
- end
170
-
171
- # Notify skipped reps
172
- skipped_reps.each do |rep|
173
- Nanoc3::NotificationCenter.post(:compilation_started, rep)
174
- Nanoc3::NotificationCenter.post(:compilation_ended, rep)
175
- end
176
-
177
- # Raise error if some active but non-compileable reps are left
178
- if !active_reps.empty?
179
- raise Nanoc3::Errors::RecursiveCompilation.new(active_reps)
180
- end
181
- end
182
-
183
- # Compiles the given item representation.
184
- #
185
- # This method should not be called directly; please use
186
- # Nanoc3::Compiler#run instead, and pass this item representation's item as
187
- # its first argument.
188
- #
189
- # +rep+:: The rep that is to be compiled.
190
- def compile_rep(rep)
191
- # Start
192
- Nanoc3::NotificationCenter.post(:compilation_started, rep)
193
- Nanoc3::NotificationCenter.post(:visit_started, rep.item)
194
-
195
- # Apply matching rule
196
- compilation_rule_for(rep).apply_to(rep)
197
- rep.compiled = true
198
-
199
- # Write if rep is routed
200
- rep.write unless rep.raw_path.nil?
201
-
202
- # Stop
203
- Nanoc3::NotificationCenter.post(:visit_ended, rep.item)
204
- Nanoc3::NotificationCenter.post(:compilation_ended, rep)
205
- end
206
-
207
- # Returns the dependency tracker for this site.
208
- def dependency_tracker
209
- @dependency_tracker ||= Nanoc3::DependencyTracker.new(@site.items)
210
- end
211
-
212
- # Marks the necessary items as outdated.
213
- def mark_outdated_items(reps, force)
214
- if force
215
- reps.each { |r| r.force_outdated = true }
216
- else
217
- dependency_tracker.mark_outdated_items
218
- end
219
- end
220
-
221
- # Clears the list of dependencies for items that will be recompiled.
222
- def forget_dependencies_if_outdated(items)
223
- items.each do |i|
224
- if i.outdated? || i.dependencies_outdated?
225
- dependency_tracker.forget_dependencies_for(i)
226
- end
227
- end
228
- end
229
-
230
- def remember_old_dependencies
231
- @old_dependencies = dependency_tracker.instance_eval { @graph }.dup
232
- end
233
-
234
- # Prints the dependency graph.
235
- def print_dependency_graph
236
- graph = dependency_tracker.instance_eval { @graph }
237
- puts "DEPENDENCY GRAPH:"
238
- graph.each_pair do |key, values|
239
- puts "#{key.inspect} depends on:"
240
- values.each do |value|
241
- puts " #{value.inspect}"
242
- end
243
- puts " (nothing!)" if values.empty?
244
- puts
245
- end
246
- end
247
-
248
- end
249
-
250
- end
@@ -1,104 +0,0 @@
1
- # encoding: utf-8
2
-
3
- module Nanoc3::DataSources
4
-
5
- # Nanoc3::DataSources::LastFM provides data about recently played tracks
6
- # from from a single Last.fm user as items (Nanoc3::Item instances).
7
- #
8
- # The configuration must have a "username" attribute containing the username
9
- # of the account from which to fetch the data, and an "api_key" attribute
10
- # containing the API key (which can be obtained from the Last.fm site).
11
- #
12
- # The items returned by this data source will be mounted at {root}/{id},
13
- # where +id+ is a sequence number that is not necessarily unique for this
14
- # bookmark (because delicious.com unfortunately does not provide unique IDs
15
- # for each track).
16
- #
17
- # The items returned by this data source will have the following attributes:
18
- #
19
- # +:name+:: The name of the track.
20
- #
21
- # +played_at+:: The timestamp when the track was played (a Time instance).
22
- #
23
- # +url+:: The Last.fm URL corresponding to the track (a String instance).
24
- #
25
- # +artist+:: A hash containing information about the track's artist.
26
- #
27
- # The +artist+ hash consists of the following keys:
28
- #
29
- # +name+:: The name of the artist.
30
- #
31
- # +url+:: The Last.fm URL corresponding to the artist (a String instance).
32
- class LastFM < Nanoc3::DataSource
33
-
34
- def items
35
- @items ||= begin
36
- require 'json'
37
- require 'time'
38
- require 'uri'
39
- require 'enumerator'
40
-
41
- # Check configuration
42
- if self.config[:username].nil?
43
- raise RuntimeError, "LastFM data source requires a username in the configuration"
44
- end
45
- if self.config[:api_key].nil?
46
- raise RuntimeError, "LastFM data source requires an API key in the configuration"
47
- end
48
-
49
- # Get data
50
- @http_client ||= Nanoc3::Extra::CHiCk::Client.new
51
- status, headers, data = *@http_client.get(
52
- 'http://ws.audioscrobbler.com/2.0/' +
53
- '?method=user.getRecentTracks' +
54
- '&format=json' +
55
- '&user=' + URI.escape(self.config[:username]) +
56
- '&api_key=' + URI.escape(self.config[:api_key])
57
- )
58
-
59
- # Parse as JSON
60
- parsed_data = JSON.parse(data)
61
- raw_items = parsed_data['recenttracks']['track']
62
-
63
- # Convert to items
64
- raw_items.enum_with_index.map do |raw_item, i|
65
- # Get artist data
66
- artist_status, artist_headers, artist_data = *@http_client.get(
67
- 'http://ws.audioscrobbler.com/2.0/' +
68
- '?method=artist.getInfo' +
69
- '&format=json' +
70
- (
71
- raw_item['artist']['mbid'].empty? ?
72
- '&artist=' + URI.escape(raw_item['artist']['#text']) :
73
- '&mbid=' + URI.escape(raw_item['artist']['mbid'])
74
- ) +
75
- '&api_key=' + URI.escape(self.config[:api_key])
76
- )
77
-
78
- # Parse as JSON
79
- parsed_artist_data = JSON.parse(artist_data)
80
- raw_artist_info = parsed_artist_data['artist']
81
-
82
- # Build data
83
- content = ''
84
- attributes = {
85
- :name => raw_item['name'],
86
- :artist => {
87
- :name => raw_artist_info['name'],
88
- :url => raw_artist_info['url']
89
- },
90
- :url => raw_item['url'],
91
- :played_at => Time.parse(raw_item['date']['#text'])
92
- }
93
- identifier = "/#{i}/"
94
- mtime = nil
95
-
96
- # Build item
97
- Nanoc3::Item.new(content, attributes, identifier, mtime)
98
- end
99
- end
100
- end
101
-
102
- end
103
-
104
- end