manga-tools 0.1.3 → 0.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cbd23ebbac4de4066358e4f01d81465f87fea705f324dc3de5e4984a61248ab6
4
- data.tar.gz: 652062faa457e12a19d5e19baad357483697a2aa1c50c7f1b42b4f141ea39235
3
+ metadata.gz: b4e6fa4035509554d7e20e8a8eba9c21b813bb8e68dc800b3c44a58419dfa2e0
4
+ data.tar.gz: 1ae99cc528222dcad3a93cfb36cc34ec86474a157a642f51223ff5b7daa082ac
5
5
  SHA512:
6
- metadata.gz: 0276b8fb60c70d5d7e3de81c8d3678e4b7ad165668ddaab36570a07e7492b0f1962920ef9887cb3bee8ec5084d3ad414e8407cd361e34d9b0400de506d46566a
7
- data.tar.gz: 43344b6b8dbe7d75f9316f0870fa4feb2c1fa8ae127bb3ad3dd72969bb1252d0a3c71b36ecbe7ed84b36ddae58b18df90580493abfeb077a9ac43cb5e1c78c4f
6
+ metadata.gz: 58d66eea0fd9957cb9a2a563810b4397fb0d476b6d99a7f56b4a3668a22cea0351aa3eca8758d5bf267a04eb7b34c842340ed9e42875750f973eb164157df91e
7
+ data.tar.gz: a61b64bbf848a173b45a16698e6a7cc7da586a13c0a7c681ebc75555ea958421d3327541258f4b2ea989d5501234df94fb0b3c443be7d43769b775a02fd4b394
@@ -0,0 +1,12 @@
1
+ # These are supported funding model platforms
2
+
3
+ github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4
+ patreon: yagihiro
5
+ open_collective: # Replace with a single Open Collective username
6
+ ko_fi: # Replace with a single Ko-fi username
7
+ tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8
+ community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9
+ liberapay: # Replace with a single Liberapay username
10
+ issuehunt: # Replace with a single IssueHunt username
11
+ otechie: # Replace with a single Otechie username
12
+ custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
@@ -19,11 +19,17 @@ AllCops:
19
19
  Exclude:
20
20
  - 'bin/*'
21
21
 
22
+ Lint/AmbiguousBlockAssociation:
23
+ Exclude:
24
+ - 'spec/**/*'
25
+
22
26
  Metrics/AbcSize:
23
27
  Max: 37
24
28
 
25
29
  Metrics/BlockLength:
26
30
  Max: 36
31
+ Exclude:
32
+ - 'spec/**/*'
27
33
 
28
34
  Metrics/CyclomaticComplexity:
29
35
  Max: 10
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- manga-tools (0.1.3)
4
+ manga-tools (0.2.0)
5
5
  faraday (~> 1.0)
6
6
  thor (~> 1.0)
7
7
 
data/README.md CHANGED
@@ -29,6 +29,18 @@ Or install it yourself as:
29
29
  $ manga-tools search "ONE PIECE"
