mangdown 0.13.4 → 0.14.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1b46ede1b55f92c1ff03e43c64890bcb77f98838
4
- data.tar.gz: ce4927534522cc6bf0ab39deece40bb58d7524d8
3
+ metadata.gz: ceaa6642dfcb70513d084ec3b636d2c0855a9928
4
+ data.tar.gz: e6f2920b7f48afb586aeb038543a2f32ac6e1267
5
5
  SHA512:
6
- metadata.gz: 669578148a673bdd314a4f49cb541947e473d9286e907f51effc28e298d877ac56f68c44830c4c6b123732deacb289395d96bf13245d5f2db28d8e61305748f1
7
- data.tar.gz: c4e48d29330badd3ebd2166262b6fa62783b0ce614ccac4907ec7b7e2c4259618aa16c8535d0498770451ee507bc5f8f33f599757235573063eb0c5145e7a0c9
6
+ metadata.gz: e6ef238870bac0cd688769574919aea5ab8dffcd59b52f17ce1cbe315310179394c572972ffa34045a036a4f1d7eb200700a12ff9fb2e00756bb8b3afceddc8a
7
+ data.tar.gz: f9c15ca24c70f1abd1eabbe33af7a90ed68f300fe5397877a8a76ab3ee1966d9cd2e2b4337871a637c0a0dcd3bbbbc6c86b6df7bd1feefe2dc3933849ae3f606
data/README.md CHANGED
@@ -2,9 +2,16 @@
2
2
 
3
3
  There is a simple built-in client, "M", that you can use for finding manga:
4
4
 
5
- ```
5
+ ```ruby
6
+ require 'mangdown/client'
7
+
8
+ # Search for an exact match
6
9
  results = M.find("Dragon Ball")
7
10
 
11
+ # Or if you need more flexibilty when searching for a manga,
12
+ # use are Regex
13
+ results = M.find(/dragon ball(\ssd)?$/i)
14
+
8
15
  # Get a Mangdown::Manga object
9
16
  manga = results.first.to_manga
10
17
 
@@ -1,54 +1,118 @@
1
1
  module Mangdown
2
2
  class Mangareader < Adapter::Base
3
- Mangdown::ADAPTERS << self
3
+
4
+ site :mangareader
5
+
6
+ attr_reader :root
4
7
 
5
8
  def initialize(uri, doc, name)
6
9
  super
7
- @root ||= 'http://www.mangareader.net'
8
- @manga_list_css = 'ul.series_alpha li a'
9
- @manga_name_css = 'h2.aname'
10
- @chapter_list_css = 'div#chapterlist td a'
11
- @manga_list_uri = "#{@root}/alphabetical"
12
- @manga_link_prefix = @root
13
- @reverse_chapters = false
14
- @manga_uri_regex =
15
- /#{@root}(\/\d+)?(\/[^\/]+)(\.html)?/i
16
- @chapter_uri_regex =
17
- /#{@root}(\/[^\/]+){1,2}\/(\d+|chapter-\d+\.html)/i
18
- @page_uri_regex = /.+\.(png|jpg|jpeg)$/i
10
+
11
+ @root = 'http://www.mangareader.net'
19
12
  end
20
13
 
21
- def manga_name
22
- CGI.unescapeHTML(super)
14
+ def is_manga_list?(uri = @uri)
15
+ uri == "#{root}/alphabetical"
23
16
  end
24
17
 
