phish_dot_net_client 0.0.1
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/.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
|