rindle 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.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format nested
2
+ --color
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use --create --install 1.9.3@rindle
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rindle.gemspec
4
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,19 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard 'rspec', :version => 2 do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
7
+ watch('spec/spec_helper.rb') { "spec" }
8
+
9
+ # Rails example
10
+ watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
11
+ watch(%r{^app/(.*)(\.erb|\.haml)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
12
+ watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
13
+ watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
14
+ watch('config/routes.rb') { "spec/routing" }
15
+ watch('app/controllers/application_controller.rb') { "spec/controllers" }
16
+ # Capybara request specs
17
+ watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/requests/#{m[1]}_spec.rb" }
18
+ end
19
+
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Arthur Andersen
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,19 @@
1
+ # Rindle: A Gem packaging multiple kindle tools
2
+
3
+ Rindle is a package of useful classes to manage the kindles content.
4
+ In mature versions it should provide an object-oriented interface to
5
+ access the collections and documents on the kindle with all their
6
+ metadata. And it should provide a convenient way for converting
7
+ various eBook formats.
8
+
9
+ ## Libraries for kindle access
10
+
11
+ First load the kindle:
12
+
13
+ Rindle::Kindle.load '/path/to/root'
14
+
15
+ Then just use `Kindle::Collection` or `Kindle::File` with an
16
+ ActiveRecord like interface:
17
+
18
+ Rindle::Collection.first named: 'Test Collection'
19
+ Rindle::Collection.all named: /(.*)[1|2]$/
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,156 @@
1
+ module Rindle
2
+ class Collection
3
+ class << self
4
+ def all options = {}
5
+ filtered = []
6
+ Rindle.collections.each_pair do |name, collection|
7
+ match = true
8
+ options.each_pair do |key,value|
9
+ case key
10
+ when :named
11
+ match = match && name =~ /#{value}/
12
+ when :including
13
+ match = match && collection.include?(value)
14
+ when :accessed
15
+ time = value.is_a?(Integer) ? Time.at(value) : value
16
+ match = match && collection.last_access == time
17
+ end
18
+ end
19
+ filtered << collection if match
20
+ end
21
+ filtered
22
+ end
23
+
24
+ def first options = {}
25
+ Rindle.collections.each_pair do |name, collection|
26
+ match = true
27
+ options.each_pair do |key,value|
28
+ case key
29
+ when :named
30
+ match = match && collection.name =~ /#{value}/
31
+ when :including
32
+ match = match && collection.include?(value)
33
+ when :accessed
34
+ time = value.is_a?(Integer) ? Time.at(value) : value
35
+ match = match && collection.last_access == time
36
+ end
37
+ end
38
+ return collection if match
39
+ end
40
+ nil
41
+ end
42
+
43
+ def find(method = :all, options={})
44
+ self.send method, options
45
+ end
46
+
47
+ def find_by_name name
48
+ find(:first, :named => name)
49
+ end
50
+
51
+ def create name, options = {}
52
+ collection = Collection.new(name, options)
53
+ Rindle.collections[collection.name] = collection
54
+ end
55
+
56
+ def exists? options
57
+ !Collection.first(options).nil?
58
+ end
59
+ end
60
+
61
+ attr_reader :name, :indices, :last_access
62
+
63
+ def == other
64
+ other.is_a?(Rindle::Collection) &&
65
+ name == other.name &&
66
+ indices == other.indices &&
67
+ last_access == other.last_access
68
+ end
69
+
70
+ def initialize name, options = {}
71
+ { :indices => [], :last_access => nil }.merge!(options)
72
+ @name = name.gsub('@en-US', '')
73
+ @indices = options[:indices] || []
74
+ @last_access = Time.at(options[:last_access]) if options[:last_access]
75
+ end
76
+
77
+ # Returns a hash that may be saved to `collections.json` file.
78
+ def to_hash
79
+ {
80
+ "#{@name}@en-US" => {
81
+ "items" => @indices,
82
+ "lastAccess" => @last_access.to_i
83
+ }
84
+ }
85
+ end
86
+
87
+ # Renames the collection. This changes the collections name and
88
+ # updates the Collections hash.
89
+ def rename! new_name
90
+ Rindle.collections.delete @name
91
+ @name = new_name
92
+ Rindle.collections[@name] = self
93
+ end
94
+
95
+ # Destroys the collection. This removes the collections key from
96
+ # the collections hash.
97
+ def destroy!
98
+ Rindle.collections.delete @name
99
+ end
100
+
101
+ # Adds an index or a document to the collection.
102
+ def add index
103
+ index = index.index if index.is_a?(Document)
104
+ unless indices.include?(index)
105
+ indices << obj.index
106
+ @documents = nil
107
+ end
108
+ end
109
+
110
+ # Removes an entry from this collection.
111
+ def remove index
112
+ index = index.index if index.is_a?(Document)
113
+ if indices.include?(index)
114
+ indices.delete index
115
+ @documents = nil
116
+ end
117
+ end
118
+
119
+ # Sets the indices array and resets the documents memoized array
120
+ # of `Document` objects.
121
+ def indices= indices
122
+ @documents = nil
123
+ @indices = indices
124
+ end
125
+
126
+ # Returns an `Array` of `Document` objects.
127
+ def documents
128
+ @documents ||= @indices.map { |i| Rindle.index[i] }
129
+ end
130
+
131
+ # Sets the array of `Document` objects.
132
+ def documents= documents
133
+ indices = documents.map(&:index)
134
+ @documents = documents
135
+ end
136
+
137
+ # Returns true if the collection includes the given indec,
138
+ # `Document` or `Array`.
139
+ def include? obj
140
+ if obj.is_a?(Array)
141
+ obj.inject(true) { |acc, o| acc = acc and include?(o) }
142
+ elsif obj.is_a?(Document)
143
+ indices.include? obj.index
144
+ elsif obj.is_a?(String)
145
+ indices.include? obj
146
+ else
147
+ false
148
+ end
149
+ end
150
+
151
+ # Update the last access timestamp.
152
+ def touch
153
+ last_access = Time.now
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,43 @@
1
+ require 'rubygems'
2
+ require 'json'
3
+
4
+ require 'rindle/collection'
5
+
6
+ module Rindle
7
+ class Collections < Hash
8
+
9
+ class NoSuchFile < Exception; end
10
+
11
+ def initialize(kindle_root)
12
+ @collections_file = File.join(kindle_root, 'system', 'collections.json')
13
+ end
14
+
15
+ def self.load(kindle_root)
16
+ Collections.new(kindle_root).load
17
+ end
18
+
19
+ def save
20
+ hash = {}
21
+ values.each do |coll|
22
+ hash.merge! col.to_hash
23
+ end
24
+ File.open(@collections_file, 'w+') do |f|
25
+ JSON.dump(hash, f)
26
+ end
27
+ end
28
+
29
+ def load
30
+ unless File.exists?(@collections_file)
31
+ raise NoSuchFile, "Not found: #{@collections_file}"
32
+ end
33
+
34
+ hash = File.open(@collections_file, 'r') { |file| JSON.load(file) }
35
+ hash.each_pair do |name, data|
36
+ col = Collection.new name, :indices => data['items'],
37
+ :last_access => data['lastAccess']
38
+ self[col.name] = col
39
+ end
40
+ self
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,131 @@
1
+ require "digest/sha1"
2
+
3
+ module Rindle
4
+ class Document
5
+ class NotFound < Exception; end
6
+
7
+ class << self
8
+ def all options = {}
9
+ filtered = []
10
+ Rindle.index.each_pair do |index, doc|
11
+ match = true
12
+ options.each_pair do |key, value|
13
+ case key
14
+ when :named
15
+ match = match && doc.filename =~ /#{value.is_a?(String) ? Regexp.escape(value) : value}/
16
+ when :indexed
17
+ match = match && index =~ /#{value.is_a?(String) ? Regexp.escape(value) : value}/
18
+ end
19
+ end
20
+ filtered << doc if match
21
+ end
22
+ filtered
23
+ end
24
+
25
+ def first options = {}
26
+ Rindle.index.each_pair do |index, doc|
27
+ match = true
28
+ options.each_pair do |key, value|
29
+ case key
30
+ when :named
31
+ match = match && doc.filename =~ /#{value.is_a?(String) ? Regexp.escape(value) : value}/
32
+ when :indexed
33
+ match = match && index =~ /#{value.is_a?(String) ? Regexp.escape(value) : value}/
34
+ end
35
+ end
36
+ return doc if match
37
+ end
38
+ nil
39
+ end
40
+
41
+ def find method = :all, options = {}
42
+ self.send method, options
43
+ end
44
+
45
+ def unassociated
46
+ unassociated = []
47
+ Rindle.index.each_pair do |index, doc|
48
+ unless Rindle.collections.values.inject(false) { |acc, col| acc = acc or col.include?(index) }
49
+ unassociated << doc
50
+ end
51
+ end
52
+ unassociated
53
+ end
54
+
55
+ def find_by_name name
56
+ Rindle.index.values.each do |doc|
57
+ return doc if doc.filename =~ /#{name.is_a?(String) ? Regexp.escape(name) : name}/
58
+ end
59
+ nil
60
+ end
61
+
62
+ def find_by_path path
63
+ Rindle.index.values.each do |doc|
64
+ return doc if doc.path =~ /#{path.is_a?(String) ? Regexp.escape(path) : path}/
65
+ end
66
+ nil
67
+ end
68
+
69
+ def find_by_index index
70
+ Rindle.index[index]
71
+ end
72
+ end
73
+
74
+ attr_reader :index, :path
75
+
76
+ def initialize path
77
+ self.path = path
78
+ end
79
+
80
+ # Sets the path variable and updates the index
81
+ def path= path
82
+ @path = path
83
+ @index = generate_index
84
+ end
85
+
86
+ # Generates the index for the current path
87
+ def generate_index
88
+ if path =~ /([\w\s]+)-asin_([A-Z0-9]+)-type_([A-Z]+)-v_[0-9]+.azw/
89
+ "##{$2}^#{$3}"
90
+ else
91
+ "*#{Digest::SHA1.hexdigest(File.join('/mnt/us', path))}"
92
+ end
93
+ end
94
+
95
+ # Two documents are the same if the indices are equal.
96
+ def == other
97
+ @index == other.index
98
+ end
99
+
100
+ # Returns the filesize of this document.
101
+ def filesize
102
+ @filesize ||= File.size(File.join(Rindle.root_path, @path))
103
+ end
104
+
105
+ # Returns the filename of this document.
106
+ def filename
107
+ File.basename(@path)
108
+ end
109
+
110
+ # Returns true if the `path` looks like an amazon kindle store file.
111
+ def amazon?
112
+ !(path !~ /([\w\s]+)-asin_([A-Z0-9]+)-type_([A-Z]+)-v_[0-9]+.azw/)
113
+ end
114
+
115
+ # Renames the document. This also means that the index is changed
116
+ # and the Index-hash is updated.
117
+ def rename! new_name
118
+ Rindle.index.delete(@index)
119
+ self.path = @path.gsub filename, new_name
120
+ # TODO: update references in Rindle.collections
121
+ Rindle.index[@index] = self
122
+ end
123
+
124
+ # Returns an array of all the collections, this document is in.
125
+ def collections
126
+ Rindle.collections.select do |col|
127
+ col.include? self.index
128
+ end
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,43 @@
1
+ # -*- coding: utf-8 -*-
2
+ require "rubygems"
3
+ require "json"
4
+
5
+ module Rindle
6
+ class Index < Hash
7
+ def initialize
8
+ @index = {}
9
+ end
10
+
11
+ def self.load(root_path)
12
+ Index.new.load(root_path)
13
+ end
14
+
15
+ def load(root_path)
16
+ @root_path = root_path
17
+ documents = Dir[File.join(@root_path, '{documents,pictures}', '*.{mobi,azw,azw1,pdf,rtf}')]
18
+ documents.each do |element|
19
+ add(element.gsub(@root_path, ''))
20
+ end
21
+ self
22
+ end
23
+
24
+ # Adds a path to the index. This means that the correct sha1 sum
25
+ # is generated and used as index for the newly created document
26
+ # object.
27
+ def add path
28
+ doc = Document.new path
29
+ self[doc.index] = doc
30
+ doc.index
31
+ end
32
+
33
+ # Removes either an entry either by `Document` or index.
34
+ def remove obj
35
+ if obj.is_a?(Document)
36
+ delete(index(obj))
37
+ else
38
+ delete(obj)
39
+ end
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,16 @@
1
+ module Rindle
2
+ module Mixins
3
+ module Regexp
4
+
5
+ # converts the expressin to a string and
6
+ # strips the special characters for marking
7
+ # the beginning and end of a string
8
+ def strip
9
+ self.source.gsub(/^\^/,'').gsub(/\$$/,'')
10
+ end
11
+
12
+ end
13
+ end
14
+ end
15
+
16
+ Regexp.send :include, Rindle::Mixins::Regexp
@@ -0,0 +1,3 @@
1
+ module Rindle
2
+ VERSION = "0.1.0"
3
+ end
data/lib/rindle.rb ADDED
@@ -0,0 +1,58 @@
1
+ require 'rindle/version'
2
+ require 'rindle/index'
3
+ require 'rindle/collections'
4
+ require 'rindle/collection'
5
+ require 'rindle/document'
6
+
7
+ require 'rindle/mixins/regexp'
8
+
9
+ # This module is used to load the Kindles state into the
10
+ # data structures, which are used to let you access the Kindles
11
+ # content in a way, that reminds one of the ActiveRecord usage
12
+ #
13
+ # Load from a specific path
14
+ #
15
+ # Rindle.load(path)
16
+ #
17
+ # After that you may use the models Collection, Document, Album
18
+ module Rindle
19
+ @@root_path = nil
20
+ @@collections = nil
21
+ @@index = nil
22
+
23
+ class NotLoaded < Exception; end
24
+
25
+ def self.root_path
26
+ if @@root_path
27
+ @@root_path
28
+ else
29
+ raise NotLoaded
30
+ end
31
+ end
32
+
33
+ def self.index
34
+ if @@index
35
+ @@index
36
+ else
37
+ raise NotLoaded
38
+ end
39
+ end
40
+
41
+ def self.collections
42
+ if @@collections
43
+ @@collections
44
+ else
45
+ raise NotLoaded
46
+ end
47
+ end
48
+
49
+ def self.load root_path
50
+ @@root_path = root_path
51
+ @@index = Index.load(root_path)
52
+ @@collections = Collections.load(root_path)
53
+ end
54
+
55
+ def self.save
56
+ @@collections.save
57
+ end
58
+ end
data/rindle.gemspec ADDED
@@ -0,0 +1,20 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/rindle/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Arthur Leonard Andersen"]
6
+ gem.email = ["leoc.git@gmail.com"]
7
+ gem.description = %q{The Rindle gem provides an object-oriented way to manage kindle collection data.}
8
+ gem.summary = %q{Access kindle collection data}
9
+ gem.homepage = "https://github.com/leoc/rindle"
10
+
11
+ gem.files = `git ls-files`.split("\n")
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "rindle"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Rindle::VERSION
17
+
18
+ gem.add_development_dependency 'rspec'
19
+ gem.add_development_dependency 'guard-rspec'
20
+ end
@@ -0,0 +1 @@
1
+ This directory contains dummy data for the specs.
File without changes
File without changes
@@ -0,0 +1,14 @@
1
+ {
2
+ "collection1@en-US":{
3
+ "items":["*18be6fcd5d5df39c1a96cd22596bbe7fe01db9b7", "*0849dd9b85fc341d10104f56985e423b3848e1f3"],
4
+ "lastAccess": 1298745909919
5
+ },
6
+ "collection2@en-US":{
7
+ "items":["*440f49b58ae78d34f4b8ad3233f04f6b8f5490c2"],
8
+ "lastAccess": 1298745909918
9
+ },
10
+ "amazon books@en-US":{
11
+ "items":["#B001UQ5HVA^EBSP","#B000JQU1VS^EBOK"],
12
+ "lastAccess": 1298745909917
13
+ }
14
+ }
File without changes
@@ -0,0 +1,100 @@
1
+ require "spec_helper"
2
+
3
+ describe Rindle::Collection do
4
+ before(:all) do
5
+ Rindle.load(kindle_root)
6
+ end
7
+
8
+
9
+ context '#exists?' do
10
+ it 'returns true if the collection exists' do
11
+ Rindle::Collection.exists?(:named => 'collection1').should == true
12
+ end
13
+
14
+ it 'returns false if the collection does not exist' do
15
+ Rindle::Collection.exists?(:named => 'collection5').should == false
16
+ end
17
+ end
18
+
19
+ context '#all' do
20
+ it 'invokes Collection.find with parameter :all' do
21
+ Rindle::Collection.find(:all).map(&:name).should == Rindle::Collection.all.map(&:name)
22
+ end
23
+ end
24
+
25
+ context '#first' do
26
+ it 'invokes Collection.find with parameter :first' do
27
+ Rindle::Collection.find(:first).should == Rindle::Collection.first
28
+ end
29
+ end
30
+
31
+ context '#find' do
32
+ it 'returns an array of Kindle::Collection objects' do
33
+ Rindle::Collection.find
34
+ end
35
+
36
+ context 'finds all filtered' do
37
+ it 'by name with string' do
38
+ name = "collection"
39
+ collections = Rindle::Collection.find(:all, :named => name)
40
+ collections.map(&:name).should == ['collection1', 'collection2']
41
+ end
42
+
43
+ it 'by name with regular expression' do
44
+ name = /collection[1|3]/
45
+ collections = Rindle::Collection.find(:all, :named => name)
46
+ collections.map(&:name).should == ['collection1']
47
+ end
48
+
49
+ it 'by including index' do
50
+ collections = Rindle::Collection.find(:all, :including => '#B001UQ5HVA^EBSP')
51
+ collections.map(&:name).should == ['amazon books']
52
+ end
53
+
54
+ it 'by including all of an array of indices' do
55
+ collections = Rindle::Collection.find(:all, :including => ["*18be6fcd5d5df39c1a96cd22596bbe7fe01db9b7", "*0849dd9b85fc341d10104f56985e423b3848e1f3"])
56
+ collections.map(&:name).should == ['collection1']
57
+ end
58
+
59
+ it 'by last access time' do
60
+ collections = Rindle::Collection.find(:all, :accessed => 1298745909917)
61
+ collections.map(&:name).should == ['amazon books']
62
+ end
63
+ end
64
+
65
+ context 'finds first filtered' do
66
+ it 'by name' do
67
+ collection = Rindle::Collection.find(:first, :named => 'collection1')
68
+ collection.name.should == 'collection1'
69
+ end
70
+
71
+ it 'by included index' do
72
+ collection = Rindle::Collection.find(:first, :including => '*440f49b58ae78d34f4b8ad3233f04f6b8f5490c2')
73
+ collection.name.should == 'collection2'
74
+ end
75
+
76
+ it 'by last access time' do
77
+ collection = Rindle::Collection.find(:first, :accessed => 1298745909917)
78
+ collection.name.should == 'amazon books'
79
+ end
80
+ end
81
+ end
82
+
83
+ context '#documents' do
84
+ it 'returns an array of Rindle::Document objects' do
85
+ collection = Rindle::Collection.find(:first, :named => 'collection1')
86
+ collection.documents.each do |document|
87
+ document.should be_a(Rindle::Document)
88
+ end
89
+ end
90
+ end
91
+
92
+ context '#to_hash' do
93
+ it 'returns a hash that could be found in collections.json' do
94
+ now = Time.now.to_i
95
+ collection = Rindle::Collection.new("test", :indices => ['a','b','c'],
96
+ :last_access => now)
97
+ collection.to_hash.should == { "test@en-US" => { "items" => ['a', 'b', 'c'], "lastAccess" => now } }
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,12 @@
1
+ require "spec_helper"
2
+
3
+ describe Rindle::Collections do
4
+ it 'loads the kindles collections.json file' do
5
+ collections = Rindle::Collections.load(kindle_root)
6
+ Hash[collections.map{|k,c| [k,c.indices] } ].should == {
7
+ "collection1" => ["*18be6fcd5d5df39c1a96cd22596bbe7fe01db9b7", "*0849dd9b85fc341d10104f56985e423b3848e1f3"],
8
+ "collection2" => ["*440f49b58ae78d34f4b8ad3233f04f6b8f5490c2"],
9
+ "amazon books" => ["#B001UQ5HVA^EBSP","#B000JQU1VS^EBOK"]
10
+ }
11
+ end
12
+ end
@@ -0,0 +1,59 @@
1
+ require "spec_helper"
2
+
3
+ describe Rindle::Document do
4
+ before(:all) do
5
+ Rindle.load(kindle_root)
6
+ end
7
+
8
+ after(:all) do
9
+ Rindle.reset
10
+ end
11
+
12
+ it 'equals another if the index is the same' do
13
+ doc1 = Rindle::Document.new('documents/ABC-asin_B001UQ5HVA-type_EBSP-v1.azw')
14
+ doc2 = Rindle::Document.new('documents/ABC-asin_B001UQ5HVA-type_EBSP-v1.azw')
15
+ doc1.should == doc2
16
+ end
17
+
18
+ describe '.find' do
19
+ context 'finds :all filtered' do
20
+ it 'by name with string' do
21
+ docs = Rindle::Document.find(:all, :named => 'A test aswell.mobi')
22
+ docs.map(&:filename).should == ['A test aswell.mobi']
23
+ end
24
+
25
+ it 'by name with regular expression' do
26
+ docs = Rindle::Document.find(:all, :named => /t([es]+)t/)
27
+ docs.map(&:filename).should == ['A test aswell.mobi',
28
+ 'This is a test document.rtf']
29
+ end
30
+
31
+ it 'by index' do
32
+ docs = Rindle::Document.find(:all, :indexed => '*18be6fcd5d5df39c1a96cd22596bbe7fe01db9b7')
33
+ docs.map(&:filename).should == ['A test aswell.mobi']
34
+ end
35
+ end
36
+
37
+ context 'finds :first filtered' do
38
+ it 'by name with string' do
39
+ doc = Rindle::Document.find(:first, :named => 'A test aswell.mobi')
40
+ doc.filename.should == 'A test aswell.mobi'
41
+ end
42
+ it 'by name with regular expression' do
43
+ doc = Rindle::Document.find(:first, :named => /t([es]+)t/)
44
+ doc.filename.should == 'A test aswell.mobi'
45
+ end
46
+ it 'by index' do
47
+ doc = Rindle::Document.find(:first, :indexed => '*18be6fcd5d5df39c1a96cd22596bbe7fe01db9b7')
48
+ doc.filename.should == 'A test aswell.mobi'
49
+ end
50
+ end
51
+ end
52
+
53
+ describe '.unassociated' do
54
+ it 'should return unassociated documents' do
55
+ docs = Rindle::Document.unassociated
56
+ docs.map(&:filename).should == [ 'This is a test document.rtf' ]
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,49 @@
1
+ require "spec_helper"
2
+
3
+ describe Rindle::Index do
4
+ before(:each) do
5
+ @index = Rindle::Index.new
6
+ end
7
+
8
+ context '#add' do
9
+ it 'generates and adds for sha1 sum for custom books' do
10
+ index = @index.add('documents/A test aswell.mobi')
11
+ index.should == "*18be6fcd5d5df39c1a96cd22596bbe7fe01db9b7"
12
+ @index.should have_key("*18be6fcd5d5df39c1a96cd22596bbe7fe01db9b7")
13
+ end
14
+ it 'generates and adds amazon indices for EBOK and EBSP files' do
15
+ index = @index.add('documents/Salvia Divinorum Shamanic Plant-asin_B001UQ5HVA-type_EBSP-v_0.azw')
16
+ index.should == "#B001UQ5HVA^EBSP"
17
+ @index.should have_key("#B001UQ5HVA^EBSP")
18
+ end
19
+ end
20
+
21
+ context '#new' do
22
+ it 'creates an empty index' do
23
+ @index.should be_empty
24
+ end
25
+
26
+ it 'derives from Hash' do
27
+ @index.should be_a(Hash)
28
+ end
29
+ end
30
+
31
+ it 'is not empty after #load if there are documents on the kindle' do
32
+ Dir[File.join(kindle_root, '{documents,pictures}', '*.{mobi,azw,azw1,pdf,rtf}')].should_not be_empty
33
+ Rindle::Index.load(kindle_root).should_not == {}
34
+ end
35
+
36
+
37
+ it 'indexes the dummy files from spec data' do
38
+ @index = Rindle::Index.load(kindle_root)
39
+ @index.values.map { |o| [ o.index, o.filename ] }.should =~
40
+ [
41
+ [ "*18be6fcd5d5df39c1a96cd22596bbe7fe01db9b7", "A test aswell.mobi" ],
42
+ [ "#B001UQ5HVA^EBSP", "Salvia Divinorum Shamanic Plant-asin_B001UQ5HVA-type_EBSP-v_0.azw" ],
43
+ [ "*440f49b58ae78d34f4b8ad3233f04f6b8f5490c2", "A book in another collection.mobi" ],
44
+ [ "#B000JQU1VS^EBOK", "The Adventures of Sherlock Holme-asin_B000JQU1VS-type_EBOK-v_0.azw" ],
45
+ [ "*0849dd9b85fc341d10104f56985e423b3848e1f3", "Definitely a Test.pdf" ],
46
+ [ "*3a102b4032d485025650409b2f7753a1158b199d", "This is a test document.rtf" ]
47
+ ]
48
+ end
49
+ end
@@ -0,0 +1,11 @@
1
+ describe 'Regexp Mixins' do
2
+ it 'should strip special char for beginning of string' do
3
+ expr = /^match/
4
+ expr.strip.should == 'match'
5
+ end
6
+
7
+ it 'should strip special char for end of string' do
8
+ expr = /match$/
9
+ expr.strip.should == 'match'
10
+ end
11
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ describe Rindle do
4
+ before(:each) do
5
+ Rindle.reset
6
+ end
7
+
8
+ context '.collections' do
9
+ it 'raises a NotLoaded error if not loaded' do
10
+ lambda { Rindle::collections }.should raise_error(Rindle::NotLoaded)
11
+ end
12
+ it 'returns an instance of Kindle::Collections if module loaded' do
13
+ Rindle::load(kindle_root)
14
+ Rindle::collections.should be_a(Rindle::Collections)
15
+ end
16
+ end
17
+
18
+ context '.index' do
19
+ it 'raises a NotLoaded error if not loaded' do
20
+ lambda { Rindle::index }.should raise_error(Rindle::NotLoaded)
21
+ end
22
+ it 'returns an instance of Kindle::Index if module loaded' do
23
+ Rindle::load(kindle_root)
24
+ Rindle::index.should be_a(Rindle::Index)
25
+ end
26
+ end
27
+
28
+ context '.root_path' do
29
+ it 'provieds a string if loaded' do
30
+ Rindle::load(kindle_root)
31
+ Rindle::root_path.should be_a(String)
32
+ end
33
+ it 'raises a NotLoaded error if not loaded' do
34
+ lambda { Rindle::root_path }.should raise_error(Rindle::NotLoaded)
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,18 @@
1
+ require 'rindle'
2
+
3
+ def kindle_root; File.join(File.dirname(__FILE__), 'data', 'kindle'); end
4
+
5
+ # this is to reset the Singleton'ish nature of the Kindle module
6
+ module Rindle
7
+ def self.reset
8
+ self.class_variables.each do |var|
9
+ eval "#{var} = nil"
10
+ end
11
+ end
12
+ end
13
+
14
+ RSpec.configure do |config|
15
+ config.treat_symbols_as_metadata_keys_with_true_values = true
16
+ config.run_all_when_everything_filtered = true
17
+ config.filter_run :focus
18
+ end
metadata ADDED
@@ -0,0 +1,122 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rindle
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Arthur Leonard Andersen
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-05-17 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: &21636720 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *21636720
25
+ - !ruby/object:Gem::Dependency
26
+ name: guard-rspec
27
+ requirement: &21636100 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *21636100
36
+ description: The Rindle gem provides an object-oriented way to manage kindle collection
37
+ data.
38
+ email:
39
+ - leoc.git@gmail.com
40
+ executables: []
41
+ extensions: []
42
+ extra_rdoc_files: []
43
+ files:
44
+ - .gitignore
45
+ - .rspec
46
+ - .rvmrc
47
+ - Gemfile
48
+ - Guardfile
49
+ - LICENSE
50
+ - README.md
51
+ - Rakefile
52
+ - lib/rindle.rb
53
+ - lib/rindle/collection.rb
54
+ - lib/rindle/collections.rb
55
+ - lib/rindle/document.rb
56
+ - lib/rindle/index.rb
57
+ - lib/rindle/mixins/regexp.rb
58
+ - lib/rindle/version.rb
59
+ - rindle.gemspec
60
+ - spec/data/README.md
61
+ - spec/data/kindle/documents/A book in another collection.mobi
62
+ - spec/data/kindle/documents/A test aswell.mobi
63
+ - spec/data/kindle/documents/Definitely a Test.pdf
64
+ - spec/data/kindle/documents/Salvia Divinorum Shamanic Plant-asin_B001UQ5HVA-type_EBSP-v_0.azw
65
+ - spec/data/kindle/documents/The Adventures of Sherlock Holme-asin_B000JQU1VS-type_EBOK-v_0.azw
66
+ - spec/data/kindle/documents/This is a test document.rtf
67
+ - spec/data/kindle/system/collections.json
68
+ - spec/data/mount/.gitkeep
69
+ - spec/rindle/collection_spec.rb
70
+ - spec/rindle/collections_spec.rb
71
+ - spec/rindle/document_spec.rb
72
+ - spec/rindle/index_spec.rb
73
+ - spec/rindle/mixins/regexp_spec.rb
74
+ - spec/rindle_spec.rb
75
+ - spec/spec_helper.rb
76
+ homepage: https://github.com/leoc/rindle
77
+ licenses: []
78
+ post_install_message:
79
+ rdoc_options: []
80
+ require_paths:
81
+ - lib
82
+ required_ruby_version: !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ segments:
89
+ - 0
90
+ hash: 391700311857255160
91
+ required_rubygems_version: !ruby/object:Gem::Requirement
92
+ none: false
93
+ requirements:
94
+ - - ! '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ segments:
98
+ - 0
99
+ hash: 391700311857255160
100
+ requirements: []
101
+ rubyforge_project:
102
+ rubygems_version: 1.8.17
103
+ signing_key:
104
+ specification_version: 3
105
+ summary: Access kindle collection data
106
+ test_files:
107
+ - spec/data/README.md
108
+ - spec/data/kindle/documents/A book in another collection.mobi
109
+ - spec/data/kindle/documents/A test aswell.mobi
110
+ - spec/data/kindle/documents/Definitely a Test.pdf
111
+ - spec/data/kindle/documents/Salvia Divinorum Shamanic Plant-asin_B001UQ5HVA-type_EBSP-v_0.azw
112
+ - spec/data/kindle/documents/The Adventures of Sherlock Holme-asin_B000JQU1VS-type_EBOK-v_0.azw
113
+ - spec/data/kindle/documents/This is a test document.rtf
114
+ - spec/data/kindle/system/collections.json
115
+ - spec/data/mount/.gitkeep
116
+ - spec/rindle/collection_spec.rb
117
+ - spec/rindle/collections_spec.rb
118
+ - spec/rindle/document_spec.rb
119
+ - spec/rindle/index_spec.rb
120
+ - spec/rindle/mixins/regexp_spec.rb
121
+ - spec/rindle_spec.rb
122
+ - spec/spec_helper.rb