klipbook 2.1.3 → 3.0.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.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +9 -0
  3. data/.ruby-version +1 -1
  4. data/.travis.yml +1 -1
  5. data/CHANGELOG.txt +11 -0
  6. data/Gemfile +2 -15
  7. data/Gemfile.lock +73 -113
  8. data/LICENSE.txt +18 -17
  9. data/README.md +63 -69
  10. data/Rakefile +3 -30
  11. data/bin/klipbook +3 -221
  12. data/klipbook.gemspec +32 -118
  13. data/lib/klipbook/cli.rb +69 -0
  14. data/lib/klipbook/commands/command.rb +39 -0
  15. data/lib/klipbook/commands/export.rb +50 -0
  16. data/lib/klipbook/commands/exporters/exporter.rb +49 -0
  17. data/lib/klipbook/{tohtml → commands/exporters}/html_book_summary.erb +1 -1
  18. data/lib/klipbook/commands/exporters/html_exporter.rb +25 -0
  19. data/lib/klipbook/commands/exporters/json_exporter.rb +17 -0
  20. data/lib/klipbook/commands/exporters/markdown_book_summary.erb +12 -0
  21. data/lib/klipbook/commands/exporters/markdown_exporter.rb +19 -0
  22. data/lib/klipbook/commands/list.rb +17 -0
  23. data/lib/klipbook/config.rb +18 -10
  24. data/lib/klipbook/logger.rb +15 -0
  25. data/lib/klipbook/sources/amazon_site/book_scraper.rb +0 -2
  26. data/lib/klipbook/sources/book.rb +36 -21
  27. data/lib/klipbook/sources/clipping.rb +10 -8
  28. data/lib/klipbook/sources/kindle_device/entry.rb +8 -6
  29. data/lib/klipbook/sources/kindle_device/entry_parser.rb +66 -64
  30. data/lib/klipbook/sources/kindle_device/file.rb +45 -43
  31. data/lib/klipbook/sources/kindle_device/file_parser.rb +23 -21
  32. data/lib/klipbook/sources/source.rb +30 -0
  33. data/lib/klipbook/version.rb +1 -1
  34. data/lib/klipbook.rb +11 -11
  35. metadata +56 -113
  36. data/.document +0 -5
  37. data/.yardopts +0 -1
  38. data/Guardfile +0 -19
  39. data/features/fixtures/clippings-for-three-books.txt +0 -105
  40. data/features/list.feature +0 -31
  41. data/features/step_definitions/list_steps.rb +0 -15
  42. data/features/step_definitions/tohtml_steps.rb +0 -61
  43. data/features/step_definitions/tojson_steps.rb +0 -17
  44. data/features/support/env.rb +0 -16
  45. data/features/tohtml.feature +0 -51
  46. data/features/tojson.feature +0 -11
  47. data/lib/klipbook/colours.rb +0 -16
  48. data/lib/klipbook/commands/list_books.rb +0 -19
  49. data/lib/klipbook/commands/tohtml.rb +0 -17
  50. data/lib/klipbook/commands/tojson.rb +0 -18
  51. data/lib/klipbook/sources/invalid_source_error.rb +0 -12
  52. data/lib/klipbook/tohtml/html_printer.rb +0 -39
  53. data/lib/klipbook/tojson/book_file.rb +0 -58
  54. data/spec/lib/klipbook/commands/list_books_spec.rb +0 -43
  55. data/spec/lib/klipbook/commands/tohtml_spec.rb +0 -36
  56. data/spec/lib/klipbook/sources/book_spec.rb +0 -33
  57. data/spec/lib/klipbook/sources/kindle_device/entry_parser_spec.rb +0 -339
  58. data/spec/lib/klipbook/sources/kindle_device/file_parser_spec.rb +0 -68
  59. data/spec/lib/klipbook/sources/kindle_device/file_spec.rb +0 -163
  60. data/spec/lib/klipbook/tohtml/html_printer_spec.rb +0 -88
  61. data/spec/lib/klipbook/tojson/book_file_spec.rb +0 -76
  62. data/spec/spec_helper.rb +0 -7
