klipbook 0.3.0 → 1.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.
- data/Gemfile +6 -5
- data/Gemfile.lock +62 -25
- data/Guardfile +1 -2
- data/README.md +66 -18
- data/Rakefile +5 -1
- data/bin/klipbook +85 -1
- data/example.png +0 -0
- data/features/collate.feature +51 -0
- data/features/fixtures/clippings-for-three-books.txt +105 -0
- data/features/list.feature +31 -0
- data/features/step_definitions/collate_steps.rb +61 -0
- data/features/step_definitions/list_steps.rb +15 -0
- data/features/support/env.rb +5 -1
- data/klipbook.gemspec +49 -32
- data/lib/klipbook/book.rb +18 -0
- data/lib/klipbook/clipping.rb +4 -10
- data/lib/klipbook/collator.rb +17 -0
- data/lib/klipbook/config.rb +22 -0
- data/lib/klipbook/fetcher.rb +29 -0
- data/lib/klipbook/invalid_source_error.rb +12 -0
- data/lib/klipbook/output/book_helpers.rb +12 -0
- data/lib/klipbook/{book_summary.erb → output/html_book_summary.erb} +65 -11
- data/lib/klipbook/output/html_summary_writer.rb +42 -0
- data/lib/klipbook/printer.rb +18 -0
- data/lib/klipbook/sources/amazon_site/book_scraper.rb +67 -0
- data/lib/klipbook/sources/amazon_site/scraper.rb +78 -0
- data/lib/klipbook/sources/kindle_device/entry.rb +11 -0
- data/lib/klipbook/sources/kindle_device/entry_parser.rb +85 -0
- data/lib/klipbook/sources/kindle_device/file.rb +57 -0
- data/lib/klipbook/sources/kindle_device/file_parser.rb +33 -0
- data/lib/klipbook/version.rb +1 -1
- data/lib/klipbook.rb +18 -5
- data/spec/lib/klipbook/book_spec.rb +33 -0
- data/spec/lib/klipbook/collator_spec.rb +40 -0
- data/spec/lib/klipbook/fetcher_spec.rb +81 -0
- data/spec/lib/klipbook/output/html_summary_writer_spec.rb +90 -0
- data/spec/lib/klipbook/printer_spec.rb +45 -0
- data/spec/lib/klipbook/sources/kindle_device/entry_parser_spec.rb +275 -0
- data/spec/lib/klipbook/sources/kindle_device/file_parser_spec.rb +68 -0
- data/spec/lib/klipbook/sources/kindle_device/file_spec.rb +163 -0
- metadata +158 -58
- data/features/list_books.feature +0 -23
- data/features/print_book_summary.feature +0 -10
- data/features/step_definitions/klipbook_steps.rb +0 -87
- data/lib/klipbook/book_summary.rb +0 -35
- data/lib/klipbook/cli.rb +0 -49
- data/lib/klipbook/clippings_file.rb +0 -50
- data/lib/klipbook/clippings_parser.rb +0 -98
- data/lib/klipbook/runner.rb +0 -29
- data/spec/lib/klipbook/book_summary_spec.rb +0 -30
- data/spec/lib/klipbook/clipping_spec.rb +0 -17
- data/spec/lib/klipbook/clippings_file_spec.rb +0 -60
- data/spec/lib/klipbook/clippings_parser_spec.rb +0 -367
- data/spec/lib/klipbook/runner_spec.rb +0 -87
@@ -1,98 +0,0 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
|
3
|
-
module Klipbook
|
4
|
-
class ClippingsParser
|
5
|
-
def extract_clippings_from(file_text)
|
6
|
-
clippings_text_from(file_text).map { |clipping_text| build_clipping_from(clipping_text) }.compact
|
7
|
-
end
|
8
|
-
|
9
|
-
def build_clipping_from(clipping_text)
|
10
|
-
return nil if clipping_text.blank?
|
11
|
-
|
12
|
-
attributes = extract_attributes(clipping_text)
|
13
|
-
|
14
|
-
Clipping.new(attributes)
|
15
|
-
end
|
16
|
-
|
17
|
-
def extract_attributes(clipping_text)
|
18
|
-
|
19
|
-
lines = clipping_text.lstrip.lines.to_a
|
20
|
-
|
21
|
-
return nil if lines.length < 2
|
22
|
-
|
23
|
-
title_line = lines[0].strip
|
24
|
-
metadata = lines[1].strip
|
25
|
-
text_lines = lines[3..-1]
|
26
|
-
|
27
|
-
return nil unless valid_metadata?(metadata)
|
28
|
-
|
29
|
-
{
|
30
|
-
title: extract_title(title_line),
|
31
|
-
author: extract_author(title_line),
|
32
|
-
type: extract_type(metadata),
|
33
|
-
location: extract_location(metadata),
|
34
|
-
page: extract_page(metadata),
|
35
|
-
added_on: extract_added_date(metadata),
|
36
|
-
text: extract_text(text_lines)
|
37
|
-
}
|
38
|
-
end
|
39
|
-
|
40
|
-
def strip_control_characters(file_text)
|
41
|
-
file_text.gsub("\r", '').gsub("\xef\xbb\xbf", '')
|
42
|
-
end
|
43
|
-
|
44
|
-
private
|
45
|
-
|
46
|
-
def clippings_text_from(file_text)
|
47
|
-
strip_control_characters(file_text).split('==========')
|
48
|
-
end
|
49
|
-
|
50
|
-
def valid_metadata?(metadata)
|
51
|
-
metadata.match(/^-.*Added on/)
|
52
|
-
end
|
53
|
-
|
54
|
-
def extract_title(title_line)
|
55
|
-
if title_line =~ /\(.+\)\Z/
|
56
|
-
title_line.scan(/(.*)\s+\(.+\)\Z/).first.first
|
57
|
-
else
|
58
|
-
title_line
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
def extract_author(title_line)
|
63
|
-
match = title_line.scan /\(([^\(]+)\)\Z/
|
64
|
-
match.empty? ? nil : match.first.first
|
65
|
-
end
|
66
|
-
|
67
|
-
def extract_type(metadata)
|
68
|
-
type = metadata.scan(/^-( Your)? (\w+)/).first[1]
|
69
|
-
type.downcase.to_sym
|
70
|
-
end
|
71
|
-
|
72
|
-
def extract_added_date(metadata)
|
73
|
-
metadata.scan(/Added on (.+)$/i).first.first
|
74
|
-
end
|
75
|
-
|
76
|
-
def extract_location(metadata)
|
77
|
-
match = metadata.scan(/Loc(ation|\.) ([0-9]+-?)/)
|
78
|
-
|
79
|
-
return nil if match.empty?
|
80
|
-
|
81
|
-
location = match.first[1]
|
82
|
-
location.to_i
|
83
|
-
end
|
84
|
-
|
85
|
-
def extract_page(metadata)
|
86
|
-
match = metadata.scan(/Page (\d+)/)
|
87
|
-
|
88
|
-
return nil if match.empty?
|
89
|
-
|
90
|
-
location = match.first.first
|
91
|
-
location.to_i
|
92
|
-
end
|
93
|
-
|
94
|
-
def extract_text(text_lines)
|
95
|
-
text_lines.join('').rstrip
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
data/lib/klipbook/runner.rb
DELETED
@@ -1,29 +0,0 @@
|
|
1
|
-
module Klipbook
|
2
|
-
class Runner
|
3
|
-
def initialize(input_file)
|
4
|
-
@clippings_file = Klipbook::ClippingsFile.new(input_file.read.strip)
|
5
|
-
end
|
6
|
-
|
7
|
-
def list_books(output=$stdout)
|
8
|
-
if @clippings_file.books.empty?
|
9
|
-
output.puts 'Your clippings file contains no books'
|
10
|
-
else
|
11
|
-
output.puts 'The list of books in your clippings file:'
|
12
|
-
@clippings_file.books.each_with_index do |book, index|
|
13
|
-
author = book.author ? " by #{book.author}" : ''
|
14
|
-
output.puts "[#{index + 1}] #{book.title}#{author}"
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
def print_book_summary(book_number, output, options={})
|
20
|
-
if book_number < 1 or book_number > @clippings_file.books.length
|
21
|
-
$stderr.puts "Sorry but you must specify a book index between 1 and #{@clippings_file.books.length}"
|
22
|
-
return
|
23
|
-
end
|
24
|
-
|
25
|
-
book_summary = @clippings_file.books[book_number - 1]
|
26
|
-
output.write book_summary.as_html(options)
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
@@ -1,30 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Klipbook::BookSummary do
|
4
|
-
describe '#clippings' do
|
5
|
-
subject { Klipbook::BookSummary.new('fake title', 'fake author', clippings).clippings }
|
6
|
-
|
7
|
-
let(:first_clipping) { FakeClipping.new(order: 10) }
|
8
|
-
let(:second_clipping) { FakeClipping.new(order: 22) }
|
9
|
-
let(:third_clipping) { FakeClipping.new(order: 200) }
|
10
|
-
|
11
|
-
let(:clippings) do
|
12
|
-
[
|
13
|
-
third_clipping,
|
14
|
-
second_clipping,
|
15
|
-
first_clipping
|
16
|
-
]
|
17
|
-
end
|
18
|
-
|
19
|
-
it 'provides clippings in sorted order' do
|
20
|
-
subject.should == [ first_clipping, second_clipping, third_clipping ]
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
# A fake clipping that will naturally sort even if the order attribute isn't set
|
25
|
-
class FakeClipping < OpenStruct
|
26
|
-
def <=>(other)
|
27
|
-
self.order <=> other.order
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
@@ -1,17 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Klipbook::Clipping do
|
4
|
-
it 'treats the added_on attribute as a date when created' do
|
5
|
-
clipping = Klipbook::Clipping.new(added_on: "Sunday, December 4, 2011, 07:33 AM")
|
6
|
-
clipping.added_on.should == DateTime.new(2011,12,4,7,33)
|
7
|
-
end
|
8
|
-
|
9
|
-
it 'sort based upon location, treating nils as 0' do
|
10
|
-
first_clipping = Klipbook::Clipping.new(location: nil)
|
11
|
-
second_clipping = Klipbook::Clipping.new(location: 23)
|
12
|
-
third_clipping = Klipbook::Clipping.new(location: 345)
|
13
|
-
|
14
|
-
sorted_clippings = [second_clipping, third_clipping, first_clipping].sort
|
15
|
-
sorted_clippings.should == [ first_clipping, second_clipping, third_clipping ]
|
16
|
-
end
|
17
|
-
end
|
@@ -1,60 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
require 'ostruct'
|
3
|
-
|
4
|
-
describe Klipbook::ClippingsFile do
|
5
|
-
|
6
|
-
let(:clippings_file) { Klipbook::ClippingsFile.new('', parser) }
|
7
|
-
|
8
|
-
let (:parser) do
|
9
|
-
fake_parser = Object.new
|
10
|
-
stub(fake_parser).extract_clippings_from { clippings }
|
11
|
-
fake_parser
|
12
|
-
end
|
13
|
-
|
14
|
-
describe '#books' do
|
15
|
-
subject { clippings_file.books }
|
16
|
-
|
17
|
-
context 'with an empty clippings file' do
|
18
|
-
let(:clippings) { [] }
|
19
|
-
|
20
|
-
it { should be_empty }
|
21
|
-
end
|
22
|
-
|
23
|
-
context 'with a single clipping for a single book' do
|
24
|
-
let(:clippings) do
|
25
|
-
[ OpenStruct.new(title: 'First fake book title') ]
|
26
|
-
end
|
27
|
-
|
28
|
-
it 'contains an entry for the book' do
|
29
|
-
subject.map(&:title).should == [ 'First fake book title' ]
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
context 'with clippings for two books' do
|
34
|
-
let(:clippings) do
|
35
|
-
[
|
36
|
-
OpenStruct.new(title: 'Second fake book title'),
|
37
|
-
OpenStruct.new(title: 'First fake book title')
|
38
|
-
]
|
39
|
-
end
|
40
|
-
|
41
|
-
it 'contains entries for the two books in alphabetical order' do
|
42
|
-
subject.map(&:title).should == [ 'First fake book title', 'Second fake book title' ]
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
context 'with multiple clippings for two books' do
|
47
|
-
let(:clippings) do
|
48
|
-
[
|
49
|
-
OpenStruct.new(title: 'First fake book title'),
|
50
|
-
OpenStruct.new(title: 'Second fake book title'),
|
51
|
-
OpenStruct.new(title: 'First fake book title')
|
52
|
-
]
|
53
|
-
end
|
54
|
-
|
55
|
-
it 'contains entries for the two books in alphabetical order' do
|
56
|
-
subject.map(&:title).should == [ 'First fake book title', 'Second fake book title' ]
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
@@ -1,367 +0,0 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
|
-
describe Klipbook::ClippingsParser do
|
6
|
-
|
7
|
-
describe '#extract_attributes' do
|
8
|
-
|
9
|
-
subject { Klipbook::ClippingsParser.new.extract_attributes(content) }
|
10
|
-
|
11
|
-
context 'on a clipping with the title line "Book title (Author\'s Name)"' do
|
12
|
-
let (:content) do
|
13
|
-
"Book title (Author's Name)\n" +
|
14
|
-
"- Highlight Loc. 466-69 | Added on Thursday, April 21, 2011, 07:31 AM\n" +
|
15
|
-
"\n" +
|
16
|
-
"Highlight text\n"
|
17
|
-
end
|
18
|
-
|
19
|
-
it 'extracts the title as "Book title"' do
|
20
|
-
subject[:title].should == 'Book title'
|
21
|
-
end
|
22
|
-
|
23
|
-
it 'extracts the author as "Author\'s Name"' do
|
24
|
-
subject[:author].should == "Author's Name"
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
context 'on a clipping with the title line "Book title (sub title) (Author\'s Name)"' do
|
29
|
-
let (:content) do
|
30
|
-
"Book title (sub title) (Author's Name)\n" +
|
31
|
-
"- Highlight Loc. 466-69 | Added on Thursday, April 21, 2011, 07:31 AM\n" +
|
32
|
-
"\n" +
|
33
|
-
"Highlight text\n"
|
34
|
-
end
|
35
|
-
|
36
|
-
it 'extracts the title as "Book title (sub title)"' do
|
37
|
-
subject[:title].should == 'Book title (sub title)'
|
38
|
-
end
|
39
|
-
|
40
|
-
it 'extracts the author "Author\'s Name"' do
|
41
|
-
subject[:author].should == "Author's Name"
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
context 'on a clipping with the title line "Book title"' do
|
46
|
-
let (:content) do
|
47
|
-
"Book title\n" +
|
48
|
-
"- Highlight Loc. 466-69 | Added on Thursday, April 21, 2011, 07:31 AM\n" +
|
49
|
-
"\n" +
|
50
|
-
"Highlight text\n"
|
51
|
-
end
|
52
|
-
|
53
|
-
it 'extracts the title as "Book title"' do
|
54
|
-
subject[:title].should == 'Book title'
|
55
|
-
end
|
56
|
-
|
57
|
-
it 'extracts no author' do
|
58
|
-
subject[:author].should be_nil
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
context 'on a highlight' do
|
63
|
-
let (:content) do
|
64
|
-
"Book title\n" +
|
65
|
-
"- Highlight Loc. 466-69 | Added on Thursday, April 21, 2011, 07:31 AM\n" +
|
66
|
-
"\n" +
|
67
|
-
"The first line of the highlight\n" +
|
68
|
-
"The second line of the highlight"
|
69
|
-
end
|
70
|
-
|
71
|
-
it 'marks the clipping as a highlight' do
|
72
|
-
subject[:type].should == :highlight
|
73
|
-
end
|
74
|
-
|
75
|
-
it 'extracts the highlighted text' do
|
76
|
-
subject[:text].should == "The first line of the highlight\nThe second line of the highlight"
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
context 'on a 4th-generation highlight' do
|
81
|
-
let (:content) do
|
82
|
-
"Book title\n" +
|
83
|
-
"- Your Highlight Location 466-69 | Added on Thursday, April 21, 2011, 07:31 AM\n" +
|
84
|
-
"\n" +
|
85
|
-
"The first line of the highlight\n" +
|
86
|
-
"The second line of the highlight"
|
87
|
-
end
|
88
|
-
|
89
|
-
it 'marks the clipping as a highlight' do
|
90
|
-
subject[:type].should == :highlight
|
91
|
-
end
|
92
|
-
|
93
|
-
it 'extracts the highlighted text' do
|
94
|
-
subject[:text].should == "The first line of the highlight\nThe second line of the highlight"
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
context 'on a note' do
|
99
|
-
let (:content) do
|
100
|
-
"Book title\n" +
|
101
|
-
"- Note Loc. 623 | Added on Thursday, April 21, 2011, 07:31 AM\n" +
|
102
|
-
"\n" +
|
103
|
-
"The note text"
|
104
|
-
end
|
105
|
-
|
106
|
-
it 'marks the clipping as a note' do
|
107
|
-
subject[:type].should == :note
|
108
|
-
end
|
109
|
-
|
110
|
-
it 'extracts the note text' do
|
111
|
-
subject[:text].should == "The note text"
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
context 'on a 4th-generation note' do
|
116
|
-
let (:content) do
|
117
|
-
"Book title\n" +
|
118
|
-
"- Your Note Location 623 | Added on Thursday, April 21, 2011, 07:31 AM\n" +
|
119
|
-
"\n" +
|
120
|
-
"The note text"
|
121
|
-
end
|
122
|
-
|
123
|
-
it 'marks the clipping as a note' do
|
124
|
-
subject[:type].should == :note
|
125
|
-
end
|
126
|
-
|
127
|
-
it 'extracts the note text' do
|
128
|
-
subject[:text].should == "The note text"
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
context 'on a bookmark' do
|
133
|
-
let (:content) do
|
134
|
-
"Book title\n" +
|
135
|
-
"- Bookmark on Page 1 | Loc. 406 | Added on Thursday, April 21, 2011, 07:31 AM\n" +
|
136
|
-
"\n" +
|
137
|
-
"\n"
|
138
|
-
end
|
139
|
-
|
140
|
-
it 'marks the clipping as a bookmark' do
|
141
|
-
subject[:type].should == :bookmark
|
142
|
-
end
|
143
|
-
|
144
|
-
it 'extracts empty text' do
|
145
|
-
subject[:text].should == ''
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|
149
|
-
context 'on a 4th-generation bookmark' do
|
150
|
-
let (:content) do
|
151
|
-
"Book title\n" +
|
152
|
-
"- Your Bookmark on Page 1 | Location 406 | Added on Thursday, April 21, 2011, 07:31 AM\n" +
|
153
|
-
"\n" +
|
154
|
-
"\n"
|
155
|
-
end
|
156
|
-
|
157
|
-
it 'marks the clipping as a bookmark' do
|
158
|
-
subject[:type].should == :bookmark
|
159
|
-
end
|
160
|
-
|
161
|
-
it 'extracts empty text' do
|
162
|
-
subject[:text].should == ''
|
163
|
-
end
|
164
|
-
end
|
165
|
-
|
166
|
-
context 'on a clipping with a single location value' do
|
167
|
-
let (:content) do
|
168
|
-
"Book title\n" +
|
169
|
-
"- Highlight Loc. 465 | Added on Thursday, April 21, 2011, 07:31 AM\n" +
|
170
|
-
"\n" +
|
171
|
-
"The first line of the highlight\n" +
|
172
|
-
"The second line of the highlight"
|
173
|
-
end
|
174
|
-
|
175
|
-
it 'extracts the location' do
|
176
|
-
subject[:location].should == 465
|
177
|
-
end
|
178
|
-
|
179
|
-
it 'extracts the added_on date' do
|
180
|
-
subject[:added_on].should == 'Thursday, April 21, 2011, 07:31 AM'
|
181
|
-
end
|
182
|
-
end
|
183
|
-
|
184
|
-
context 'on a 4th-generation clipping with a single location value' do
|
185
|
-
let (:content) do
|
186
|
-
"Book title\n" +
|
187
|
-
"- Your Highlight Location 465 | Added on Thursday, April 21, 2011, 07:31 AM\n" +
|
188
|
-
"\n" +
|
189
|
-
"The first line of the highlight\n" +
|
190
|
-
"The second line of the highlight"
|
191
|
-
end
|
192
|
-
|
193
|
-
it 'extracts the location' do
|
194
|
-
subject[:location].should == 465
|
195
|
-
end
|
196
|
-
|
197
|
-
it 'extracts the added_on date' do
|
198
|
-
subject[:added_on].should == 'Thursday, April 21, 2011, 07:31 AM'
|
199
|
-
end
|
200
|
-
end
|
201
|
-
|
202
|
-
context 'on a clipping with a location range' do
|
203
|
-
let (:content) do
|
204
|
-
"Book title\n" +
|
205
|
-
"- Highlight Loc. 466-69 | Added on Thursday, April 21, 2011, 07:31 AM\n" +
|
206
|
-
"\n" +
|
207
|
-
"The first line of the highlight\n" +
|
208
|
-
"The second line of the highlight"
|
209
|
-
end
|
210
|
-
|
211
|
-
it 'extracts the first element of the location range' do
|
212
|
-
subject[:location].should == 466
|
213
|
-
end
|
214
|
-
|
215
|
-
it 'extracts the added_on date' do
|
216
|
-
subject[:added_on].should == 'Thursday, April 21, 2011, 07:31 AM'
|
217
|
-
end
|
218
|
-
end
|
219
|
-
|
220
|
-
context 'on a 4th-generation clipping with a location range' do
|
221
|
-
let (:content) do
|
222
|
-
"Book title\n" +
|
223
|
-
"- Your Highlight Location 466-69 | Added on Thursday, April 21, 2011, 07:31 AM\n" +
|
224
|
-
"\n" +
|
225
|
-
"The first line of the highlight\n" +
|
226
|
-
"The second line of the highlight"
|
227
|
-
end
|
228
|
-
|
229
|
-
it 'extracts the first element of the location range' do
|
230
|
-
subject[:location].should == 466
|
231
|
-
end
|
232
|
-
|
233
|
-
it 'extracts the added_on date' do
|
234
|
-
subject[:added_on].should == 'Thursday, April 21, 2011, 07:31 AM'
|
235
|
-
end
|
236
|
-
end
|
237
|
-
|
238
|
-
context 'on a highlight with a page number and location range' do
|
239
|
-
let (:content) do
|
240
|
-
"Book title\n" +
|
241
|
-
"- Highlight on Page 171 | Loc. 1858-59 | Added on Thursday, April 21, 2011, 07:31 AM\n" +
|
242
|
-
"\n" +
|
243
|
-
"Some highlighted text\n" +
|
244
|
-
"\n"
|
245
|
-
end
|
246
|
-
|
247
|
-
it 'extracts the first element of the location range' do
|
248
|
-
subject[:location].should == 1858
|
249
|
-
end
|
250
|
-
|
251
|
-
it 'extracts the page number' do
|
252
|
-
subject[:page].should == 171
|
253
|
-
end
|
254
|
-
|
255
|
-
it 'extracts the date' do
|
256
|
-
subject[:added_on].should == 'Thursday, April 21, 2011, 07:31 AM'
|
257
|
-
end
|
258
|
-
end
|
259
|
-
|
260
|
-
context 'on a 4th-generation highlight with a page number and location range' do
|
261
|
-
let (:content) do
|
262
|
-
"Book title\n" +
|
263
|
-
"- Your Highlight on Page 171 | Location 1858-59 | Added on Thursday, April 21, 2011, 07:31 AM\n" +
|
264
|
-
"\n" +
|
265
|
-
"Some highlighted text\n" +
|
266
|
-
"\n"
|
267
|
-
end
|
268
|
-
|
269
|
-
it 'extracts the first element of the location range' do
|
270
|
-
subject[:location].should == 1858
|
271
|
-
end
|
272
|
-
|
273
|
-
it 'extracts the page number' do
|
274
|
-
subject[:page].should == 171
|
275
|
-
end
|
276
|
-
|
277
|
-
it 'extracts the date' do
|
278
|
-
subject[:added_on].should == 'Thursday, April 21, 2011, 07:31 AM'
|
279
|
-
end
|
280
|
-
end
|
281
|
-
|
282
|
-
context 'on a highlight with a page number and no location' do
|
283
|
-
let (:content) do
|
284
|
-
"Book title\n" +
|
285
|
-
"- Highlight on Page 9 | Added on Thursday, April 21, 2011, 07:31 AM\n" +
|
286
|
-
"\n" +
|
287
|
-
"Clipping"
|
288
|
-
end
|
289
|
-
|
290
|
-
it 'extracts no location' do
|
291
|
-
subject[:location].should be_nil
|
292
|
-
end
|
293
|
-
|
294
|
-
it 'extracts the page number' do
|
295
|
-
subject[:page].should == 9
|
296
|
-
end
|
297
|
-
|
298
|
-
it 'extracts the date' do
|
299
|
-
subject[:added_on].should == 'Thursday, April 21, 2011, 07:31 AM'
|
300
|
-
end
|
301
|
-
end
|
302
|
-
|
303
|
-
context 'on a 4th-generation highlight with a page number and no location' do
|
304
|
-
let (:content) do
|
305
|
-
"Book title\n" +
|
306
|
-
"- Your Highlight on Page 9 | Added on Thursday, April 21, 2011, 07:31 AM\n" +
|
307
|
-
"\n" +
|
308
|
-
"Clipping"
|
309
|
-
end
|
310
|
-
|
311
|
-
it 'extracts no location' do
|
312
|
-
subject[:location].should be_nil
|
313
|
-
end
|
314
|
-
|
315
|
-
it 'extracts the page number' do
|
316
|
-
subject[:page].should == 9
|
317
|
-
end
|
318
|
-
|
319
|
-
it 'extracts the date' do
|
320
|
-
subject[:added_on].should == 'Thursday, April 21, 2011, 07:31 AM'
|
321
|
-
end
|
322
|
-
end
|
323
|
-
end
|
324
|
-
|
325
|
-
describe '#build_clipping_from' do
|
326
|
-
|
327
|
-
let(:it) { Klipbook::ClippingsParser.new }
|
328
|
-
|
329
|
-
context 'with an empty string' do
|
330
|
-
subject { it.build_clipping_from(" ") }
|
331
|
-
|
332
|
-
it { should be_nil }
|
333
|
-
end
|
334
|
-
|
335
|
-
it 'builds a clipping using the extracted attributes' do
|
336
|
-
attributes = {
|
337
|
-
title: 'Dummy title',
|
338
|
-
type: :highlight,
|
339
|
-
location: 42,
|
340
|
-
added_on: '10 August 2011',
|
341
|
-
text: 'Some highlighted text'
|
342
|
-
}
|
343
|
-
stub(it).extract_attributes { attributes }
|
344
|
-
stub(Klipbook::Clipping).new
|
345
|
-
|
346
|
-
it.build_clipping_from('Dummy content')
|
347
|
-
|
348
|
-
Klipbook::Clipping.should have_received.new.with(attributes)
|
349
|
-
end
|
350
|
-
end
|
351
|
-
|
352
|
-
describe "#strip_control_characters" do
|
353
|
-
let(:it) { Klipbook::ClippingsParser.new }
|
354
|
-
|
355
|
-
it "removes all carriage return characters" do
|
356
|
-
processed_text = it.strip_control_characters("An example \r clippings text\r")
|
357
|
-
processed_text.should_not include("\r")
|
358
|
-
end
|
359
|
-
|
360
|
-
it "removes all Unicode byte order marks" do
|
361
|
-
processed_text = it.strip_control_characters("An example \xef\xbb\xbf clippings text\xef\xbb\xbf")
|
362
|
-
processed_text.should_not include("\xef\xbb\xbf")
|
363
|
-
end
|
364
|
-
end
|
365
|
-
end
|
366
|
-
|
367
|
-
|
@@ -1,87 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Klipbook::Runner do
|
4
|
-
|
5
|
-
let (:input_file) do
|
6
|
-
input_file = Object.new
|
7
|
-
stub(input_file).read { '' }
|
8
|
-
input_file
|
9
|
-
end
|
10
|
-
|
11
|
-
let (:output) do
|
12
|
-
output = Object.new
|
13
|
-
stub(output).puts
|
14
|
-
output
|
15
|
-
end
|
16
|
-
|
17
|
-
let (:clippings_file) do
|
18
|
-
clipplings = Object.new
|
19
|
-
stub(clipplings).books { books }
|
20
|
-
clipplings
|
21
|
-
end
|
22
|
-
|
23
|
-
before :each do
|
24
|
-
stub(Klipbook::ClippingsFile).new { clippings_file }
|
25
|
-
end
|
26
|
-
|
27
|
-
describe '#list_books' do
|
28
|
-
|
29
|
-
subject { Klipbook::Runner.new(input_file).list_books(output) }
|
30
|
-
|
31
|
-
context 'with an empty clippings file' do
|
32
|
-
|
33
|
-
let (:books) { [] }
|
34
|
-
|
35
|
-
it 'displays a message saying that the clipping file contains no books' do
|
36
|
-
subject
|
37
|
-
output.should have_received.puts('Your clippings file contains no books')
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
context 'with a clipping file with multiple books' do
|
42
|
-
let (:books) do
|
43
|
-
[ OpenStruct.new(title: 'My first fake title'), OpenStruct.new(title: 'Another fake book', author: 'Rock Riphard') ]
|
44
|
-
end
|
45
|
-
|
46
|
-
it 'displays a message describing the book list' do
|
47
|
-
subject
|
48
|
-
output.should have_received.puts('The list of books in your clippings file:')
|
49
|
-
end
|
50
|
-
|
51
|
-
it 'displays an indexed list of book titles including the author when available' do
|
52
|
-
subject
|
53
|
-
output.should have_received.puts('[1] My first fake title')
|
54
|
-
output.should have_received.puts('[2] Another fake book by Rock Riphard')
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
describe '#print_book_summary' do
|
60
|
-
|
61
|
-
describe 'with a clipping files with two books' do
|
62
|
-
let (:book_two) do
|
63
|
-
book_two = Object.new
|
64
|
-
end
|
65
|
-
|
66
|
-
let (:books) do
|
67
|
-
[ OpenStruct.new(title: 'My first fake title'), book_two ]
|
68
|
-
end
|
69
|
-
|
70
|
-
[0, 3].each do |book_number|
|
71
|
-
it "displays an error message if the book index requested is #{book_number}" do
|
72
|
-
stub($stderr).puts
|
73
|
-
Klipbook::Runner.new(input_file).print_book_summary(book_number, output)
|
74
|
-
$stderr.should have_received.puts('Sorry but you must specify a book index between 1 and 2')
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
it 'outputs the html summary of the book selected' do
|
79
|
-
mock_html_output = Object.new
|
80
|
-
stub(output).write
|
81
|
-
stub(book_two).as_html { mock_html_output }
|
82
|
-
Klipbook::Runner.new(input_file).print_book_summary(2, output)
|
83
|
-
output.should have_received.write(mock_html_output)
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|