manga-tools 0.1.0 → 0.1.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 +4 -4
- data/.gitignore +1 -0
- data/.rubocop.yml +1 -1
- data/Gemfile.lock +1 -1
- data/lib/manga/tools.rb +8 -6
- data/lib/manga/tools/cache.rb +88 -86
- data/lib/manga/tools/cli.rb +133 -131
- data/lib/manga/tools/http.rb +32 -30
- data/lib/manga/tools/version.rb +3 -5
- data/manga-tools.gemspec +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 00e79a7d0c8e37ab036decdaba2d9cd556d97256a523a59d6af0c1c63c07d2b5
|
4
|
+
data.tar.gz: 46a97ec12c433b9ce51ee222d82df047edc3b8f311304149dd8d3e9a946b27d3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3635bfe7dd5cd5c4093fb8281d8137e34c4a3c551c4e377e7332f6712a933190f0515d1159e56bfb78bc6ea917226b6561f1d053afdc5d1b805e4a5e417e9454
|
7
|
+
data.tar.gz: 6170c78319d8e7a74ed93860dde010b87b23c9281385bb65bfeec4af5eb2c9c51f02ceb771797126c9365f9b3295d8d5b4db5b812e16dc3af3e022f33cba45bd
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
data/Gemfile.lock
CHANGED
data/lib/manga/tools.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
3
|
+
require_relative 'tools/cache'
|
4
|
+
require_relative 'tools/cli'
|
5
|
+
require_relative 'tools/http'
|
6
|
+
require_relative 'tools/version'
|
7
7
|
|
8
|
-
module Manga
|
9
|
-
|
8
|
+
module Manga
|
9
|
+
module Tools
|
10
|
+
class Error < StandardError; end
|
11
|
+
end
|
10
12
|
end
|
data/lib/manga/tools/cache.rb
CHANGED
@@ -3,109 +3,111 @@
|
|
3
3
|
require 'digest/md5'
|
4
4
|
require 'fileutils'
|
5
5
|
|
6
|
-
module Manga
|
7
|
-
|
8
|
-
|
9
|
-
class
|
10
|
-
|
11
|
-
|
12
|
-
|
6
|
+
module Manga
|
7
|
+
module Tools
|
8
|
+
# Cache class
|
9
|
+
class Cache
|
10
|
+
class << self
|
11
|
+
# Initialize cache
|
12
|
+
def init
|
13
|
+
FileUtils.mkdir_p(cache_current_year_dir)
|
14
|
+
end
|
15
|
+
|
16
|
+
def root_dir
|
17
|
+
@root_dir ||= "#{Dir.home}/.manga-tools"
|
18
|
+
end
|
19
|
+
|
20
|
+
def cache_root_dir
|
21
|
+
@cache_root_dir ||= "#{root_dir}/cache"
|
22
|
+
end
|
23
|
+
|
24
|
+
def cache_current_year_dir
|
25
|
+
@cache_current_year_dir ||= "#{cache_root_dir}/#{Time.now.year}"
|
26
|
+
end
|
27
|
+
|
28
|
+
# @return [Integer] default expires in seconds (1day)
|
29
|
+
def default_expires_in
|
30
|
+
@default_expires_in ||= 1 * 24 * 60 * 60
|
31
|
+
end
|
32
|
+
|
33
|
+
# @param key [String] A key to generate a key for the cache
|
34
|
+
# @return [String] string of cached data
|
35
|
+
def fetch(key:)
|
36
|
+
raise ArgumentError, 'mast pass a block' unless block_given?
|
37
|
+
|
38
|
+
cache = new(key)
|
39
|
+
# cache.clear
|
40
|
+
return cache.load_data if cache.available?
|
41
|
+
|
42
|
+
result = yield(cache)
|
43
|
+
cache.save_meta_data
|
44
|
+
cache.save_data(result)
|
45
|
+
result
|
46
|
+
end
|
13
47
|
end
|
14
48
|
|
15
|
-
|
16
|
-
|
49
|
+
# @return [Integer|nil] A cache deadline in seconds.
|
50
|
+
attr_accessor :expires_in
|
51
|
+
# @return [String] A key to generate a key for the cache.
|
52
|
+
attr_reader :key
|
53
|
+
# @return [String] A key for the cache.
|
54
|
+
attr_reader :cache_key
|
55
|
+
# @return [String] A path to the cache metadata file.
|
56
|
+
attr_reader :meta_file_path
|
57
|
+
# @return [String] A path to the cached data file.
|
58
|
+
attr_reader :data_file_path
|
59
|
+
|
60
|
+
# @param key [String] A key to generate a key for the cache.
|
61
|
+
def initialize(key)
|
62
|
+
raise ArgumentError, 'mast pass the key param' unless key
|
63
|
+
|
64
|
+
@expires_in = self.class.default_expires_in
|
65
|
+
@key = key
|
66
|
+
@cache_key = Digest::MD5.hexdigest(key)
|
67
|
+
@meta_file_path = "#{self.class.cache_current_year_dir}/#{@cache_key}"
|
68
|
+
@data_file_path = "#{@meta_file_path}.data"
|
17
69
|
end
|
18
70
|
|
19
|
-
|
20
|
-
|
21
|
-
|
71
|
+
# @return [Boolean] True if a cache is available, otherwise return false.
|
72
|
+
def available?
|
73
|
+
t = File.mtime(meta_file_path)
|
74
|
+
saved_expires_in = File.read(meta_file_path).strip.to_i
|
22
75
|
|
23
|
-
|
24
|
-
|
76
|
+
Time.now <= (t + saved_expires_in)
|
77
|
+
rescue StandardError
|
78
|
+
false
|
25
79
|
end
|
26
80
|
|
27
|
-
# @return [
|
28
|
-
def
|
29
|
-
|
81
|
+
# @return [String] Reads and returns the cache data.
|
82
|
+
def load_data
|
83
|
+
File.read(data_file_path)
|
30
84
|
end
|
31
85
|
|
32
|
-
#
|
33
|
-
|
34
|
-
|
35
|
-
raise ArgumentError, 'mast pass a block' unless block_given?
|
86
|
+
# Save the metadata for the cache.
|
87
|
+
def save_meta_data
|
88
|
+
@expires_in = self.class.default_expires_in unless expires_in
|
36
89
|
|
37
|
-
|
38
|
-
|
39
|
-
|
90
|
+
File.open(meta_file_path, 'w') do |f|
|
91
|
+
f.write(expires_in.to_s)
|
92
|
+
end
|
40
93
|
|
41
|
-
|
42
|
-
cache.save_meta_data
|
43
|
-
cache.save_data(result)
|
44
|
-
result
|
94
|
+
self
|
45
95
|
end
|
46
|
-
end
|
47
|
-
|
48
|
-
# @return [Integer|nil] A cache deadline in seconds.
|
49
|
-
attr_accessor :expires_in
|
50
|
-
# @return [String] A key to generate a key for the cache.
|
51
|
-
attr_reader :key
|
52
|
-
# @return [String] A key for the cache.
|
53
|
-
attr_reader :cache_key
|
54
|
-
# @return [String] A path to the cache metadata file.
|
55
|
-
attr_reader :meta_file_path
|
56
|
-
# @return [String] A path to the cached data file.
|
57
|
-
attr_reader :data_file_path
|
58
|
-
|
59
|
-
# @param key [String] A key to generate a key for the cache.
|
60
|
-
def initialize(key)
|
61
|
-
raise ArgumentError, 'mast pass the key param' unless key
|
62
|
-
|
63
|
-
@expires_in = self.class.default_expires_in
|
64
|
-
@key = key
|
65
|
-
@cache_key = Digest::MD5.hexdigest(key)
|
66
|
-
@meta_file_path = "#{self.class.cache_current_year_dir}/#{@cache_key}"
|
67
|
-
@data_file_path = "#{@meta_file_path}.data"
|
68
|
-
end
|
69
|
-
|
70
|
-
# @return [Boolean] True if a cache is available, otherwise return false.
|
71
|
-
def available?
|
72
|
-
t = File.mtime(meta_file_path)
|
73
|
-
saved_expires_in = File.read(meta_file_path).strip.to_i
|
74
|
-
|
75
|
-
Time.now <= (t + saved_expires_in)
|
76
|
-
rescue StandardError
|
77
|
-
false
|
78
|
-
end
|
79
96
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
# Save the metadata for the cache.
|
86
|
-
def save_meta_data
|
87
|
-
@expires_in = self.class.default_expires_in unless expires_in
|
97
|
+
# @param str [String] Save the specified cache data
|
98
|
+
def save_data(str)
|
99
|
+
File.open(data_file_path, 'w') do |f|
|
100
|
+
f.write(str.force_encoding('utf-8'))
|
101
|
+
end
|
88
102
|
|
89
|
-
|
90
|
-
f.write(expires_in.to_s)
|
103
|
+
self
|
91
104
|
end
|
92
105
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
def save_data(str)
|
98
|
-
File.open(data_file_path, 'w') do |f|
|
99
|
-
f.write(str.force_encoding('utf-8'))
|
106
|
+
# for debug
|
107
|
+
def clear
|
108
|
+
File.delete(meta_file_path) if File.exist?(meta_file_path)
|
109
|
+
File.delete(data_file_path) if File.exist?(data_file_path)
|
100
110
|
end
|
101
|
-
|
102
|
-
self
|
103
|
-
end
|
104
|
-
|
105
|
-
# for debug
|
106
|
-
def clear
|
107
|
-
File.delete(meta_file_path) if File.exist?(meta_file_path)
|
108
|
-
File.delete(data_file_path) if File.exist?(data_file_path)
|
109
111
|
end
|
110
112
|
end
|
111
113
|
end
|
data/lib/manga/tools/cli.rb
CHANGED
@@ -5,156 +5,158 @@ require 'nokogiri'
|
|
5
5
|
require 'stringio'
|
6
6
|
require 'thor'
|
7
7
|
|
8
|
-
module Manga
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
# desc "pub {name}", "publication date of {name}"
|
16
|
-
# def pub(name)
|
17
|
-
# doc = Nokogiri::HTML(
|
18
|
-
# URI.open('https://tsutaya.tsite.jp/feature/book/release/comic/index', 'User-Agent' => UserAgent)
|
19
|
-
# )
|
20
|
-
#
|
21
|
-
# File.open('.index.txt', 'w') {|f| f.write doc }
|
22
|
-
#
|
23
|
-
# results = {}
|
24
|
-
#
|
25
|
-
# current_date = ''
|
26
|
-
# state = :title
|
27
|
-
# current_data = {}
|
28
|
-
# doc.css('h3, div.comic_list div.c_cols-1of3 span').each do |element|
|
29
|
-
# case element.name
|
30
|
-
# when 'h3'
|
31
|
-
# results[element.content] = []
|
32
|
-
# current_date = element.content
|
33
|
-
# when 'span'
|
34
|
-
# current_data[state] = element.content
|
35
|
-
# case state
|
36
|
-
# when :title
|
37
|
-
# state = :author
|
38
|
-
# when :author
|
39
|
-
# state = :label
|
40
|
-
# when :label
|
41
|
-
# state = :title
|
42
|
-
# results[current_date] << current_data
|
43
|
-
# current_data = {}
|
44
|
-
# end
|
45
|
-
# end
|
46
|
-
# end
|
47
|
-
#
|
48
|
-
# puts JSON.pretty_generate(results)
|
49
|
-
# end
|
50
|
-
|
51
|
-
desc 'search WORD', 'Search for a given WORD'
|
52
|
-
def search(word)
|
53
|
-
Manga::Tools::Cache.init
|
54
|
-
|
55
|
-
t = Time.now
|
56
|
-
url = "https://calendar.gameiroiro.com/manga.php?year=#{t.year}&month=#{t.month}"
|
57
|
-
data = Manga::Tools::Cache.fetch(key: url) do |cache|
|
58
|
-
res = Manga::Tools::Http.get(url)
|
59
|
-
cache.expires_in = Manga::Tools::Http.seconds_to_cache(res)
|
60
|
-
results = generate_internal_data(t, res.body)
|
61
|
-
results.to_json
|
8
|
+
module Manga
|
9
|
+
module Tools
|
10
|
+
# CLI class
|
11
|
+
class CLI < Thor
|
12
|
+
def self.exit_on_failure?
|
13
|
+
true
|
62
14
|
end
|
63
15
|
|
64
|
-
|
16
|
+
# desc "pub {name}", "publication date of {name}"
|
17
|
+
# def pub(name)
|
18
|
+
# doc = Nokogiri::HTML(
|
19
|
+
# URI.open('https://tsutaya.tsite.jp/feature/book/release/comic/index', 'User-Agent' => UserAgent)
|
20
|
+
# )
|
21
|
+
#
|
22
|
+
# File.open('.index.txt', 'w') {|f| f.write doc }
|
23
|
+
#
|
24
|
+
# results = {}
|
25
|
+
#
|
26
|
+
# current_date = ''
|
27
|
+
# state = :title
|
28
|
+
# current_data = {}
|
29
|
+
# doc.css('h3, div.comic_list div.c_cols-1of3 span').each do |element|
|
30
|
+
# case element.name
|
31
|
+
# when 'h3'
|
32
|
+
# results[element.content] = []
|
33
|
+
# current_date = element.content
|
34
|
+
# when 'span'
|
35
|
+
# current_data[state] = element.content
|
36
|
+
# case state
|
37
|
+
# when :title
|
38
|
+
# state = :author
|
39
|
+
# when :author
|
40
|
+
# state = :label
|
41
|
+
# when :label
|
42
|
+
# state = :title
|
43
|
+
# results[current_date] << current_data
|
44
|
+
# current_data = {}
|
45
|
+
# end
|
46
|
+
# end
|
47
|
+
# end
|
48
|
+
#
|
49
|
+
# puts JSON.pretty_generate(results)
|
50
|
+
# end
|
51
|
+
|
52
|
+
desc 'search WORD', 'Search for a given WORD'
|
53
|
+
def search(word)
|
54
|
+
Manga::Tools::Cache.init
|
55
|
+
|
56
|
+
t = Time.now
|
57
|
+
url = "https://calendar.gameiroiro.com/manga.php?year=#{t.year}&month=#{t.month}"
|
58
|
+
data = Manga::Tools::Cache.fetch(key: url) do |cache|
|
59
|
+
res = Manga::Tools::Http.get(url)
|
60
|
+
cache.expires_in = Manga::Tools::Http.seconds_to_cache(res)
|
61
|
+
results = generate_internal_data(t, res.body)
|
62
|
+
results.to_json
|
63
|
+
end
|
65
64
|
|
66
|
-
|
67
|
-
hash.each do |date, items|
|
68
|
-
next if items.empty?
|
65
|
+
hash = JSON.parse(data)
|
69
66
|
|
70
|
-
|
71
|
-
|
67
|
+
puts "Searching '#{word}' ..."
|
68
|
+
hash.each do |date, items|
|
69
|
+
next if items.empty?
|
70
|
+
|
71
|
+
items.each do |item|
|
72
|
+
puts "Found: #{date}, #{item['title']}" if item['title'].index(word)
|
73
|
+
end
|
72
74
|
end
|
75
|
+
puts 'Finished.'
|
73
76
|
end
|
74
|
-
puts 'Finished.'
|
75
|
-
end
|
76
77
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
78
|
+
private
|
79
|
+
|
80
|
+
# @param time [Time] a target time
|
81
|
+
# @param data [String] a string of HTTP response body.
|
82
|
+
def generate_internal_data(time, data)
|
83
|
+
doc = Nokogiri::HTML(StringIO.open(data))
|
84
|
+
|
85
|
+
results = {}
|
86
|
+
current_date = nil
|
87
|
+
state = :genre
|
88
|
+
data = {}
|
89
|
+
doc.css(targets.join(', ')).each do |element|
|
90
|
+
value = rm_spaces(element.content)
|
91
|
+
case element.name
|
92
|
+
when 'td'
|
93
|
+
current_date = format('%<ym>s/%<day>02d', ym: time.strftime('%Y/%m'), day: value.to_i)
|
94
|
+
results[current_date] = []
|
95
|
+
when 'p', 'a'
|
96
|
+
case state
|
97
|
+
when :genre
|
98
|
+
guard_genre(element)
|
99
|
+
data[state] = value
|
100
|
+
state = :title
|
101
|
+
when :title
|
102
|
+
guard_title(element)
|
103
|
+
data[state] = value
|
104
|
+
data[:link] = element['href']
|
105
|
+
state = :company
|
106
|
+
when :company
|
107
|
+
guard_company(element)
|
108
|
+
data[state] = value
|
109
|
+
state = :authors
|
110
|
+
when :authors
|
111
|
+
guard_authors(element)
|
112
|
+
data[state] = value
|
113
|
+
state = :genre
|
114
|
+
results[current_date] << data
|
115
|
+
data = {}
|
116
|
+
end
|
117
|
+
end
|
118
|
+
rescue StandardError
|
119
|
+
# when authors, authors may not be present.
|
120
|
+
if state == :authors
|
121
|
+
data[state] = ''
|
112
122
|
state = :genre
|
113
123
|
results[current_date] << data
|
114
124
|
data = {}
|
115
125
|
end
|
126
|
+
retry
|
116
127
|
end
|
117
|
-
|
118
|
-
|
119
|
-
if state == :authors
|
120
|
-
data[state] = ''
|
121
|
-
state = :genre
|
122
|
-
results[current_date] << data
|
123
|
-
data = {}
|
124
|
-
end
|
125
|
-
retry
|
128
|
+
|
129
|
+
results
|
126
130
|
end
|
127
131
|
|
128
|
-
|
129
|
-
|
132
|
+
def targets
|
133
|
+
@targets ||= [
|
134
|
+
'td.day-td',
|
135
|
+
'div.product-description-right p.p-genre',
|
136
|
+
'div.product-description-right a',
|
137
|
+
'div.product-description-right p.p-company'
|
138
|
+
]
|
139
|
+
end
|
130
140
|
|
131
|
-
|
132
|
-
@
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
'div.product-description-right p.p-company'
|
137
|
-
]
|
138
|
-
end
|
141
|
+
# Remove HTML spaces ( ) and white spaces.
|
142
|
+
# @param str [String] a string
|
143
|
+
def rm_spaces(str)
|
144
|
+
str.gsub("\u00A0", '').strip
|
145
|
+
end
|
139
146
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
str.gsub("\u00A0", '').strip
|
144
|
-
end
|
147
|
+
def guard_genre(element)
|
148
|
+
raise 'invalid state' unless element.name == 'p' && element['class'] == 'p-genre'
|
149
|
+
end
|
145
150
|
|
146
|
-
|
147
|
-
|
148
|
-
|
151
|
+
def guard_title(element)
|
152
|
+
raise 'invalid state' unless element.name == 'a'
|
153
|
+
end
|
149
154
|
|
150
|
-
|
151
|
-
|
152
|
-
|
155
|
+
def guard_company(element)
|
156
|
+
raise 'invalid state' unless element.name == 'p' && element['class'] == 'p-company'
|
157
|
+
end
|
153
158
|
|
154
|
-
|
155
|
-
raise 'invalid state' unless element.name == 'p' && element['class'] == 'p-company'
|
159
|
+
alias guard_authors guard_company
|
156
160
|
end
|
157
|
-
|
158
|
-
alias guard_authors guard_company
|
159
161
|
end
|
160
162
|
end
|
data/lib/manga/tools/http.rb
CHANGED
@@ -3,41 +3,43 @@
|
|
3
3
|
require 'faraday'
|
4
4
|
require 'uri'
|
5
5
|
|
6
|
-
module Manga
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
6
|
+
module Manga
|
7
|
+
module Tools
|
8
|
+
# HTTP client class
|
9
|
+
module Http
|
10
|
+
# rubocop:disable Layout/LineLength
|
11
|
+
UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36'
|
12
|
+
# rubocop:enable Layout/LineLength
|
12
13
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
14
|
+
# @param url [String] an url string, eg: `https://example.com`
|
15
|
+
# @return [Faraday::Connection] a connection object
|
16
|
+
def self.connection(url)
|
17
|
+
@connection ||= Faraday.new(
|
18
|
+
url: url,
|
19
|
+
headers: { 'User-Agent' => UA }
|
20
|
+
) do |f|
|
21
|
+
f.response :logger
|
22
|
+
end
|
21
23
|
end
|
22
|
-
end
|
23
24
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
25
|
+
# @param url [String] an url string, eg: `https://example.com/path/to/object`
|
26
|
+
# @return [Faraday::Response] a response object
|
27
|
+
def self.get(url)
|
28
|
+
u = URI.parse(url)
|
29
|
+
connection("#{u.scheme}://#{u.host}").get(u.request_uri)
|
30
|
+
end
|
30
31
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
32
|
+
# @param response [Faraday::Response] a response object
|
33
|
+
# @return [nil|Integer] number of second to cache
|
34
|
+
#
|
35
|
+
# supported http header
|
36
|
+
# - cache-control: max-age=[sec]
|
37
|
+
def self.seconds_to_cache(response)
|
38
|
+
result = response['cache-control']&.split(/,/)&.map(&:strip)&.find { |item| item =~ /max-age=(.+)/ }
|
39
|
+
return nil unless result
|
39
40
|
|
40
|
-
|
41
|
+
Regexp.last_match(1).to_i
|
42
|
+
end
|
41
43
|
end
|
42
44
|
end
|
43
45
|
end
|
data/lib/manga/tools/version.rb
CHANGED
data/manga-tools.gemspec
CHANGED
@@ -12,7 +12,7 @@ Gem::Specification.new do |spec|
|
|
12
12
|
spec.description = 'Tools to support the automation of daily manga-related activities.'
|
13
13
|
spec.homepage = 'https://github.com/yagihiro/manga-tools'
|
14
14
|
spec.license = 'MIT'
|
15
|
-
spec.required_ruby_version = Gem::Requirement.new('>= 2.
|
15
|
+
spec.required_ruby_version = Gem::Requirement.new('>= 2.6.0')
|
16
16
|
|
17
17
|
# spec.metadata['allowed_push_host'] = 'TODO: Set to 'http://mygemserver.com''
|
18
18
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: manga-tools
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Hiroki Yagita
|
@@ -52,7 +52,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
52
52
|
requirements:
|
53
53
|
- - ">="
|
54
54
|
- !ruby/object:Gem::Version
|
55
|
-
version: 2.
|
55
|
+
version: 2.6.0
|
56
56
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
57
57
|
requirements:
|
58
58
|
- - ">="
|