ann_wrapper 1.1.4 → 1.1.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1e57e389662bc751a163ecff82d950d89a79ff80
4
- data.tar.gz: 4916dd0335181b68cef44b1ae9b75f98c2eef270
3
+ metadata.gz: 3a9e7afef7ed3479ae7b65cd78990ac4ec590094
4
+ data.tar.gz: 9ac1f2658558da468d3c3bd36aa83827d989baf8
5
5
  SHA512:
6
- metadata.gz: 1358df5bfe4a4e892cbea93af1db570b11e31c30a3b2cd31cc99664b151945470eedcef7e88caf6607cb1e2cf4a66b980d8cbb382e8c1787d36fc0f0d0267353
7
- data.tar.gz: 666e68b082a5c2f555497b08820769a06f8b2dfe78f2fca695ba9c27d7652467d74bc5a5b10d77bf44c4b632a59bea52af02feb9c9626e5222b7a09dc98bdf68
6
+ metadata.gz: 033dc1f439ea2110322bd23366a51377fb550e691ea4f1adf00e327b0d2b26db677029dc37d1813fe621e908bb33747fc8a08203126d42d048e67c50cb72be61
7
+ data.tar.gz: 12606ade1ae66a7a131fb80eff2eeaab8f7ea90a4eeb7dccb5fafcc2f7981e222a160e458d89d620cb6effaba2bdfcdfba5da3c712ec6cb350310541c3c72ab7
@@ -1,5 +1,5 @@
1
1
  language: ruby
2
2
  rvm:
3
- - "1.9.2"
4
3
  - "1.9.3"
5
4
  - "2.0.0"
5
+ - "2.1.5"
data/Gemfile CHANGED
@@ -1,4 +1,7 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
+ # coveralls test coverage
4
+ gem 'coveralls', require: false
5
+
3
6
  # Specify your gem's dependencies in ann_wrapper.gemspec
4
7
  gemspec
