mangadex 5.5.8 → 5.7.5.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '038676da7b94f64da0e1ba27d2a0f2563153bf785fc027830132330fcaa6db41'
4
- data.tar.gz: 219bc69785823ce34145b10103149d3f397803a7a8a77732a5ee98983162e211
3
+ metadata.gz: 52dec750d8fbff40af52a2be9375a2b9729d8b7a3422b8f6dac5504d79be8eff
4
+ data.tar.gz: 277f46e56d6f84b10e3b16878d4a9196eb70323e99133bbd6294f608e3dc6137
5
5
  SHA512:
6
- metadata.gz: fab8f18bd656d148f42513e316389b6505aa63c24fc9be2722f8ac4752f9dfb2bfde8749769af69acdf2d90714aa64e87f82ad40a0405cc3bd7158240bb998cc
7
- data.tar.gz: 4f539c62ada95b984cf96f04b2450385b3bf348d8efcbbb501ab67ac9f21f70c38fb082763b68a3c3851fb572047a07650ab4f80a975987aadea9fb5f4ae505a
6
+ metadata.gz: 81f339164b6a02619c57f9c9d25daf5c1f652b6e547f3923a7957f7a8afed622c14cc11f4368009d9a37d8c414a60e5210cc83785b4409895e045e3bf6b024a0
7
+ data.tar.gz: ccd3bc3cad6e3f140df1bc79a9b402508178baabb2730364aecf1270a99a20fd932d4e5e9af301e93b879b808c72db2044e4cd197c9edd562cb159f03ce901bd
@@ -0,0 +1,19 @@
1
+ name: Docker Image CI
2
+
3
+ on:
4
+ push:
5
+ branches: ["culture"]
6
+ pull_request:
7
+ branches: ["culture"]
8
+
9
+ jobs:
10
+ build:
11
+ runs-on: ubuntu-latest
12
+
13
+ steps:
14
+ - uses: actions/checkout@v3
15
+ - name: Build the Docker image
16
+ run: docker build . --file Dockerfile --tag mangadex
17
+
18
+ - name: Run tests
19
+ run: docker run --rm -t mangadex:latest bundle exec rspec
@@ -0,0 +1,20 @@
1
+ name: Publish Gem
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - v*
7
+ jobs:
8
+ build:
9
+ runs-on: ubuntu-latest
10
+
11
+ steps:
12
+ - uses: actions/checkout@v1
13
+
14
+ - name: Release Gem
15
+ if: contains(github.ref, 'refs/tags/v')
16
+ uses: cadwallion/publish-rubygems-action@master
17
+ env:
18
+ GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
19
+ RUBYGEMS_API_KEY: ${{secrets.RUBYGEMS_API_KEY}}
20
+ RELEASE_COMMAND: rake release
data/.gitignore CHANGED
@@ -15,3 +15,7 @@
15
15
 
16
16
  # Misc
17
17
  .DS_Store
18
+ .env*
19
+
20
+ # Docker
21
+ .devcontainer/
data/Dockerfile CHANGED
@@ -5,3 +5,10 @@ WORKDIR /app
5
5
  COPY . /app
6
6
 
7
7
  RUN bundle install --jobs 4 --retry 5 --quiet
8
+
9
+ COPY entrypoint.sh /usr/bin/
10
+ RUN chmod +x /usr/bin/entrypoint.sh
11
+
12
+ ENTRYPOINT [ "entrypoint.sh" ]
13
+
14
+ CMD [ "bin/console" ]
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- mangadex (5.5.8)
4
+ mangadex (5.7.5.1)
5
5
  psych (~> 4.0.1)
6
6
  rest-client (~> 2.1)
7
7
  sorbet-runtime
@@ -9,91 +9,50 @@ PATH
9
9
  GEM
10
10
  remote: https://rubygems.org/
11
11
  specs:
12
- activesupport (7.0.2.3)
13
- concurrent-ruby (~> 1.0, >= 1.0.2)
14
- i18n (>= 1.6, < 2)
15
- minitest (>= 5.1)
16
- tzinfo (~> 2.0)
17
- addressable (2.8.0)
18
- public_suffix (>= 2.0.2, < 5.0)
19
12
  coderay (1.1.3)
20
- concurrent-ruby (1.1.9)
21
- crack (0.4.5)
22
- rexml
23
13
  diff-lcs (1.5.0)
24
14
  domain_name (0.5.20190701)
25
15
  unf (>= 0.0.5, < 1.0.0)
26
- hashdiff (1.0.1)
27
16
  http-accept (1.7.0)
28
17
  http-cookie (1.0.5)
29
18
  domain_name (~> 0.5)
30
- i18n (1.10.0)
31
- concurrent-ruby (~> 1.0)
32
19
  method_source (1.0.0)
33
20
  mime-types (3.4.1)
34
21
  mime-types-data (~> 3.2015)
35
22
  mime-types-data (3.2022.0105)
36
- minitest (5.15.0)
37
- mustermann (1.1.1)
38
- ruby2_keywords (~> 0.0.1)
39
23
  netrc (0.11.0)
40
24
  pry (0.14.1)
41
25
  coderay (~> 1.1)
42
26
  method_source (~> 1.0)
43
- psych (4.0.4)
27
+ psych (4.0.6)
44
28
  stringio
45
- public_suffix (4.0.6)
46
- rack (2.2.3)
47
- rack-protection (2.2.0)
48
- rack
49
29
  rake (13.0.6)
