phish_dot_net_client 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +10 -0
- data/.rspec +1 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +32 -0
- data/README.md +70 -0
- data/Rakefile +55 -0
- data/lib/phish_dot_net_client.rb +142 -0
- data/lib/phish_dot_net_client/set.rb +17 -0
- data/lib/phish_dot_net_client/setlist.rb +182 -0
- data/lib/phish_dot_net_client/song.rb +47 -0
- data/lib/phish_dot_net_client/song_transition.rb +18 -0
- data/lib/phish_dot_net_client/version.rb +3 -0
- data/phish_dot_net_client.gemspec +31 -0
- data/spec/phish_dot_net_client/set_spec.rb +12 -0
- data/spec/phish_dot_net_client/setlist_spec.rb +115 -0
- data/spec/phish_dot_net_client/song_spec.rb +25 -0
- data/spec/phish_dot_net_client/song_transition_spec.rb +27 -0
- data/spec/phish_dot_net_client_spec.rb +98 -0
- data/spec/spec_helper.rb +7 -0
- metadata +177 -0
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
GEM
|
2
|
+
remote: https://rubygems.org/
|
3
|
+
specs:
|
4
|
+
diff-lcs (1.2.4)
|
5
|
+
json (1.8.0)
|
6
|
+
mime-types (1.25)
|
7
|
+
mini_portile (0.5.1)
|
8
|
+
nokogiri (1.6.0)
|
9
|
+
mini_portile (~> 0.5.0)
|
10
|
+
rake (10.1.0)
|
11
|
+
rest-client (1.6.7)
|
12
|
+
mime-types (>= 1.16)
|
13
|
+
rspec (2.14.1)
|
14
|
+
rspec-core (~> 2.14.0)
|
15
|
+
rspec-expectations (~> 2.14.0)
|
16
|
+
rspec-mocks (~> 2.14.0)
|
17
|
+
rspec-core (2.14.5)
|
18
|
+
rspec-expectations (2.14.3)
|
19
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
20
|
+
rspec-mocks (2.14.3)
|
21
|
+
yard (0.8.7.2)
|
22
|
+
|
23
|
+
PLATFORMS
|
24
|
+
ruby
|
25
|
+
|
26
|
+
DEPENDENCIES
|
27
|
+
json (~> 1.8.0)
|
28
|
+
nokogiri (~> 1.6.0)
|
29
|
+
rake
|
30
|
+
rest-client (~> 1.6.7)
|
31
|
+
rspec
|
32
|
+
yard
|
data/README.md
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
# Phish.net API Client
|
2
|
+
|
3
|
+
A Phish.net API client, with support for parsing the 'setlistdata' field. Inspired by the other Ruby Phish.net API [client](http://api.phish.net/wrappers/ruby.php) written by [Stephen Blackstone](http://phish.net/user/steveb3210).
|
4
|
+
|
5
|
+
[rdoc](http://rubydoc.info/github/alexebird/phish_dot_net_client/master/frames/file/README.md)
|
6
|
+
|
7
|
+
|
8
|
+
## Install
|
9
|
+
|
10
|
+
Gemfile:
|
11
|
+
|
12
|
+
gem 'phish_dot_net_client'
|
13
|
+
|
14
|
+
Rubygems:
|
15
|
+
|
16
|
+
gem install phish_dot_net_client
|
17
|
+
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
# Setup frequently used parameters
|
22
|
+
PhishDotNetClient.apikey = 'private-api-key'
|
23
|
+
PhishDotNetClient.authorize 'fluffhead', 'passwurd'
|
24
|
+
PhishDotNetClient.clear_auth # clears the stored apikey, username, and authkey
|
25
|
+
|
26
|
+
# Call API methods by replacing '.' with '_' in the method name
|
27
|
+
PhishDotNetClient.pnet_shows_setlists_latest
|
28
|
+
|
29
|
+
# The 'pnet_' prefix is optional
|
30
|
+
PhishDotNetClient.shows_setlists_latest
|
31
|
+
|
32
|
+
# Pass arguments to the API call with a hash
|
33
|
+
PhishDotNetClient.shows_setlists_get :showdate => '2013-07-31'
|
34
|
+
PhishDotNetClient.shows_query :month => '7', :country => 'USA', :state => 'CA'
|
35
|
+
|
36
|
+
# Arguments are not checked for validity before being passed
|
37
|
+
PhishDotNetClient.shows_setlists_get :fluff => 'head' # returns {"success" => 0,"reason" => "General API Error"}
|
38
|
+
|
39
|
+
# All methods return JSON parsed into Ruby Hashes/Arrays
|
40
|
+
tweez = PhishDotNetClient.shows_setlists_get :showdate => '2013-07-31'
|
41
|
+
# => [
|
42
|
+
# {
|
43
|
+
# "showdate" => "2013-07-31",
|
44
|
+
# "city" => "Stateline",
|
45
|
+
# "state" => "NV",
|
46
|
+
# "setlistdata" => #<PhishDotNetClient::Setlist ...>
|
47
|
+
|
48
|
+
See [Phish.net API docs](http://api.phish.net/docu/) for available API methods.
|
49
|
+
|
50
|
+
|
51
|
+
### setlistdata Parsing
|
52
|
+
|
53
|
+
JSON objects that have a "setlistdata" will have that field parsed and replaced with
|
54
|
+
a [Setlist](http://rubydoc.info/github/alexebird/phish_dot_net_client/master/PhishDotNetClient/Setlist) object. See rdoc for how to use the parsed setlist.
|
55
|
+
|
56
|
+
|
57
|
+
## Known Issues
|
58
|
+
|
59
|
+
- Song titles with a ',' character don't get parsed correctly due to the ',' being interpreted as a boundary between song titles. See lib/phish_dot_net_client/setlist.rb#133 for the regex and parsing code.
|
60
|
+
|
61
|
+
|
62
|
+
|
63
|
+
## Testing
|
64
|
+
|
65
|
+
To run specs:
|
66
|
+
|
67
|
+
bundle exec rake spec
|
68
|
+
|
69
|
+
Pull requests are welcome!
|
70
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'rspec/core/rake_task'
|
2
|
+
|
3
|
+
|
4
|
+
desc "Parse the documentation from Phish.net to get the current method calls"
|
5
|
+
task :parse_method_docs do
|
6
|
+
# require 'nokogiri'
|
7
|
+
require 'open-uri'
|
8
|
+
|
9
|
+
doc_url = "http://api.phish.net/docu/navigation.php"
|
10
|
+
methods = {}
|
11
|
+
longest_method_name = 0
|
12
|
+
doc = Nokogiri::HTML(open(doc_url))
|
13
|
+
|
14
|
+
doc.css("#static-list li.sub span").each_slice(2) do |method,scope|
|
15
|
+
method = method.content
|
16
|
+
if method.size > longest_method_name
|
17
|
+
longest_method_name = method.size
|
18
|
+
end
|
19
|
+
methods[method] = { :scope => scope.content }
|
20
|
+
end
|
21
|
+
|
22
|
+
puts "{"
|
23
|
+
methods.keys.sort.each_with_index do |method,i|
|
24
|
+
comma = (i+1) == methods.size ? "" : ","
|
25
|
+
puts %[ %-#{longest_method_name + 3}s => { :scope => %s }%s] %
|
26
|
+
['"' + method.to_s + '"', '"' + methods[method][:scope] + '"', comma]
|
27
|
+
end
|
28
|
+
puts "}"
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
desc "Default: run specs. Set COVERAGE=true before any spec-related task to generate code coverage."
|
33
|
+
task :default => :spec
|
34
|
+
|
35
|
+
desc "Run all specs"
|
36
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
37
|
+
t.pattern = "./spec/**/*_spec.rb"
|
38
|
+
# t.verbose = false
|
39
|
+
end
|
40
|
+
|
41
|
+
desc "Run specs in spec/units"
|
42
|
+
RSpec::Core::RakeTask.new('spec:units') do |t|
|
43
|
+
t.pattern = "./spec/units/**/*_spec.rb"
|
44
|
+
end
|
45
|
+
|
46
|
+
desc "Run specs in spec/integration"
|
47
|
+
RSpec::Core::RakeTask.new('spec:integration') do |t|
|
48
|
+
t.pattern = "./spec/integration/**/*_spec.rb"
|
49
|
+
end
|
50
|
+
|
51
|
+
desc "Generate documentation"
|
52
|
+
task :doc do
|
53
|
+
require 'yard'
|
54
|
+
sh "yard"
|
55
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'restclient'
|
4
|
+
require 'json'
|
5
|
+
require 'nokogiri'
|
6
|
+
|
7
|
+
require 'phish_dot_net_client/set'
|
8
|
+
require 'phish_dot_net_client/setlist'
|
9
|
+
require 'phish_dot_net_client/song'
|
10
|
+
require 'phish_dot_net_client/song_transition'
|
11
|
+
require 'phish_dot_net_client/version'
|
12
|
+
|
13
|
+
# @todo write doc
|
14
|
+
module PhishDotNetClient
|
15
|
+
extend self
|
16
|
+
|
17
|
+
# The possible API methods. Generated from +rake parse_method_docs+.
|
18
|
+
# API_METHODS =
|
19
|
+
# {
|
20
|
+
# "pnet.api.authkey.get" => { :scope => "protected" },
|
21
|
+
# "pnet.api.authorize" => { :scope => "protected" },
|
22
|
+
# "pnet.api.authorized.check" => { :scope => "protected" },
|
23
|
+
# "pnet.api.isAuthorized" => { :scope => "protected" },
|
24
|
+
# "pnet.artists.get" => { :scope => "public" },
|
25
|
+
# "pnet.blog.get" => { :scope => "public" },
|
26
|
+
# "pnet.blog.item.get" => { :scope => "public" },
|
27
|
+
# "pnet.collections.get" => { :scope => "protected" },
|
28
|
+
# "pnet.collections.query" => { :scope => "protected" },
|
29
|
+
# "pnet.forum.canpost" => { :scope => "protected" },
|
30
|
+
# "pnet.forum.get" => { :scope => "public" },
|
31
|
+
# "pnet.forum.thread.get" => { :scope => "protected" },
|
32
|
+
# "pnet.forum.thread.new" => { :scope => "protected" },
|
33
|
+
# "pnet.forum.thread.respond" => { :scope => "protected" },
|
34
|
+
# "pnet.news.comments.get" => { :scope => "public" },
|
35
|
+
# "pnet.news.get" => { :scope => "public" },
|
36
|
+
# "pnet.reviews.query" => { :scope => "protected" },
|
37
|
+
# "pnet.reviews.recent" => { :scope => "public" },
|
38
|
+
# "pnet.shows.links.get" => { :scope => "protected" },
|
39
|
+
# "pnet.shows.query" => { :scope => "protected" },
|
40
|
+
# "pnet.shows.setlists.get" => { :scope => "protected" },
|
41
|
+
# "pnet.shows.setlists.latest" => { :scope => "public" },
|
42
|
+
# "pnet.shows.setlists.random" => { :scope => "public" },
|
43
|
+
# "pnet.shows.setlists.recent" => { :scope => "public" },
|
44
|
+
# "pnet.shows.setlists.tiph" => { :scope => "public" },
|
45
|
+
# "pnet.shows.upcoming" => { :scope => "public" },
|
46
|
+
# "pnet.user.get" => { :scope => "protected" },
|
47
|
+
# "pnet.user.myshows.add" => { :scope => "protected" },
|
48
|
+
# "pnet.user.myshows.get" => { :scope => "protected" },
|
49
|
+
# "pnet.user.myshows.remove" => { :scope => "protected" },
|
50
|
+
# "pnet.user.register" => { :scope => "protected" },
|
51
|
+
# "pnet.user.shows.rate" => { :scope => "protected" },
|
52
|
+
# "pnet.user.uid.get" => { :scope => "protected" },
|
53
|
+
# "pnet.user.username.check" => { :scope => "protected" }
|
54
|
+
# }
|
55
|
+
|
56
|
+
# The base URL for API calls
|
57
|
+
BASE_URL = "https://api.phish.net/api.js"
|
58
|
+
|
59
|
+
# "https://api.phish.net/api.js?api=2.0&method=pnet.shows.query&format=json&apikey=920FF765772E442F3E22&year=2011"
|
60
|
+
|
61
|
+
@@default_params = { api: "2.0",
|
62
|
+
format: "json" }
|
63
|
+
|
64
|
+
def apikey=(private_api_key)
|
65
|
+
@@default_params.merge!(:apikey => private_api_key)
|
66
|
+
end
|
67
|
+
|
68
|
+
def authorize(username=nil, passwd=nil)
|
69
|
+
resp = call_api_method("pnet.api.authorize", :username => username, :passwd => passwd)
|
70
|
+
|
71
|
+
if resp['success'] == 1 && resp.has_key?('authkey')
|
72
|
+
@@default_params.merge!(:username => username, :authkey => resp['authkey'])
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def clear_auth
|
77
|
+
[:apikey, :username, :authkey].each { |key| @@default_params.delete(key) }
|
78
|
+
end
|
79
|
+
|
80
|
+
# @param api_method [String] the method to call
|
81
|
+
# @param params [Hash] the url parameters for the api call
|
82
|
+
#
|
83
|
+
# @return [Hash, Array] the parsed JSON of API response
|
84
|
+
# @api private
|
85
|
+
def call_api_method(api_method, params={})
|
86
|
+
# method_data = API_METHODS[api_method]
|
87
|
+
# ensure_api_key if method_data[:scope] == "protected"
|
88
|
+
|
89
|
+
params.merge!(:method => api_method)
|
90
|
+
response = RestClient.get BASE_URL, { :params => @@default_params.merge(params) }
|
91
|
+
|
92
|
+
if response.code < 200 || response.code > 299
|
93
|
+
raise "non 2xx reponse: status=#{response.code}"
|
94
|
+
end
|
95
|
+
|
96
|
+
parsed = JSON.parse(response)
|
97
|
+
|
98
|
+
if parsed.is_a?(Array)
|
99
|
+
parsed.each do |obj|
|
100
|
+
obj["setlistdata"] = Setlist.new(obj["setlistdata"]) if obj.has_key?("setlistdata")
|
101
|
+
end
|
102
|
+
elsif parsed.is_a?(Hash)
|
103
|
+
parsed["setlistdata"] = Setlist.new(parsed["setlistdata"]) if parsed.has_key?("setlistdata")
|
104
|
+
end
|
105
|
+
|
106
|
+
return parsed
|
107
|
+
end
|
108
|
+
|
109
|
+
# @api private
|
110
|
+
def method_missing(name, *args)
|
111
|
+
api_method = get_api_method(name)
|
112
|
+
|
113
|
+
if api_method
|
114
|
+
call_api_method(api_method, *args)
|
115
|
+
else
|
116
|
+
super(name, *args)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# private
|
121
|
+
|
122
|
+
# @api private
|
123
|
+
def get_api_method(rb_method_name)
|
124
|
+
api_method_name = rb_method_name.to_s.gsub("_", ".")
|
125
|
+
|
126
|
+
unless api_method_name.match(/\Apnet\./)
|
127
|
+
api_method_name = 'pnet.' + api_method_name
|
128
|
+
end
|
129
|
+
|
130
|
+
return api_method_name
|
131
|
+
|
132
|
+
# if API_METHODS.has_key?(api_method_name)
|
133
|
+
# return api_method_name
|
134
|
+
# else
|
135
|
+
# return nil
|
136
|
+
# end
|
137
|
+
end
|
138
|
+
|
139
|
+
# def ensure_api_key
|
140
|
+
# raise "api key is required" if @@default_params[:apikey].nil?
|
141
|
+
# end
|
142
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
module PhishDotNetClient
|
5
|
+
class Set
|
6
|
+
|
7
|
+
attr_reader :position
|
8
|
+
attr_reader :name
|
9
|
+
attr_reader :songs
|
10
|
+
|
11
|
+
def initialize(attrs={})
|
12
|
+
@songs = []
|
13
|
+
@position = attrs[:position]
|
14
|
+
@name = attrs[:name]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,182 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
module PhishDotNetClient
|
5
|
+
class Setlist
|
6
|
+
|
7
|
+
attr_reader :sets
|
8
|
+
attr_reader :footnotes
|
9
|
+
|
10
|
+
def initialize(setlistdata)
|
11
|
+
@sets, @footnotes = self.class.parse_setlistdata(setlistdata)
|
12
|
+
end
|
13
|
+
|
14
|
+
def songs
|
15
|
+
return @sets.map(&:songs).reduce([]){|memo,songs| memo += songs }
|
16
|
+
end
|
17
|
+
|
18
|
+
# @api private
|
19
|
+
def self.parse_setlistdata(setlistdata)
|
20
|
+
doc = Nokogiri::HTML(setlistdata)
|
21
|
+
|
22
|
+
footnotes = {}
|
23
|
+
footnotes_text = doc.css('.pnetfootnotes').first
|
24
|
+
if footnotes_text
|
25
|
+
footnotes_text = footnotes_text.content.split(/(?=\[\d+\])/)
|
26
|
+
footnotes_text.each do |note|
|
27
|
+
note.sub!(/\[(?<num>\d+)\]/, '').strip!
|
28
|
+
num = $~[:num]
|
29
|
+
footnotes[num.to_i] = { text: note }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
sets = []
|
34
|
+
all_songs = []
|
35
|
+
transitions_tokens = []
|
36
|
+
slug_instances = {}
|
37
|
+
|
38
|
+
setnodes = doc.css(".pnetset")
|
39
|
+
position_in_show = 1
|
40
|
+
|
41
|
+
setnodes.each_with_index do |n,set_index|
|
42
|
+
setname = n.css(".pnetsetlabel").first.content
|
43
|
+
set = Set.new(:name => setname.sub!(/:\z/, ''), :position => set_index + 1)
|
44
|
+
songs_doc = n.css("a")
|
45
|
+
songs_doc.each_with_index do |song,song_index|
|
46
|
+
position_in_set = song_index + 1
|
47
|
+
title = song.content
|
48
|
+
url = song.attr('href')
|
49
|
+
slug = URI.parse(url).path
|
50
|
+
slug.sub!(/\A\/song\//, '')
|
51
|
+
|
52
|
+
slug_instances[slug] ||= 0
|
53
|
+
slug_instances[slug] += 1
|
54
|
+
|
55
|
+
song = Song.new(:title => title,
|
56
|
+
:url => url,
|
57
|
+
:slug => slug,
|
58
|
+
:instance => slug_instances[slug],
|
59
|
+
:position_in_set => position_in_set,
|
60
|
+
:position_in_show => position_in_show)
|
61
|
+
set.songs << song
|
62
|
+
all_songs << song
|
63
|
+
position_in_show += 1
|
64
|
+
end
|
65
|
+
|
66
|
+
transitions_text = n.content
|
67
|
+
transitions_tokens += tokenize_transitions_text(transitions_text, transitions_tokens)
|
68
|
+
|
69
|
+
sets << set
|
70
|
+
end
|
71
|
+
|
72
|
+
augment_songs(transitions_tokens, all_songs, footnotes)
|
73
|
+
|
74
|
+
return sets, footnotes
|
75
|
+
end
|
76
|
+
|
77
|
+
# @api private
|
78
|
+
def self.augment_songs(tokens, songs, footnotes)
|
79
|
+
songs = Array.new(songs) # make a copy
|
80
|
+
|
81
|
+
get_song_by_title = lambda do |title,instance|
|
82
|
+
songs.each do |song|
|
83
|
+
if song.title == title && song.instance == instance
|
84
|
+
return song
|
85
|
+
end
|
86
|
+
end
|
87
|
+
raise "no song with that title ('#{title}')"
|
88
|
+
end
|
89
|
+
|
90
|
+
tokens.each_index do |i|
|
91
|
+
prev_i = i >= 1 ? i - 1 : nil
|
92
|
+
next_i = i < tokens.size - 1 ? i + 1 : nil
|
93
|
+
|
94
|
+
token = tokens[i]
|
95
|
+
case token[:type]
|
96
|
+
when :note_ref
|
97
|
+
prev_token = tokens[prev_i]
|
98
|
+
song = get_song_by_title.call(prev_token[:title], prev_token[:instance])
|
99
|
+
num = token[:number]
|
100
|
+
footnotes[num][:song] = song
|
101
|
+
song.footnotes << num
|
102
|
+
when :transition
|
103
|
+
prev_token = tokens[prev_i]
|
104
|
+
if prev_token[:type] == :note_ref
|
105
|
+
prev_token = tokens[prev_i-1]
|
106
|
+
end
|
107
|
+
next_token = tokens[next_i]
|
108
|
+
from_song = get_song_by_title.call(prev_token[:title], prev_token[:instance])
|
109
|
+
to_song = get_song_by_title.call(next_token[:title], next_token[:instance])
|
110
|
+
PhishDotNetClient::SongTransition.new(token[:transition_type], from_song, to_song)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# @api private
|
116
|
+
def self.tokenize_transitions_text(transitions_text, existing_tokens=[])
|
117
|
+
tokens = []
|
118
|
+
|
119
|
+
# Account for multiple songs with the same title in a song list
|
120
|
+
song_title_counts = {}
|
121
|
+
existing_tokens.each do |token|
|
122
|
+
if token[:type] == :song
|
123
|
+
title = token[:title]
|
124
|
+
song_title_counts[title] ||= 0
|
125
|
+
song_title_counts[title] += 1
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
tokens.push(/\A[a-z0-9\s]+:/i => lambda do |match|
|
130
|
+
return { type: :set_name, name: match.to_s.strip.sub(':', '') }
|
131
|
+
end)
|
132
|
+
|
133
|
+
tokens.push(/\A[äa-z0-9\-?\.!:\/'\s\(\)]+[a-z0-9\)?!']/i => lambda do |match|
|
134
|
+
title = match.to_s.strip
|
135
|
+
song_title_counts[title] ||= 0
|
136
|
+
song_title_counts[title] += 1
|
137
|
+
return { type: :song, title: title, instance: song_title_counts[title] }
|
138
|
+
end)
|
139
|
+
|
140
|
+
tokens.push(/\A\[(?<num>\d+)\]/ => lambda do |match|
|
141
|
+
return { type: :note_ref, number: match[:num].to_i }
|
142
|
+
end)
|
143
|
+
|
144
|
+
tokens.push(/\A,/ => lambda do |match|
|
145
|
+
return { type: :transition, transition_type: :no_segue }
|
146
|
+
end)
|
147
|
+
|
148
|
+
tokens.push(/\A>/ => lambda do |match|
|
149
|
+
return { type: :transition, transition_type: :segue }
|
150
|
+
end)
|
151
|
+
|
152
|
+
tokens.push(/\A->/ => lambda do |match|
|
153
|
+
return { type: :transition, transition_type: :deep_segue }
|
154
|
+
end)
|
155
|
+
|
156
|
+
tokens.push(/\A\s+/ => lambda do |match|
|
157
|
+
return nil
|
158
|
+
end)
|
159
|
+
|
160
|
+
parsed_set = []
|
161
|
+
|
162
|
+
until transitions_text.empty?
|
163
|
+
matched = false
|
164
|
+
|
165
|
+
tokens.each do |matcher|
|
166
|
+
token_regex, processor = matcher.keys.first, matcher.values.first
|
167
|
+
|
168
|
+
if transitions_text.slice!(token_regex)
|
169
|
+
matched = true
|
170
|
+
token = processor.call($~)
|
171
|
+
parsed_set.push(token) if token
|
172
|
+
break
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
raise "could not parse: '#{transitions_text}'" unless matched
|
177
|
+
end
|
178
|
+
|
179
|
+
return parsed_set
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
module PhishDotNetClient
|
5
|
+
class Song
|
6
|
+
attr_reader :title
|
7
|
+
attr_reader :url
|
8
|
+
attr_reader :slug
|
9
|
+
attr_reader :instance
|
10
|
+
attr_reader :position_in_set
|
11
|
+
attr_reader :position_in_show
|
12
|
+
attr_reader :footnotes
|
13
|
+
attr_accessor :pre_transition
|
14
|
+
attr_accessor :post_transition
|
15
|
+
|
16
|
+
def initialize(attrs={})
|
17
|
+
@title = attrs[:title]
|
18
|
+
@url = attrs[:url]
|
19
|
+
@slug = attrs[:slug]
|
20
|
+
@instance = attrs[:instance]
|
21
|
+
@position_in_set = attrs[:position_in_set]
|
22
|
+
@position_in_show = attrs[:position_in_show]
|
23
|
+
@footnotes = []
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_s
|
27
|
+
s = StringIO.new
|
28
|
+
|
29
|
+
if pre_transition
|
30
|
+
s.print "#{pre_transition.from_song.title}(#{pre_transition.from_song.instance})..."
|
31
|
+
else
|
32
|
+
s.print "x..."
|
33
|
+
end
|
34
|
+
|
35
|
+
s.print "#{@title}(#{@instance})"
|
36
|
+
|
37
|
+
if post_transition
|
38
|
+
s.puts "...#{post_transition.to_song.title}(#{post_transition.to_song.instance})"
|
39
|
+
else
|
40
|
+
s.puts "...x"
|
41
|
+
end
|
42
|
+
|
43
|
+
s.puts
|
44
|
+
return s.string
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
module PhishDotNetClient
|
5
|
+
class SongTransition
|
6
|
+
|
7
|
+
attr_reader :type
|
8
|
+
attr_reader :from_song
|
9
|
+
attr_reader :to_song
|
10
|
+
|
11
|
+
def initialize(type, from_song, to_song)
|
12
|
+
@type = type
|
13
|
+
@from_song = from_song
|
14
|
+
@to_song = to_song
|
15
|
+
@from_song.post_transition = @to_song.pre_transition = self
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
lib = File.expand_path('../lib/', __FILE__)
|
3
|
+
$:.unshift lib unless $:.include?(lib)
|
4
|
+
require 'phish_dot_net_client/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'phish_dot_net_client'
|
8
|
+
spec.version = PhishDotNetClient::VERSION
|
9
|
+
spec.date = Date.today.to_s
|
10
|
+
spec.licenses = ['MIT']
|
11
|
+
spec.authors = ["Alexander Bird"]
|
12
|
+
spec.email = ["alexebird@gmail.com"]
|
13
|
+
spec.homepage = "https://github.com/alexebird/phish_dot_net_client"
|
14
|
+
spec.summary = %q{Phish.net API client with setlist parsing}
|
15
|
+
spec.description = %q{Calls any Phish.net API method. Supports authentication. Parses 'setlistdata' fields.}
|
16
|
+
|
17
|
+
spec.required_ruby_version = '>= 1.9.1'
|
18
|
+
spec.required_rubygems_version = '>= 1.3.6'
|
19
|
+
|
20
|
+
spec.add_dependency 'rest-client', '~> 1.6.7'
|
21
|
+
spec.add_dependency 'json', '~> 1.8.0'
|
22
|
+
spec.add_dependency 'nokogiri', '~> 1.6.0'
|
23
|
+
spec.add_development_dependency 'rake'
|
24
|
+
spec.add_development_dependency 'rspec', '~> 2.14'
|
25
|
+
spec.add_development_dependency 'yard', '~> 0.8'
|
26
|
+
|
27
|
+
spec.files = `git ls-files`.split($/)
|
28
|
+
spec.test_files = spec.files.grep(%r{^spec/})
|
29
|
+
|
30
|
+
spec.require_paths = ["lib"]
|
31
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe PhishDotNetClient::Set do
|
4
|
+
let(:set) { PhishDotNetClient::Set }
|
5
|
+
let(:set_instance) { set.new }
|
6
|
+
|
7
|
+
%w[position name songs].each do |attr|
|
8
|
+
it "has a #{attr} attribute" do
|
9
|
+
expect(set_instance.respond_to? attr.to_sym).to be_true
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe PhishDotNetClient::Setlist do
|
4
|
+
# Set 1: Runaway Jim[1], Foam, Punch You In the Eye[2] > Bathtub Gin, Cavern
|
5
|
+
# Set 2: Also Sprach Zarathustra -> Cavern
|
6
|
+
# Encore: Sleeping Monkey
|
7
|
+
let(:setlistdata) { "<p class='pnetset pnetset1'><span class='pnetsetlabel'>Set 1:<\/span> <a href=\"http:\/\/phish.net\/song\/runaway-jim\">Runaway Jim<\/a><sup>[1]<\/sup>, <a href=\"http:\/\/phish.net\/song\/foam\">Foam<\/a>, <a href=\"http:\/\/phish.net\/song\/punch-you-in-the-eye\">Punch You In the Eye<\/a><sup>[2]<\/sup> > <a href=\"http:\/\/phish.net\/song\/bathtub-gin\">Bathtub Gin<\/a>, <a href=\"http:\/\/phish.net\/song\/cavern\">Cavern<\/a><\/p><p class='pnetset pnetset2'><span class='pnetsetlabel'>Set 2:<\/span> <a href=\"http:\/\/phish.net\/song\/also-sprach-zarathustra\">Also Sprach Zarathustra<\/a> -> <a href=\"http:\/\/phish.net\/song\/cavern\">Cavern<\/a> <p class='pnetset pnetsete'><span class='pnetsetlabel'>Encore:<\/span> <a href=\"http:\/\/phish.net\/song\/sleeping-monkey\">Sleeping Monkey<\/a> <p class='pnetfootnotes'>[1] O.J. reference.<br \/>[2] \"Anti-drum solo.\"<br \/><\/p>" }
|
8
|
+
let(:setlist) { PhishDotNetClient::Setlist }
|
9
|
+
let(:setlist_instance) { setlist.new(setlistdata) }
|
10
|
+
let(:setlist_dumb_instance) { setlist.new('') }
|
11
|
+
|
12
|
+
it "has a sets attribute" do
|
13
|
+
expect(setlist_dumb_instance.respond_to? :sets).to be_true
|
14
|
+
end
|
15
|
+
|
16
|
+
it "has a footnotes attribute" do
|
17
|
+
expect(setlist_dumb_instance.respond_to? :footnotes).to be_true
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "#initialize" do
|
21
|
+
it "sets the sets" do
|
22
|
+
expect(setlist_instance.sets).to be_instance_of(Array)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe ".parse_setlistdata" do
|
27
|
+
let(:parsed_setlist) { setlist.parse_setlistdata(setlistdata) }
|
28
|
+
|
29
|
+
it "returns Array(sets), Hash(footnotes)" do
|
30
|
+
expect(parsed_setlist).to be_instance_of(Array)
|
31
|
+
sets = parsed_setlist.first
|
32
|
+
expect(sets).to be_instance_of(Array)
|
33
|
+
notes = parsed_setlist.last
|
34
|
+
expect(notes).to eq({ 1 => { text: %[O.J. reference.], song: sets[0].songs[0] },
|
35
|
+
2 => { text: %["Anti-drum solo."], song: sets[0].songs[2] } })
|
36
|
+
|
37
|
+
set1 = sets[0]
|
38
|
+
expect(set1).to be_instance_of(PhishDotNetClient::Set)
|
39
|
+
set1.songs.each do |song|
|
40
|
+
expect(song).to be_instance_of(PhishDotNetClient::Song)
|
41
|
+
end
|
42
|
+
|
43
|
+
set2 = sets[1]
|
44
|
+
expect(set2).to be_instance_of(PhishDotNetClient::Set)
|
45
|
+
set2.songs.each do |song|
|
46
|
+
expect(song).to be_instance_of(PhishDotNetClient::Song)
|
47
|
+
end
|
48
|
+
|
49
|
+
encore = sets[2]
|
50
|
+
expect(encore).to be_instance_of(PhishDotNetClient::Set)
|
51
|
+
encore.songs.each do |song|
|
52
|
+
expect(song).to be_instance_of(PhishDotNetClient::Song)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe "#songs" do
|
58
|
+
it "returns all songs" do
|
59
|
+
expect(setlist_instance.songs.size).to eq(8)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe ".augment_songs" do
|
64
|
+
let(:song1) { PhishDotNetClient::Song.new(:title => "Song 1", :instance => 1) }
|
65
|
+
let(:song2) { PhishDotNetClient::Song.new(:title => "Song 2", :instance => 1) }
|
66
|
+
let(:song1again) { PhishDotNetClient::Song.new(:title => "Song 1", :instance => 2) }
|
67
|
+
let(:transitions_tokens) { setlist.tokenize_transitions_text("Set 1: Song 1[1] > Song 2 -> Song 1") }
|
68
|
+
let(:footnotes) { { 1 => { text: %[O.J. reference.] },
|
69
|
+
2 => { text: %["Anti-drum solo."] } } }
|
70
|
+
|
71
|
+
it "adds transitions to the songs" do
|
72
|
+
setlist.augment_songs(transitions_tokens, [song1, song2, song1again], footnotes)
|
73
|
+
expect(song1.post_transition).to be_instance_of(PhishDotNetClient::SongTransition)
|
74
|
+
expect(song1.post_transition).to eq(song2.pre_transition)
|
75
|
+
expect(song1.post_transition.type).to eq(:segue)
|
76
|
+
end
|
77
|
+
|
78
|
+
it "accounts for songs with the same title" do
|
79
|
+
setlist.augment_songs(transitions_tokens, [song1, song2, song1again], footnotes)
|
80
|
+
expect(song2.post_transition).to eq(song1again.pre_transition)
|
81
|
+
expect(song2.post_transition.type).to eq(:deep_segue)
|
82
|
+
end
|
83
|
+
|
84
|
+
it "adds notes to the songs" do
|
85
|
+
setlist.augment_songs(transitions_tokens, [song1, song2, song1again], footnotes)
|
86
|
+
expect(song1.footnotes).to eq([1])
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe ".tokenize_transitions_text" do
|
91
|
+
let(:setlist_str) { "Set 1: Runaway Jim[1] -> Foam, Punch You In the Eye[2] > Bathtub Gin, Foam" }
|
92
|
+
let(:parsed_text) { setlist.tokenize_transitions_text(setlist_str) }
|
93
|
+
|
94
|
+
it "parses the set's text" do
|
95
|
+
expect(parsed_text).to eq([
|
96
|
+
{ type: :set_name, name: "Set 1" },
|
97
|
+
{ type: :song, title: "Runaway Jim", instance: 1 },
|
98
|
+
{ type: :note_ref, number: 1 },
|
99
|
+
{ type: :transition, transition_type: :deep_segue },
|
100
|
+
{ type: :song, title: "Foam", instance: 1 },
|
101
|
+
{ type: :transition, transition_type: :no_segue },
|
102
|
+
{ type: :song, title: "Punch You In the Eye", instance: 1 },
|
103
|
+
{ type: :note_ref, number: 2 },
|
104
|
+
{ type: :transition, transition_type: :segue },
|
105
|
+
{ type: :song, title: "Bathtub Gin", instance: 1 },
|
106
|
+
{ type: :transition, transition_type: :no_segue },
|
107
|
+
{ type: :song, title: "Foam", instance: 2 }
|
108
|
+
])
|
109
|
+
end
|
110
|
+
|
111
|
+
it "raises an error for unparsable text" do
|
112
|
+
expect{ setlist.tokenize_transitions_text("asdf !@###") }.to raise_error
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe PhishDotNetClient::Song do
|
4
|
+
let(:song) { PhishDotNetClient::Song }
|
5
|
+
let(:song_instance) { song.new }
|
6
|
+
|
7
|
+
%w[title url slug instance position_in_set position_in_show footnotes pre_transition post_transition].each do |attr|
|
8
|
+
it "has a #{attr} attribute" do
|
9
|
+
expect(song_instance.respond_to? attr.to_sym).to be_true
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "#initialize" do
|
14
|
+
it "sets the attributes" do
|
15
|
+
s = song.new(:title => 'title', :url => 'http://url', :slug => "slug", :instance => "inst",
|
16
|
+
:position_in_set => 3, :position_in_show => 5)
|
17
|
+
expect(s.title).to eq('title')
|
18
|
+
expect(s.url).to eq('http://url')
|
19
|
+
expect(s.slug).to eq('slug')
|
20
|
+
expect(s.instance).to eq('inst')
|
21
|
+
expect(s.position_in_set).to eq(3)
|
22
|
+
expect(s.position_in_show).to eq(5)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe PhishDotNetClient::SongTransition do
|
4
|
+
let(:from_song) { PhishDotNetClient::Song.new(:title => "Song 1") }
|
5
|
+
let(:to_song) { PhishDotNetClient::Song.new(:title => "Song 2") }
|
6
|
+
let(:song_trans) { PhishDotNetClient::SongTransition }
|
7
|
+
let!(:song_trans_instance) { PhishDotNetClient::SongTransition.new(:deep_segue, from_song, to_song) }
|
8
|
+
|
9
|
+
%w[type from_song to_song].each do |attr|
|
10
|
+
it "has a #{attr} attribute" do
|
11
|
+
expect(song_trans_instance.respond_to? attr.to_sym).to be_true
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "#initialize" do
|
16
|
+
it "sets the attributes" do
|
17
|
+
expect(song_trans_instance.type).to eq(:deep_segue)
|
18
|
+
expect(song_trans_instance.from_song).to eq(from_song)
|
19
|
+
expect(song_trans_instance.to_song).to eq(to_song)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "sets the songs transitions" do
|
23
|
+
expect(from_song.post_transition).to eq(song_trans_instance)
|
24
|
+
expect(to_song.pre_transition).to eq(song_trans_instance)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe PhishDotNetClient do
|
4
|
+
let(:pnet) { PhishDotNetClient }
|
5
|
+
let(:fake_api_key) { "fake-apikey" }
|
6
|
+
|
7
|
+
def access_default_params(key)
|
8
|
+
return pnet.class_variable_get(:@@default_params)[key]
|
9
|
+
end
|
10
|
+
|
11
|
+
it "pre-sets the api version to '2.0'" do
|
12
|
+
expect(access_default_params(:api)).to eq('2.0')
|
13
|
+
end
|
14
|
+
|
15
|
+
it "pre-sets the response format to json" do
|
16
|
+
expect(access_default_params(:format)).to eq('json')
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "#apikey=" do
|
20
|
+
it "sets the api key" do
|
21
|
+
pnet.apikey = fake_api_key
|
22
|
+
expect(access_default_params(:apikey)).to eq(fake_api_key)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "#authorize" do
|
27
|
+
it "sets the username" do
|
28
|
+
# pnet.authorize('uzer', 'asdfasdf')
|
29
|
+
# expect(access_default_params(:username)).to eq('uzer')
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "#clear_auth" do
|
34
|
+
before(:each) do
|
35
|
+
pnet.clear_auth
|
36
|
+
end
|
37
|
+
|
38
|
+
it "clears the apikey" do
|
39
|
+
expect(access_default_params(:apikey)).to be_nil
|
40
|
+
end
|
41
|
+
|
42
|
+
it "clears the authkey" do
|
43
|
+
expect(access_default_params(:authkey)).to be_nil
|
44
|
+
end
|
45
|
+
|
46
|
+
it "clears the username" do
|
47
|
+
expect(access_default_params(:username)).to be_nil
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "#call_api_method" do
|
52
|
+
# context "when the api_method is protected" do
|
53
|
+
# it "raises an error if no apikey is specified" do
|
54
|
+
# pnet.clear_auth
|
55
|
+
# expect{ pnet.call_api_method("pnet.shows.query") }.to raise_error
|
56
|
+
# end
|
57
|
+
# end
|
58
|
+
|
59
|
+
it "calls the api" do
|
60
|
+
rv = pnet.call_api_method("pnet.shows.setlists.latest")
|
61
|
+
expect(rv[0]["setlistdata"]).to be_instance_of(PhishDotNetClient::Setlist)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe "#method_missing" do
|
66
|
+
context "when the api method is a valid" do
|
67
|
+
it "delegates to call_api_method" do
|
68
|
+
expect{ pnet.method_missing :"pnet_shows_setlists_latest" }.not_to raise_error
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# context "when the method name is not a valid api method" do
|
73
|
+
# it "passes the method_missing call on to super" do
|
74
|
+
# expect{ pnet.method_missing :"asdf_jkl" }.to raise_error(NoMethodError)
|
75
|
+
# end
|
76
|
+
# end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe "#get_api_method" do
|
80
|
+
# it "returns nil when the api_method doesn't exist" do
|
81
|
+
# expect(pnet.get_api_method('doesnt_exist')).to be_nil
|
82
|
+
# end
|
83
|
+
|
84
|
+
it "returns the api method" do
|
85
|
+
expect(pnet.get_api_method("pnet_api_authorize")).to eq("pnet.api.authorize")
|
86
|
+
end
|
87
|
+
|
88
|
+
it "adds the 'pnet.' prefix if it's not there" do
|
89
|
+
expect(pnet.get_api_method("api_authorize")).to eq("pnet.api.authorize")
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# describe "#ensure_api_key" do
|
94
|
+
# it "raises an error if api_key isn't set" do
|
95
|
+
# expect{ pnet.ensure_api_key }.to raise_error
|
96
|
+
# end
|
97
|
+
# end
|
98
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,177 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: phish_dot_net_client
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: 0.0.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Alexander Bird
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2013-10-09 00:00:00 -04:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: rest-client
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ~>
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 1
|
30
|
+
- 6
|
31
|
+
- 7
|
32
|
+
version: 1.6.7
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: json
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
segments:
|
44
|
+
- 1
|
45
|
+
- 8
|
46
|
+
- 0
|
47
|
+
version: 1.8.0
|
48
|
+
type: :runtime
|
49
|
+
version_requirements: *id002
|
50
|
+
- !ruby/object:Gem::Dependency
|
51
|
+
name: nokogiri
|
52
|
+
prerelease: false
|
53
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
54
|
+
none: false
|
55
|
+
requirements:
|
56
|
+
- - ~>
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
segments:
|
59
|
+
- 1
|
60
|
+
- 6
|
61
|
+
- 0
|
62
|
+
version: 1.6.0
|
63
|
+
type: :runtime
|
64
|
+
version_requirements: *id003
|
65
|
+
- !ruby/object:Gem::Dependency
|
66
|
+
name: rake
|
67
|
+
prerelease: false
|
68
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
69
|
+
none: false
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
segments:
|
74
|
+
- 0
|
75
|
+
version: "0"
|
76
|
+
type: :development
|
77
|
+
version_requirements: *id004
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: rspec
|
80
|
+
prerelease: false
|
81
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
82
|
+
none: false
|
83
|
+
requirements:
|
84
|
+
- - ~>
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
segments:
|
87
|
+
- 2
|
88
|
+
- 14
|
89
|
+
version: "2.14"
|
90
|
+
type: :development
|
91
|
+
version_requirements: *id005
|
92
|
+
- !ruby/object:Gem::Dependency
|
93
|
+
name: yard
|
94
|
+
prerelease: false
|
95
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
96
|
+
none: false
|
97
|
+
requirements:
|
98
|
+
- - ~>
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
segments:
|
101
|
+
- 0
|
102
|
+
- 8
|
103
|
+
version: "0.8"
|
104
|
+
type: :development
|
105
|
+
version_requirements: *id006
|
106
|
+
description: Calls any Phish.net API method. Supports authentication. Parses 'setlistdata' fields.
|
107
|
+
email:
|
108
|
+
- alexebird@gmail.com
|
109
|
+
executables: []
|
110
|
+
|
111
|
+
extensions: []
|
112
|
+
|
113
|
+
extra_rdoc_files: []
|
114
|
+
|
115
|
+
files:
|
116
|
+
- .gitignore
|
117
|
+
- .rspec
|
118
|
+
- Gemfile
|
119
|
+
- Gemfile.lock
|
120
|
+
- README.md
|
121
|
+
- Rakefile
|
122
|
+
- lib/phish_dot_net_client.rb
|
123
|
+
- lib/phish_dot_net_client/set.rb
|
124
|
+
- lib/phish_dot_net_client/setlist.rb
|
125
|
+
- lib/phish_dot_net_client/song.rb
|
126
|
+
- lib/phish_dot_net_client/song_transition.rb
|
127
|
+
- lib/phish_dot_net_client/version.rb
|
128
|
+
- phish_dot_net_client.gemspec
|
129
|
+
- spec/phish_dot_net_client/set_spec.rb
|
130
|
+
- spec/phish_dot_net_client/setlist_spec.rb
|
131
|
+
- spec/phish_dot_net_client/song_spec.rb
|
132
|
+
- spec/phish_dot_net_client/song_transition_spec.rb
|
133
|
+
- spec/phish_dot_net_client_spec.rb
|
134
|
+
- spec/spec_helper.rb
|
135
|
+
has_rdoc: true
|
136
|
+
homepage: https://github.com/alexebird/phish_dot_net_client
|
137
|
+
licenses:
|
138
|
+
- MIT
|
139
|
+
post_install_message:
|
140
|
+
rdoc_options: []
|
141
|
+
|
142
|
+
require_paths:
|
143
|
+
- lib
|
144
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
145
|
+
none: false
|
146
|
+
requirements:
|
147
|
+
- - ">="
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
segments:
|
150
|
+
- 1
|
151
|
+
- 9
|
152
|
+
- 1
|
153
|
+
version: 1.9.1
|
154
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
155
|
+
none: false
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
segments:
|
160
|
+
- 1
|
161
|
+
- 3
|
162
|
+
- 6
|
163
|
+
version: 1.3.6
|
164
|
+
requirements: []
|
165
|
+
|
166
|
+
rubyforge_project:
|
167
|
+
rubygems_version: 1.3.7.1
|
168
|
+
signing_key:
|
169
|
+
specification_version: 3
|
170
|
+
summary: Phish.net API client with setlist parsing
|
171
|
+
test_files:
|
172
|
+
- spec/phish_dot_net_client/set_spec.rb
|
173
|
+
- spec/phish_dot_net_client/setlist_spec.rb
|
174
|
+
- spec/phish_dot_net_client/song_spec.rb
|
175
|
+
- spec/phish_dot_net_client/song_transition_spec.rb
|
176
|
+
- spec/phish_dot_net_client_spec.rb
|
177
|
+
- spec/spec_helper.rb
|