dayone-kindle 0.1.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 +7 -0
- data/.travis.yml +6 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +23 -0
- data/README.md +5 -0
- data/Rakefile +10 -0
- data/bin/dayone-kindle +22 -0
- data/lib/dayone-kindle.rb +48 -0
- data/lib/dayone-kindle/cli.rb +81 -0
- data/lib/dayone-kindle/clipping_parser.rb +81 -0
- data/lib/dayone-kindle/clippings_parser.rb +15 -0
- data/lib/dayone-kindle/data_store.rb +24 -0
- data/lib/dayone-kindle/day_one.rb +67 -0
- data/lib/dayone-kindle/device.rb +52 -0
- data/lib/dayone-kindle/highlight.rb +51 -0
- data/lib/dayone-kindle/version.rb +3 -0
- data/test/clipping_parser_test.rb +19 -0
- data/test/highlight_test.rb +27 -0
- data/test/test_helper.rb +8 -0
- metadata +107 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: e91e2417dc8198ecae3088a1fe6b8730aa93fc5e
|
|
4
|
+
data.tar.gz: f00a1bc2e8ca3f915239c29264ff94df2313269a
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 5a0387c105b5d61d2bbf118fbb14ad5f1a5af970abe09c87e9d3c1cf07c6662b6d199625003f7bef4543964fc3b7822d79ed9c43a61d9808bedd3688e3aa05e1
|
|
7
|
+
data.tar.gz: aa0e67f393e5e31a32d7111dad575ff3ee4e577c7ba5a9c399842c8c9d4f4c89c1219e11a980db436f8f8a489aca01a5a12369f0d8998305b3b2659e8f470fd3
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
GEM
|
|
2
|
+
remote: https://rubygems.org/
|
|
3
|
+
specs:
|
|
4
|
+
ansi (1.5.0)
|
|
5
|
+
builder (3.2.2)
|
|
6
|
+
minitest (5.6.1)
|
|
7
|
+
minitest-reporters (1.0.16)
|
|
8
|
+
ansi
|
|
9
|
+
builder
|
|
10
|
+
minitest (>= 5.0)
|
|
11
|
+
ruby-progressbar
|
|
12
|
+
rake (10.4.2)
|
|
13
|
+
ruby-progressbar (1.7.5)
|
|
14
|
+
|
|
15
|
+
PLATFORMS
|
|
16
|
+
ruby
|
|
17
|
+
|
|
18
|
+
DEPENDENCIES
|
|
19
|
+
minitest-reporters
|
|
20
|
+
rake
|
|
21
|
+
|
|
22
|
+
BUNDLED WITH
|
|
23
|
+
1.10.4
|
data/README.md
ADDED
data/Rakefile
ADDED
data/bin/dayone-kindle
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
if RUBY_VERSION.to_f < 2.0
|
|
4
|
+
$stderr.puts 'dayone-kindle require Ruby 2.0+.'
|
|
5
|
+
exit 1
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
Encoding.default_external = Encoding::UTF_8
|
|
9
|
+
Encoding.default_internal = Encoding::UTF_8
|
|
10
|
+
|
|
11
|
+
require 'optparse'
|
|
12
|
+
|
|
13
|
+
# resolve bin path, ignoring symlinks
|
|
14
|
+
require 'pathname'
|
|
15
|
+
bin_file = Pathname.new(__FILE__).realpath
|
|
16
|
+
|
|
17
|
+
# add self to libpath
|
|
18
|
+
$:.unshift File.expand_path('../../lib', bin_file)
|
|
19
|
+
|
|
20
|
+
require 'dayone-kindle'
|
|
21
|
+
|
|
22
|
+
DayOneKindle::CLI.run
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
require 'time'
|
|
2
|
+
require 'fileutils'
|
|
3
|
+
|
|
4
|
+
# https://github.com/rails/rails/blob/master/activesupport/lib/active_support/core_ext/object/try.rb
|
|
5
|
+
class Object
|
|
6
|
+
def try(*a, &b)
|
|
7
|
+
try!(*a, &b) if a.empty? || respond_to?(a.first)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def try!(*a, &b)
|
|
11
|
+
if a.empty? && block_given?
|
|
12
|
+
if b.arity == 0
|
|
13
|
+
instance_eval(&b)
|
|
14
|
+
else
|
|
15
|
+
yield self
|
|
16
|
+
end
|
|
17
|
+
else
|
|
18
|
+
public_send(*a, &b)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# https://github.com/rails/rails/blob/master/activesupport/lib/active_support/core_ext/array/conversions.rb
|
|
24
|
+
class Array
|
|
25
|
+
def to_sentence(words_connector: ', ', two_words_connector: ' and ', last_word_connector: ', and ')
|
|
26
|
+
case length
|
|
27
|
+
when 0
|
|
28
|
+
''
|
|
29
|
+
when 1
|
|
30
|
+
"#{self[0]}"
|
|
31
|
+
when 2
|
|
32
|
+
"#{self[0]}#{two_words_connector}#{self[1]}"
|
|
33
|
+
else
|
|
34
|
+
"#{self[0...-1].join(words_connector)}#{last_word_connector}#{self[-1]}"
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
LIB = File.dirname(File.expand_path(__FILE__))
|
|
40
|
+
|
|
41
|
+
require LIB + '/dayone-kindle/day_one'
|
|
42
|
+
require LIB + '/dayone-kindle/highlight'
|
|
43
|
+
require LIB + '/dayone-kindle/data_store'
|
|
44
|
+
require LIB + '/dayone-kindle/clipping_parser'
|
|
45
|
+
require LIB + '/dayone-kindle/clippings_parser'
|
|
46
|
+
require LIB + '/dayone-kindle/device'
|
|
47
|
+
require LIB + '/dayone-kindle/cli'
|
|
48
|
+
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
module DayOneKindle
|
|
2
|
+
module CLI
|
|
3
|
+
def self.dialog(value)
|
|
4
|
+
script = <<-END
|
|
5
|
+
tell app "System Events"
|
|
6
|
+
display dialog "#{value}"
|
|
7
|
+
end tell
|
|
8
|
+
END
|
|
9
|
+
|
|
10
|
+
system('osascript ' + script.split(/\n/).map { |line| "-e '#{line}'" }.join(' ') + '> /dev/null 2>&1')
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.options
|
|
14
|
+
return @options if @options
|
|
15
|
+
|
|
16
|
+
options = {
|
|
17
|
+
ask_confirmation: true,
|
|
18
|
+
tags: [],
|
|
19
|
+
dry_run: false,
|
|
20
|
+
archive: true
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
optparse = OptionParser.new do |opts|
|
|
24
|
+
opts.banner = 'Usage: dayone-kindle [options]'
|
|
25
|
+
|
|
26
|
+
opts.on '--dry', 'Outputs highlights instead of importing them (use for testing)' do
|
|
27
|
+
options[:dry_run] = true
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
opts.on '-t', '--tags reading,quote', Array, 'Tags to be saved with highlights' do |t|
|
|
31
|
+
options[:tags] = t
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
opts.on '--auto-confirm', 'Do not ask for confirmation before import' do
|
|
35
|
+
options[:ask_confirmation] = false
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
opts.on '--no-archive', 'Do not archive imported highlights on device' do
|
|
39
|
+
options[:archive] = false
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
optparse.parse!
|
|
44
|
+
|
|
45
|
+
@options = options
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def self.run
|
|
49
|
+
tags = options[:tags]
|
|
50
|
+
|
|
51
|
+
DayOneKindle::Device.find_at('/Volumes').each do |kindle|
|
|
52
|
+
next if kindle.highlights.empty?
|
|
53
|
+
|
|
54
|
+
if options[:ask_confirmation]
|
|
55
|
+
label = "#{kindle.name} detected. Highlights will be imported to Day One."
|
|
56
|
+
next unless dialog(label)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
store = DayOneKindle::DataStore.new(kindle.highlights, tags)
|
|
60
|
+
puts "#{store.entries.count} highlights to import"
|
|
61
|
+
|
|
62
|
+
puts "Tags: #{tags.empty? ? 'no tags' : tags.join(', ')}"
|
|
63
|
+
|
|
64
|
+
if options[:dry_run]
|
|
65
|
+
puts 'Dry run, no highlight imported'
|
|
66
|
+
else
|
|
67
|
+
entries = store.save!
|
|
68
|
+
puts "#{entries.count} highlights imported with tags"
|
|
69
|
+
|
|
70
|
+
if options[:archive]
|
|
71
|
+
path = kindle.archive_highlights!
|
|
72
|
+
puts "Highlights archived at #{path}"
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
kindle.clear_highlights!
|
|
76
|
+
puts 'Highlights cleared from device'
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
module DayOneKindle
|
|
2
|
+
class ClippingParser
|
|
3
|
+
attr_reader :raw
|
|
4
|
+
|
|
5
|
+
REGEX = %r{^
|
|
6
|
+
(?<book>.+)\s\((?<authors>.+)\)\n
|
|
7
|
+
\-\s(?<metadata>.+)\n\n
|
|
8
|
+
(?<quote>.+)?
|
|
9
|
+
$}xi
|
|
10
|
+
|
|
11
|
+
def initialize(raw)
|
|
12
|
+
@raw = raw
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def highlight
|
|
16
|
+
return unless metadata.match(/Highlight/i)
|
|
17
|
+
return unless quote
|
|
18
|
+
|
|
19
|
+
Highlight.new(
|
|
20
|
+
book: book,
|
|
21
|
+
highlight: quote,
|
|
22
|
+
time: time,
|
|
23
|
+
page: page,
|
|
24
|
+
location: location,
|
|
25
|
+
authors: authors
|
|
26
|
+
)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def book
|
|
32
|
+
@book ||= begin
|
|
33
|
+
b = matches.try(:[], :book)
|
|
34
|
+
b && !b.empty? && b.strip
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def quote
|
|
39
|
+
@quote ||= begin
|
|
40
|
+
q = matches.try(:[], :quote)
|
|
41
|
+
q && !q.empty? && q && q.gsub(/ \u00a0/, "\n\n").strip
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def time
|
|
46
|
+
@time ||= begin
|
|
47
|
+
string = metadata.match(/added on (?<time>.+)/i).try(:[], :time)
|
|
48
|
+
string && !string.empty? && Time.parse(string.strip)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def page
|
|
53
|
+
@page ||= begin
|
|
54
|
+
page = metadata.match(/page (?<page>\d+)/i).try(:[], :page)
|
|
55
|
+
page && !page.empty? && page.to_i
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def location
|
|
60
|
+
@location ||= begin
|
|
61
|
+
location = metadata.match(/location (?<location>[\d-]+)/i).try(:[], :location)
|
|
62
|
+
location && !location.empty? && location
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def authors
|
|
67
|
+
@authors ||= begin
|
|
68
|
+
string = matches.try(:[], :authors)
|
|
69
|
+
string && !string.empty? && string.split(';')
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def metadata
|
|
74
|
+
@metadata ||= matches[:metadata]
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def matches
|
|
78
|
+
@matches ||= raw.match(REGEX)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module DayOneKindle
|
|
2
|
+
class DataStore
|
|
3
|
+
attr_reader :entries, :tags
|
|
4
|
+
|
|
5
|
+
def initialize(entries, tags = [])
|
|
6
|
+
@entries = entries
|
|
7
|
+
@tags = tags
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def save!
|
|
11
|
+
entries.map { |entry| save_entry!(entry) }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def save_entry!(entry)
|
|
17
|
+
DayOne::Entry.new(
|
|
18
|
+
entry.to_markdown,
|
|
19
|
+
created_at: entry.time,
|
|
20
|
+
tags: tags
|
|
21
|
+
).save!
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
module DayOneKindle
|
|
2
|
+
module DayOne
|
|
3
|
+
def self.journal_location
|
|
4
|
+
@journal_location ||= begin
|
|
5
|
+
prefs_file = File.join(ENV['HOME'], 'Library/Group Containers/5U8NS4GX82.dayoneapp/Data/Preferences/dayone.plist')
|
|
6
|
+
raise 'DayOne preference file not found' unless File.exists?(prefs_file)
|
|
7
|
+
|
|
8
|
+
prefs = File.read(prefs_file)
|
|
9
|
+
match = prefs.match(/<key>JournalPackageURL<\/key>\n\t<string>([^<>]+)<\/string>/)
|
|
10
|
+
raise 'DayOne journal not found' unless match && match[1]
|
|
11
|
+
match[1]
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
class Entry
|
|
16
|
+
attr_accessor :content, :created_at, :tags
|
|
17
|
+
|
|
18
|
+
def initialize(content, options = {})
|
|
19
|
+
@content = content
|
|
20
|
+
@tags = options[:tags] || []
|
|
21
|
+
@created_at = options[:created_at] || Time.now
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def uuid
|
|
25
|
+
@uuid ||= `uuidgen`.gsub('-', '').strip
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def file
|
|
29
|
+
@file ||= File.join(DayOne::journal_location, 'entries', "#{uuid}.doentry")
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def to_xml
|
|
33
|
+
tags_xml = ''
|
|
34
|
+
|
|
35
|
+
unless tags.empty?
|
|
36
|
+
tags_xml = <<XML
|
|
37
|
+
<key>Tags</key>
|
|
38
|
+
<array>
|
|
39
|
+
#{tags.map { |tag| " <string>#{tag}</string>" }.join("\n ") }
|
|
40
|
+
</array>
|
|
41
|
+
XML
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
<<-XML
|
|
45
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
46
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
47
|
+
<plist version="1.0">
|
|
48
|
+
<dict>
|
|
49
|
+
<key>Creation Date</key>
|
|
50
|
+
<date>#{created_at.utc.iso8601}</date>
|
|
51
|
+
<key>Entry Text</key>
|
|
52
|
+
<string>#{content}</string>
|
|
53
|
+
#{tags_xml.strip}
|
|
54
|
+
<key>UUID</key>
|
|
55
|
+
<string>#{uuid}</string>
|
|
56
|
+
</dict>
|
|
57
|
+
</plist>
|
|
58
|
+
XML
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def save!
|
|
62
|
+
File.open(file, 'w') { |f| f.write(to_xml) }
|
|
63
|
+
uuid
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
module DayOneKindle
|
|
2
|
+
class Device
|
|
3
|
+
CLIPPINGS_PATH = '/documents/My Clippings.txt'.freeze
|
|
4
|
+
VERSION_PATH = '/system/version.txt'.freeze
|
|
5
|
+
BACKUP_PATH = '/clippings-backups'.freeze
|
|
6
|
+
BACKUP_NAME = '%Y%m%d-%H%M%S.txt'.freeze
|
|
7
|
+
|
|
8
|
+
attr_reader :path, :name, :clippings_path, :clippings_backups_path
|
|
9
|
+
|
|
10
|
+
def initialize(path, name)
|
|
11
|
+
@path = path
|
|
12
|
+
@name = name
|
|
13
|
+
@clippings_path = File.join(path, CLIPPINGS_PATH)
|
|
14
|
+
@clippings_backups_path = File.join(path, BACKUP_PATH)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def highlights
|
|
18
|
+
ClippingsParser.new(raw_clippings).highlights
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def archive_highlights!
|
|
22
|
+
Dir.mkdir(clippings_backups_path) unless Dir.exist?(clippings_backups_path)
|
|
23
|
+
backup_name = Time.now.strftime(BACKUP_NAME)
|
|
24
|
+
backup_file_path = File.join(clippings_backups_path, backup_name)
|
|
25
|
+
FileUtils.cp(clippings_path, backup_file_path)
|
|
26
|
+
backup_file_path
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def clear_highlights!
|
|
30
|
+
File.truncate(clippings_path, 0)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.find_at(mount_path)
|
|
34
|
+
volumes = Dir.entries(mount_path) - ['.', '..']
|
|
35
|
+
|
|
36
|
+
volumes.map do |name|
|
|
37
|
+
path = File.join(mount_path, name)
|
|
38
|
+
version_path = File.join(path, VERSION_PATH)
|
|
39
|
+
|
|
40
|
+
if File.exist?(version_path) && IO.read(version_path) =~ /^Kindle/
|
|
41
|
+
new(path, name)
|
|
42
|
+
end
|
|
43
|
+
end.compact
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def raw_clippings
|
|
49
|
+
File.read(clippings_path).gsub("\r", '')
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
module DayOneKindle
|
|
2
|
+
class Highlight
|
|
3
|
+
attr_reader :book, :highlight, :time, :page, :location, :authors
|
|
4
|
+
|
|
5
|
+
def initialize(options)
|
|
6
|
+
# Faking required named arguments for ruby 2.0 compatibility
|
|
7
|
+
%i(book highlight time).each do |param|
|
|
8
|
+
raise ArgumentError, "missing keyword :#{param}" unless options[param]
|
|
9
|
+
instance_variable_set(:"@#{param}", options[param])
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
@page = options[:page]
|
|
13
|
+
@location = options[:location]
|
|
14
|
+
@authors = options[:authors] || []
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def to_markdown
|
|
18
|
+
[
|
|
19
|
+
markdown_header,
|
|
20
|
+
'',
|
|
21
|
+
markdown_authors,
|
|
22
|
+
'',
|
|
23
|
+
markdown_highlight,
|
|
24
|
+
'',
|
|
25
|
+
markdown_meta
|
|
26
|
+
].compact.join("\n")
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def markdown_header
|
|
32
|
+
"# Quote from #{book || 'a book'}"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def markdown_authors
|
|
36
|
+
return if authors.empty?
|
|
37
|
+
"By #{authors.map { |a| "*#{a}*" }.to_sentence}."
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def markdown_meta
|
|
41
|
+
meta = []
|
|
42
|
+
meta << "Page #{page}" if page
|
|
43
|
+
meta << "Location #{location}" if location
|
|
44
|
+
meta.join(' - ') unless meta.empty?
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def markdown_highlight
|
|
48
|
+
'> ' + highlight.gsub(/\n/, "\n>")
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
class ClippingParserTest < Minitest::Test
|
|
4
|
+
def test_parsing
|
|
5
|
+
clipping = <<EOF
|
|
6
|
+
Shibumi (Trevanian)
|
|
7
|
+
- Your Highlight on Page 121 | Location 1851-1853 | Added on Friday, November 28, 2014 3:31:42 PM
|
|
8
|
+
|
|
9
|
+
Do not fall into the error of the artisan who boasts of twenty years experience in his craft while in fact he has had only one year of experience—twenty times.
|
|
10
|
+
EOF
|
|
11
|
+
highlight = DayOneKindle::ClippingParser.new(clipping).highlight
|
|
12
|
+
|
|
13
|
+
assert_equal 'Shibumi', highlight.book
|
|
14
|
+
assert_equal ['Trevanian'], highlight.authors
|
|
15
|
+
assert_equal 121, highlight.page
|
|
16
|
+
assert_equal '1851-1853', highlight.location
|
|
17
|
+
assert_equal Time.new(2014, 11, 28, 15, 31, 42), highlight.time
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
class HighlightTest < Minitest::Test
|
|
4
|
+
def test_to_markdown
|
|
5
|
+
highlight = DayOneKindle::Highlight.new(
|
|
6
|
+
book: 'The Pragmatic Programmer: From Journeyman to Master',
|
|
7
|
+
authors: ['Andrew Hunt', 'David Thomas'],
|
|
8
|
+
highlight: 'Embrace the fact that debugging is just problem solving, and attack it as such.',
|
|
9
|
+
time: Time.new(2014, 3, 5, 15, 4, 22),
|
|
10
|
+
page: 121,
|
|
11
|
+
location: '1850-1850'
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
expected_markdown = <<EOF
|
|
15
|
+
# Quote from The Pragmatic Programmer: From Journeyman to Master
|
|
16
|
+
|
|
17
|
+
By *Andrew Hunt* and *David Thomas*.
|
|
18
|
+
|
|
19
|
+
> Embrace the fact that debugging is just problem solving, and attack it as such.
|
|
20
|
+
|
|
21
|
+
Page 121 - Location 1850-1850
|
|
22
|
+
EOF
|
|
23
|
+
expected_markdown.strip!
|
|
24
|
+
|
|
25
|
+
assert_equal expected_markdown, highlight.to_markdown
|
|
26
|
+
end
|
|
27
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: dayone-kindle
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Tim Petricola
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2015-10-25 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: rspec
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: 3.3.0
|
|
20
|
+
type: :development
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: 3.3.0
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: rake
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: 10.4.2
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: 10.4.2
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: thor
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: 0.19.1
|
|
48
|
+
type: :runtime
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: 0.19.1
|
|
55
|
+
description:
|
|
56
|
+
email: tim.petricola@gmail.com
|
|
57
|
+
executables:
|
|
58
|
+
- dayone-kindle
|
|
59
|
+
extensions: []
|
|
60
|
+
extra_rdoc_files: []
|
|
61
|
+
files:
|
|
62
|
+
- ".travis.yml"
|
|
63
|
+
- Gemfile
|
|
64
|
+
- Gemfile.lock
|
|
65
|
+
- README.md
|
|
66
|
+
- Rakefile
|
|
67
|
+
- bin/dayone-kindle
|
|
68
|
+
- lib/dayone-kindle.rb
|
|
69
|
+
- lib/dayone-kindle/cli.rb
|
|
70
|
+
- lib/dayone-kindle/clipping_parser.rb
|
|
71
|
+
- lib/dayone-kindle/clippings_parser.rb
|
|
72
|
+
- lib/dayone-kindle/data_store.rb
|
|
73
|
+
- lib/dayone-kindle/day_one.rb
|
|
74
|
+
- lib/dayone-kindle/device.rb
|
|
75
|
+
- lib/dayone-kindle/highlight.rb
|
|
76
|
+
- lib/dayone-kindle/version.rb
|
|
77
|
+
- test/clipping_parser_test.rb
|
|
78
|
+
- test/highlight_test.rb
|
|
79
|
+
- test/test_helper.rb
|
|
80
|
+
homepage: https://github.com/TimPetricola/dayone-kindle
|
|
81
|
+
licenses:
|
|
82
|
+
- MIT
|
|
83
|
+
metadata: {}
|
|
84
|
+
post_install_message:
|
|
85
|
+
rdoc_options: []
|
|
86
|
+
require_paths:
|
|
87
|
+
- lib
|
|
88
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
89
|
+
requirements:
|
|
90
|
+
- - ">="
|
|
91
|
+
- !ruby/object:Gem::Version
|
|
92
|
+
version: '0'
|
|
93
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
94
|
+
requirements:
|
|
95
|
+
- - ">="
|
|
96
|
+
- !ruby/object:Gem::Version
|
|
97
|
+
version: '0'
|
|
98
|
+
requirements: []
|
|
99
|
+
rubyforge_project:
|
|
100
|
+
rubygems_version: 2.4.5
|
|
101
|
+
signing_key:
|
|
102
|
+
specification_version: 4
|
|
103
|
+
summary: Import highlights from Kindle to Day One
|
|
104
|
+
test_files:
|
|
105
|
+
- test/clipping_parser_test.rb
|
|
106
|
+
- test/highlight_test.rb
|
|
107
|
+
- test/test_helper.rb
|