50
- request_interceptor (1.0.0)
51
- activesupport (>= 4.0)
52
- rack
53
- sinatra
54
- smart_properties (~> 1.0)
55
- webmock (~> 3.0)
56
30
  rest-client (2.1.0)
57
31
  http-accept (>= 1.7.0, < 2.0)
58
32
  http-cookie (>= 1.0.2, < 2.0)
59
33
  mime-types (>= 1.16, < 4.0)
60
34
  netrc (~> 0.8)
61
- rexml (3.2.5)
62
- rspec (3.11.0)
63
- rspec-core (~> 3.11.0)
64
- rspec-expectations (~> 3.11.0)
65
- rspec-mocks (~> 3.11.0)
66
- rspec-core (3.11.0)
67
- rspec-support (~> 3.11.0)
68
- rspec-expectations (3.11.0)
35
+ rspec (3.12.0)
36
+ rspec-core (~> 3.12.0)
37
+ rspec-expectations (~> 3.12.0)
38
+ rspec-mocks (~> 3.12.0)
39
+ rspec-core (3.12.0)
40
+ rspec-support (~> 3.12.0)
41
+ rspec-expectations (3.12.0)
69
42
  diff-lcs (>= 1.2.0, < 2.0)
70
- rspec-support (~> 3.11.0)
71
- rspec-mocks (3.11.0)
43
+ rspec-support (~> 3.12.0)
44
+ rspec-mocks (3.12.0)
72
45
  diff-lcs (>= 1.2.0, < 2.0)
73
- rspec-support (~> 3.11.0)
74
- rspec-support (3.11.0)
75
- ruby2_keywords (0.0.5)
76
- sinatra (2.2.0)
77
- mustermann (~> 1.0)
78
- rack (~> 2.2)
79
- rack-protection (= 2.2.0)
80
- tilt (~> 2.0)
81
- smart_properties (1.17.0)
82
- sorbet (0.5.9742)
83
- sorbet-static (= 0.5.9742)
84
- sorbet-runtime (0.5.10030)
85
- sorbet-static (0.5.9742-x86_64-linux)
46
+ rspec-support (~> 3.12.0)
47
+ rspec-support (3.12.0)
48
+ sorbet (0.5.10535)
49
+ sorbet-static (= 0.5.10535)
50
+ sorbet-runtime (0.5.10539)
51
+ sorbet-static (0.5.10535-x86_64-linux)
86
52
  stringio (3.0.2)
87
- tilt (2.0.10)
88
- tzinfo (2.0.4)
89
- concurrent-ruby (~> 1.0)
90
53
  unf (0.1.4)
91
54
  unf_ext
92
55
  unf_ext (0.0.8.2)
93
- webmock (3.14.0)
94
- addressable (>= 2.8.0)
95
- crack (>= 0.3.2)
96
- hashdiff (>= 0.4.0, < 2.0.0)
97
56
 
98
57
  PLATFORMS
99
58
  x86_64-linux
@@ -103,7 +62,6 @@ DEPENDENCIES
103
62
  mangadex!
104
63
  pry
105
64
  rake (~> 13.0)
106
- request_interceptor (~> 1.0.0)
107
65
  rspec (~> 3.0)
108
66
  sorbet