30
30
  ```
31
31
 
32
+ * Follow the specified title
33
+
34
+ ```
35
+ $ manga-tools follow "key"
36
+ ```
37
+
38
+ * Displays a list of the titles you are following
39
+
40
+ ```
41
+ $ manga-tools follow-list
42
+ ```
43
+
32
44
  ## Development
33
45
 
34
46
  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.
@@ -1,7 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'tools/cli'
4
+ require_relative 'tools/client'
5
+ require_relative 'tools/formatter'
4
6
  require_relative 'tools/http'
7
+ require_relative 'tools/session'
8
+ require_relative 'tools/session_store'
5
9
  require_relative 'tools/version'
6
10
 
7
11
  module Manga
@@ -2,27 +2,41 @@
2
2
 
3
3
  require 'json'
4
4
  require 'thor'
5
- require_relative 'http'
5
+ require_relative 'client'
6
+ require_relative 'formatter'
6
7
 
7
8
  module Manga
8
9
  module Tools
9
10
  # CLI class
10
11
  class CLI < Thor
12
+ HOST_OPTION = { aliases: '-h', desc: 'Specifies the host to connect to for development' }.freeze
13
+
11
14
  def self.exit_on_failure?
12
15
  true
13
16
  end
14
17
 
15
18
  desc 'search WORD', 'Search titles for a given WORD'
19
+ method_option :host, HOST_OPTION
16
20
  def search(word)
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)
21
+ client = Manga::Tools::Client.new
22
+ results = client.search(word, options)
23
+ Manga::Tools::Formatter.display(:search, word, results)
24
+ end
25
+
26
+ desc 'follow KEY', 'Follow a title for a given follow KEY'
27
+ method_option :host, HOST_OPTION
28
+ def follow(key)
29
+ client = Manga::Tools::Client.new
30
+ results = client.follow(key, options)
31
+ Manga::Tools::Formatter.display(:follow, key, results)
32
+ end
20
33
 
21
- puts "Searching '#{word}' ..."
22
- results.each do |item|
23
- puts "#{item['published_at']}: #{item['title']}"
24
- end
25
- puts 'Finished.'
34
+ desc 'follow-list', 'Displays the follow list'
35
+ method_option :host, HOST_OPTION
36
+ def follow_list
37
+ client = Manga::Tools::Client.new
38
+ results = client.follow_list(options)
39
+ Manga::Tools::Formatter.display(:follow_list, results)
26
40
  end
27
41
  end
28
42
  end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cgi'
4
+ require 'json'
5
+ require_relative 'http'
6
+ require_relative 'session'
7
+
8
+ module Manga
9
+ module Tools
10
+ # Client Application class
11
+ class Client
12
+ # @return [Manga::Tools::Session] a session instance
13
+ attr :session
14
+
15
+ def initialize
16
+ @session = Manga::Tools::Session.new
17
+ end
18
+
19
+ # Search `word` with options.
20
+ # @param word [String] search string
21
+ # @param options [Hash] command options from Thor
22
+ def search(word, options)
23
+ session.options = adapt_to_dev_env(options)
24
+ session.get("/publications?keyword=#{CGI.escape(word)}")
25
+ end
26
+
27
+ # Follow a title for a given follow `key`
28
+ # @param key [String] follow key
29
+ # @param options [Hash] command options from Thor
30
+ def follow(key, options)
31
+ params = { key: key }
32
+ session.options = adapt_to_dev_env(options)
33
+ session.post('/follows', params)
34
+ end
35
+
36
+ # Displays the follow list
37
+ # @param options [Hash] command options from Thor
38
+ def follow_list(options)
39
+ session.options = adapt_to_dev_env(options)
40
+ session.get('/follows')
41
+ end
42
+
43
+ private
44
+
45
+ def adapt_to_dev_env(options)
46
+ opts = options.dup
47
+
48
+ if opts[:host]
49
+ # If the `host` option is specified, it is assumed to be the development environment
50
+ opts[:session_file_name] = 'session-development.txt'
51
+ end
52
+
53
+ opts
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Manga
4
+ module Tools
5
+ # Formatter class
6
+ class Formatter
7
+ class << self
8
+ def display(command, *results)
9
+ case command
10
+ when :search
11
+ display_search(results)
12
+ when :follow
13
+ display_follow(results)
14
+ when :follow_list
15
+ display_follow_list(results)
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def display_search(results)
22
+ puts "Searching '#{results[0]}' ..."
23
+ results[1].each do |item|
24
+ puts "#{item['published_at']} [#{item['follow_key']}]: #{item['title']}"
25
+ end
26
+ puts 'Finished.'
27
+ end
28
+
29
+ def display_follow(results)
30
+ puts "Followed '#{results[1]['title']}'."
31
+ puts 'Finished.'
32
+ end
33
+
34
+ def display_follow_list(results)
35
+ puts 'Listing follow list...'
36
+ results[0].each do |item|
37
+ puts (item['title']).to_s
38
+ end
39
+ puts 'Finished.'
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -9,35 +9,48 @@ module Manga
9
9
  # HTTP client class
10
10
  module Http
11
11
  # @param url [String] an url string, eg: `https://example.com`
12
+ # @param session_id [String, nil] A Session ID string or nil
12
13
  # @return [Faraday::Connection] a connection object
13
- def self.connection(url)
14
- @connection ||= Faraday.new(
15
- url: url,
16
- headers: { 'User-Agent' => user_agent }
17
- ) do |f|
14
+ def self.connection(url, session_id)
15
+ headers = { 'User-Agent' => user_agent }
16
+ headers['X-Session-ID'] = session_id if session_id
17
+
18
+ @connection = Faraday.new(url: url, headers: headers) do |f|
18
19
  # f.response :logger
20
+ f.response :raise_error
19
21
  end
20
22
  end
21
23
 
22
24
  # @param url [String] an url string, eg: `https://example.com/path/to/object`
25
+ # @param session_id [String, nil] A Session ID string or nil
23
26
  # @return [Faraday::Response] a response object
24
- def self.get(url)
27
+ def self.get(url, session_id)
25
28
  u = URI.parse(url)
26
- connection("#{u.scheme}://#{u.host}").get(u.request_uri)
29
+ connection(connection_url(u), session_id).get(u.request_uri)
27
30
  end
28
31
 
29
- # @param response [Faraday::Response] a response object
30
- # @return [nil|Integer] number of second to cache
31
- #
32
- # supported http header
33
- # - cache-control: max-age=[sec]
34
- def self.seconds_to_cache(response)
35
- result = response['cache-control']&.split(/,/)&.map(&:strip)&.find { |item| item =~ /max-age=(.+)/ }
36
- return nil unless result
32
+ def self.post(url, session_id, params)
33
+ u = URI.parse(url)
34
+ connection(connection_url(u), session_id).post do |req|
35
+ req.headers['Content-Type'] = 'application/json'
36
+ req.url u.request_uri
37
+ req.body = params.to_json
38
+ end
39
+ end
37
40
 