25
- def build_page_uri(uri, manga, chapter, page_num)
26
- slug = slug(manga)
27
- "#{root}/#{slug}/#{chapter}/#{page_num}"
18
+ def is_manga?(uri = @uri)
19
+ uri.slice(/#{root}(\/\d+)?(\/[^\/]+)(\.html)?/i) == uri
28
20
  end
29
21
 
30
- def num_pages
31
- doc.css('select')[1].css('option').length
22
+ def is_chapter?(uri = @uri)
23
+ uri.slice(/#{root}(\/[^\/]+){1,2}\/(\d+|chapter-\d+\.html)/i) == uri
32
24
  end
33
25
 
34
- def page_image_src
35
- page_image[:src]
26
+ def is_page?(uri = @uri)
27
+ uri.slice(/.+\.(png|jpg|jpeg)$/i) == uri
28
+ end
29
+
30
+ # Only valid mangas should be returned (using is_manga?(uri))
31
+ def manga_list
32
+ doc.css('ul.series_alpha li a').map { |a|
33
+ uri = "#{root}#{a[:href]}"
34
+ manga = { uri: uri, name: a.text.strip, site: site }
35
+
36
+ manga if is_manga?(uri)
37
+ }.compact
36
38
  end
37
39
 
38
- def page_image_name
39
- page_image[:alt].sub(/([^\d]*)(\d+)(\.\w+)?$/) {
40
- "#{Regexp.last_match[1]}" +
41
- "#{Regexp.last_match[2].to_s.rjust(3, '0')}"
40
+ def manga
41
+ { uri: uri, name: manga_name, site: site }
42
+ end
43
+
44
+ # Only valid chapters should be returned (using is_chapter?(uri))
45
+ def chapter_list
46
+ doc.css('div#chapterlist td a').map { |a|
47
+ uri = root + a[:href].sub(root, '')
48
+ chapter = { uri: uri, name: a.text.strip, site: site }
49
+
50
+ chapter if is_chapter?(uri)
51
+ }.compact
52
+ end
53
+
54
+ def chapter
55
+ { uri: uri,
56
+ manga: manga_name,
57
+ name: chapter_name,
58
+ chapter: chapter_number,
59
+ site: site }
60
+ end
61
+
62
+ def page_list
63
+ last_page = doc.css('select')[1].css('option').length
64
+ (1..last_page).map { |page|
65
+ slug = manga_name.gsub(' ', '-').gsub(/[:,]/, '')
66
+ uri = "#{root}/#{slug}/#{chapter_number}/#{page}"
67
+ uri = Mangdown::Uri.new(uri).downcase
68
+
69
+ { uri: uri, name: page, site: site } }
70
+ end
71
+
72
+ def page
73
+ page_image = doc.css('img')[0]
74
+ uri = page_image[:src]
75
+ name = page_image[:alt].sub(/([^\d]*)(\d+)(\.\w+)?$/) {
76
+ Regexp.last_match[1].to_s + Regexp.last_match[2].to_s.rjust(3, '0')
42
77
  }
78
+
79
+ { uri: uri, name: name, site: site }
43
80
  end
44
81
 
45
82
  private
46
- def page_image
47
- doc.css('img')[0]
83
+
84
+ def manga_name
85
+ if is_manga?
86
+ name = doc.css('h2.aname').text
87
+ elsif is_chapter?
88
+ name = chapter_manga_name
89
+ end
90
+
91
+ CGI.unescapeHTML(name) if name
92
+ end
93
+
94
+ def chapter_name
95
+ if @name
96
+ @name.sub(/\s(\d+)$/) { |num| ' ' + num.to_i.to_s.rjust(5, '0') }
97
+ else
98
+ doc.css("").text # Not implimented
99
+ end
100
+ end
101
+
102
+ def chapter_manga_name
103
+ if @name
104
+ @name.slice(/(^.+)\s/, 1)
105
+ else
106
+ doc.css("").text # Not implimented
107
+ end
48
108
  end
49
109
 
50
- def slug(string)
51
- string.gsub(' ', '-').gsub(/[:,]/, '')
110
+ def chapter_number
111
+ if @name
112
+ @name.slice(/\d+\z/).to_i
113
+ else
114
+ doc.css("").text # Not implimented
115
+ end
52
116
  end
53
117
  end
54
118
  end
@@ -2,7 +2,7 @@ module Mangdown
2
2
  module Adapter
3
3
  class NoAdapterError < StandardError
4
4
  def initialize(site)
5
- super("Bad Site: No Properties Specified for Site: #{site.inspect}")
5
+ super("Bad Site: No Adapter Specified for Site: #{site.inspect}")
6
6
  end
7
7
  end
8
8
  end
@@ -1,143 +1,81 @@
1
1
  module Mangdown
2
2
  module Adapter
3
3
  class Base
4
-
5
- attr_reader :root
6
- def initialize(uri, doc, name)
7
- @uri, @doc, @name = uri, doc, name
8
- #@root = ''
9
- #@manga_list_css = ''
10
- #@chapter_list_css = ''
11
- #@manga_name_css = ''
12
- #@chapter_name_css = ''
13
- #@chapter_manga_name_css = ''
14
- #@chapter_number_css = ''
15
- #@manga_list_uri = ''
16
- #@manga_link_prefix = ''
17
- #@reverse_chapters = true || false
18
- #@manga_uri_regex = /.*/i
19
- #@chapter_uri_regex = /.*/i
20
- #@page_uri_regex = /.*/i
21
- end
22
4
 
23
- def self.type
24
- name.split('::').last.downcase.to_sym
5
+ # Returns something truthy if this adapter should be used for the
6
+ # given url or adapter name
7
+ def self.for?(url_or_adapter_name)
8
+ url_or_adapter_name[site.to_s]
25
9
  end
26
10
 
27
- def type
28
- self.class.type
11
+ def self.site(site = nil)
12
+ site ? @_site = site : @_site
29
13
  end
30
-
31
- # Override this if you want to use an adapter name for a site that is
32
- # not matched in the site's url
33
- # e.g. CoolAdapterName < Adapter::Base
34
- # def site
35
- # "mangareader"
36
- # end
37
- def self.site
38
- type.to_s
14
+
15
+ attr_reader :uri, :doc, :name
16
+ def initialize(uri, doc, name)
17
+ @uri = uri
18
+ @doc = doc
19
+ @name = name
39
20
  end
40
21
 
41
22
  def site
42
23
  self.class.site
43
24
  end
44
25
 
26
+ # Overwrite if you want to check the uri if it belongs to a manga list
27
+ def is_manga_list?(uri = @uri)
28
+ raise NotImplementedError
29
+ end
30
+
45
31
  # Must return true/false if uri represents a manga for adapter
46
32
  def is_manga?(uri = @uri)
47
- uri.slice(@manga_uri_regex) == uri
33
+ raise NotImplementedError
48
34
  end
49
35
 
50
36
  # Must return true/false if uri represents a chapter for adapter
51
37
  def is_chapter?(uri = @uri)
52
- uri.slice(@chapter_uri_regex) == uri
38
+ raise NotImplementedError
53
39
  end
54
40
 
55
41
  # Must return true/false if uri represents a page for adapter
56
42
  def is_page?(uri = @uri)
57
- uri.slice(@page_uri_regex) == uri
58
- end
59
-
60
- # Must return a string
61
- def manga_name
62
- if is_manga?
63
- doc.css(@manga_name_css).text
64
- elsif is_chapter?
65
- chapter_manga_name
66
- end
67
- end
68
-
69
- def chapter_name
70
- if @name
71
- @name.sub(/\s(\d+)$/) { |num| ' ' + num.to_i.to_s.rjust(5, '0') }
72
- else
73
- doc.css(@chapter_name_css).text
74
- end
75
- end
76
-
77
- def chapter_manga_name
78
- if @name
79
- @name.slice(/(^.+)\s/, 1)
80
- else
81
- doc.css(@chapter_manga_name_css).text
82
- end
83
- end
84
-
85
- def chapter_number
86
- raise NoMethodError, "Not a chapter" unless is_chapter?
87
- if @name
88
- @name.slice(/\d+\z/).to_i
89
- else
90
- doc.css(@chapter_number_css).text
91
- end
43
+ raise NotImplementedError
92
44
  end
93
45
 
94
- # Must return a uri for a page given the arguments
95
- def build_page_uri(uri, manga, chapter, page_num)
46
+ # Return Array of Hash with keys: :uri, :name, :site
47
+ def manga_list
48
+ raise NotImplementedError
96
49
  end
97
50
 
98
- # Must return the number of pages for a chapter
99
- def num_pages
51
+ # Return Hash with keys: :uri, :name, :site
52
+ def manga
53
+ raise NotImplementedError
100
54
  end
101
55
 
102
- # Must return the src for the page image
103
- def page_image_src
56
+ # Return Array of Hash with keys: :uri, :name, :site
57
+ def chapter_list
58
+ raise NotImplementedError
104
59
  end
105
60
 
106
- # Must return the name of the page image
107
- def page_image_name
61
+ # Return Hash with keys: :uri, :name, :chapter, :manga, :site
62
+ def chapter
63
+ raise NotImplementedError
108
64
  end
109
65
 
110
- # If no block given, must return an array arrays
111
- # [manga_uri, manga_name, adapter_type]
112
- # If block given, then the block may alter this array
113
- # Only valid mangas should be returned (using is_manga?(uri))
114
- def collect_manga_list
115
- doc.css(@manga_list_css).map { |a|
116
- manga = ["#{@manga_link_prefix}#{a[:href]}", a.text.strip, type]
117
- if is_manga?(manga.first)
118
- block_given? ? yield(manga) : manga
119
- end
120
- }.compact
66
+ # Return Array of Hash with keys: :uri, :name, :site
67
+ def page_list
68
+ raise NotImplementedError
121
69
  end
122
70
 
123
- # If no block given, must return an array arrays
124
- # [chapter_uri, chapter_name, adapter_type]
125
- # If block given, then the block may alter this array
126
- # Only valid chapters should be returned (using is_chapter?(uri))
127
- def manga_chapters
128
- chapters = doc.css(@chapter_list_css).map { |a|
129
- link = root + a[:href].sub(root, '')
130
- chapter = [link, a.text.strip, type]
131
- if is_chapter?(chapter.first)
132
- block_given? ? yield(chapter) : chapter
133
- end
134
- }.compact
135
- @reverse_chapters ? chapters.reverse : chapters
71
+ # Return Hash with keys: :uri, :name, :site
72
+ def page
73
+ raise NotImplementedError
136
74
  end
137
75
 
138
76
  private
139
77
  def doc
140
- @doc ||= Tools.get_doc(@uri)
78
+ @doc ||= Tools.get_doc(uri)
141
79
  end
142
80
  end
143
81
  end
@@ -4,35 +4,21 @@ module Mangdown
4
4
  include Equality
5
5
  include Enumerable
6
6
 
7
- attr_reader :uri, :pages
7
+ attr_reader :uri, :pages, :name, :manga, :chapter
8
+ attr_accessor :adapter
8
9
 
9
- def initialize(uri, name = nil, manga = nil, chapter = nil)
10
- # use a valid name
10
+ def initialize(uri, name, manga, chapter = nil)
11
11
  @name = name
12
12
  @manga = manga
13
13
  @chapter = chapter
14
14
  @uri = Mangdown::Uri.new(uri)
15
- @properties = Properties.new(@uri, nil, nil, name)
16
-
17
- load_pages
18
- end
19
-
20
- def name
21
- @name ||= @properties.chapter_name
22
- end
23
-
24
- def manga
25
- @manga ||= @properties.manga_name
26
- end
27
-
28
- def chapter
29
- @chapter ||= @properties.chapter_number
15
+ @pages = []
30
16
  end
31
17
 
32
18
  def inspect
33
19
  "#<#{self.class} @name=#{name} @uri=#{uri} " +
34
- "@pages=[#{pages.first(10).join(',')}" +
35
- "#{",..." if pages.length > 10}]>"
20
+ "@pages=[#{pages.first(3).join(',')}" +
21
+ "#{",..." if pages.length > 3}]>"
36
22
  end
37
23
  alias_method :to_s, :inspect
38
24
 
@@ -94,19 +80,19 @@ module Mangdown
94
80
  { failed: failed, succeeded: succeeded, skipped: skipped }
95
81
  end
96
82
 
97
- private
98
- def setup_download_dir!(dir)
99
- set_path(dir)
100
- FileUtils.mkdir_p(to_path) unless Dir.exists?(to_path)
101
- end
102
-
103
83
  def load_pages
104
- @pages ||= []
84
+ return @pages if @pages.any?
105
85
 
106
86
  fetch_each_page do |page| @pages << page end
107
87
  @pages.sort_by!(&:name)
108
88
  end
109
89
 
90
+ private
91
+ def setup_download_dir!(dir)
92
+ set_path(dir)
93
+ FileUtils.mkdir_p(to_path) unless Dir.exists?(to_path)
94
+ end
95
+
110
96
  # get page objects for all pages in a chapter
111
97
  def fetch_each_page
112
98
  pages = build_page_hashes
@@ -118,21 +104,19 @@ module Mangdown
118
104
 
119
105
  # get the docs for number of pages
120
106
  def build_page_hashes
121
- (1..@properties.num_pages).map { |num|
122
- uri_str = @properties.build_page_uri(uri, manga, chapter, num)
123
- uri = Mangdown::Uri.new(uri_str).downcase
124
- MDHash.new(uri: uri, name: num, chapter: name, manga: manga)
125
- }
107
+ adapter.page_list.map { |page|
108
+ page.merge!(chapter: name, manga: manga)
109
+ MDHash.new(page) }
126
110
  end
127
111
 
128
112
  # get the page name and uri
129
113
  def get_page(uri, doc)
130
- properties = Properties.new(uri, nil, doc)
131
- uri = properties.page_image_src
132
- page = properties.page_image_name
133
- site = properties.type
114
+ # Local binding for adapter
115
+ adapter = Mangdown.adapter!(uri, nil, doc)
116
+ page = adapter.page
117
+ page.merge!(chapter: name, manga: manga)
134
118
 
135
- MDHash.new(uri: uri, name: page, chapter: name, manga: manga, site: site)
119
+ MDHash.new(page)
136
120
  end
137
121
  end
138
122
  end
@@ -1,35 +1,25 @@
1
+ require_relative '../mangdown'
2
+
1
3
  module M
2
4
  extend self
3
5
 
4
- include ::Mangdown
5
-
6
6
  DATA_FILE_PATH = Dir.home + '/.manga_list.yml'
7
7
  HELP_FILE_PATH = File.expand_path(
8
8
  '../../README.md', File.dirname(__FILE__)
9
9
  )
10
- =begin
11
- MANGA_PAGES = (1..9).map { |p|
12
- "http://www.wiemanga.com/search/?name_sel=contain" +
13
- "&author_sel=contain&completed_series=either&page=#{p}.html"
14
- } +
15
- [
16
- 'http://www.mangareader.net/alphabetical',
17
- 'http://mangafox.me/manga/'
18
- ]
19
- =end
10
+
20
11
  MANGA_PAGES = ['http://www.mangareader.net/alphabetical']
21
12
 
22
13
  # return a list of hash with :uri and :name of mangas found in list
23
14
  def find(search)
24
- validate_search(search)
25
- current_manga_list.mangas.select { |manga|
26
- manga[:name].downcase.include?(search.downcase)
27
- }
15
+ search = Regexp.new(/^#{search}$/) if search.respond_to?(:to_str)
16
+
17
+ current_manga_list.select { |manga| manga[:name] =~ search }
28
18
  end
29
19
 
30
20
  # cbz all subdirectories in a directory
31
21
  def cbz(dir)
32
- Dir.exist?(dir) ? CBZ.all(dir) : raise(Errno::ENOENT, dir)
22
+ Dir.exist?(dir) ? Mangdown::CBZ.all(dir) : raise(Errno::ENOENT, dir)
33
23
  end
34
24
 
35
25
  # display help file
@@ -62,20 +52,15 @@ module M
62
52
  # otherwise fetch new data and write it to the data file
63
53
  def current_manga_list
64
54
  data = data_from_file
65
- return MangaList.from_data(data) if data
55
+ return Mangdown::MangaList.from_data(data) if data
66
56
 
67
- MangaList.new(*MANGA_PAGES).tap { |list|
68
- File.open(path, 'w+') { |f| f.write(list.to_yaml) }
69
- }
57
+ list = MANGA_PAGES.inject([]) { |manga, uri|
58
+ list = Mangdown::MDHash.new(uri: uri).to_manga_list
59
+ list.merge(manga) }
60
+ File.open(path, 'w+') { |f| f.write(list.to_yaml) }
61
+ list
70
62
  rescue Object => error
71
63
  puts "#{path} may be corrupt: #{error.message}"
72
64
  raise
73
65
  end
74
-
75
- # check if the search key contains letters or numbers
76
- def validate_search(search)
77
- unless search =~ /\w/
78
- raise ArgumentError, "Searches must contain letters and numbers"
79
- end
80
- end
81
66
  end
@@ -7,25 +7,19 @@ module Mangdown
7
7
  include Equality
8
8
  include Enumerable
9
9
 
10
- attr_reader :uri, :chapters
10
+ attr_reader :uri, :chapters, :name
11
+ attr_accessor :adapter
11
12
 
12
- def initialize(uri, name = nil)
13
+ def initialize(uri, name)
13
14
  @name = name
14
15
  @uri = Mangdown::Uri.new(uri)
15
16
  @chapters = []
16
- @properties = Properties.new(uri)
17
-
18
- get_chapters
19
- end
20
-
21
- def name
22
- @name || @properties.manga_name
23
17
  end
24
18
 
25
19
  def inspect
26
20
  "#<#{self.class} @name=#{name} @uri=#{uri} " +
27
- "@chapters=[#{chapters.first(10).join(',')}" +
28
- "#{",..." if chapters.length > 10}]>"
21
+ "@chapters=[#{chapters.first(3).join(',')}" +
22
+ "#{",..." if chapters.length > 3}]>"
29
23
  end
30
24
  alias_method :to_s, :inspect
31
25
 
@@ -42,17 +36,29 @@ module Mangdown
42
36
  def download_to(dir, start = 0, stop = -1, opts = { force_download: false })
43
37
  start, stop = validate_indeces!(start, stop)
44
38
  setup_download_dir!(dir)
39
+ failed = []
40
+ succeeded = []
41
+ skipped = []
45
42
 
46
43
  bar = progress_bar(start, stop)
47
44
  chapters[start..stop].each do |md_hash|
48
45
  chapter = md_hash.to_chapter
49
-
50
- if chapter.download_to(to_path, opts)
51
- bar.increment!
46
+ chapter_result = chapter.download_to(to_path, opts)
47
+
48
+ if chapter_result[:failed].any?
49
+ failed << chapter
50
+ elsif chapter_result[:succeeded].any?
51
+ succeeded << chapter
52
+ elsif chapter_result[:skipped].any?
53
+ skipped << chapter
54
+ end
55
+ if chapter_result[:failed].any?
56
+ STDERR.puts("error: #{chapter.name} was not fully downloaded")
52
57
  else
53
- STDERR.puts("error: #{chapter.name} was not downloaded")
58
+ bar.increment!
54
59
  end
55
60
  end
61
+ { failed: failed, succeeded: succeeded, skipped: skipped }
56
62
  end
57
63
 
58
64
  # explicit conversion to manga
@@ -75,14 +81,14 @@ module Mangdown
75
81
  @chapters.each(&block)
76
82
  end
77
83
 
78
- private
79
84
  # push MDHashes of manga chapters to @chapters
80
- def get_chapters
81
- @chapters += @properties.manga_chapters do |uri, chapter, site|
82
- MDHash.new(uri: uri, name: chapter, manga: name, site: site)
83
- end
85
+ def load_chapters
86
+ @chapters += adapter.chapter_list.map { |chapter|
87
+ chapter.merge!(manga: name)
88
+ MDHash.new(chapter) }
84
89
  end
85
90
 
91
+ private
86
92
  def chapter_indeces(start, stop)
87
93
  length = chapters.length
88
94
  [start, stop].map { |i| i < 0 ? length + i : i }
@@ -1,32 +1,41 @@
1
1
  module Mangdown
2
-
3
- # a list of manga
4
2
  class MangaList
3
+ include Enumerable
5
4
 
6
- def self.from_data(mangas)
7
- new(nil, mangas: mangas)
5
+ def self.from_data(manga)
6
+ new(manga)
8
7
  end
9
8
 
10
- attr_reader :mangas
9
+ attr_reader :manga
11
10
 
12
- def initialize(*uris, mangas: [])
13
- @mangas = mangas.map! { |hash| MDHash.new(hash) }
14
- if uris.any?
15
- uris.each { |uri| get_mangas(uri) }
16
- end
11
+ def initialize(manga = [])
12
+ @manga = manga.map! { |hash| MDHash.new(hash) }
17
13
  end
18
14
 
15
+ def each(&block)
16
+ manga.each(&block)
17
+ end
18
+
19
19
  def to_yaml
20
- @mangas.map(&:to_hash).to_yaml
20
+ @manga.map(&:to_hash).to_yaml
21
21
  end
22
22
 
23
- private
24
- # get a list of mangas from the uri
25
- def get_mangas(uri)
23
+ def load_manga(uri)
24
+ adapter = Mangdown.adapter!(uri)
25
+
26
+ manga = adapter.manga_list.map { |manga| MDHash.new(manga) }
26
27
 
27
- @mangas += Properties.new(uri).collect_manga_list { |uri, name, site|
28
- MDHash.new(uri: uri, name: name, site: site)
29
- }
28
+ merge(manga)
29
+ end
30
+
31
+ def to_a
32
+ manga
33
+ end
34
+
35
+ def merge(other)
36
+ @manga += other.to_a
37
+
38
+ return self
30
39
  end
31
40
  end
32
41
  end