109
67
 
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- [![Ruby](https://github.com/thedrummeraki/mangadex/actions/workflows/ruby.yml/badge.svg)](https://github.com/thedrummeraki/mangadex/actions/workflows/ruby.yml)<a href="https://rubygems.org/gems/mangadex"><img src="https://badgen.net/rubygems/v/mangadex" /></a>
1
+ [![Docker Image CI](https://github.com/thedrummeraki/mangadex/actions/workflows/docker-image.yml/badge.svg)](https://github.com/thedrummeraki/mangadex/actions/workflows/docker-image.yml)<a href="https://rubygems.org/gems/mangadex"><img src="https://badgen.net/rubygems/v/mangadex" /></a>
2
2
 
3
3
  # Mangadex
4
4
 
@@ -7,6 +7,7 @@ Welcome to `mangadex`, your next favourite Ruby gem for interacting with [Mangad
7
7
  ## Important information
8
8
 
9
9
  **By using this gem you accept**:
10
+
10
11
  - To **credit [Mangadex](https://mangadex.org)**. This gem is your friendly neighbourhood wrapper on _their_ API.
11
12
  - To **credit scanlation groups**, especially if you offer the ability to read chapters.
12
13
  - **Not to run any ads** on the service that will use this gem. Please do not make money off of Mangadex's services.
@@ -196,6 +197,7 @@ end
196
197
  ```
197
198
 
198
199
  Already created your `User` class? Make sure it has all of the following:
200
+
199
201
  - `mangade_user_id`: ID used to identify your user on Mangadex
200
202
  - `username`: Your username
201
203
  - `session`: The session token (valid for 15 minutes)
@@ -282,7 +284,7 @@ class SessionController < ApplicationController
282
284
  rescue Mangadex::Errors::AuthenticationError => error
283
285
  # See https://api.mangadex.org/docs.html to learn more about errors
284
286
  Rails.logger.error(error.response.errors)
285
-
287
+
286
288
  # Handle authentication errors here
287
289
  end
288
290
 
@@ -325,7 +327,7 @@ class ProtectedController < ApplicationController
325
327
  end
326
328
  ```
327
329
 
328
- We're going with managing (list, create, show, edit, delete) MDLists (ie: custom lists). __We're not using strong params below to keep things simple, but you should, especially when mutating data (ie: creating and editing)__.
330
+ We're going with managing (list, create, show, edit, delete) MDLists (ie: custom lists). **We're not using strong params below to keep things simple, but you should, especially when mutating data (ie: creating and editing)**.
329
331
 
330
332
  ```ruby
331
333
  class CustomListsController < ProtectedController
@@ -384,10 +386,34 @@ end
384
386
 
385
387
  ## Development
386
388
 
389
+ ### Docker
390
+
391
+ You can use Docker to get started with dev work on the repo. After installing Dcoker, you can build the image:
392
+
393
+ ```
394
+ docker build -t mangadex .
395
+ ```
396
+
397
+ Then run the ruby console with the gem loaded
398
+
399
+ ```
400
+ docker run --rm -it mangadex:latest
401
+ ```
402
+
403
+ You can also log in directly when setting the `MD_USERNAME` and `MD_PASSWORD` (or `MD_EMAIL`) environment variables:
404
+
405
+ ```
406
+ docker run --rm -e MD_USERNAME=username -e MD_PASSWORD=password -it mangadex:latest
407
+ ```
408
+
409
+ ### Locally
410
+
387
411
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
388
412
 
389
413
  To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
390
414
 
415
+ You can also
416
+
391
417
  ## Contributing
392
418
 
393
419
  Bug reports and pull requests are welcome on GitHub at https://github.com/thedrummeraki/mangadex. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
data/entrypoint.sh ADDED
@@ -0,0 +1,5 @@
1
+ #!/bin/bash
2
+ set -e
3
+
4
+ # Then exec the container's main process (what's set as CMD in the Dockerfile).
5
+ exec "$@"
data/lib/mangadex/auth.rb CHANGED
@@ -48,7 +48,7 @@ module Mangadex
48
48
  Mangadex.context.user = user
49
49
 
50
50
  if block_given?
51
- return yield(user)
51
+ return yield(user)
52
52
  end
53
53
 
54
54
  user
@@ -58,12 +58,7 @@ module Mangadex
58
58
 
59
59
  sig { returns(Hash) }
60
60
  def self.check_token
61
- JSON.parse(
62
- Mangadex::Internal::Request.get(
63
- '/auth/check',
64
- raw: true,
65
- )
66
- )
61
+ Mangadex::Internal::Request.get('/auth/check')
67
62
  end
68
63
 
69
64
  sig { returns(T.any(T::Boolean, Mangadex::Api::Response)) }
@@ -0,0 +1,47 @@
1
+ module Mangadex
2
+ class ChapterReadMarker
3
+ extend T::Sig
4
+
5
+ sig { params(id: String).returns(T::Api::GenericResponse) }
6
+ def self.get(id)
7
+ Mangadex::Internal::Definition.must(id)
8
+
9
+ Mangadex::Internal::Request.get(
10
+ '/manga/{id}/read' % {id: id}
11
+ )
12
+ end
13
+
14
+ sig { params(id: String, args: T::Api::Arguments).returns(T::Api::GenericResponse) }
15
+ def self.create(id, **args)
16
+ Mangadex::Internal::Definition.must(id)
17
+
18
+ Mangadex::Internal::Request.post(
19
+ '/manga/{id}/read' % {id: id},
20
+ payload: Mangadex::Internal::Definition.validate(args, {
21
+ update_history: { accepts: [true, false] }
22
+ })
23
+ )
24
+ end
25
+
26
+ sig { sig(args: T::Api::Arguments).returns(T::Api::GenericResponse) }
27
+ def self.list(**args)
28
+ to_a = Mangadex::Internal::Definition.converts(:to_a)
29
+
30
+ Mangadex::Internal::Request.get(
31
+ '/manga/list',
32
+ Mangadex::Internal::Definition.validate(args, {
33
+ ids: { accepts: [String], converts: to_a, required: true },
34
+ grouped: { accepts: [true, false] },
35
+ })
36
+ )
37
+ end
38
+
39
+ sig { returns(T::Api::GenericResponse) }
40
+ def self.user_list
41
+ Mangadex::Internal::Request.get(
42
+ '/user/history',
43
+ auth: true,
44
+ )
45
+ end
46
+ end
47
+ end
@@ -1,4 +1,10 @@
1
1
  # typed: true
2
+
3
+ require_relative "definitions/accepts"
4
+
5
+ require_relative "definitions/base"
6
+ require_relative "definitions/content_rating"
7
+
2
8
  module Mangadex
3
9
  module Internal
4
10
  class Definition
@@ -8,7 +14,7 @@ module Mangadex
8
14
  def initialize(key, value, converts: nil, accepts: nil, required: false)
9
15
  @converts = converts
10
16
  @key = key
11
- @value = convert_value(value)
17
+ @value = convert_value(value.presence)
12
18
  @raw_value = value
13
19
  @accepts = accepts
14
20
  @required = required ? true : false
@@ -39,6 +45,10 @@ module Mangadex
39
45
  end
40
46
 
41
47
  def convert_value(value)
48
+ if converts.is_a?(Symbol)
49
+ converts_proc = self.class.converts(converts)
50
+ return converts_proc.call(value) if converts_proc
51
+ end
42
52
  if converts.is_a?(Proc)
43
53
  converts.call(value)
44
54
  elsif converts.is_a?(String) || converts.is_a?(Symbol)
@@ -94,7 +104,10 @@ module Mangadex
94
104
 
95
105
  class << self
96
106
  def converts(key=nil)
97
- procs = { to_a: -> ( x ) { Array(x) } }
107
+ procs = {
108
+ to_a: -> ( x ) { Array(x) },
109
+ to_i: -> ( x ) { Integer(x.to_s, 10) if x },
110
+ }
98
111
  return procs if key.nil?
99
112
 
100
113
  procs[key]
@@ -115,13 +128,16 @@ module Mangadex
115
128
  translated_language: { accepts: [String], converts: converts(:to_a) },
116
129
  original_language: { accepts: [String] },
117
130
  excluded_original_language: { accepts: [String] },
118
- content_rating: { accepts: %w(safe suggestive erotica pornographic), converts: converts(:to_a) },
131
+ content_rating: Definitions::ContentRating,
119
132
  include_future_updates: { accepts: %w(0 1) },
120
133
  created_at_since: { accepts: %r{^\d{4}-[0-1]\d-([0-2]\d|3[0-1])T([0-1]\d|2[0-3]):[0-5]\d:[0-5]\d$} },
121
134
  updated_at_since: { accepts: %r{^\d{4}-[0-1]\d-([0-2]\d|3[0-1])T([0-1]\d|2[0-3]):[0-5]\d:[0-5]\d$} },
122
135
  publish_at_since: { accepts: %r{^\d{4}-[0-1]\d-([0-2]\d|3[0-1])T([0-1]\d|2[0-3]):[0-5]\d:[0-5]\d$} },
123
136
  order: { accepts: Hash },
124
137
  includes: { accepts: [String], converts: converts(:to_a) },
138
+ include_empty_pages: { accepts: [0, 1], converts: converts(:to_i) },
139
+ include_future_publish_at: { accepts: [0, 1], converts: converts(:to_i) },
140
+ include_external_url: { accepts: [0, 1], converts: converts(:to_i) },
125
141
  },
126
142
  )
127
143
  end
@@ -151,11 +167,20 @@ module Mangadex
151
167
  end
152
168
 
153
169
  definition.each do |key, definition|
154
- validator = Definition.new(key, args[key], **definition.symbolize_keys)
155
- validation_error = validator.error
170
+ validation_error = if definition.is_a?(Class) && definition < Definitions::Base
171
+ validator = definition.new(args[key])
172
+ validator.validate
173
+ validator.error_message
174
+ elsif !definition.is_a?(Class)
175
+ validator = Definition.new(key, args[key], **definition.symbolize_keys)
176
+ validator.error
177
+ else
178
+ raise "Invalid definition class: #{definition}"
179
+ end
180
+
156
181
  if validation_error
157
182
  errors << { message: validation_error }
158
- elsif !validator.empty?
183
+ elsif !validator.empty? && validator.value
159
184
  args[key] = validator.value
160
185
  end
161
186
  end
@@ -173,7 +198,9 @@ module Mangadex
173
198
  raise ArgumentError, "Validation error: #{error_message}"
174
199
  end
175
200
 
176
- args.symbolize_keys
201
+ args.symbolize_keys.select do |_, value|
202
+ value.presence
203
+ end
177
204
  end
178
205
  end
179
206
  end
@@ -0,0 +1,72 @@
1
+ # typed: false
2
+
3
+ module Mangadex
4
+ module Internal
5
+ module Definitions
6
+ class Accepts
7
+ VALID_CONDITIONS = [:and, :or]
8
+
9
+ class Possibility
10
+ def initialize(accepted:)
11
+ @accepted = accepted
12
+ end
13
+
14
+ def inspect
15
+ "{#{@accepted.class.name}: #{@accepted}}"
16
+ end
17
+ end
18
+
19
+ def initialize(array: nil, class: nil, value: nil, condition: :and)
20
+ @array = array
21
+ @class = binding.local_variable_get(:class)
22
+ @value = value
23
+ @condition = ensure_valid_condition!(condition.to_s.to_sym)
24
+ end
25
+
26
+ def validate!(value)
27
+ valid = if @condition == :or
28
+ validate_or!(value)
29
+ else
30
+ validate_and!(value)
31
+ end
32
+
33
+ raise ArgumentError, "Value `#{value}` must be #{nature}: #{possibilities}" unless valid
34
+ end
35
+
36
+ private
37
+
38
+ def ensure_valid_condition!(condition)
39
+ return condition if VALID_CONDITIONS.include?(condition)
40
+
41
+ raise "Condition `#{condition}` must be one of #{VALID_CONDITIONS}"
42
+ end
43
+
44
+ def nature
45
+ @condition == :or ? "one of" : "all of"
46
+ end
47
+
48
+ def validate_and!(value)
49
+ possibilities.all? { potentially_valid?(value) }
50
+ end
51
+
52
+ def validate_or!(value)
53
+ possibilities.any? { potentially_valid?(value) }
54
+ end
55
+
56
+ def possibilities
57
+ @possibilities ||= [
58
+ @array,
59
+ @class,
60
+ @value,
61
+ ].compact.map { |pos| Possibility.new(accepted: pos) }
62
+ end
63
+
64
+ def potentially_valid?(value)
65
+ @array.include?(value) || \
66
+ value.is_a?(@class) || \
67
+ value == @value
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,94 @@
1
+ # typed: false
2
+
3
+ module Mangadex
4
+ module Internal
5
+ module Definitions
6
+ class Base
7
+ attr_reader :key, :accepts, :converts, :errors
8
+
9
+ def initialize(value, key:, accepts:, required: false, converts: nil)
10
+ @value = value
11
+ @key = key
12
+ @accepts = accepts
13
+ @required = required
14
+ @converts = converts
15
+ @errors = Array.new
16
+ end
17
+
18
+ def validate
19
+ validate_required
20
+ return if !@required && empty?
21
+
22
+ validate_accepts
23
+
24
+ nil
25
+ end
26
+
27
+ def validate!
28
+ validate
29
+
30
+ raise_if_any_errors!
31
+ end
32
+
33
+ def valid?
34
+ validate!
35
+ true
36
+ rescue ArgumentError
37
+ false
38
+ end
39
+
40
+ def error_message
41
+ return unless errors.any?
42
+
43
+ compile_error_message
44
+ end
45
+
46
+ def empty?
47
+ converted_value.respond_to?(:empty?) ? converted_value.empty? : converted_value.to_s.strip.empty?
48
+ end
49
+
50
+ def value
51
+ converted_value
52
+ end
53
+
54
+ protected
55
+
56
+ def validate_required
57
+ return unless @required
58
+
59
+ add_error("Missing :#{key}") if empty?
60
+ false
61
+ end
62
+
63
+ def validate_accepts
64
+ raise NotImplementedError
65
+ end
66
+
67
+ def converted_value
68
+ @converted_value ||= if converts.is_a?(Proc)
69
+ converts.call(@value)
70
+ elsif converts.is_a?(String) || converts.is_a?(Symbol)
71
+ @value.send(converts)
72
+ else
73
+ @value
74
+ end
75
+ end
76
+
77
+ def add_error(message)
78
+ @errors << message
79
+ @errors.uniq!
80
+ end
81
+
82
+ def compile_error_message
83
+ errors.join(', ')
84
+ end
85
+
86
+ private
87
+
88
+ def raise_if_any_errors!
89
+ raise ArgumentError, "Validation error: #{compile_error_message}" if errors.any?
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,33 @@
1
+ # typed: false
2
+
3
+ module Mangadex
4
+ module Internal
5
+ module Definitions
6
+ class ContentRating < Base
7
+ def initialize(value)
8
+ super(
9
+ value,
10
+ key: :content_rating,
11
+ accepts: Accepts.new(
12
+ array: Mangadex::ContentRating::VALUES,
13
+ class: Mangadex::ContentRating,
14
+ condition: :or,
15
+ ),
16
+ converts: :to_s,
17
+ required: false,
18
+ )
19
+ end
20
+
21
+ def validate_accepts
22
+ @accepts.validate!(converted_value)
23
+ rescue ArgumentError => error
24
+ add_error(error.message)
25
+ end
26
+
27
+ def validate_condition
28
+
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -6,6 +6,7 @@ module Mangadex
6
6
  module Internal
7
7
  class Request
8
8
  ALLOWED_METHODS = %i(get post put delete).freeze
9
+ SENSITIVE_FIELDS = %w(password token oldPassword newPassword)
9
10
 
10
11
  attr_accessor :path, :headers, :payload, :method, :raw
11
12
  attr_reader :response
@@ -16,19 +17,19 @@ module Mangadex
16
17
  method: :get,
17
18
  headers: headers,
18
19
  payload: nil,
19
- ).run!(raw: raw, auth: auth)
20
+ ).run_with_info!(raw: raw, auth: auth)
20
21
  end
21
22
 
22
23
  def self.post(path, headers: nil, auth: false, payload: nil, raw: false)
23
- new(path, method: :post, headers: headers, payload: payload).run!(raw: raw, auth: auth)
24
+ new(path, method: :post, headers: headers, payload: payload).run_with_info!(raw: raw, auth: auth)
24
25
  end
25
26
 
26
27
  def self.put(path, headers: nil, auth: false, payload: nil, raw: false)
27
- new(path, method: :put, headers: headers, payload: payload).run!(raw: raw, auth: auth)
28
+ new(path, method: :put, headers: headers, payload: payload).run_with_info!(raw: raw, auth: auth)
28
29
  end
29
30
 
30
31
  def self.delete(path, headers: nil, auth: false, payload: nil, raw: false)
31
- new(path, method: :delete, headers: headers, payload: payload).run!(raw: raw, auth: auth)
32
+ new(path, method: :delete, headers: headers, payload: payload).run_with_info!(raw: raw, auth: auth)
32
33
  end
33
34
 
34
35
  def initialize(path, method:, headers: nil, payload: nil)
@@ -47,19 +48,16 @@ module Mangadex
47
48
  )
48
49
  end
49
50
 
50
- def run!(raw: false, auth: false)
51
- payload_details = request_payload ? "Payload: #{sensitive_request_payload}" : "{no-payload}"
52
- puts("[#{self.class.name}] #{method.to_s.upcase} #{request_url} #{payload_details}")
51
+ def run_with_info!(*args, **kwargs)
52
+ measure_time_taken do
53
+ run!(*args, **kwargs)
54
+ end
55
+ end
53
56
 
57
+ def run!(raw: false, auth: false)
54
58
  raise Mangadex::Errors::UserNotLoggedIn.new if auth && Mangadex.context.user.nil?
55
59
 
56
- start_time = Time.now
57
-
58
60
  @response = request.execute
59
- end_time = Time.now
60
- elapsed_time = ((end_time - start_time) * 1000).to_i
61
- puts("[#{self.class.name}] took #{elapsed_time} ms")
62
-
63
61
  raw_request = raw || Mangadex.context.force_raw_requests
64
62
 
65
63
  if (body = @response.body)
@@ -95,6 +93,18 @@ module Mangadex
95
93
  data
96
94
  end
97
95
 
96
+ def measure_time_taken(&block)
97
+ payload_details = request_payload ? "Payload: #{sensitive_request_payload}" : "{no-payload}"
98
+ puts("[#{self.class.name}] #{method.to_s.upcase} #{request_url} #{payload_details}")
99
+ start_time = Time.now
100
+ result = yield
101
+
102
+ result
103
+ ensure
104
+ elapsed_time = ((Time.now - start_time) * 1000).to_i
105
+ puts("[#{self.class.name}] took #{elapsed_time} ms")
106
+ end
107
+
98
108
  def request_url
99
109
  request_path = path.start_with?('/') ? path : "/#{path}"
100
110
  "#{Mangadex.configuration.mangadex_url}#{request_path}"
@@ -106,7 +116,7 @@ module Mangadex
106
116
  JSON.generate(payload)
107
117
  end
108
118
 
109
- def sensitive_request_payload(sensitive_fields: %w(password token))
119
+ def sensitive_request_payload(sensitive_fields: SENSITIVE_FIELDS)
110
120
  payload = JSON.parse(request_payload)
111
121
  sensitive_fields.map(&:to_s).each do |field|
112
122
  payload[field] = '[REDACTED]' if payload.key?(field)
@@ -21,6 +21,7 @@ module Mangadex
21
21
  :version,
22
22
  :chapter_numbers_reset_on_new_volume,
23
23
  :available_translated_languages,
24
+ :latest_uploaded_chapter,
24
25
  :created_at,
25
26
  :updated_at
26
27
 
@@ -31,16 +32,17 @@ module Mangadex
31
32
  Mangadex::Internal::Request.get(
32
33
  '/manga',
33
34
  Mangadex::Internal::Definition.validate(args, {
34
- limit: { accepts: Integer },
35
+ limit: { accepts: Integer, converts: :to_i },
35
36
  offset: { accepts: Integer },
36
37
  title: { accepts: String },
38
+ author_or_artist: { accepts: String },
37
39
  authors: { accepts: [String] },
38
40
  artists: { accepts: [String] },
39
41
  year: { accepts: Integer },
40
42
  included_tags: { accepts: [String] },
41
- included_tags_mode: { accepts: %w(OR AND), converts: to_a },
43
+ included_tags_mode: { accepts: %w(OR AND) },
42
44
  excluded_tags: { accepts: [String] },
43
- excluded_tags_mode: { accepts: %w(OR AND), converts: to_a },
45
+ excluded_tags_mode: { accepts: %w(OR AND) },
44
46
  status: { accepts: %w(ongoing completed hiatus cancelled), converts: to_a },
45
47
  original_language: { accepts: [String] },
46
48
  excluded_original_language: { accepts: [String] },
@@ -114,11 +116,17 @@ module Mangadex
114
116
 
115
117
  sig { params(args: T::Api::Arguments).returns(T::Api::MangaResponse) }
116
118
  def self.random(**args)
119
+ to_a = Mangadex::Internal::Definition.converts(:to_a)
120
+
117
121
  Mangadex::Internal::Request.get(
118
122
  '/manga/random',
119
123
  Mangadex::Internal::Definition.validate(args, {
120
124
  includes: { accepts: Array },
121
125
  content_rating: { accepts: %w(safe suggestive erotica pornographic), converts: to_a },
126
+ included_tags: { accepts: [String] },
127
+ included_tags_mode: { accepts: %w(OR AND) },
128
+ excluded_tags: { accepts: [String] },
129
+ excluded_tags_mode: { accepts: %w(OR AND) },
122
130
  })
123
131
  )
124
132
  end
@@ -0,0 +1,19 @@
1
+ # typed: false
2
+
3
+ module Mangadex
4
+ class Rating < MangadexObject
5
+ has_attributes \
6
+ :rating,
7
+ :created_at
8
+
9
+ def self.list(**args)
10
+ Mangadex::Internal::Request.get(
11
+ '/rating',
12
+ Mangadex::Internal::Definition.validate(args, {
13
+ manga: { accepts: Array[String] },
14
+ }),
15
+ auth: true,
16
+ )
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,42 @@
1
+ module Mangadex
2
+ class Report < MangadexObject
3
+ has_attributes \
4
+ :details,
5
+ :object_id,
6
+ :status,
7
+ :created_at
8
+
9
+ sig { params(args: T::Api::Arguments).returns(Mangadex::Api::Response[Report]) }
10
+ def self.list(**args)
11
+ to_a = Mangadex::Internal::Definition.converts(:to_a)
12
+
13
+ Mangadex::Internal::Request.get(
14
+ '/report',
15
+ Mangadex::Internal::Definition.validate(args, {
16
+ limit: { accepts: Integer },
17
+ offset: { accepts: Integer },
18
+ category: { accepts: %w(manga chapter scanlation_group user author) },
19
+ reason_id: { accepts: String },
20
+ object_id: { accepts: String },
21
+ status: { accepts: %w(waiting accepted refused autoresolved) },
22
+ order: { accepts: Hash },
23
+ includes: { accepts: Array, converts: to_a },
24
+ }),
25
+ auth: true,
26
+ )
27
+ end
28
+
29
+ sig { params(args: T::Api::Arguments).returns(T::Api::GenericResponse) }
30
+ def create(**args)
31
+ Mangadex::Internal::Request.post(
32
+ '/report',
33
+ payload: Mangadex::Internal::Definition.validate(args, {
34
+ category: { accepts: %w(manga chapter scanlation_group user), required: true },
35
+ reason: { accepts: String, required: true },
36
+ object_id: { accepts: String, required: true },
37
+ details: { accepts: String },
38
+ }),
39
+ )
40
+ end
41
+ end
42
+ end
@@ -10,7 +10,7 @@ module Mangadex
10
10
  class << self
11
11
  def list(category)
12
12
  args = Mangadex::Internal::Definition.validate({category: category}, {
13
- category: { accepts: %w(manga chapter scanlation_group user), required: true },
13
+ category: { accepts: %w(manga chapter scanlation_group user author), required: true },
14
14
  })
15
15
 
16
16
  Mangadex::Internal::Request.get(
@@ -19,6 +19,9 @@ module Mangadex
19
19
  end
20
20
 
21
21
  def create(**args)
22
+ # TODO: create a "deprecated" log function
23
+ puts("[DEPRECATED] Use Mangadex::Report.create(...) instead.")
24
+
22
25
  Mangadex::Internal::Request.post(
23
26
  '/report',
24
27
  payload: Mangadex::Internal::Definition.validate(args, {
@@ -7,11 +7,14 @@ module T
7
7
  Text = T.type_alias { T.any(String, Symbol) }
8
8
 
9
9
  Arguments = T.type_alias do
10
- T.any(
11
- Text,
12
- T::Array[Text],
13
- Integer,
14
- T::Hash[Text, Text],
10
+ T.nilable(
11
+ T.any(
12
+ Text,
13
+ T::Array[Text],
14
+ Integer,
15
+ T::Hash[Text, Text],
16
+ Mangadex::ContentRating,
17
+ )
15
18
  )
16
19
  end
17
20
  MangaResponse = T.type_alias do
@@ -26,6 +29,12 @@ module T
26
29
  Mangadex::Api::Response[T::Array[Mangadex::Chapter]]
27
30
  )
28
31
  end
32
+ UserResponse = T.type_alias do
33
+ T.any(
34
+ Mangadex::Api::Response[Mangadex::User],
35
+ Mangadex::Api::Response[T::Array[Mangadex::User]]
36
+ )
37
+ end
29
38
  GenericResponse = T.type_alias do
30
39
  T.any(
31
40
  ::Hash,
@@ -0,0 +1,33 @@
1
+ # typed: false
2
+
3
+ module Mangadex
4
+ class Statistic < MangadexObject
5
+ has_attributes \
6
+ :rating,
7
+ :average,
8
+ :bayesian,
9
+ :distribution,
10
+ :follows
11
+
12
+ sig { params(uuid: String).returns(T::Api::GenericResponse) }
13
+ def self.get(uuid)
14
+ Mangadex::Internal::Definition.must(uuid)
15
+
16
+ Mangadex::Internal::Request.get(
17
+ '/statistics/manga/%{uuid}' % {uuid: uuid},
18
+ )
19
+ end
20
+
21
+ sig { params(args: T::Api::Arguments).returns(T::Api::GenericResponse) }
22
+ def self.list(**args)
23
+ to_a = Mangadex::Internal::Definition.converts(:to_a)
24
+
25
+ Mangadex::Internal::Request.get(
26
+ '/statistics/manga',
27
+ Mangadex::Internal::Definition.validate(args, {
28
+ manga: { accepts: [String], converts: to_a },
29
+ })
30
+ )
31
+ end
32
+ end
33
+ end
@@ -19,6 +19,9 @@ require_relative "user"
19
19
  require_relative "tag"
20
20
  require_relative "scanlation_group"
21
21
  require_relative "report_reason"
22
+ require_relative "report"
23
+ require_relative "rating"
24
+ require_relative "statistic"
22
25
 
23
26
  # Relationship
24
27
  require_relative "relationship"
data/lib/mangadex/user.rb CHANGED
@@ -6,6 +6,74 @@ module Mangadex
6
6
  :roles,
7
7
  :version
8
8
 
9
+ sig { params(args: T::Api::Arguments).returns(T::Api::UserResponse) }
10
+ def self.list(**args)
11
+ Mangadex::Internal::Request.get(
12
+ '/user',
13
+ Mangadex::Internal::Definition.validate(args, {
14
+ limit: { accepts: Integer },
15
+ offset: { accepts: Integer },
16
+ ids: { accepts: Array },
17
+ username: { accepts: String },
18
+ order: { accepts: Hash },
19
+ }),
20
+ auth: true,
21
+ )
22
+ end
23
+
24
+ sig { params(id: String).returns(T::Api::UserResponse) }
25
+ def self.get(id)
26
+ Mangadex::Internal::Request.get(
27
+ "/user/#{id}",
28
+ )
29
+ end
30
+
31
+ sig { params(id: String).returns(T::Api::GenericResponse) }
32
+ def self.delete(id)
33
+ Mangadex::Internal::Request.delete(
34
+ "/user/#{id}",
35
+ auth: true,
36
+ )
37
+ end
38
+
39
+ sig { params(code: String).returns(T::Api::GenericResponse) }
40
+ def self.delete_code(code)
41
+ Mangadex::Internal::Request.post(
42
+ "/user/delete/#{code}",
43
+ )
44
+ end
45
+
46
+ sig { params(old_password: String, new_password: String).returns(T::Api::GenericResponse) }
47
+ def self.update_password(old_password:, new_password:)
48
+ payload = {
49
+ oldPassword: old_password,
50
+ newPassword: new_password,
51
+ }
52
+
53
+ Mangadex::Internal::Request.post(
54
+ '/user/password',
55
+ payload: payload,
56
+ auth: true,
57
+ )
58
+ end
59
+
60
+ sig { params(email: String).returns(T::Api::GenericResponse) }
61
+ def self.update_email(email:)
62
+ Mangadex::Internal::Request.post(
63
+ '/user/email',
64
+ payload: { email: email },
65
+ auth: true,
66
+ )
67
+ end
68
+
69
+ sig { returns(T::Api::UserResponse) }
70
+ def self.current
71
+ Mangadex::Internal::Request.get(
72
+ '/user/me',
73
+ auth: true,
74
+ )
75
+ end
76
+
9
77
  sig { params(args: T::Api::Arguments).returns(Mangadex::Api::Response[Mangadex::Chapter]) }
10
78
  def self.feed(**args)
11
79
  Mangadex::Internal::Request.get(
@@ -2,9 +2,9 @@
2
2
  module Mangadex
3
3
  module Version
4
4
  MAJOR = "5"
5
- MINOR = "5"
6
- TINY = "8"
7
- PATCH = nil
5
+ MINOR = "7"
6
+ TINY = "5"
7
+ PATCH = "1"
8
8
 
9
9
  STRING = [MAJOR, MINOR, TINY].compact.join('.')
10
10
  FULL = [MAJOR, MINOR, TINY, PATCH].compact.join('.')
data/mangadex.gemspec CHANGED
@@ -29,7 +29,6 @@ Gem::Specification.new do |spec|
29
29
  spec.add_development_dependency "bundler", "~> 2.2.19"
30
30
  spec.add_development_dependency "rake", "~> 13.0"
31
31
  spec.add_development_dependency "rspec", "~> 3.0"
32
- spec.add_development_dependency "request_interceptor", "~> 1.0.0"
33
32
  spec.add_development_dependency "pry"
34
33
  spec.add_development_dependency "sorbet"
35
34
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mangadex
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.5.8
4
+ version: 5.7.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Akinyele Cafe-Febrissy
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-05-27 00:00:00.000000000 Z
11
+ date: 2022-11-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: psych
@@ -94,20 +94,6 @@ dependencies:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
96
  version: '3.0'
97
- - !ruby/object:Gem::Dependency
98
- name: request_interceptor
99
- requirement: !ruby/object:Gem::Requirement
100
- requirements:
101
- - - "~>"
102
- - !ruby/object:Gem::Version
103
- version: 1.0.0
104
- type: :development
105
- prerelease: false
106
- version_requirements: !ruby/object:Gem::Requirement
107
- requirements:
108
- - - "~>"
109
- - !ruby/object:Gem::Version
110
- version: 1.0.0
111
97
  - !ruby/object:Gem::Dependency
112
98
  name: pry
113
99
  requirement: !ruby/object:Gem::Requirement
@@ -144,6 +130,8 @@ extensions: []
144
130
  extra_rdoc_files: []
145
131
  files:
146
132
  - ".github/dependabot.yml"
133
+ - ".github/workflows/docker-image.yml"
134
+ - ".github/workflows/publish-gem.yml"
147
135
  - ".github/workflows/ruby.yml"
148
136
  - ".gitignore"
149
137
  - ".rspec"
@@ -161,6 +149,7 @@ files:
161
149
  - docker-compose.yml
162
150
  - docs/authentication.md
163
151
  - docs/context.md
152
+ - entrypoint.sh
164
153
  - lib/config.rb
165
154
  - lib/errors.rb
166
155
  - lib/extensions.rb
@@ -175,20 +164,27 @@ files:
175
164
  - lib/mangadex/auth.rb
176
165
  - lib/mangadex/author.rb
177
166
  - lib/mangadex/chapter.rb
167
+ - lib/mangadex/chapter_read_marker.rb
178
168
  - lib/mangadex/content_rating.rb
179
169
  - lib/mangadex/cover_art.rb
180
170
  - lib/mangadex/custom_list.rb
181
171
  - lib/mangadex/internal.rb
182
172
  - lib/mangadex/internal/context.rb
183
173
  - lib/mangadex/internal/definition.rb
174
+ - lib/mangadex/internal/definitions/accepts.rb
175
+ - lib/mangadex/internal/definitions/base.rb
176
+ - lib/mangadex/internal/definitions/content_rating.rb
184
177
  - lib/mangadex/internal/request.rb
185
178
  - lib/mangadex/internal/with_attributes.rb
186
179
  - lib/mangadex/manga.rb
187
180
  - lib/mangadex/mangadex_object.rb
181
+ - lib/mangadex/rating.rb
188
182
  - lib/mangadex/relationship.rb
183
+ - lib/mangadex/report.rb
189
184
  - lib/mangadex/report_reason.rb
190
185
  - lib/mangadex/scanlation_group.rb
191
186
  - lib/mangadex/sorbet.rb
187
+ - lib/mangadex/statistic.rb
192
188
  - lib/mangadex/storage.rb
193
189
  - lib/mangadex/storage/basic.rb
194
190
  - lib/mangadex/storage/memory.rb
@@ -250,7 +246,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
250
246
  - !ruby/object:Gem::Version
251
247
  version: '0'
252
248
  requirements: []
253
- rubygems_version: 3.2.32
249
+ rubygems_version: 3.3.7
254
250
  signing_key:
255
251
  specification_version: 4
256
252
  summary: Your next favourite Ruby gem for interacting with Mangadex.org