tvdb_client 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +23 -0
  3. data/.rspec +2 -0
  4. data/.rubyversion +1 -0
  5. data/CHANGELOG.md +4 -0
  6. data/Gemfile +2 -0
  7. data/Guardfile +20 -0
  8. data/README.md +43 -0
  9. data/Rakefile +9 -0
  10. data/conf/settings.example.yml +7 -0
  11. data/lib/tvdb_client.rb +13 -0
  12. data/lib/tvdb_client/authorization.rb +46 -0
  13. data/lib/tvdb_client/client.rb +30 -0
  14. data/lib/tvdb_client/connection.rb +98 -0
  15. data/lib/tvdb_client/series.rb +54 -0
  16. data/lib/tvdb_client/series/base.rb +32 -0
  17. data/lib/tvdb_client/series/series_episodes.rb +18 -0
  18. data/lib/tvdb_client/series/series_filter.rb +21 -0
  19. data/lib/tvdb_client/series/series_images.rb +14 -0
  20. data/lib/tvdb_client/service/threading.rb +8 -0
  21. data/lib/tvdb_client/service/threading/threaded_request.rb +51 -0
  22. data/lib/tvdb_client/settings.rb +10 -0
  23. data/lib/tvdb_client/version.rb +3 -0
  24. data/spec/spec_helper.rb +53 -0
  25. data/spec/support/fake_TVDB.rb +118 -0
  26. data/spec/support/fixtures/responses/login.json +3 -0
  27. data/spec/support/fixtures/responses/refresh_token.json +3 -0
  28. data/spec/support/fixtures/responses/series.json +33 -0
  29. data/spec/support/fixtures/responses/series_episodes_page_1.json +56 -0
  30. data/spec/support/fixtures/responses/series_episodes_page_2.json +56 -0
  31. data/spec/support/fixtures/responses/series_episodes_page_n.json +41 -0
  32. data/spec/support/fixtures/responses/series_episodes_query_airedSeason.json +41 -0
  33. data/spec/support/fixtures/responses/series_episodes_query_params.json +12 -0
  34. data/spec/support/fixtures/responses/series_episodes_summary.json +26 -0
  35. data/spec/support/fixtures/responses/series_filter.json +5 -0
  36. data/spec/support/fixtures/responses/series_filter_params.json +27 -0
  37. data/spec/support/fixtures/responses/series_images.json +9 -0
  38. data/spec/support/fixtures/responses/series_images_query.json +35 -0
  39. data/spec/support/fixtures/responses/series_images_query_params.json +60 -0
  40. data/spec/support/fixtures/settings.example.yml +6 -0
  41. data/spec/support/shared_contexts/authentication.rb +13 -0
  42. data/spec/support/shared_contexts/connection.rb +20 -0
  43. data/spec/support/shared_contexts/credentials.rb +20 -0
  44. data/spec/support/shared_contexts/series_subclass.rb +19 -0
  45. data/spec/tvdb_client/authorization_spec.rb +69 -0
  46. data/spec/tvdb_client/client_spec.rb +45 -0
  47. data/spec/tvdb_client/connection_spec.rb +159 -0
  48. data/spec/tvdb_client/series/base_spec.rb +13 -0
  49. data/spec/tvdb_client/series/series_episodes_spec.rb +55 -0
  50. data/spec/tvdb_client/series/series_filter_spec.rb +45 -0
  51. data/spec/tvdb_client/series/series_images_spec.rb +43 -0
  52. data/spec/tvdb_client/series_spec.rb +69 -0
  53. data/spec/tvdb_client/service/threading/threaded_request_spec.rb +54 -0
  54. data/spec/tvdb_client/version_spec.rb +12 -0
  55. data/tasks/version_bump.rake +102 -0
  56. data/templates/changelog_entry_template.md.erb +3 -0
  57. data/templates/version.rb.erb +3 -0
  58. data/tvdb_client.gemspec +41 -0
  59. metadata +412 -0
