manga-tools 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +0 -1
- data/Gemfile.lock +1 -5
- data/README.md +6 -5
- data/lib/manga/tools.rb +0 -1
- data/lib/manga/tools/cli.rb +7 -140
- data/lib/manga/tools/http.rb +7 -6
- data/lib/manga/tools/version.rb +1 -1
- metadata +2 -3
- data/lib/manga/tools/cache.rb +0 -113
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: da4ed03340c47790056a5ffeaa86a4f986010d8dfcb7c8fd87568300c9f66e30
|
4
|
+
data.tar.gz: a4f1644d7fca880a904463bf10376c86aaac49aac3a2233ec323ee8d0287bc42
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 94e0f51017fd81f30360f03ec4c29cf82318401e507dc267cc354e6e4cba43278b5161e3b12f3af4f0ecfdc2e4dc21af6e8f0c10c0f0ea5b4fd22fb96f06e4dc
|
7
|
+
data.tar.gz: 45d686425259594ae0e45114f8e7d70970df515db271f3c3c6dee8c6316c83deaa8d844a6be9b199abe7fe1854032a127a56eed64e2da785245281b8dafbca15
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
manga-tools (0.1.
|
4
|
+
manga-tools (0.1.2)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
@@ -14,10 +14,7 @@ GEM
|
|
14
14
|
multipart-post (>= 1.2, < 3)
|
15
15
|
json (2.3.1)
|
16
16
|
method_source (1.0.0)
|
17
|
-
mini_portile2 (2.4.0)
|
18
17
|
multipart-post (2.1.1)
|
19
|
-
nokogiri (1.10.10)
|
20
|
-
mini_portile2 (~> 2.4.0)
|
21
18
|
parallel (1.19.2)
|
22
19
|
parser (2.7.1.4)
|
23
20
|
ast (~> 2.4.1)
|
@@ -67,7 +64,6 @@ PLATFORMS
|
|
67
64
|
DEPENDENCIES
|
68
65
|
faraday
|
69
66
|
manga-tools!
|
70
|
-
nokogiri
|
71
67
|
pry
|
72
68
|
rake
|
73
69
|
rspec
|
data/README.md
CHANGED
@@ -1,13 +1,10 @@
|
|
1
1
|
# Manga::Tools
|
2
2
|
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/manga-tools.svg)](https://badge.fury.io/rb/manga-tools)
|
3
4
|
![Ruby](https://github.com/yagihiro/manga-tools/workflows/Ruby/badge.svg)
|
4
5
|
[![Maintainability](https://api.codeclimate.com/v1/badges/e2797c91a0bd6f521905/maintainability)](https://codeclimate.com/github/yagihiro/manga-tools/maintainability)
|
5
6
|
[![Test Coverage](https://api.codeclimate.com/v1/badges/e2797c91a0bd6f521905/test_coverage)](https://codeclimate.com/github/yagihiro/manga-tools/test_coverage)
|
6
7
|
|
7
|
-
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/manga/tools`. To experiment with that code, run `bin/console` for an interactive prompt.
|
8
|
-
|
9
|
-
TODO: Delete this and the text above, and describe your gem
|
10
|
-
|
11
8
|
## Installation
|
12
9
|
|
13
10
|
Add this line to your application's Gemfile:
|
@@ -26,7 +23,11 @@ Or install it yourself as:
|
|
26
23
|
|
27
24
|
## Usage
|
28
25
|
|
29
|
-
|
26
|
+
* A search command to find out the release date of a manga
|
27
|
+
|
28
|
+
```
|
29
|
+
$ manga-tools search "ONE PIECE"
|
30
|
+
```
|
30
31
|
|
31
32
|
## Development
|
32
33
|
|
data/lib/manga/tools.rb
CHANGED
data/lib/manga/tools/cli.rb
CHANGED
@@ -1,9 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'json'
|
4
|
-
require 'nokogiri'
|
5
|
-
require 'stringio'
|
6
4
|
require 'thor'
|
5
|
+
require_relative 'http'
|
7
6
|
|
8
7
|
module Manga
|
9
8
|
module Tools
|
@@ -13,150 +12,18 @@ module Manga
|
|
13
12
|
true
|
14
13
|
end
|
15
14
|
|
16
|
-
|
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'
|
15
|
+
desc 'search WORD', 'Search titles for a given WORD'
|
53
16
|
def search(word)
|
54
|
-
|
55
|
-
|
56
|
-
|
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
|
64
|
-
|
65
|
-
hash = JSON.parse(data)
|
17
|
+
url = "https://manga-tools-server.herokuapp.com/publications?keyword=#{CGI.escape(word)}"
|
18
|
+
res = Manga::Tools::Http.get(url)
|
19
|
+
results = JSON.parse(res.body)
|
66
20
|
|
67
21
|
puts "Searching '#{word}' ..."
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
items.each do |item|
|
72
|
-
puts "Found: #{date}, #{item['title']}" if item['title'].index(word)
|
73
|
-
end
|
22
|
+
results.each do |item|
|
23
|
+
puts "#{item['published_at']}: #{item['title']}"
|
74
24
|
end
|
75
25
|
puts 'Finished.'
|
76
26
|
end
|
77
|
-
|
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] = ''
|
122
|
-
state = :genre
|
123
|
-
results[current_date] << data
|
124
|
-
data = {}
|
125
|
-
end
|
126
|
-
retry
|
127
|
-
end
|
128
|
-
|
129
|
-
results
|
130
|
-
end
|
131
|
-
|
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
|
140
|
-
|
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
|
146
|
-
|
147
|
-
def guard_genre(element)
|
148
|
-
raise 'invalid state' unless element.name == 'p' && element['class'] == 'p-genre'
|
149
|
-
end
|
150
|
-
|
151
|
-
def guard_title(element)
|
152
|
-
raise 'invalid state' unless element.name == 'a'
|
153
|
-
end
|
154
|
-
|
155
|
-
def guard_company(element)
|
156
|
-
raise 'invalid state' unless element.name == 'p' && element['class'] == 'p-company'
|
157
|
-
end
|
158
|
-
|
159
|
-
alias guard_authors guard_company
|
160
27
|
end
|
161
28
|
end
|
162
29
|
end
|
data/lib/manga/tools/http.rb
CHANGED
@@ -2,23 +2,20 @@
|
|
2
2
|
|
3
3
|
require 'faraday'
|
4
4
|
require 'uri'
|
5
|
+
require_relative 'version'
|
5
6
|
|
6
7
|
module Manga
|
7
8
|
module Tools
|
8
9
|
# HTTP client class
|
9
10
|
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
|
13
|
-
|
14
11
|
# @param url [String] an url string, eg: `https://example.com`
|
15
12
|
# @return [Faraday::Connection] a connection object
|
16
13
|
def self.connection(url)
|
17
14
|
@connection ||= Faraday.new(
|
18
15
|
url: url,
|
19
|
-
headers: { 'User-Agent' =>
|
16
|
+
headers: { 'User-Agent' => user_agent }
|
20
17
|
) do |f|
|
21
|
-
f.response :logger
|
18
|
+
# f.response :logger
|
22
19
|
end
|
23
20
|
end
|
24
21
|
|
@@ -40,6 +37,10 @@ module Manga
|
|
40
37
|
|
41
38
|
Regexp.last_match(1).to_i
|
42
39
|
end
|
40
|
+
|
41
|
+
def self.user_agent
|
42
|
+
@user_agent ||= "manga-tools #{Manga::Tools::VERSION}"
|
43
|
+
end
|
43
44
|
end
|
44
45
|
end
|
45
46
|
end
|
data/lib/manga/tools/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
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.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Hiroki Yagita
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-08-
|
11
|
+
date: 2020-08-18 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Tools to support the automation of daily manga-related activities.
|
14
14
|
email:
|
@@ -34,7 +34,6 @@ files:
|
|
34
34
|
- bin/setup
|
35
35
|
- exe/manga-tools
|
36
36
|
- lib/manga/tools.rb
|
37
|
-
- lib/manga/tools/cache.rb
|
38
37
|
- lib/manga/tools/cli.rb
|
39
38
|
- lib/manga/tools/http.rb
|
40
39
|
- lib/manga/tools/version.rb
|
data/lib/manga/tools/cache.rb
DELETED
@@ -1,113 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'digest/md5'
|
4
|
-
require 'fileutils'
|
5
|
-
|
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
|
47
|
-
end
|
48
|
-
|
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"
|
69
|
-
end
|
70
|
-
|
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
|
75
|
-
|
76
|
-
Time.now <= (t + saved_expires_in)
|
77
|
-
rescue StandardError
|
78
|
-
false
|
79
|
-
end
|
80
|
-
|
81
|
-
# @return [String] Reads and returns the cache data.
|
82
|
-
def load_data
|
83
|
-
File.read(data_file_path)
|
84
|
-
end
|
85
|
-
|
86
|
-
# Save the metadata for the cache.
|
87
|
-
def save_meta_data
|
88
|
-
@expires_in = self.class.default_expires_in unless expires_in
|
89
|
-
|
90
|
-
File.open(meta_file_path, 'w') do |f|
|
91
|
-
f.write(expires_in.to_s)
|
92
|
-
end
|
93
|
-
|
94
|
-
self
|
95
|
-
end
|
96
|
-
|
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
|
102
|
-
|
103
|
-
self
|
104
|
-
end
|
105
|
-
|
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)
|
110
|
-
end
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|