myanimelist_client 0.1.0

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 322a8bcf59174eb6fc400792ec2d5217d034655f
4
+ data.tar.gz: f93f47d5ff102dca9d61b94bc5838d7eab2dd083
5
+ SHA512:
6
+ metadata.gz: 516a2cfacb909d01e1cdc2f3e92a1e79b519ea075e8f42e200fb10107cd8c9c0f9747142a9dec3eac8b3d3b6cd6ee70d5cf8ae88d99356df570f4a1da3ccc972
7
+ data.tar.gz: a49ba46023cc1d23b66e85e468bc7fef4b6a7518dcd46c15dfbaedfccfd9760bd8e5f9fe7848954365a156aa58ab29f7e3b770331a83b8aa7d99969c3d582960
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ /*~
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.1
5
+ before_install: gem install bundler -v 1.13.6
@@ -0,0 +1 @@
1
+ - LICENSE.txt
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in myanimelist_client.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Oli4242
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,196 @@
1
+ # MyAnimeList Client
2
+ A gem for the [MyAnimeList.net API](https://myanimelist.net/modules.php?go=api).
3
+
4
+ For now you can:
5
+
6
+ * [Search anime](https://myanimelist.net/modules.php?go=api#animemangasearch)
7
+ * [Search manga](https://myanimelist.net/modules.php?go=api#animemangasearch)
8
+ * [Verify credentials](https://myanimelist.net/modules.php?go=api#verifycred)
9
+
10
+ ## Installation
11
+
12
+ Add this line to your application's Gemfile:
13
+
14
+ ```ruby
15
+ gem 'myanimelist_client'
16
+ ```
17
+
18
+ And then execute:
19
+
20
+ $ bundle
21
+
22
+ Or install it yourself as:
23
+
24
+ $ gem install myanimelist_client
25
+
26
+ ## Overview
27
+ ```ruby
28
+ require 'myanimelist_client'
29
+
30
+ # First, create a client
31
+ # MyAnimeList.net requires a valid account to consume their API
32
+ client = MyanimelistClient.new 'username', 'password'
33
+
34
+ # Then you may want to check if your username / password are ok
35
+ if client.verify_credentials.ok?
36
+
37
+ # Now you can use the API
38
+ # Let's search a good anime!
39
+ results = client.search_anime 'K-On'
40
+
41
+ # Everything is nicely wrapped in small objects
42
+ results.sort_by(&:score).reverse!.each do |anime|
43
+ puts "#{anime.title} (#{anime.english}) - #{anime.score}"
44
+ end
45
+
46
+ end
47
+ ```
48
+
49
+ Nice, isn't it?
50
+
51
+ ## Usage
52
+
53
+ ### The Client
54
+ The client memorizes your username and password (myanimelist.net requires a valid account in order to consume their API) then it allows you to use the API:
55
+
56
+ ```ruby
57
+ client = MyanimelistClient.new('username', 'password')
58
+ ```
59
+
60
+ ### Verify Credentials
61
+ Check your username / password with `#verify_credentials`:
62
+
63
+ ```ruby
64
+ user = client.verify_credentials # => MyanimelistClient::UserResponse
65
+
66
+ user.ok? # true when everything went right
67
+ user.error? # true when an error occured
68
+ user.raw # the raw response from the API or the error message
69
+ ```
70
+
71
+ ### Search Anime
72
+ Search anime with `#search_anime`:
73
+
74
+ ```ruby
75
+ results = client.search_anime('anime name')
76
+ # => MyanimelistClient::SearchResponse
77
+
78
+ results.length # => Fixnum
79
+ results.size # => Fixnum
80
+ results.empty? # => true or false
81
+ results.to_a # => Array
82
+ results[0] # => MyanimelistClient::SearchEntry or nil
83
+
84
+ # It includes Ruby's Enumerable module:
85
+ results.each do |anime|
86
+ anime.id # => String or nil
87
+ anime.title # => String or nil
88
+ anime.english # => String or nil
89
+ anime.synonyms # => String or nil
90
+ anime.episodes # => Fixnum or nil
91
+ anime.score # => Float or nil
92
+ anime.type # => String or nil
93
+ anime.status # => String or nil
94
+ anime.start_date # => String or nil
95
+ anime.end_date # => String or nil
96
+ anime.synopsis # => String or nil
97
+ anime.image # => String or nil
98
+
99
+ anime.manga? # => true
100
+ anime.anime? # => false
101
+ anime.upcoming? # => true when the anime is not yet aired
102
+ anime.ongoing? # => true when the anime is currently airing
103
+ anime.finished? # => true when the anime is finished
104
+
105
+ anime.ok? # when no error occured
106
+ anime.error? # when an error occured
107
+ anime.raw # => String containing the raw response from the API or the error message
108
+
109
+ anime.to_h # => Hash
110
+ end
111
+
112
+ ```
113
+
114
+ ### Search Manga
115
+ Search manga with `#search_manga`:
116
+
117
+ ```ruby
118
+ results = client.search_manga('anime name')
119
+ # => MyanimelistClient::SearchResponse
120
+
121
+ results.length # => Fixnum
122
+ results.size # => Fixnum
123
+ results.empty? # => true or false
124
+ results.to_a # => Array
125
+ results[0] # => MyanimelistClient::SearchEntry or nil
126
+
127
+ # It includes Ruby's Enumerable module:
128
+ results.each do |manga|
129
+ manga.id # => String or nil
130
+ manga.title # => String or nil
131
+ manga.english # => String or nil
132
+ manga.synonyms # => String or nil
133
+ manga.chapters # => Fixnum or nil
134
+ manga.volumes # => Fixnum or nil
135
+ manga.score # => Float or nil
136
+ manga.type # => String or nil
137
+ manga.status # => String or nil
138
+ manga.start_date # => String or nil
139
+ manga.end_date # => String or nil
140
+ manga.synopsis # => String or nil
141
+ manga.image # => String or nil
142
+
143
+ manga.manga? # => true
144
+ manga.anime? # => false
145
+ manga.upcoming? # => true when the manga is not yet published
146
+ manga.ongoing? # => true when the manga is currently publishing
147
+ manga.finished? # => true when the manga is finished
148
+
149
+ manga.ok? # when no error occured
150
+ manga.error? # when an error occured
151
+ manga.raw # => String containing the raw response from the API or the error message
152
+
153
+ manga.to_h # => Hash
154
+ end
155
+
156
+ ```
157
+
158
+ ## Contributing
159
+ Report issues at https://github.com/Oli4242/myanimelist_client.
160
+
161
+ If you want to make a pull request:
162
+
163
+ 1. Fork it
164
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
165
+ 3. Write and run the specs before you commit (`rake spec`)
166
+ 4. Commit your changes (`git commit -am 'Add some feature'`)
167
+ 5. Push to the branch (`git push origin my-new-feature`)
168
+ 6. Create new Pull Request
169
+
170
+ ## Thanks
171
+ Thanks to [harveyico](https://github.com/harveyico) for his [myanimelist gem](https://github.com/harveyico/myanimelist), it helped me (I was using it before and its code kinda influenced me).
172
+
173
+ Of course, thanks to the myanimelist.net team for their work.
174
+
175
+ ## License MIT
176
+ > The MIT License (MIT)
177
+ >
178
+ > Copyright (c) 2016 Oli4242
179
+ >
180
+ > Permission is hereby granted, free of charge, to any person obtaining a copy
181
+ > of this software and associated documentation files (the "Software"), to deal
182
+ > in the Software without restriction, including without limitation the rights
183
+ > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
184
+ > copies of the Software, and to permit persons to whom the Software is
185
+ > furnished to do so, subject to the following conditions:
186
+ >
187
+ > The above copyright notice and this permission notice shall be included in
188
+ > all copies or substantial portions of the Software.
189
+ >
190
+ > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
191
+ > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
192
+ > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
193
+ > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
194
+ > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
195
+ > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
196
+ > THE SOFTWARE.
@@ -0,0 +1,9 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+ require "yard"
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ YARD::Rake::YardocTask.new
8
+
9
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "myanimelist_client"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,8 @@
1
+ require 'rest-client'
2
+ require 'nokogiri'
3
+
4
+ require 'myanimelist_client/version'
5
+ require 'myanimelist_client/myanimelist_client'
6
+ require 'myanimelist_client/user_response'
7
+ require 'myanimelist_client/search_response'
8
+ require 'myanimelist_client/search_entry'
@@ -0,0 +1,107 @@
1
+ # This is the main class. It represents a client that can consume the {https://myanimelist.net/modules.php?go=api MyAnimeList.net API}.
2
+ #
3
+ # It allows to search for anime and manga titles as well as verify your username / password.
4
+ #
5
+ # @example
6
+ # require 'myanimelist_client'
7
+ #
8
+ # # MyAnimeList requires a valid account in order to consume their API:
9
+ # client = MyanimelistClient.new 'username', 'password'
10
+ #
11
+ # # Verify credentials:
12
+ # user = client.verify_credentials # => UserResponse
13
+ #
14
+ # if user.error?
15
+ # STDERR.puts "An error occured: #{user.raw}"
16
+ # exit
17
+ # end
18
+ #
19
+ # # Search anime:
20
+ # results = client.search_anime 'anime name' # => SearchResponse
21
+ #
22
+ # puts 'Error...' if results.error?
23
+ # puts 'Found nothing...' if results.ok? && results.empty?
24
+ #
25
+ # results.sort_by(:score).reverse!.each do |anime|
26
+ # puts "#{anime.title} - #{anime.score}"
27
+ # end
28
+ #
29
+ # # Search manga:
30
+ # results = client.search_manga 'manga name' # => SearchResponse
31
+ #
32
+ # results.select{ |manga| manga.volumes < 10 }.each do |manga|
33
+ # puts "#{manga.title} - #{manga.english}"
34
+ # end
35
+ #
36
+ # @attr [String] username Returns the username
37
+ # @attr [String] password Returns the password
38
+ #
39
+ class MyanimelistClient
40
+ attr_accessor :username, :password
41
+
42
+ # @param [String] username A valid myanimelist username
43
+ # @param [String] password A valid myanimelist password
44
+ #
45
+ def initialize username, password
46
+ @username = username
47
+ @password = password
48
+ end
49
+
50
+ # Allows to check username/password.
51
+ # @see https://myanimelist.net/modules.php?go=api#verifycred The MyAnimeList's API Documentation about verify_credentials.xml
52
+ # @return [UserResponse] The API response or the error message nicely wraped in an object.
53
+ #
54
+ def verify_credentials
55
+ begin
56
+ response = RestClient::Request.execute(
57
+ method: :get,
58
+ url: 'https://myanimelist.net/api/account/verify_credentials.xml',
59
+ user: @username,
60
+ password: @password
61
+ )
62
+ UserResponse.new response.body
63
+ rescue RestClient::ExceptionWithResponse => e
64
+ UserResponse.new e.response.body
65
+ end
66
+ end
67
+
68
+ # Allows to search anime titles.
69
+ # @see https://myanimelist.net/modules.php?go=api#animemangasearch The MyAnimeList's API Documentation about search.xml
70
+ # @param [String] query The search query.
71
+ # @return [SearchResponse] The API response or the error message nicely wraped in an object.
72
+ #
73
+ def search_anime query
74
+ begin
75
+ escaped_query = CGI::escape query
76
+ response = RestClient::Request.execute(
77
+ method: :get,
78
+ url: "https://myanimelist.net/api/anime/search.xml?q=#{escaped_query}",
79
+ user: @username,
80
+ password: @password
81
+ )
82
+ SearchResponse.new response.body
83
+ rescue RestClient::ExceptionWithResponse => e
84
+ SearchResponse.new e.response.body, :error
85
+ end
86
+ end
87
+
88
+ # Allows to search manga titles.
89
+ # @see https://myanimelist.net/modules.php?go=api#animemangasearch The MyAnimeList's API Documentation about search.xml
90
+ # @param [String] query The search query.
91
+ # @return [SearchResponse] The API response or the error message nicely wraped in an object.
92
+ #
93
+ def search_manga query
94
+ begin
95
+ escaped_query = CGI::escape query
96
+ response = RestClient::Request.execute(
97
+ method: :get,
98
+ url: "https://myanimelist.net/api/manga/search.xml?q=#{escaped_query}",
99
+ user: @username,
100
+ password: @password
101
+ )
102
+ SearchResponse.new response.body
103
+ rescue RestClient::ExceptionWithResponse => e
104
+ SearchResponse.new e.response.body, :error
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,124 @@
1
+ # This class is used to reprensent an entry within a {SearchResponse}. It can describe either an anime or a manga.
2
+ #
3
+ # @example
4
+ # require 'myanimelist_client'
5
+ #
6
+ # client = MyanimelistClient.new 'username', 'password'
7
+ #
8
+ # client.search_anime('anime name').each do |entry|
9
+ # # entry is a SearchResponse.
10
+ #
11
+ # # It exposes all its attributes:
12
+ # entry.id # => String or nil
13
+ # entry.title # => String or nil
14
+ # entry.english # => String or nil
15
+ # entry.synonyms # => String or nil
16
+ # entry.episodes # => Fixnum or nil
17
+ # entry.chapters # => Fixnum or nil
18
+ # entry.volumes # => Fixnum or nil
19
+ # entry.score # => Float or nil
20
+ # entry.type # => String or nil
21
+ # entry.status # => String or nil
22
+ # entry.start_date # => String or nil
23
+ # entry.end_date # => String or nil
24
+ # entry.synopsis # => String or nil
25
+ # entry.image # => String or nil
26
+ #
27
+ # # It provides some useful predicates:
28
+ # entry.manga? # => true or false
29
+ # entry.anime? # => true or false
30
+ # entry.upcoming? # => true or false
31
+ # entry.ongoing? # => true or false
32
+ # entry.finished? # => true or false
33
+ #
34
+ # # It can be converted to a hash:
35
+ # entry.to_h # => Hash
36
+ # end
37
+ #
38
+ # @attr [String, nil] id Returns the ID.
39
+ # @attr [String, nil] title Returns the title.
40
+ # @attr [String, nil] english Returns the english title.
41
+ # @attr [String, nil] synonyms Returns the synonyms.
42
+ # @attr [Fixnum, nil] episodes Returns the number of episodes (+nil+ when the entry describes a manga).
43
+ # @attr [Fixnum, nil] chapters Returns the number of chapters (+nil+ when the entry describes an anime).
44
+ # @attr [Fixnum, nil] volumes Returns the number of volumes (+nil+ when the entry describes an anime).
45
+ # @attr [Float, nil] score Returns the score.
46
+ # @attr [String, nil] type Returns the type.
47
+ # @attr [String, nil] status Returns the status.
48
+ # @attr [String, nil] start_date Returns the start date.
49
+ # @attr [String, nil] end_date Returns the end date.
50
+ # @attr [String, nil] synopsis Returns the synopsis.
51
+ # @attr [String, nil] image Returns the image's URL.
52
+ #
53
+ class MyanimelistClient::SearchEntry
54
+
55
+ # The list of attributes defining the entry for internal usage.
56
+ ATTRIBUTES = %w(
57
+ id
58
+ title
59
+ english
60
+ synonyms
61
+ episodes
62
+ chapters
63
+ volumes
64
+ score
65
+ type
66
+ status
67
+ start_date
68
+ end_date
69
+ synopsis
70
+ image
71
+ )
72
+
73
+ # attr_reader *ATTRIBUTES # this line cause a warning in yard
74
+ send :attr_reader, *ATTRIBUTES # this line doesn't... :/
75
+
76
+ # @param [Hash] options A hash used to hydrate the object.
77
+ def initialize options={}
78
+ ATTRIBUTES.each do |attribute|
79
+ instance_variable_set "@#{attribute}", options[attribute] || options[attribute.to_sym]
80
+ end
81
+ end
82
+
83
+ # Creates a hash representing the entry.
84
+ # @return [Hash]
85
+ def to_h
86
+ values = ATTRIBUTES.map{|attribute| send attribute }
87
+ Hash[ATTRIBUTES.zip values]
88
+ end
89
+
90
+ # Performs a loose equality based on the value of the attributes (see {ATTRIBUTES}).
91
+ def == other
92
+ ATTRIBUTES.each do |attribute|
93
+ if !other.respond_to?(attribute) || send(attribute) != other.send(attribute)
94
+ return false
95
+ end
96
+ end
97
+ true
98
+ end
99
+
100
+ # Returns +true+ when the entry describes an anime.
101
+ def anime?
102
+ !!@episodes
103
+ end
104
+
105
+ # Returns +true+ when the entry describes a manga.
106
+ def manga?
107
+ !!(@chapters && @volumes)
108
+ end
109
+
110
+ # Returns +true+ when the anime is not yet aired / when the manga is not yet published.
111
+ def upcoming?
112
+ !!(/not\s+yet/i =~ @status)
113
+ end
114
+
115
+ # Returns +true+ when the anime is currently airing / when the manga is currently publishing.
116
+ def ongoing?
117
+ !!(/currently|publishing/i =~ @status)
118
+ end
119
+
120
+ # Returns +true+ when the anime or manga is finished.
121
+ def finished?
122
+ !!(/finished/i =~ @status)
123
+ end
124
+ end
@@ -0,0 +1,122 @@
1
+ # This class represents a response to a search request. It is returned by {#search_anime} and {#search_manga}.
2
+ #
3
+ # +SearchResponse+ is responsible for parsing and wraping the XML response from the myanimelist API (or the error message).
4
+ #
5
+ # It wraps every entry in a {SearchEntry} instance and it includes the Ruby's +Enumerable+ module so it is easy to iterate over, sort it, etc.
6
+ #
7
+ # @example
8
+ # require 'myanimelist_client'
9
+ #
10
+ # client = MyanimelistClient.new 'username', 'password'
11
+ #
12
+ # results = client.search_anime 'anime name'
13
+ # # => SearchResponse
14
+ #
15
+ # # It provides two predicates for error handling:
16
+ # results.ok? # => true or false
17
+ # results.error? # => true or false
18
+ #
19
+ # # On error, results.raw may contain an error message:
20
+ # if results.error?
21
+ # results.raw # => String or nil
22
+ # end
23
+ #
24
+ # # On success you can browse the results:
25
+ # if results.ok?
26
+ # # It provides basic informations:
27
+ # results.length # => Fixnum
28
+ # results.empty? # => true or false
29
+ #
30
+ # # It is browsable more or less like an Array would be:
31
+ # results.first == results[0]
32
+ # # => true
33
+ #
34
+ # # It is enumerable so you can use #each, #map, #sort_by, #select, etc.
35
+ # # An exemple:
36
+ # top3 = results.sort_by(&:score).reverse!.take(3).to_a
37
+ # top3.each_with_index do |entry, index|
38
+ # puts "#{index+1}. #{entry.title} - #{entry.score}"
39
+ # end
40
+ #
41
+ # # Yes, it is awesome ! :3
42
+ #
43
+ # end
44
+ #
45
+ # @attr [String, nil] raw Returns the raw response from the API or the error message.
46
+ #
47
+ class MyanimelistClient::SearchResponse
48
+ include Enumerable
49
+ attr_reader :raw
50
+
51
+ # @param [String, nil] raw_xml The raw XML response from the API or an error message.
52
+ # @param [String, nil] error The error flag, set it to true if an error occured.
53
+ def initialize raw_xml, error=false
54
+ @raw = raw_xml
55
+ @error = !!error
56
+ @entries = []
57
+
58
+ parsed_xml = Nokogiri::XML raw_xml
59
+ animes = parsed_xml.css 'anime entry'
60
+ mangas = parsed_xml.css 'manga entry'
61
+
62
+ (animes + mangas).each do |entry|
63
+ @entries.push MyanimelistClient::SearchEntry.new(
64
+ id: entry.at_css('id')&.content,
65
+ title: entry.at_css('title')&.content,
66
+ english: entry.at_css('english')&.content,
67
+ synonyms: entry.at_css('synonyms')&.content,
68
+ episodes: entry.at_css('episodes')&.content&.to_i,
69
+ chapters: entry.at_css('chapters')&.content&.to_i,
70
+ volumes: entry.at_css('volumes')&.content&.to_i,
71
+ score: entry.at_css('score')&.content&.to_f,
72
+ type: entry.at_css('type')&.content,
73
+ status: entry.at_css('status')&.content,
74
+ start_date: entry.at_css('start_date')&.content,
75
+ end_date: entry.at_css('end_date')&.content,
76
+ synopsis: entry.at_css('synopsis')&.content,
77
+ image: entry.at_css('image')&.content
78
+ )
79
+ end
80
+ end
81
+
82
+ # Similar to +Array+'s +#each+.
83
+ # @return [self, Enumerator]
84
+ def each &block
85
+ if block_given?
86
+ @entries.each &block
87
+ self
88
+ else
89
+ @entries.each
90
+ end
91
+ end
92
+
93
+ # Returns the number of entries returned by the search request.
94
+ # @return [Fixnum] The number of entries (either animes or mangas) in the search response.
95
+ def length
96
+ @entries.size
97
+ end
98
+
99
+ alias_method :size, :length
100
+
101
+ # Is it empty?
102
+ def empty?
103
+ @entries.empty?
104
+ end
105
+
106
+ # Allows to browse the entries like with an array.
107
+ # @param [Fixnum] index The index of the desired entry.
108
+ # @return [SearchEntry, nil]
109
+ def [] index
110
+ @entries[index]
111
+ end
112
+
113
+ # Returns +true+ if an error occured.
114
+ def error?
115
+ @error
116
+ end
117
+
118
+ # Returns +true+ if no error occured.
119
+ def ok?
120
+ not error?
121
+ end
122
+ end
@@ -0,0 +1,53 @@
1
+ # This class represents a User. It is returned by {#verify_credentials}.
2
+ #
3
+ # +UserResponse+ is responsible for parsing and wraping the XML (or any error message) from the API.
4
+ #
5
+ # @example
6
+ # require 'myanimelist_client'
7
+ #
8
+ # client = MyanimelistClient.new 'username', 'password'
9
+ #
10
+ # user = client.verify_credentials
11
+ # # => UserResponse
12
+ #
13
+ # # It provides two predicates to handle login errors:
14
+ # user.error? # => true or false
15
+ # user.ok? # => true or false
16
+ #
17
+ # # On error, user.raw may contain an error message:
18
+ # if user.error?
19
+ # user.raw # => String or nil
20
+ # end
21
+ #
22
+ # # On success you can access to user's attributes:
23
+ # if user.ok?
24
+ # user.id # => String or nil
25
+ # user.username # => String or nil
26
+ # end
27
+ #
28
+ # @attr [String, nil] raw Returns the raw response from the API. Or the raw error message.
29
+ # @attr [String, nil] id Return the MyAnimeList ID of the current account.
30
+ # @attr [String, nil] username Returns the MyAnimeList username of the current account.
31
+ #
32
+ class MyanimelistClient::UserResponse
33
+ attr_reader :raw, :id, :username
34
+
35
+ # @param [String, nil] raw_xml The raw response from the API or an error message (or even +nil+).
36
+ def initialize raw_xml
37
+ @raw = raw_xml
38
+
39
+ parsed_xml = Nokogiri::XML raw_xml
40
+ @id = parsed_xml.at_css('id')&.content
41
+ @username = parsed_xml.at_css('username')&.content
42
+ end
43
+
44
+ # Returns +true+ if an error occured.
45
+ def error?
46
+ @raw.nil? || @id.nil? || @username.nil?
47
+ end
48
+
49
+ # Returns +true+ if no error occured.
50
+ def ok?
51
+ not error?
52
+ end
53
+ end
@@ -0,0 +1,4 @@
1
+ class MyanimelistClient
2
+ # Versioning.
3
+ VERSION = "0.1.0"
4
+ end
@@ -0,0 +1,42 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'myanimelist_client/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "myanimelist_client"
8
+ spec.version = MyanimelistClient::VERSION
9
+ spec.authors = ["Oli4242"]
10
+ spec.email = ["Oli4242@users.noreply.github.com"]
11
+
12
+ spec.summary = %q{A client for the MyAnimeList.net API}
13
+ # spec.description = %q{TODO: Write a longer description or delete this line.}
14
+ spec.homepage = "https://github.com/Oli4242/myanimelist_client"
15
+ spec.license = "MIT"
16
+
17
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
18
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
19
+ if spec.respond_to?(:metadata)
20
+ spec.metadata['allowed_push_host'] = 'https://rubygems.org'
21
+ else
22
+ raise "RubyGems 2.0 or newer is required to protect against " \
23
+ "public gem pushes."
24
+ end
25
+
26
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
27
+ f.match(%r{^(test|spec|features)/})
28
+ end
29
+ spec.bindir = "exe"
30
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
31
+ spec.require_paths = ["lib"]
32
+
33
+ spec.required_ruby_version = '>= 2.3'
34
+
35
+ spec.add_development_dependency "bundler", "~> 1.13"
36
+ spec.add_development_dependency "rake", "~> 10.0"
37
+ spec.add_development_dependency "rspec", "~> 3.0"
38
+ spec.add_development_dependency "yard", "~> 0.9"
39
+
40
+ spec.add_dependency "rest-client", "~> 2.0"
41
+ spec.add_dependency "nokogiri", "~> 1.6"
42
+ end
metadata ADDED
@@ -0,0 +1,146 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: myanimelist_client
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Oli4242
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-11-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.13'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.13'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: yard
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.9'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.9'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rest-client
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '2.0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '2.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: nokogiri
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '1.6'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '1.6'
97
+ description:
98
+ email:
99
+ - Oli4242@users.noreply.github.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - ".gitignore"
105
+ - ".rspec"
106
+ - ".travis.yml"
107
+ - ".yardopts"
108
+ - Gemfile
109
+ - LICENSE.txt
110
+ - README.md
111
+ - Rakefile
112
+ - bin/console
113
+ - bin/setup
114
+ - lib/myanimelist_client.rb
115
+ - lib/myanimelist_client/myanimelist_client.rb
116
+ - lib/myanimelist_client/search_entry.rb
117
+ - lib/myanimelist_client/search_response.rb
118
+ - lib/myanimelist_client/user_response.rb
119
+ - lib/myanimelist_client/version.rb
120
+ - myanimelist_client.gemspec
121
+ homepage: https://github.com/Oli4242/myanimelist_client
122
+ licenses:
123
+ - MIT
124
+ metadata:
125
+ allowed_push_host: https://rubygems.org
126
+ post_install_message:
127
+ rdoc_options: []
128
+ require_paths:
129
+ - lib
130
+ required_ruby_version: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - ">="
133
+ - !ruby/object:Gem::Version
134
+ version: '2.3'
135
+ required_rubygems_version: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ version: '0'
140
+ requirements: []
141
+ rubyforge_project:
142
+ rubygems_version: 2.5.1
143
+ signing_key:
144
+ specification_version: 4
145
+ summary: A client for the MyAnimeList.net API
146
+ test_files: []