@@ -0,0 +1,14 @@
1
+ module TVDB
2
+ class Series
3
+ class Images < TVDB::Series::Base
4
+
5
+ def initialize( options )
6
+ @connection = options.fetch( :connection )
7
+ @series_id = options.fetch( :series_id )
8
+ @parameters = options.fetch( :params ) { {} }
9
+ @route = "/series/#{series_id}/images"
10
+ end
11
+
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,8 @@
1
+ require_relative 'threading/threaded_request'
2
+
3
+ module TVDB
4
+ module Service
5
+ module Threading
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,51 @@
1
+ module TVDB
2
+ module Service
3
+ module Threading
4
+ class ThreadedRequest
5
+
6
+ attr_reader :connection, :pool_size
7
+ attr_accessor :threads
8
+
9
+ def initialize( options )
10
+ @connection = options.fetch( :connection )
11
+ @pool_size = options.fetch( :pool_size ) { 10 }
12
+ @threads = Array.new
13
+ end
14
+
15
+ def make_request( route )
16
+ response = connection.get( route )
17
+
18
+ return response if response.code != 200
19
+
20
+ links = connection.get( route ).body["links"]
21
+ first_page = links["first"]
22
+ last_page = links["last"]
23
+ @results = Array.new
24
+
25
+ for page in first_page..last_page
26
+ manage_thread_pool
27
+
28
+ threads << Thread.new( page ) do |page_num|
29
+ params = { :params => { page: page_num } }
30
+ @results << connection.get( route, params ).body["data"]
31
+ end
32
+ end
33
+
34
+ threads.each { |thread| thread.join }
35
+
36
+ return @results.flatten
37
+ end
38
+
39
+ private
40
+
41
+ def manage_thread_pool
42
+ while threads.length > 10
43
+ threads.each { |thread| threads.delete( thread ) if thread.stop? }
44
+ sleep 0.01
45
+ end
46
+ end
47
+
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,10 @@
1
+ require 'settingslogic'
2
+
3
+ # Public: Reads in the config/settings.yml files and makes its entries
4
+ # available as Settings.whatever. You must require lib/settings.rb
5
+ # in classes where you wish to use this functionality. See manifest_scraper.rb
6
+ # for an example
7
+ #
8
+ class Settings < Settingslogic
9
+ source File.expand_path( "../../../conf/settings.example.yml", __FILE__ )
10
+ end
@@ -0,0 +1,3 @@
1
+ module TVDB
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,53 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # Require this file using `require "spec_helper"` to ensure that it is only
4
+ # loaded once.
5
+ #
6
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
+
8
+ require 'spork'
9
+ require 'rspec'
10
+ require 'pp'
11
+ require 'pry-byebug'
12
+ require 'yaml'
13
+
14
+ Spork.prefork do
15
+ require 'webmock/rspec'
16
+ require 'simplecov'
17
+
18
+ ENV['RSPEC'] = 'true'
19
+
20
+ SimpleCov.start do
21
+ add_filter "/bundle"
22
+ add_filter "/spec"
23
+ end
24
+
25
+ WebMock.disable_net_connect!( allow_localhost: true )
26
+
27
+ RSpec.configure do |config|
28
+ config.before(:each) do
29
+ stub_request( :any, /api.thetvdb.localhost.com/).to_rack( FakeTVDB )
30
+ end
31
+
32
+ config.run_all_when_everything_filtered = true
33
+ config.filter_run :focus
34
+
35
+ # Run specs in random order to surface order dependencies. If you find an
36
+ # order dependency and want to debug it, you can fix the order by providing
37
+ # the seed, which is printed after each run.
38
+ # --seed 1234
39
+ config.order = 'random'
40
+ config.fail_fast = true
41
+ end
42
+ end
43
+
44
+ Spork.each_run do
45
+ # This code will be run each time you run your specs.
46
+ RSpec.configure do |config|
47
+ base_dir = File.expand_path("../../", __FILE__)
48
+
49
+ Dir["#{base_dir}/lib/tvdb_client/**/*.rb"].each {|f| require f}
50
+ Dir["#{base_dir}/spec/support/**/*.rb"].sort.each { |f| require f}
51
+ end
52
+ end
53
+
@@ -0,0 +1,118 @@
1
+ require 'sinatra/base'
2
+
3
+ class FakeTVDB < Sinatra::Base
4
+ post '/login' do
5
+ if JSON.parse( request.body.read )["username"] == "validuser"
6
+ json_response 200, 'responses/login.json'
7
+ else
8
+ unauthed_json_response
9
+ end
10
+ end
11
+
12
+ get '/refresh_token' do
13
+ if bad_auth_header?( request )
14
+ status 401
15
+ else
16
+ json_response 200, 'responses/refresh_token.json'
17
+ end
18
+ end
19
+
20
+ get '/series/:id' do
21
+ series_routes( request, params["id"], 'responses/series.json')
22
+ end
23
+
24
+ get '/not/modified' do
25
+ not_modified_response
26
+ end
27
+
28
+ get '/series/:id/episodes' do
29
+ page = params["page"]
30
+
31
+ if params["id"] == "76703"
32
+ if page == "1" || !page
33
+ series_routes( request, params["id"], 'responses/series_episodes_page_1.json')
34
+ elsif page == "2"
35
+ series_routes( request, params["id"], 'responses/series_episodes_page_2.json')
36
+ end
37
+ elsif params["id"] == "99999"
38
+ series_routes( request, params["id"], 'responses/series_episodes_page_n.json')
39
+ else
40
+ not_found_json_response( params["id"] )
41
+ end
42
+ end
43
+
44
+ get '/series/:id/episodes/query' do
45
+ aired_season = params["airedSeason"]
46
+
47
+ json_response 200, 'responses/series_episodes_query_airedSeason.json'
48
+ end
49
+
50
+ get '/series/:id/episodes/query/params' do
51
+ json_response 200, 'responses/series_episodes_query_params.json'
52
+ end
53
+
54
+ get '/series/:id/episodes/summary' do
55
+ json_response 200, 'responses/series_episodes_summary.json'
56
+ end
57
+
58
+ get '/series/:id/images' do
59
+ json_response 200, 'responses/series_images.json'
60
+ end
61
+
62
+ get '/series/:id/images/query' do
63
+ json_response 200, 'responses/series_images_query.json'
64
+ end
65
+
66
+ get '/series/:id/images/query/params' do
67
+ json_response 200, 'responses/series_images_query_params.json'
68
+ end
69
+
70
+ get '/series/:id/filter' do
71
+ return not_modified_response unless params.has_key?( "keys" )
72
+ json_response 200, 'responses/series_filter.json' if params["keys"] == "seriesName"
73
+ end
74
+
75
+ get '/series/:id/filter/params' do
76
+ json_response 200, 'responses/series_filter_params.json'
77
+ end
78
+
79
+ private
80
+
81
+ def series_routes( request, id, json_response_string )
82
+ if bad_auth_header?( request )
83
+ unauthed_json_response
84
+ elsif id == '76703' || id == "99999"
85
+ json_response 200, json_response_string
86
+ else
87
+ not_found_json_response( id )
88
+ end
89
+ end
90
+
91
+ def json_response(response_code, file_name)
92
+ content_type :json
93
+ status response_code
94
+ File.open(File.dirname(__FILE__) + '/fixtures/' + file_name, 'rb').read
95
+ end
96
+
97
+ def unauthed_json_response
98
+ content_type :json
99
+ status 401
100
+ '{"Error":"API Key Required"}'
101
+ end
102
+
103
+ def not_found_json_response( id )
104
+ content_type :json
105
+ status 404
106
+ "{\"Error\": \"ID: #{id} not found\"}"
107
+ end
108
+
109
+ def not_modified_response
110
+ content_type :json
111
+ status 304
112
+ ""
113
+ end
114
+
115
+ def bad_auth_header?( request )
116
+ request.env["HTTP_AUTHORIZATION"] == "Bearer" ? true : false
117
+ end
118
+ end
@@ -0,0 +1,3 @@
1
+ {
2
+ "token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0MzcyNDUzNTcsImlkIjoiTXkgb3duIHNjcmlwdHMiLCJvcmlnX2lhdCI6MTQzNzI0MTc1NywidXNlcmlkIjoyNDM5NDEsInVzZXJuYW1lIjoiZm9vIn0.TQCkOSmKht3imZ5RRu6VDtCcEfwgwccEdIKas_zxBdX9A7ILGbnS98CPn6ZLTVd76M-U6vUbinOMfRKWN99JCsE1FTKV84hBt8fd7UPIcH08e6n9eUlwJVAU4GjB6y4dwY3JPjAYKQicykm4k7Up_GmJ8pUJ0qpgDZg2z9bHdpX1wf8nj66RdVZsVLmdczHW3kCDrhiAHfec58j-j6jBARSmseVP4vJ8Rud77RxgQuZDgY4AjG-nHpfiooX_Xo_pS9M8I6boskXHgUUI9MGUqONpYDeo2DXyCT3BRxNpklWu-fDZ2QvNx2CqL5njwJ7gte9_6DeGmiPmZ7UYcK71sg"
3
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0Mzc0MjE1MjMsImlkIjoiTXkgb3duIHNjcmlwdHMiLCJvcmlnX2lhdCI6MTQzNzQxNzg2OCwidXNlcmlkIjoyNDM5NDEsInVzZXJuYW1lIjoiZm9vIn0.kp9OFogBHG202F1NW_HX1SGoHijSwj4w8CWZa55xfmZRypSjTyqj93MOLfh6qSjSOZ2au4RuZqqUfRaWC3IgIrondGAk6dStq0dNs9dBkJ9kfcUO6U7_RCAjziv597kV7Q1dyRGr1g8g3j4l_dTdqjHlyQSk2jXiSl6zulKYCmfo9PHC0ID_jo__i2yh3he21_tc_5rTaJXz8q-CiIsl7juGWqV_egqLsab3Rj1RSWrWJGtPXzQIC2nn-u5-8fmcU8htMoribb2PGYr0it6UFNm-_iBMtJnNJjSGfCJNLhKpP7KqOzj_5C_yHH1QzJnS4AA_AjIjbKnp30jnzzPnwQ"
3
+ }
@@ -0,0 +1,33 @@
1
+ {
2
+ "data": {
3
+ "id": 76703,
4
+ "seriesName": "Pokémon",
5
+ "aliases": [
6
+ "Pokemon",
7
+ "Diamond and Pearl",
8
+ "Pocket Monsters",
9
+ "Pokémon: Black & White"
10
+ ],
11
+ "banner": "graphical/76703-g6.jpg",
12
+ "seriesId": "467",
13
+ "status": "Continuing",
14
+ "firstAired": "1997-04-01",
15
+ "network": "TV Tokyo",
16
+ "networkId": "",
17
+ "runtime": "30",
18
+ "genre": [
19
+ "Animation",
20
+ "Children"
21
+ ],
22
+ "actors": [],
23
+ "overview": " A young boy named Ash Ketchum embarks on a journey to become a \"Pokemon Master\" with his first Pokemon, Pikachu. Joining him on his travels are Brock, a girl-obsessed Rock Pokemon Trainer, and Misty, a tomboyish Water Pokemon Trainer who may have a crush on him. Ash and Co. end up traveling through various regions, including Kanto, the Orange Islands, and Johto, and then enter the Pokemon League competitions there. Along the way, they run into many confrontations with Jessie, James, and Meowth, a trio of Pokemon thieves who are apart of an evil organization called \"Team Rocket\". But everytime Team Rocket try to do their evil deeds, they fail thanks to Ash and his Pokemon.",
24
+ "lastUpdated": 1437155768,
25
+ "airsDayOfWeek": "",
26
+ "airsTime": "",
27
+ "rating": "TV-Y",
28
+ "imdbId": "tt0176385",
29
+ "zap2itId": "",
30
+ "added": "",
31
+ "addedBy": null
32
+ }
33
+ }
@@ -0,0 +1,56 @@
1
+ {
2
+ "errors": null,
3
+ "links": {
4
+ "first": 1,
5
+ "last": 2,
6
+ "next": 2,
7
+ "prev": null
8
+ },
9
+ "data": [
10
+ {
11
+ "absoluteNumber": "1",
12
+ "airedEpisodeNumber": 1,
13
+ "airedSeason": "1",
14
+ "dvdEpisodeNumber": null,
15
+ "dvdSeason": null,
16
+ "episodeName": "Pokémon! I Choose You!",
17
+ "firstAired": "1997-04-01",
18
+ "id": 208318,
19
+ "language": {
20
+ "episodeName": "en",
21
+ "overview": "en"
22
+ },
23
+ "overview": "After beginning his Pokémon journey, Ash is supposed to meet Professor Oak - a Pokémon researcher who is giving Pokémon to all the 10-year-olds in Pallet Town. Arriving late, Ash finds that all the Pokémon are taken - all except for Pikachu. After a harrowing misadventure together, Pikachu and Ash form an unbreakable bond of friendship."
24
+ },
25
+ {
26
+ "absoluteNumber": "83",
27
+ "airedEpisodeNumber": 1,
28
+ "airedSeason": "2",
29
+ "dvdEpisodeNumber": null,
30
+ "dvdSeason": null,
31
+ "episodeName": "Pallet Party Panic",
32
+ "firstAired": "1999-12-04",
33
+ "id": 208400,
34
+ "language": {
35
+ "episodeName": "en",
36
+ "overview": "en"
37
+ },
38
+ "overview": "Following the completion of the Pokémon League, Ash is back home in Pallet Town. Professor Oak and Delia throw him a party which of course Team Rocket crashes, but Ash is able to send them off with the help of Charizard, who proceeds to wreck the party. Later that day Professor Oak asks Ash to run a errand for him. He wants Ash to go to the Orange Islands and pick up the GS Ball from Professor Ivy. He agrees and leaves soon after. Along the way they become trapped ina forest where the same flock of Spearow from the beginning of Ash's journey is still causing trouble, along with their newly evolved Fearow leader. Now they must figure out a way to get out of the forest before the Spearow manage to get to them."
39
+ },
40
+ {
41
+ "absoluteNumber": "119",
42
+ "airedEpisodeNumber": 1,
43
+ "airedSeason": "3",
44
+ "dvdEpisodeNumber": null,
45
+ "dvdSeason": null,
46
+ "episodeName": "Don't Touch That 'Dile",
47
+ "firstAired": "2000-10-14",
48
+ "id": 208436,
49
+ "language": {
50
+ "episodeName": "en",
51
+ "overview": "en"
52
+ },
53
+ "overview": "Ash begins his journey in Johto, a region largely unexplored and populated with Pokémon entirely new to him and his friends.\r\n"
54
+ }
55
+ ]
56
+ }
@@ -0,0 +1,56 @@
1
+ {
2
+ "errors": null,
3
+ "links": {
4
+ "first": 1,
5
+ "last": 2,
6
+ "next": null,
7
+ "prev": 1
8
+ },
9
+ "data": [
10
+ {
11
+ "absoluteNumber": "749",
12
+ "airedEpisodeNumber": 6,
13
+ "airedSeason": "15",
14
+ "dvdEpisodeNumber": null,
15
+ "dvdSeason": null,
16
+ "episodeName": "Jostling for the Junior Cup!",
17
+ "firstAired": "2012-08-02",
18
+ "id": 4359831,
19
+ "language": {
20
+ "episodeName": "en",
21
+ "overview": "en"
22
+ },
23
+ "overview": "Ash and his friends arrive in Lacunosa Town for the Junior Cup. The Cup begins with an exhibition match between Cynthia and Caitlyn. Iris and Georgia face off against each other but Iris’ newly caught Dragonite refuses to listen to Iris’ commands."
24
+ },
25
+ {
26
+ "absoluteNumber": null,
27
+ "airedEpisodeNumber": 6,
28
+ "airedSeason": "16",
29
+ "dvdEpisodeNumber": null,
30
+ "dvdSeason": null,
31
+ "episodeName": "Battling on thin ice!",
32
+ "firstAired": "2013-11-14",
33
+ "id": 4709580,
34
+ "language": {
35
+ "episodeName": "en",
36
+ "overview": "en"
37
+ },
38
+ "overview": "Ash challenges Viola to a rematch, now ready for Surskit's freezing strategy. "
39
+ },
40
+ {
41
+ "absoluteNumber": "7",
42
+ "airedEpisodeNumber": 7,
43
+ "airedSeason": "1",
44
+ "dvdEpisodeNumber": null,
45
+ "dvdSeason": null,
46
+ "episodeName": "The Water Flowers of Cerulean City",
47
+ "firstAired": "1997-05-13",
48
+ "id": 208324,
49
+ "language": {
50
+ "episodeName": "en",
51
+ "overview": "en"
52
+ },
53
+ "overview": "In his search to collect Trainer Badges, Ash heads for Cerulean City, intending to confront the Cerulean Gym Leader. For some reason, Misty expresses a desire to avoid Cerulean City and disappears somewhere along the way. Without concern for Misty, Ash and Pikachu continue toward the Cerulean City Gym. When they arrive, the exterior of the Gym gives the strange appearance of an aquarium. Upon entering the building, Ash and Pikachu are entranced by a synchronized swimming show being performed by three beautiful sisters. Could this truly be the Cerulean Gym? And what could be the role of the three beautiful swimming sisters? More importantly, will Ash be able to defeat the strangely absent Gym Leader to earn his next badge?\\\r\\\n"
54
+ }
55
+ ]
56
+ }
@@ -0,0 +1,41 @@
1
+ {
2
+ "errors": null,
3
+ "links": {
4
+ "first": 1,
5
+ "last": 20,
6
+ "next": 2,
7
+ "prev": 1
8
+ },
9
+ "data": [
10
+ {
11
+ "absoluteNumber": "749",
12
+ "airedEpisodeNumber": 6,
13
+ "airedSeason": "15",
14
+ "dvdEpisodeNumber": null,
15
+ "dvdSeason": null,
16
+ "episodeName": "Jostling for the Junior Cup!",
17
+ "firstAired": "2012-08-02",
18
+ "id": 4359831,
19
+ "language": {
20
+ "episodeName": "en",
21
+ "overview": "en"
22
+ },
23
+ "overview": "Ash and his friends arrive in Lacunosa Town for the Junior Cup. The Cup begins with an exhibition match between Cynthia and Caitlyn. Iris and Georgia face off against each other but Iris’ newly caught Dragonite refuses to listen to Iris’ commands."
24
+ },
25
+ {
26
+ "absoluteNumber": null,
27
+ "airedEpisodeNumber": 6,
28
+ "airedSeason": "16",
29
+ "dvdEpisodeNumber": null,
30
+ "dvdSeason": null,
31
+ "episodeName": "Battling on thin ice!",
32
+ "firstAired": "2013-11-14",
33
+ "id": 4709580,
34
+ "language": {
35
+ "episodeName": "en",
36
+ "overview": "en"
37
+ },
38
+ "overview": "Ash challenges Viola to a rematch, now ready for Surskit's freezing strategy. "
39
+ }
40
+ ]
41
+ }