38
- Regexp.last_match(1).to_i
41
+ # @param url [URI] A URL instance.
42
+ # @return [String] Returns the URL to connect to.
43
+ #
44
+ # If the port is not a standard port (80 or 443), the port number is included.
45
+ def self.connection_url(url)
46
+ if [80, 443].include?(url.port)
47
+ "#{url.scheme}://#{url.host}"
48
+ else
49
+ "#{url.scheme}://#{url.host}:#{url.port}"
50
+ end
39
51
  end
40
52
 
53
+ # @return [String] Returns a user agent string
41
54
  def self.user_agent
42
55
  @user_agent ||= "manga-tools #{Manga::Tools::VERSION}"
43
56
  end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require_relative 'http'
5
+ require_relative 'session_store'
6
+
7
+ module Manga
8
+ module Tools
9
+ # Network Session class (Session ID handling + HTTP CRUD handling)
10
+ class Session
11
+ DEFAULT_HOST = 'https://manga-tools-server.herokuapp.com'
12
+
13
+ # @return [Hash] options hash instance
14
+ attr :options
15
+
16
+ # @return [SessionStore] the session store instance
17
+ attr :store
18
+
19
+ def initialize
20
+ @store = Manga::Tools::SessionStore.new
21
+ @options = {}
22
+ end
23
+
24
+ def options=(new_options)
25
+ @base_url = nil
26
+ @options = new_options
27
+ store.session_file_name = @options[:session_file_name] if @options[:session_file_name]
28
+ end
29
+
30
+ def get(path)
31
+ response = with_session { |session_id| Manga::Tools::Http.get(build_full_url(path), session_id) }
32
+ JSON.parse(response.body)
33
+ end
34
+
35
+ def post(path, params)
36
+ response = with_session { |session_id| Manga::Tools::Http.post(build_full_url(path), session_id, params) }
37
+ JSON.parse(response.body)
38
+ end
39
+
40
+ private
41
+
42
+ def with_session
43
+ session_id = store.load
44
+ res = yield session_id
45
+ session_id = res.headers['X-Session-ID']
46
+ store.store(session_id) if session_id
47
+ res
48
+ end
49
+
50
+ def base_url
51
+ @base_url ||= options[:host] ? "http://#{options[:host]}" : DEFAULT_HOST
52
+ end
53
+
54
+ def build_full_url(path)
55
+ "#{base_url}#{path}"
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fileutils'
4
+
5
+ module Manga
6
+ module Tools
7
+ # A class on session retention.
8
+ class SessionStore
9
+ # @return [String] the session file name string
10
+ attr_writer :session_file_name
11
+
12
+ def initialize
13
+ create_session_dir_if_necessary
14
+ @session_file_name = 'session.txt'
15
+ end
16
+
17
+ # Store a session.
18
+ def store(session_id)
19
+ return if exists?
20
+
21
+ File.open(session_file_path, 'w') do |f|
22
+ f.write(session_id)
23
+ end
24
+ end
25
+
26
+ # @return [String, nil] Returns the session ID if the session is stored, otherwise it returns nil
27
+ def load
28
+ return nil unless exists?
29
+
30
+ File.read(session_file_path)
31
+ end
32
+
33
+ # @return [Boolean] true if the session exists, otherwise returns false.
34
+ def exists?
35
+ File.exist?(session_file_path)
36
+ end
37
+
38
+ # @return [String] the session file path
39
+ def session_file_path
40
+ "#{session_dir}/#{@session_file_name}"
41
+ end
42
+
43
+ private
44
+
45
+ # @return [String] the root directory
46
+ def root_dir
47
+ @root_dir ||= "#{Dir.home}/.manga-tools"
48
+ end
49
+
50
+ # @return [String] the session directory
51
+ def session_dir
52
+ @session_dir ||= "#{root_dir}/session"
53
+ end
54
+
55
+ def create_session_dir_if_necessary
56
+ FileUtils.mkdir_p(session_dir)
57
+ end
58
+ end
59
+ end
60
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Manga
4
4
  module Tools
5
- VERSION = '0.1.3'
5
+ VERSION = '0.2.0'
6
6
  end
7
7
  end
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.3
4
+ version: 0.2.0
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-22 00:00:00.000000000 Z
11
+ date: 2020-09-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -46,6 +46,7 @@ executables:
46
46
  extensions: []
47
47
  extra_rdoc_files: []
48
48
  files:
49
+ - ".github/FUNDING.yml"
49
50
  - ".github/workflows/ruby.yml"
50
51
  - ".gitignore"
51
52
  - ".rspec"
@@ -63,7 +64,11 @@ files:
63
64
  - exe/manga-tools
64
65
  - lib/manga/tools.rb
65
66
  - lib/manga/tools/cli.rb
67
+ - lib/manga/tools/client.rb
68
+ - lib/manga/tools/formatter.rb
66
69
  - lib/manga/tools/http.rb
70
+ - lib/manga/tools/session.rb
71
+ - lib/manga/tools/session_store.rb
67
72
  - lib/manga/tools/version.rb
68
73
  - manga-tools.gemspec
69
74
  homepage: https://github.com/yagihiro/manga-tools