@@ -0,0 +1,17 @@
1
+ module Klipbook
2
+ module Commands
3
+ class List < Command
4
+ def run_command!(book_source, _options)
5
+ books = book_source.books
6
+ if books.empty?
7
+ logger.info 'No books available'
8
+ else
9
+ logger.info 'Book list:'
10
+ books.each_with_index do |book, index|
11
+ logger.info "[#{index + 1}] #{book.title_and_author}"
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -1,22 +1,30 @@
1
+ require 'yaml'
2
+
1
3
  module Klipbook
2
4
  class Config
3
- def initialize(config_file_name='.klipbookrc')
4
- @config_file_name = config_file_name
5
- end
5
+ DEFAULT_MAXBOOKS = 5
6
6
 
7
7
  def read
8
- merge_config_from_rc_file({})
8
+ merge_config_from_rc_file({
9
+ count: DEFAULT_MAXBOOKS,
10
+ output_dir: Dir.pwd,
11
+ force: false
12
+ })
9
13
  end
10
14
 
15
+ private
16
+
11
17
  def merge_config_from_rc_file(config)
12
- config_file = File.join(File.expand_path(ENV['HOME']), @config_file_name)
18
+ config.merge(file_config)
19
+ end
13
20
 
14
- if config_file && File.exist?(config_file)
15
- require 'yaml'
16
- config.merge!(File.open(config_file) { |file| YAML::load(file) })
21
+ def file_config
22
+ config_file = File.expand_path("~/.klipbookrc")
23
+ if File.exist?(config_file)
24
+ YAML.load(File.read(config_file)) || {}
25
+ else
26
+ {}
17
27
  end
18
-
19
- config
20
28
  end
21
29
  end
22
30
  end
@@ -0,0 +1,15 @@
1
+ module Klipbook
2
+ class Logger
3
+ def initialize(stdout=$stdout, stderr=$stderr)
4
+ @stdout, @stderr = stdout, stderr
5
+ end
6
+
7
+ def info(msg)
8
+ @stdout.puts msg
9
+ end
10
+
11
+ def error(msg)
12
+ @stderr.puts msg
13
+ end
14
+ end
15
+ end
@@ -1,5 +1,3 @@
1
- require 'mechanize'
2
-
3
1
  module Klipbook::Sources
4
2
  module AmazonSite
5
3
  class BookScraper
@@ -1,28 +1,43 @@
1
- Klipbook::Book = Struct.new(:asin, :author, :title, :last_update, :clippings) do
2
- def title_and_author
3
- author_txt = author ? " by #{author}" : ''
4
- "#{title}#{author_txt}"
5
- end
1
+ # coding: utf-8
2
+ module Klipbook
3
+ Book = Struct.new(:asin, :author, :title, :last_update, :clippings) do
4
+ def title_and_author
5
+ author_txt = author ? " by #{author}" : ''
6
+ "#{title}#{author_txt}"
7
+ end
6
8
 
7
- def get_binding
8
- binding
9
- end
9
+ def sorted_clippings
10
+ clippings.sort_by(&:location)
11
+ end
10
12
 
11
- def location_html(location)
12
- if self.asin
13
- "<a href=\"kindle://book?action=open&asin=#{asin}&location=#{location}\">loc #{location}</a>"
14
- else
15
- "loc #{location}"
13
+ def get_binding
14
+ binding
15
+ end
16
+
17
+ def location_html(location)
18
+ if asin
19
+ "<a href=\"kindle://book?action=open&asin=#{asin}&location=#{location}\">loc #{location}</a>"
20
+ else
21
+ "loc #{location}"
22
+ end
23
+ end
24
+
25
+ def location_markdown(location)
26
+ if asin
27
+ "[∞](kindle://book?action=open&asin=#{asin}&location=#{location})"
28
+ else
29
+ ""
30
+ end
16
31
  end
