manga-tools 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
- - ">="
|