nanoc3 3.0.6 → 3.0.7
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/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
|