17
- end
18
32
 
19
- def self.from_hash(hash)
20
- self.new.tap do |b|
21
- b.asin = hash['asin']
22
- b.author = hash['author']
23
- b.title = hash['title']
24
- b.last_update = hash['last_update']
25
- b.clippings = hash['clippings'].map { |clip| Klipbook::Clipping.from_hash(clip) }
33
+ def self.from_hash(hash)
34
+ self.new.tap do |b|
35
+ b.asin = hash['asin']
36
+ b.author = hash['author']
37
+ b.title = hash['title']
38
+ b.last_update = hash['last_update']
39
+ b.clippings = hash['clippings'].map { |clip| Klipbook::Clipping.from_hash(clip) }
40
+ end
26
41
  end
27
42
  end
28
43
  end
@@ -1,11 +1,13 @@
1
- Klipbook::Clipping = Struct.new(:annotation_id, :text, :location, :type, :page) do
2
- def self.from_hash(hash)
3
- self.new.tap do |b|
4
- b.annotation_id = hash['annotation_id']
5
- b.text = hash['text']
6
- b.location = hash['location']
7
- b.type = hash['type']
8
- b.page = hash['page']
1
+ module Klipbook
2
+ Clipping = Struct.new(:annotation_id, :text, :location, :type, :page) do
3
+ def self.from_hash(hash)
4
+ self.new.tap do |b|
5
+ b.annotation_id = hash['annotation_id']
6
+ b.text = hash['text']
7
+ b.location = hash['location']
8
+ b.type = hash['type']
9
+ b.page = hash['page']
10
+ end
9
11
  end
10
12
  end
11
13
  end
@@ -1,10 +1,12 @@
1
- module Klipbook::Sources
2
- module KindleDevice
3
- class Entry
4
- attr_accessor :title, :author, :type, :location, :page, :added_on, :text
1
+ module Klipbook
2
+ module Sources
3
+ module KindleDevice
4
+ class Entry
5
+ attr_accessor :title, :author, :type, :location, :page, :added_on, :text
5
6
 
6
- def initialize
7
- yield self if block_given?
7
+ def initialize
8
+ yield self if block_given?
9
+ end
8
10
  end
9
11
  end
10
12
  end
@@ -1,84 +1,86 @@
1
- module Klipbook::Sources
2
- module KindleDevice
3
- class EntryParser
4
-
5
- def build_entry(entry_text)
6
- return nil if invalid_entry?(entry_text)
7
-
8
- lines = split_text_into_lines(entry_text)
9
- title_line = lines[0].strip
10
- metadata = lines[1].strip
11
- text_lines = lines[3..-1]
12
-
13
- type = extract_type(metadata)
14
-
15
- Klipbook::Sources::KindleDevice::Entry.new do |h|
16
- h.title = extract_title(title_line)
17
- h.author = extract_author(title_line)
18
- h.location = extract_location(metadata)
19
- h.page = extract_page(metadata)
20
- h.added_on = extract_added_date(metadata)
21
- h.text = extract_content(text_lines)
22
- h.type = extract_type(metadata)
1
+ module Klipbook
2
+ module Sources
3
+ module KindleDevice
4
+ class EntryParser
5
+
6
+ def build_entry(entry_text)
7
+ return nil if invalid_entry?(entry_text)
8
+
9
+ lines = split_text_into_lines(entry_text)
10
+ title_line = lines[0].strip
11
+ metadata = lines[1].strip
12
+ text_lines = lines[3..-1]
13
+
14
+ type = extract_type(metadata)
15
+
16
+ Klipbook::Sources::KindleDevice::Entry.new do |h|
17
+ h.title = extract_title(title_line)
18
+ h.author = extract_author(title_line)
19
+ h.location = extract_location(metadata)
20
+ h.page = extract_page(metadata)
21
+ h.added_on = extract_added_date(metadata)
22
+ h.text = extract_content(text_lines)
23
+ h.type = extract_type(metadata)
24
+ end
23
25
  end
