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 +4 -0
- data/lib/nanoc3/base/compiler.rb +1 -1
- data/lib/nanoc3/base/ordered_hash.rb +200 -0
- data/lib/nanoc3/base.rb +1 -0
- data/lib/nanoc3.rb +1 -1
- metadata +3 -4
- data/lib/nanoc3/base/compiler.rb.orig +0 -250
- data/lib/nanoc3/data_sources/last_fm.rb.orig +0 -104
data/NEWS.rdoc
CHANGED
data/lib/nanoc3/base/compiler.rb
CHANGED
@@ -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
data/lib/nanoc3.rb
CHANGED
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.
|
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-
|
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
|