data/README.md CHANGED
@@ -4,8 +4,8 @@
4
4
  [![Gem Version](https://badge.fury.io/rb/ann_wrapper.png)](http://badge.fury.io/rb/ann_wrapper)
5
5
  [![Build Status](https://travis-ci.org/Getkura/ann_wrapper.png?branch=dev)](https://travis-ci.org/Getkura/ann_wrapper)
6
6
  [![Dependency Status](https://gemnasium.com/Getkura/ann_wrapper.png)](https://gemnasium.com/Getkura/ann_wrapper)
7
- [![Code Climate](https://codeclimate.com/github/Getkura/ann_wrapper.png)](https://codeclimate.com/github/Getkura/ann_wrapper)
8
-
7
+ [![Code Climate](https://codeclimate.com/github/Getkura/ann_wrapper/badges/gpa.svg)](https://codeclimate.com/github/Getkura/ann_wrapper)
8
+ [![Coverage Status](https://coveralls.io/repos/Getkura/ann_wrapper/badge.png?branch=dev)](https://coveralls.io/r/Getkura/ann_wrapper?branch=dev)
9
9
 
10
10
  A simple ruby wrapper/abstraction for the [Anime News Network API](http://www.animenewsnetwork.com/encyclopedia/api.php)
11
11
 
@@ -25,82 +25,132 @@ Or install it yourself as:
25
25
 
26
26
  ## Usage
27
27
 
28
- Fetch an anime:
28
+ ###Fetch an anime:
29
+
30
+ anime = ANN_Wrapper.fetch_anime "id"
31
+ anime.title
32
+ anime.alt_titles
33
+ anime.synopsis
34
+ anime.num_episodes
35
+ anime.genres
36
+ anime.themes
37
+ anime.vintage
38
+ anime.op_theme
39
+ anime.ed_theme
40
+ anime.id
41
+ anime.type
42
+ anime.ratings
43
+ anime.episodes
44
+ anime.staff
45
+ anime.cast
46
+ anime.images
47
+ anime.to_h
48
+
49
+ ####Example:
29
50
 
30
51
  steins_gate = ANN_Wrapper.fetch_anime 11770
31
52
 
32
- Info:
33
-
53
+ #####Info:
54
+
34
55
  steins_gate.id
35
56
  => "11770"
36
-
57
+
37
58
  steins_gate.title
38
59
  => ["Steins;Gate"]
39
-
60
+
40
61
  steins_gate.alt_titles
41
62
  => {"PT"=>["Steins-Gate e a Teoria do Caos"], "JA"=>["シュタインズ・ゲート"], "ZH-TW"=>["命運石之門"], "KO"=>["슈타인즈 게이트"]}
42
63
 
43
64
  steins_gate.synopsis
44
65
  => ["Rintaro Okabe is a self-proclaimed "mad scientist" ... "]
45
-
66
+
46
67
  steins_gate.num_episodes
47
68
  => ["24"]
48
-
69
+
49
70
  steins_gate.vintage
50
71
  => ["2011-04-03 (Advanced screening)", "2011-04-05 to 2011-09-13"]
51
-
72
+
52
73
  steins_gate.genres
53
74
  => ["adventure", "comedy", "drama", "mystery", "psychological", "romance", "science fiction", "thriller"]
54
-
75
+
55
76
  steins_gate.themes
56
77
  => ["butterfly effect", "conspiracy", "technology", "Time travel"]
57
-
78
+
58
79
  steins_gate.op_theme
59
80
  => ["\"Hacking to the Gate\" by Kanako Ito"]
60
-
81
+
61
82
  steins_gate.ed_theme
62
83
  => ["\"Tokitsukasadoru Jūni no Meiyaku\" (刻司ル十二ノ盟約) by Yui Sakakibara", "#2: \"Sukai Kuraddo no Kansokusha\" (スカイクラッドの観測者) by Kanako Ito (ep 23)", "#3: \"Another Heaven\" by Kanako Itou (ep 24)"]
63
84
 
64
85
 
65
- Cast and Staff:
66
-
86
+ #####Cast and Staff:
87
+
67
88
  steins_gate.cast.find_all {|c| c.name.include? "Hanazawa"}
68
89
  => [#<struct ANN_Cast id="53741", role="Mayuri Shiina", name="Kana Hanazawa", lang="JA">]
69
-
90
+
70
91
  steins_gate.staff.find_all {|s| s.task.eql? "Director"}
71
92
  => [
72
- #<struct ANN_Staff id="593", task="Director", name="Takuya Satō">,
73
- #<struct ANN_Staff id="9693", task="Director", name="Hiroshi Hamasaki">,
93
+ #<struct ANN_Staff id="593", task="Director", name="Takuya Satō">,
94
+ #<struct ANN_Staff id="9693", task="Director", name="Hiroshi Hamasaki">,
74
95
  #<struct ANN_Staff id="35713", task="Director", name="Tomoki Kobayashi">
75
96
  ]
76
97
 
77
98
 
78
- Episodes:
99
+ #####Episodes:
79
100
 
80
101
  steins_gate.episodes.find_all {|e| e.title.include? "Prologue"}
81
102
  => [
82
- #<struct ANN_Episode number="1", title="Prologue of the Beginning and End", lang="EN">,
103
+ #<struct ANN_Episode number="1", title="Prologue of the Beginning and End", lang="EN">,
83
104
  #<struct ANN_Episode number="24", title="The Prologue Begins With the End", lang="EN">
84
105
  ]
85
-
106
+
86
107
  steins_gate.episodes.first.to_h
87
108
  => {:number=>"1", :title=>"Prologue of the Beginning and End", :lang=>"EN"}
88
-
89
- Images:
109
+
110
+ #####Images:
90
111
 
91
112
  steins_gate.images
92
113
  => [
93
- #<struct ANN_Image src="http://cdn.animenewsnetwork.com/thumbnails/fit200x200/encyc/A11770-1864351140.1370764886.jpg", width="200", height="125">,
94
- #<struct ANN_Image src="http://cdn.animenewsnetwork.com/thumbnails/max500x600/encyc/A11770-1864351140.1370764886.jpg", width="500", height="312">,
95
- #<struct ANN_Image src="http://cdn.animenewsnetwork.com/images/encyc/A11770-1864351140.1370764886.jpg", width="900", height="562">,
96
- #<struct ANN_Image src="http://cdn.animenewsnetwork.com/thumbnails/fit200x200/encyc/A11770-8.jpg", width="200", height="200">,
114
+ #<struct ANN_Image src="http://cdn.animenewsnetwork.com/thumbnails/fit200x200/encyc/A11770-1864351140.1370764886.jpg", width="200", height="125">,
115
+ #<struct ANN_Image src="http://cdn.animenewsnetwork.com/thumbnails/max500x600/encyc/A11770-1864351140.1370764886.jpg", width="500", height="312">,
116
+ #<struct ANN_Image src="http://cdn.animenewsnetwork.com/images/encyc/A11770-1864351140.1370764886.jpg", width="900", height="562">,
117
+ #<struct ANN_Image src="http://cdn.animenewsnetwork.com/thumbnails/fit200x200/encyc/A11770-8.jpg", width="200", height="200">,
97
118
  #<struct ANN_Image src="http://cdn.animenewsnetwork.com/thumbnails/max500x600/encyc/A11770-8.jpg", width="317", height="317">
98
119
  ]
120
+
121
+ #####Ratings:
99
122
 
100
-
101
-
102
-
103
-
123
+ steings_gate.ratings
124
+ => [
125
+ #<struct ANN_Rating votes="3788", weighted="9.1129", bayesian_score="9.1075">
126
+ ]
127
+
128
+ ###Fetch a manga:
129
+
130
+ Fetching a manga works exactly the same as an anime, but you should call the `fetch_manga` method.
131
+
132
+ manga = ANN_Wrapper.fetch_manga "id"
133
+ manga.title
134
+ manga.alt_titles
135
+ manga.synopsis
136
+ manga.genres
137
+ manga.vintage
138
+ manga.themes
139
+ manga.num_tankoubon
140
+ manga.num_pages
141
+ manga.id
142
+ manga.type
143
+ manga.staff
144
+ manga.ratings
145
+ manga.images
146
+ manga.to_h
147
+
148
+ ###Batching:
149
+ Send any number of ids in an array for a batch request.
150
+ This will return an array of ANN_Anime or ANN_Manga objects.
151
+
152
+ anime = ANN_Wrapper.batch_anime(["id_1", "id_2", "id_3", ...])
153
+ manga = ANN_Wrapper.batch_manga(["id_1", "id_2", "id_3", ...])
104
154
 
105
155
  ## Contributing
106
156
 
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
10
10
  spec.email = ["getkura+ann_wrapper@gmail.com"]
11
11
  spec.description = 'A simple wrapper for the Anime News Network API'
12
12
  spec.summary = 'Anime News Network API wrapper'
13
- spec.homepage = "http://documentup.com/Getkura/ann_wrapper"
13
+ spec.homepage = "http://getkura.github.io/ann_wrapper/"
14
14
  spec.license = "MIT"
15
15
 
16
16
  spec.files = `git ls-files`.split($/)
@@ -4,35 +4,59 @@
4
4
 
5
5
  require "net/http"
6
6
  require "nokogiri"
7
- require 'ann_wrapper/ann_objects'
7
+ require 'ann_wrapper/ann_anime'
8
+ require 'ann_wrapper/ann_manga'
9
+ require 'ann_wrapper/ann_report'
8
10
 
9
11
  # wrapper class for ANN API
10
12
  module ANN_Wrapper
11
13
  extend ANN_Wrapper
12
14
 
15
+ @@type = "anime"
16
+
13
17
  # ANN API anime url
14
18
  ANN_URL = "http://cdn.animenewsnetwork.com/encyclopedia"
15
19
  ANN_API_URL = "#{ANN_URL}/api.xml"
16
20
  ANN_REPORTS_URL = "#{ANN_URL}/reports.xml"
17
21
 
18
- # fetch anime and convert to ANN_Anime
19
- def fetch_anime(id, api_url=ANN_API_URL)
22
+ # fetch up to 50 items(Animes or Mangas) in one request
23
+ def batch_items(ids, api_url=ANN_API_URL)
20
24
  # append id to API url and send request
21
- url = "#{api_url}?anime=#{id.to_s}"
25
+ url = "#{api_url}?title=#{ids.first.to_s}"
26
+ ids[1..-1].each do |id|
27
+ url << "/#{id.to_s}"
28
+ end
22
29
 
23
30
  ann = fetch(url)
24
31
 
25
- return ann if ann.is_a?(ANN_Error)
32
+ return [ann] if ann.is_a?(ANN_Error)
33
+
34
+ all_items = ann.xpath("//ann/#{@@type}")
35
+ warnings = ann.xpath('//ann/warning')
36
+
37
+ return [ANN_Error.new(get_xml_error(ann))] if all_items.empty? and warnings.empty?
38
+
39
+ all_items = all_items.map { |item| Object.const_get("ANN_#{@@type.capitalize}").new(item) }
40
+ warnings = warnings.map { |warning| ANN_Error.new(get_xml_error(warning)) }
26
41
 
27
- anime = ann.at_xpath('//ann/anime')
42
+ all_items.push(*warnings)
43
+ end
28
44
 
29
- # initialize new ann_anime or error with ann object
30
- anime.nil? ? ANN_Error.new(get_xml_error(ann)) : ANN_Anime.new(anime)
45
+ # fetch anime and convert to ANN_Anime
46
+ def fetch_item(id, api_url=ANN_API_URL)
47
+ batch_items([id], api_url).first
31
48
  end
32
49
 
33
50
  # fetch list of titles via reports
34
- def fetch_titles(type="anime", nskip=0, nlist=50, name="", api_url=ANN_REPORTS_URL)
35
- url = "#{api_url}?id=155&type=#{type}&nskip=#{nskip}&nlist=#{nlist}"
51
+ def fetch_titles(options = {})
52
+ options[:type] ||= "anime"
53
+ options[:nskip] ||= 0
54
+ options[:nlist] ||= 50
55
+ options[:name] ||= ""
56
+ options[:api_url] ||= ANN_REPORTS_URL
57
+
58
+ url = "#{options[:api_url]}?id=155&type=#{options[:type]}&name=#{options[:name]}&nskip=#{options[:nskip]}&nlist=#{options[:nlist]}"
59
+
36
60
  report = fetch(url)
37
61
 
38
62
  return report if report.is_a?(ANN_Error)
@@ -44,6 +68,15 @@ extend ANN_Wrapper
44
68
  reports.map { |item| ANN_Report.new(item) }
45
69
  end
46
70
 
71
+ def method_missing(meth, *args, &block)
72
+ if meth.to_s =~ /^(fetch|batch)_(anime|manga)$/
73
+ @@type = $2
74
+ $1 == 'fetch' ? fetch_item(*args) : batch_items(*args)
75
+ else
76
+ super
77
+ end
78
+ end
79
+
47
80
  private
48
81
  # fetch data from ANN API via http GET request
49
82
  # returns Nokogiri or ANN_Error
@@ -55,16 +88,16 @@ extend ANN_Wrapper
55
88
  # get the response body and try converting to Nokogiri object
56
89
  Nokogiri.XML(resp.body)
57
90
  rescue
58
- ANN_Error.new("xml format error, API likely unavailable")
91
+ ANN_Error.new("Could not reach valid URL")
59
92
  end
60
- end
93
+ end
61
94
 
62
95
  # attempt to grab error message from XMLObject
63
96
  def get_xml_error(xobj)
64
97
  begin
65
98
  xobj.at_xpath('//ann/warning').content
66
- rescue NameError
67
- "bad response"
99
+ rescue NoMethodError
100
+ "unrecognized response body"
68
101
  end
69
102
  end
70
103
  end
@@ -0,0 +1,44 @@
1
+ # parent with helper methods
2
+ class ANN
3
+ private
4
+ ##
5
+ # define method with supplied name and block
6
+ def create_method(name, &block)
7
+ self.class.send(:define_method, name, &block)
8
+ end
9
+
10
+
11
+ # return hash of methods and returns excluding those in excludes
12
+ def to_hash(excludes)
13
+ # get list of methods excluding above
14
+ methods = self.class.instance_methods(false).reject {|m| excludes.include? m}
15
+
16
+ # map methods and results to hash
17
+ data = methods.map do |method |
18
+ result = self.send(method)
19
+
20
+ # convert Structs to hash
21
+ if (result.is_a? Array)
22
+ result.map! do |item|
23
+ item.is_a?(Struct) ? item.hash : item
24
+ end
25
+ else
26
+ result.hash! if result.is_a?(Struct)
27
+ end
28
+
29
+ # make hash with method name and result of call
30
+ [method.to_sym, result]
31
+ end
32
+
33
+ # return hash
34
+ Hash[data]
35
+ end
36
+ end
37
+
38
+ # various ANN struct types
39
+ ANN_Error = Struct.new(:message)
40
+ ANN_Staff = Struct.new(:id, :task, :name)
41
+ ANN_Cast = Struct.new(:id, :role, :name, :lang)
42
+ ANN_Episode = Struct.new(:number, :title, :lang)
43
+ ANN_Image = Struct.new(:src, :width, :height)
44
+ ANN_Rating = Struct.new(:votes, :weighted, :bayesian_score)
@@ -0,0 +1,85 @@
1
+ require_relative 'ann_media'
2
+
3
+ class ANN_Anime < ANN_Media
4
+ # ann_anime Nokogiri object
5
+ attr_writer :ann_anime
6
+
7
+
8
+ # initialize and create info methods
9
+ def initialize(ann_anime)
10
+ @ann_anime = ann_anime
11
+
12
+ # information available from detail
13
+ @info = Hash.new
14
+ @info[:title] = "Main title"
15
+ @info[:synopsis] = "Plot Summary"
16
+ @info[:num_episodes] = "Number of episodes"
17
+ @info[:genres] = "Genres"
18
+ @info[:themes] = "Themes"
19
+ @info[:vintage] = "Vintage"
20
+ @info[:op_theme] = "Opening Theme"
21
+ @info[:ed_theme] = "Ending Theme"
22
+
23
+ # create methods
24
+ create_methods(@ann_anime, @info)
25
+
26
+ end
27
+
28
+ # @return [Nokogiri::XML::NodeSet] return all info with provided key
29
+ def find_info(key)
30
+ super(@ann_anime, key)
31
+ end
32
+
33
+ # @return [String] returns anime id
34
+ def id
35
+ @id ||= @ann_anime['id']
36
+ end
37
+
38
+ # @return [String] returns anime type
39
+ def type
40
+ @type ||= @ann_anime['type']
41
+ end
42
+
43
+ # @return [[ANN_Rating]] returns array of ANN_Episode
44
+ def ratings
45
+ super @ann_anime
46
+ end
47
+
48
+ # @return [[ANN_Episode]] returns array of ANN_Episode
49
+ def episodes
50
+ @episodes ||= @ann_anime.xpath("./episode").map do |e|
51
+ title = e.at_xpath("title")
52
+ ANN_Episode.new(e['num'], title.content, title['lang'])
53
+ end
54
+ end
55
+
56
+ # @return [[ANN_Cast]] returns array of ANN_Cast
57
+ def cast
58
+ @cast ||= @ann_anime.xpath("./cast").map do |s|
59
+ role = s.at_xpath("role")
60
+ person = s.at_xpath("person")
61
+ ANN_Cast.new(person['id'], role.content, person.content, s['lang'])
62
+ end
63
+ end
64
+
65
+ # @return [[ANN_Staff]] returns array of ANN_Staff
66
+ def staff
67
+ super @ann_anime
68
+ end
69
+
70
+ # @return [Hash] hash of self
71
+ def to_h
72
+ # create hash excluding some methods
73
+ to_hash([:to_h, :ann_anime=, :find_info])
74
+ end
75
+
76
+ ##
77
+ # These methods are created via create_method in the constructor
78
+
79
+ # @return [[String]] returns number of episodes
80
+ def num_episodes; end
81
+ # @return [[String]] returns op theme(s)
82
+ def op_theme; end
83
+ # @return [[String]] returns ed theme(s)
84
+ def ed_theme; end
85
+ end
@@ -0,0 +1,63 @@
1
+ require_relative 'ann_media'
2
+
3
+ class ANN_Manga < ANN_Media
4
+ # ann_anime Nokogiri object
5
+ attr_writer :ann_manga
6
+
7
+ def initialize(ann_manga)
8
+ @ann_manga = ann_manga
9
+
10
+ # information available from detail
11
+ @info = Hash.new
12
+ @info[:title] = "Main title"
13
+ @info[:synopsis] = "Plot Summary"
14
+ @info[:genres] = "Genres"
15
+ @info[:vintage] = "Vintage"
16
+ @info[:themes] = "Themes"
17
+ @info[:num_tankoubon] = "Number of tankoubon"
18
+ @info[:num_pages] = "Number of pages"
19
+
20
+ # create methods
21
+ create_methods(@ann_manga, @info)
22
+ end
23
+
24
+ # @return [Nokogiri::XML::NodeSet] return all info with provided key
25
+ def find_info(key)
26
+ super(@ann_manga, key)
27
+ end
28
+
29
+ # @return [String] returns manga id
30
+ def id
31
+ @id ||= @ann_manga['id']
32
+ end
33
+
34
+ # @return [String] returns manga type
35
+ def type
36
+ @type ||= @ann_manga['type']
37
+ end
38
+
39
+ # @return [[ANN_Staff]] returns array of ANN_Staff
40
+ def staff
41
+ super @ann_manga
42
+ end
43
+
44
+ # @return [[ANN_Rating]] returns array of ANN_Episode
45
+ def ratings
46
+ super @ann_manga
47
+ end
48
+
49
+ # @return [Hash] hash of self
50
+ def to_h
51
+ # create hash excluding some methods
52
+ to_hash([:to_h, :ann_manga=, :find_info])
53
+ end
54
+
55
+ ##
56
+ # These methods are created via create_method in the constructor
57
+
58
+ # @return [[String]] returns the number of tankoubon
59
+ def num_tankoubon; end
60
+
61
+ # @return [[String]] returns the number of tankoubon
62
+ def num_pages; end
63
+ end