24
- end
25
26
 
26
- private
27
+ private
27
28
 
28
- def invalid_entry?(entry_text)
29
- entry_text.blank? || incomplete_entry?(entry_text)
30
- end
29
+ def invalid_entry?(entry_text)
30
+ entry_text.blank? || incomplete_entry?(entry_text)
31
+ end
31
32
 
32
- def incomplete_entry?(entry_text)
33
- split_text_into_lines(entry_text).length < 2
34
- end
33
+ def incomplete_entry?(entry_text)
34
+ split_text_into_lines(entry_text).length < 2
35
+ end
35
36
 
36
- def split_text_into_lines(entry_text)
37
- entry_text.lstrip.lines.to_a
38
- end
37
+ def split_text_into_lines(entry_text)
38
+ entry_text.lstrip.lines.to_a
39
+ end
39
40
 
40
- def extract_title(title_line)
41
- if title_line =~ /\(.+\)\Z/
42
- title_line.scan(/(.*)\s+\(.+\)\Z/).first.first
43
- else
44
- title_line
41
+ def extract_title(title_line)
42
+ if title_line =~ /\(.+\)\Z/
43
+ title_line.scan(/(.*)\s+\(.+\)\Z/).first.first
44
+ else
45
+ title_line
46
+ end
45
47
  end
46
- end
47
48
 
48
- def extract_author(title_line)
49
- match = title_line.scan /\(([^\(]+)\)\Z/
50
- match.empty? ? nil : match.first.first
51
- end
49
+ def extract_author(title_line)
50
+ match = title_line.scan /\(([^\(]+)\)\Z/
51
+ match.empty? ? nil : match.first.first
52
+ end
52
53
 
53
- def extract_type(metadata)
54
- type = metadata.scan(/^-( Your)? (\w+)/).first[1]
55
- type.downcase.to_sym
56
- end
54
+ def extract_type(metadata)
55
+ type = metadata.scan(/^-( Your)? (\w+)/).first[1]
56
+ type.downcase.to_sym
57
+ end
57
58
 
58
- def extract_location(metadata)
59
- match = metadata.scan(/Loc(ation|\.) ([0-9]+-?)/)
59
+ def extract_location(metadata)
60
+ match = metadata.scan(/Loc(ation|\.) ([0-9]+-?)/)
60
61
 
61
- return 0 if match.empty?
62
+ return 0 if match.empty?
62
63
 
63
- location = match.first[1]
64
- location.to_i
65
- end
64
+ location = match.first[1]
65
+ location.to_i
66
+ end
66
67
 
67
- def extract_page(metadata)
68
- match = metadata.scan(/Page (\d+)/)
68
+ def extract_page(metadata)
69
+ match = metadata.scan(/Page (\d+)/)
69
70
 
70
- return nil if match.empty?
71
+ return nil if match.empty?
71
72
 
72
- location = match.first.first
73
- location.to_i
74
- end
73
+ location = match.first.first
74
+ location.to_i
75
+ end
75
76
 
76
- def extract_content(text_lines)
77
- text_lines.join('').rstrip
78
- end
77
+ def extract_content(text_lines)
78
+ text_lines.join('').rstrip
79
+ end
79
80
 
80
- def extract_added_date(metadata)
81
- DateTime.parse(metadata.scan(/Added on (.+)$/i).first.first)
81
+ def extract_added_date(metadata)
82
+ DateTime.parse(metadata.scan(/Added on (.+)$/i).first.first)
83
+ end
82
84
  end
83
85
  end
84
86
  end
@@ -1,54 +1,56 @@
1
- module Klipbook::Sources
2
- module KindleDevice
3
- class File
4
- def initialize(infile, max_books, file_parser=FileParser.new)
5
- @file_text = infile.read.strip
6
- @file_parser = file_parser
7
- @max_books = max_books
8
- end
1
+ module Klipbook
2
+ module Sources
3
+ module KindleDevice
4
+ class File
5
+ def initialize(infile, max_books, file_parser=FileParser.new)
6
+ @file_text = infile.strip
7
+ @file_parser = file_parser
8
+ @max_books = max_books
9
+ end
9
10
 
10
- # TODO Shift max books here
11
- def books
12
- @books ||= build_books.take(@max_books)
13
- end
11
+ # TODO Shift max books here
12
+ def books
13
+ @books ||= build_books.take(@max_books)
14
+ end
14
15
 
15
- private
16
+ private
16
17
 
17
- def build_books
18
- sorted_entries = extract_sorted_entries_from_file_text
19
- build_sorted_book_list(sorted_entries)
20
- end
18
+ def build_books
19
+ sorted_entries = extract_sorted_entries_from_file_text
20
+ build_sorted_book_list(sorted_entries)
21
+ end
21
22
 
22
- def extract_sorted_entries_from_file_text
23
- entries = @file_parser.extract_entries(@file_text)
24
- entries.sort { |entry_a, entry_b| entry_a.title <=> entry_b.title }
25
- end
23
+ def extract_sorted_entries_from_file_text
24
+ entries = @file_parser.extract_entries(@file_text)
25
+ entries.sort { |entry_a, entry_b| entry_a.title <=> entry_b.title }
26
+ end
26
27
 
27
- def build_sorted_book_list(sorted_entries)
28
- books_from_entries(sorted_entries).sort do |book_a, book_b|
29
- book_b.last_update <=> book_a.last_update
28
+ def build_sorted_book_list(sorted_entries)
29
+ books_from_entries(sorted_entries).sort do |book_a, book_b|
30
+ book_b.last_update <=> book_a.last_update
31
+ end
30
32
  end
31
- end
32
33
 
33
- def books_from_entries(entries)
34
- entries.select { |entry| entry.type != :bookmark }
35
- .group_by(&:title)
36
- .map { |title, book_entries| book_from_entries(book_entries) }
37
- end
34
+ def books_from_entries(entries)
35
+ entries.select { |entry| entry.type != :bookmark }
36
+ .group_by(&:title)
37
+ .map { |title, book_entries| book_from_entries(book_entries) }
38
+ end
38
39
 
39
- def book_from_entries(entries)
40
- entries.sort! { |ea, eb| ea.location <=> eb.location }
41
-
42
- Klipbook::Book.new.tap do |b|
43
- b.title = entries.first.title
44
- b.author = entries.first.author
45
- b.last_update = entries.map(&:added_on).max
46
- b.clippings = entries.map do |e|
47
- Klipbook::Clipping.new.tap do |c|
48
- c.location = e.location
49
- c.page = e.page
50
- c.text = e.text
51
- c.type = e.type
40
+ def book_from_entries(entries)
41
+ entries.sort! { |ea, eb| ea.location <=> eb.location }
42
+
43
+ Klipbook::Book.new.tap do |b|
44
+ b.title = entries.first.title
45
+ b.author = entries.first.author
46
+ b.last_update = entries.map(&:added_on).max
47
+ b.clippings = entries.map do |e|
48
+ Klipbook::Clipping.new.tap do |c|
49
+ c.location = e.location
50
+ c.page = e.page
51
+ c.text = e.text
52
+ c.type = e.type
53
+ end
52
54
  end
53
55
  end
54
56
  end
@@ -1,32 +1,34 @@
1
1
  # encoding: UTF-8
2
2
 
3
- module Klipbook::Sources
4
- module KindleDevice
5
- class FileParser
6
- def initialize(entry_parser=EntryParser.new)
7
- @entry_parser = entry_parser
8
- end
3
+ module Klipbook
4
+ module Sources
5
+ module KindleDevice
6
+ class FileParser
7
+ def initialize(entry_parser=EntryParser.new)
8
+ @entry_parser = entry_parser
9
+ end
9
10
 
10
- def extract_entries(file_text)
11
- entries_text = split_into_raw_entries_text(file_text)
11
+ def extract_entries(file_text)
12
+ entries_text = split_into_raw_entries_text(file_text)
12
13
 
13
- build_entries(entries_text)
14
- end
14
+ build_entries(entries_text)
15
+ end
15
16
 
16
- private
17
+ private
17
18
 
18
- def build_entries(entries_text)
19
- entries_text.map do |entry_text|
20
- @entry_parser.build_entry(entry_text)
21
- end.compact
22
- end
19
+ def build_entries(entries_text)
20
+ entries_text.map do |entry_text|
21
+ @entry_parser.build_entry(entry_text)
22
+ end.compact
23
+ end
23
24
 
24
- def strip_control_characters(file_text)
25
- file_text.gsub("\r", '').gsub("\xef\xbb\xbf", '')
26
- end
25
+ def strip_control_characters(file_text)
26
+ file_text.gsub("\r", '').gsub("\xef\xbb\xbf", '')
27
+ end
27
28
 
28
- def split_into_raw_entries_text(file_text)
29
- strip_control_characters(file_text).split('==========')
29
+ def split_into_raw_entries_text(file_text)
30
+ strip_control_characters(file_text).split('==========')
31
+ end
30
32
  end
31
33
  end
32
34
  end
@@ -0,0 +1,30 @@
1
+ module Klipbook
2
+ module Sources
3
+ class Source
4
+ def self.build(options)
5
+ if options.from_file
6
+ file_source(options.from_file, options.count)
7
+ elsif options.from_site
8
+ site_source(options.from_site, options.count)
9
+ else
10
+ raise "Unknown source type"
11
+ end
12
+ end
13
+
14
+ def self.site_source(credentials, max_books)
15
+ unless credentials =~ /(.+):(.+)/
16
+ logger.error "Error: your credentials need to be in username:password format."
17
+ exit 127
18
+ end
19
+
20
+ username = $1
21
+ password = $2
22
+ Sources::AmazonSite::SiteScraper.new(username, password, max_books)
23
+ end
24
+
25
+ def self.file_source(file, max_books)
26
+ Sources::KindleDevice::File.new(File.read(file), max_books)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -1,3 +1,3 @@
1
1
  module Klipbook
2
- VERSION = '2.1.3'
2
+ VERSION = '3.0.0'
3
3
  end
data/lib/klipbook.rb CHANGED
@@ -1,16 +1,21 @@
1
- # encoding: utf-8
1
+ require 'klipbook/cli'
2
2
  require 'klipbook/util/blank'
3
3
  require 'klipbook/util/struct_to_json'
4
4
  require 'klipbook/version'
5
- require 'klipbook/colours'
5
+ require 'klipbook/config'
6
+ require 'klipbook/logger'
6
7
 
7
- require 'klipbook/commands/tojson'
8
- require 'klipbook/commands/list_books'
9
- require 'klipbook/commands/tohtml'
8
+ require 'klipbook/commands/command'
9
+ require 'klipbook/commands/list'
10
+ require 'klipbook/commands/export'
11
+ require 'klipbook/commands/exporters/exporter'
12
+ require 'klipbook/commands/exporters/json_exporter'
13
+ require 'klipbook/commands/exporters/html_exporter'
14
+ require 'klipbook/commands/exporters/markdown_exporter'
10
15
 
16
+ require 'klipbook/sources/source'
11
17
  require 'klipbook/sources/book'
12
18
  require 'klipbook/sources/clipping'
13
- require 'klipbook/sources/invalid_source_error'
14
19
 
15
20
  require 'klipbook/sources/kindle_device/file_parser'
16
21
  require 'klipbook/sources/kindle_device/entry_parser'
@@ -19,8 +24,3 @@ require 'klipbook/sources/kindle_device/file'
19
24
 
20
25
  require 'klipbook/sources/amazon_site/site_scraper'
21
26
  require 'klipbook/sources/amazon_site/book_scraper'
22
-
23
- require 'klipbook/config'
24
-
25
- require 'klipbook/tohtml/html_printer'
26
- require 'klipbook/tojson/book_file'