daybreak 0.0.1

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,8 @@
1
+ *.db
2
+ coverage
3
+ doc
4
+ profile
5
+ .DS_Store
6
+ .yardoc
7
+ Gemfile.lock
8
+ *.rbc
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ --no-private --protected lib/**/*.rb - README EPIGRAPH LICENSE
data/EPIGRAPH ADDED
@@ -0,0 +1,6 @@
1
+ Above the escutcheon was a declension of the place-names: Falconer Jail 1871,
2
+ Falconer Reformatory, Falconer Federal Penitentiary, Falconer State Prison,
3
+ Falconer Correctional Facility, and the last, which had never caught on:
4
+ Daybreak House.
5
+
6
+ John Cheever, Falconer
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in daybreak.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Jeff Larson
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 ADDED
@@ -0,0 +1,13 @@
1
+ ^^ |
2
+ daybreak ^^ \ _ /
3
+ -= / \ =-
4
+ ~^~ ^ ^~^~ ~^~ ~ ~^~~^~^-=~=~=-~^~^~^~
5
+
6
+ Daybreak is a simple key value store for ruby. It has user defined persistence,
7
+ and all data is stored in a table in memory so ruby niceties are available.
8
+ Daybreak's performance is on par with other options like pstore and dbm.
9
+
10
+ $ gem install daybreak
11
+
12
+ Docs: http://propublica.github.com/daybreak/
13
+ Issue Tracker: http://propublica.github.com/daybreak/issues
data/Rakefile ADDED
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+
4
+ task :default do
5
+ require "./test/test.rb"
6
+ end
7
+
8
+ desc "Run benchmarks"
9
+ task :bench do
10
+ require "./test/bench.rb"
11
+ end
12
+
13
+ desc "Run comparisons with other libraries"
14
+ task :compare do
15
+ require "./test/compare.rb"
16
+ end
17
+
18
+ desc "Profile a simple run"
19
+ task :prof do
20
+ require "./test/prof.rb"
21
+ end
22
+
23
+ require 'erb'
24
+
25
+ desc "Write out docs to index.html"
26
+ task :doc do |t|
27
+ File.open("index.html", 'w').write ERB.new(File.open("index.erb").read).result(binding)
28
+ end
29
+
30
+ desc "Publish the docs to gh-pages"
31
+ task :publish do |t|
32
+ `git checkout gh-pages`
33
+ `git merge master`
34
+ `git push`
35
+ `git checkout master`
36
+ end
data/daybreak.gemspec ADDED
@@ -0,0 +1,18 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/daybreak/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Jeff Larson"]
6
+ gem.email = ["thejefflarson@gmail.com"]
7
+ gem.description = %q{A simple dimple key-value store for ruby.}
8
+ gem.summary = %q{Daybreak provides an in memory key-value store that is easily enumerable in ruby.}
9
+ gem.homepage = "http://propublica.github.com/daybreak/"
10
+
11
+ gem.files = `git ls-files`.split($\).reject {|f| f =~ /^(index)/}
12
+ gem.test_files = gem.files.grep(%r{^(test)/})
13
+ gem.name = "daybreak"
14
+ gem.require_paths = ["lib"]
15
+ gem.version = Daybreak::VERSION
16
+ gem.add_development_dependency 'simplecov'
17
+ gem.add_development_dependency 'ruby-prof'
18
+ end
data/lib/daybreak.rb ADDED
@@ -0,0 +1,17 @@
1
+ # Daybreak, a simple dimple key value store for ruby.
2
+ module Daybreak
3
+ # The root path for Daybreak
4
+ ROOT = File.expand_path(File.dirname(__FILE__))
5
+ end
6
+
7
+ require 'tempfile'
8
+ require 'thread'
9
+ require 'zlib'
10
+ require 'fcntl'
11
+
12
+ require "#{Daybreak::ROOT}/daybreak/version"
13
+ require "#{Daybreak::ROOT}/daybreak/locking"
14
+ require "#{Daybreak::ROOT}/daybreak/record"
15
+ require "#{Daybreak::ROOT}/daybreak/writer"
16
+ require "#{Daybreak::ROOT}/daybreak/reader"
17
+ require "#{Daybreak::ROOT}/daybreak/db"
@@ -0,0 +1,163 @@
1
+ module Daybreak
2
+ # Daybreak::DB contains the public api for Daybreak, you may extend it like
3
+ # any other Ruby class (i.e. to overwrite serialize and parse). It includes
4
+ # Enumerable for functional goodies like map, each, reduce and friends.
5
+ class DB
6
+ include Enumerable
7
+
8
+ # Create a new Daybreak::DB. The second argument is the default value
9
+ # to store when accessing a previously unset key, this follows the
10
+ # Hash standard.
11
+ # @param [String] file the path to the db file
12
+ # @param default the default value to store and return when a key is
13
+ # not yet in the database.
14
+ # @yield [key] blk a block that will return the default value to store.
15
+ def initialize(file, default=nil, &blk)
16
+ @file_name = file
17
+ reset!
18
+ @default = default
19
+ @default = blk if block_given?
20
+ read!
21
+ end
22
+
23
+ # Set a key in the database to be written at some future date. If the data
24
+ # needs to be persisted immediately, call <tt>db.set(key, value, true)</tt>.
25
+ # @param [#to_s] key the key of the storage slot in the database
26
+ # @param value the value to store
27
+ # @param [Boolean] sync if true, sync this value immediately
28
+ def []=(key, value, sync = false)
29
+ key = key.to_s
30
+ @writer.write(Record.new(key, serialize(value)))
31
+ flush! if sync
32
+ @table[key] = value
33
+ end
34
+ alias_method :set, :"[]="
35
+
36
+ # set! flushes data immediately to disk.
37
+ # @param [#to_s] key the key of the storage slot in the database
38
+ # @param value the value to store
39
+ def set!(key, value)
40
+ set key, value, true
41
+ end
42
+
43
+ # Retrieve a value at key from the database. If the default value was specified
44
+ # when this database was created, that value will be set and returned. Aliased
45
+ # as <tt>get</tt>.
46
+ # @param [#to_s] key the value to retrieve from the database.
47
+ def [](key)
48
+ key = key.to_s
49
+ if @table.has_key? key
50
+ @table[key]
51
+ elsif default?
52
+ if @default.is_a? Proc
53
+ value = @default.call(key)
54
+ else
55
+ value = @default
56
+ end
57
+ set key, value
58
+ end
59
+ end
60
+ alias_method :get, :"[]"
61
+
62
+ # Iterate over the key, value pairs in the database.
63
+ def each(&blk)
64
+ keys.each { |k| blk.call(k, get(k)) }
65
+ end
66
+
67
+ # Does this db have a default value.
68
+ def default?
69
+ !@default.nil?
70
+ end
71
+
72
+ # Does this db have a value for this key?
73
+ def has_key?(key)
74
+ @table.has_key? key.to_s
75
+ end
76
+
77
+ # Return the keys in the db.
78
+ # @return [Array<String>]
79
+ def keys
80
+ @table.keys
81
+ end
82
+
83
+ # Return the number of stored items.
84
+ # @return [Integer]
85
+ def length
86
+ @table.keys.length
87
+ end
88
+
89
+ # Serialize the data for writing to disk, if you don't want to use <tt>Marshal</tt>
90
+ # overwrite this method.
91
+ # @param value the value to be serialized
92
+ # @return [String]
93
+ def serialize(value)
94
+ Marshal.dump(value)
95
+ end
96
+
97
+ # Parse the serialized value from disk, like serialize if you want to use a
98
+ # different serialization method overwrite this method.
99
+ # @param value the value to be parsed
100
+ # @return [String]
101
+ def parse(value)
102
+ Marshal.load(value)
103
+ end
104
+
105
+ # Reset and empty the database file.
106
+ def empty!
107
+ reset!
108
+ @writer.truncate!
109
+ end
110
+
111
+ # Force all queued commits to be written to disk.
112
+ def flush!
113
+ @writer.flush!
114
+ end
115
+
116
+ # Reset the state of the database, you should call <tt>read!</tt> after calling this.
117
+ def reset!
118
+ @table = {}
119
+ @writer = Daybreak::Writer.new(@file_name)
120
+ @reader = Daybreak::Reader.new(@file_name)
121
+ end
122
+
123
+ # Close the database for reading and writing.
124
+ def close!
125
+ @writer.close!
126
+ @reader.close!
127
+ end
128
+
129
+ # Compact the database to remove stale commits and reduce the file size.
130
+ def compact!
131
+ # Create a new temporary file
132
+ tmp_file = Tempfile.new File.basename(@file_name)
133
+ copy_db = DB.new tmp_file.path
134
+
135
+ # Copy the database key by key into the temporary table
136
+ each do |key, i|
137
+ copy_db.set(key, get(key))
138
+ end
139
+ copy_db.close!
140
+
141
+ # Empty this database
142
+ empty!
143
+
144
+ # Move the copy into place
145
+ tmp_file.close
146
+ FileUtils.mv tmp_file.path, @file_name
147
+ tmp_file.unlink
148
+
149
+ # Reset this database
150
+ close!
151
+ reset!
152
+ read!
153
+ end
154
+
155
+ # Read all values from the log file. If you want to check for changed data
156
+ # call this again.
157
+ def read!
158
+ @reader.read do |record|
159
+ @table[record.key] = parse record.data
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,14 @@
1
+ module Daybreak
2
+ # File locking mixin
3
+ module Locking
4
+ # Lock a file with the type <tt>lock</tt>
5
+ def lock(fd, lock=File::LOCK_SH)
6
+ fd.flock lock
7
+ begin
8
+ yield
9
+ ensure
10
+ fd.flock File::LOCK_UN
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,39 @@
1
+ module Daybreak
2
+ # Class for building out the table, you shouldn't need to access this
3
+ # class directly. Readers are responsible for reading each record in
4
+ # the file and yeilding the parsed records.
5
+ class Reader
6
+ # @param [String] file the file to read from
7
+ def initialize(file)
8
+ @file_name = file
9
+ end
10
+
11
+ # Close's the Reader's file descriptor
12
+ def close!
13
+ @fd.close unless @fd.nil?
14
+ end
15
+
16
+ # Right now this is really expensive, every call to read will
17
+ # close and reread the whole db file, but since cross process
18
+ # consistency is handled by the user, this should be fair warning.
19
+ def read(&blk)
20
+ reopen!
21
+ while !@fd.eof?
22
+ blk.call(Record.read(@fd))
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def reopen!
29
+ close!
30
+ open!
31
+ end
32
+
33
+ def open!
34
+ @fd = File.open @file_name, 'r'
35
+ @fd.binmode
36
+ @fd.advise(:sequential) if @fd.respond_to? :advise
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,64 @@
1
+ module Daybreak
2
+ # Records define how data is serialized and read from disk.
3
+ class Record
4
+ # Thrown when either key or data is missing
5
+ class UnnacceptableDataError < Exception; end
6
+
7
+ # Thrown when there is a CRC mismatch between the data from the disk
8
+ # and what was written to disk previously.
9
+ class CorruptDataError < Exception; end
10
+ include Locking
11
+
12
+ attr_accessor :key, :data
13
+
14
+ def initialize(key = nil, data = nil)
15
+ @key = key
16
+ @data = data
17
+ end
18
+
19
+ # Read a record from an open io source, check the CRC, and set @key and @data
20
+ # @param [#read] io an IO instance to read from
21
+ def read(io)
22
+ lock io do
23
+ @key = read_bytes(io)
24
+ @data = read_bytes(io)
25
+ crc = io.read(4)
26
+ raise CorruptDataError, "CRC mismatch #{crc} should be #{crc_string}" unless crc == crc_string
27
+ end
28
+ self
29
+ end
30
+
31
+ # The serialized representation of the key value pair plus the CRC
32
+ # @return [String]
33
+ def representation
34
+ raise UnnacceptableDataError, "key and data must be defined" if @key.nil? || @data.nil?
35
+ byte_string + crc_string
36
+ end
37
+
38
+ # Create a new record to read from IO.
39
+ # @param [#read] io an IO instance to read from
40
+ def self.read(io)
41
+ new.read(io)
42
+ end
43
+
44
+ private
45
+
46
+ def byte_string
47
+ @byte_string ||= part(@key) + part(@data)
48
+ end
49
+
50
+ def crc_string
51
+ [Zlib.crc32(byte_string, 0)].pack('N')
52
+ end
53
+
54
+ def read_bytes(io)
55
+ raw = io.read(4)
56
+ length = raw.unpack('N')[0]
57
+ io.read(length)
58
+ end
59
+
60
+ def part(data)
61
+ [data.bytesize].pack('N') + data
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,4 @@
1
+ module Daybreak
2
+ # Updated using SemVer
3
+ VERSION = "0.0.1"
4
+ end
@@ -0,0 +1,110 @@
1
+ module Daybreak
2
+ # Writer's handle the actually fiddly task of committing data to disk.
3
+ # They have a Worker instance that writes in a select loop.
4
+ class Writer
5
+ # Open up the file, ready it for binary and nonblocking writing.
6
+ def initialize(file)
7
+ @fd = File.open file, 'a'
8
+ @fd.binmode
9
+
10
+ f = @fd.fcntl(Fcntl::F_GETFL, 0)
11
+ @fd.fcntl(Fcntl::F_SETFL, Fcntl::O_NONBLOCK | f)
12
+
13
+ @worker = Worker.new(@fd)
14
+ end
15
+
16
+ # Send a record to the workers queue.
17
+ def write(record)
18
+ @worker.enqueue record
19
+ end
20
+
21
+ # Finish writing
22
+ def finish!
23
+ @worker.finish!
24
+ end
25
+
26
+ # Flush pending commits, and restart the worker.
27
+ def flush!
28
+ @worker.flush!
29
+ end
30
+
31
+ # Finish writing and close the file descriptor
32
+ def close!
33
+ finish!
34
+ @fd.close
35
+ end
36
+
37
+ # Truncate the file.
38
+ def truncate!
39
+ @fd.truncate(0)
40
+ end
41
+
42
+ private
43
+
44
+ # Workers handle the actual fiddly bits of asynchronous io and
45
+ # and handle background writes.
46
+ class Worker
47
+ include Locking
48
+
49
+ def initialize(fd)
50
+ @queue = Queue.new
51
+ @fd = fd
52
+ @buffer = ""
53
+ @thread = Thread.new { work }
54
+ at_exit { finish! }
55
+ end
56
+
57
+ # Queue up a write to be committed later.
58
+ def enqueue(record)
59
+ @queue << record.representation
60
+ end
61
+
62
+ # Loop and block if we don't have work to do or if
63
+ # the file isn't ready for another write just yet.
64
+ def work
65
+ buf = ""
66
+ loop do
67
+ str = @queue.pop
68
+ if str.nil?
69
+ @fd.flush
70
+ break
71
+ end
72
+ buf << str
73
+ read, write = IO.select [], [@fd]
74
+ if write and fd = write.first
75
+ lock(fd, File::LOCK_EX) { buf = try_write fd, buf }
76
+ end
77
+ end
78
+ end
79
+
80
+ # Try and write the buffer to the file via non blocking file writes.
81
+ # If the write fails try again.
82
+ def try_write(fd, buf)
83
+ begin
84
+ s = fd.write_nonblock(buf)
85
+ if s < buf.length
86
+ buf = buf[s..-1] # didn't finish
87
+ else
88
+ buf = ""
89
+ end
90
+ rescue Errno::EAGAIN
91
+ buf = buf # try this again
92
+ end
93
+ buf
94
+ end
95
+
96
+ # finish! and start up another worker thread.
97
+ def flush!
98
+ finish!
99
+ @thread = Thread.new { work }
100
+ true
101
+ end
102
+
103
+ # Push a nil through the queue and block until the write loop is finished.
104
+ def finish!
105
+ @queue.push nil
106
+ @thread.join
107
+ end
108
+ end
109
+ end
110
+ end
data/test/bench.rb ADDED
@@ -0,0 +1,28 @@
1
+ require File.expand_path(File.dirname(__FILE__)) + '/test_helper.rb'
2
+
3
+ describe "benchmarks" do
4
+ before do
5
+ @db = Daybreak::DB.new DB_PATH
6
+ 1000.times {|i| @db[i] = i }
7
+ @db.flush!
8
+ end
9
+
10
+ bench_performance_constant "keys with sync" do |n|
11
+ n.times {|i| @db.set(i, 'i' * i, true) }
12
+ end
13
+
14
+ bench_performance_constant "inserting keys" do |n|
15
+ n.times {|i| @db[i] = 'i' * i }
16
+ end
17
+
18
+ bench_performance_constant "reading keys" do |n|
19
+ n.times {|i| assert_equal i % 1000, @db[i % 1000] }
20
+ end
21
+
22
+ after do
23
+ @db.empty!
24
+ @db.close!
25
+ File.unlink(DB_PATH)
26
+ end
27
+ end
28
+
data/test/compare.rb ADDED
@@ -0,0 +1,47 @@
1
+ require File.expand_path(File.dirname(__FILE__)) + '/test_helper.rb'
2
+ require 'pstore'
3
+
4
+ describe "compare with pstore" do
5
+ before do
6
+ @pstore = PStore.new(File.join(HERE, "test.pstore"))
7
+ end
8
+
9
+ bench_performance_constant "pstore bulk performance" do |n|
10
+ @pstore.transaction do
11
+ n.times do |i|
12
+ @pstore[i] = 'i' * i
13
+ end
14
+ end
15
+ end
16
+
17
+ after do
18
+ File.unlink File.join(HERE, "test.pstore")
19
+ end
20
+ end
21
+
22
+ require 'dbm'
23
+
24
+ describe "compare with dbm" do
25
+ before do
26
+ @dbm = DBM.open(File.join(HERE, "test-dbm"), 666, DBM::WRCREAT)
27
+ 1000.times {|i| @dbm[i.to_s] = i }
28
+ end
29
+
30
+ bench_performance_constant "DBM write performance" do |n|
31
+ n.times do |i|
32
+ @dbm[i.to_s] = 'i' * i
33
+ end
34
+ end
35
+
36
+ bench_performance_constant "DBM read performance" do |n|
37
+ n.times do |i|
38
+ assert_equal (i % 1000).to_s, @dbm[(i % 1000).to_s]
39
+ end
40
+ end
41
+
42
+ after do
43
+ @dbm.close
44
+
45
+ File.unlink File.join(HERE, "test-dbm.db")
46
+ end
47
+ end
data/test/prof.rb ADDED
@@ -0,0 +1,14 @@
1
+ require File.expand_path(File.dirname(__FILE__)) + '/test_helper.rb'
2
+
3
+ require 'ruby-prof'
4
+
5
+ result = RubyProf.profile do
6
+ db = Daybreak::DB.new './t.db'
7
+ 1000.times {|n| db[n] = n}
8
+ db.flush!
9
+ end
10
+
11
+ File.unlink './t.db'
12
+ printer = RubyProf::MultiPrinter.new(result)
13
+ FileUtils.mkdir('./profile') unless File.exists? './profile'
14
+ printer.print :path => './profile', :profile => 'profile'
data/test/test.rb ADDED
@@ -0,0 +1,81 @@
1
+ require 'rubygems'
2
+ require 'simplecov'
3
+ require 'set'
4
+ SimpleCov.start
5
+ SimpleCov.command_name "Unit tests"
6
+
7
+ require File.expand_path(File.dirname(__FILE__)) + '/test_helper.rb'
8
+
9
+ describe "database functions" do
10
+ before do
11
+ @db = Daybreak::DB.new DB_PATH
12
+ end
13
+
14
+ it "should insert" do
15
+ @db[1] = 1
16
+ assert_equal @db[1], 1
17
+ assert @db.has_key?(1)
18
+ @db[1] = '2'
19
+ assert_equal @db[1], '2'
20
+ assert_equal @db.length, 1
21
+ end
22
+
23
+ it "should persist values" do
24
+ @db.set('1', '4', true)
25
+ @db.set('4', '1', true)
26
+
27
+ assert_equal @db['1'], '4'
28
+ db2 = Daybreak::DB.new DB_PATH
29
+ assert_equal db2['1'], '4'
30
+ assert_equal db2['4'], '1'
31
+ db2.close!
32
+ end
33
+
34
+ it "should compact cleanly" do
35
+ @db[1] = 1
36
+ @db[1] = 1
37
+ @db.flush!
38
+ size = File.stat(DB_PATH).size
39
+ @db.compact!
40
+ assert_equal @db[1], 1
41
+ assert size > File.stat(DB_PATH).size
42
+ end
43
+
44
+ it "should allow for default values" do
45
+ default_db = Daybreak::DB.new(DB_PATH, 0)
46
+ assert_equal default_db[1], 0
47
+ default_db[1] = 1
48
+ assert_equal default_db[1], 1
49
+ end
50
+
51
+ it "should handle default values that are procs" do
52
+ db = Daybreak::DB.new(DB_PATH) {|key| Set.new }
53
+ assert db['foo'].is_a? Set
54
+ end
55
+
56
+ it "should be able to sync competing writes" do
57
+ @db.set! '1', 4
58
+ db2 = Daybreak::DB.new DB_PATH
59
+ db2.set! '1', 5
60
+ @db.read!
61
+ assert_equal @db['1'], 5
62
+ @db.close!
63
+ end
64
+
65
+ it " should be able to handle another process's call to compact" do
66
+ 20.times {|i| @db.set i, i, true }
67
+ db2 = Daybreak::DB.new DB_PATH
68
+ 20.times {|i| @db.set i, i + 1, true }
69
+ @db.compact!
70
+ db2.read!
71
+ assert_equal 20, db2['19']
72
+ end
73
+
74
+ after do
75
+ @db.empty!
76
+ @db.close!
77
+ File.unlink(DB_PATH)
78
+ end
79
+ end
80
+
81
+
@@ -0,0 +1,7 @@
1
+ require 'minitest/autorun'
2
+ require 'minitest/benchmark'
3
+
4
+ HERE = File.expand_path(File.dirname(__FILE__))
5
+ DB_PATH = File.join HERE, "test.db"
6
+
7
+ require File.join HERE, '..', 'lib', 'daybreak'
metadata ADDED
@@ -0,0 +1,104 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: daybreak
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Jeff Larson
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-11-19 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: simplecov
16
+ requirement: !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: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: ruby-prof
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ description: A simple dimple key-value store for ruby.
47
+ email:
48
+ - thejefflarson@gmail.com
49
+ executables: []
50
+ extensions: []
51
+ extra_rdoc_files: []
52
+ files:
53
+ - .gitignore
54
+ - .yardopts
55
+ - EPIGRAPH
56
+ - Gemfile
57
+ - LICENSE
58
+ - README
59
+ - Rakefile
60
+ - daybreak.gemspec
61
+ - lib/daybreak.rb
62
+ - lib/daybreak/db.rb
63
+ - lib/daybreak/locking.rb
64
+ - lib/daybreak/reader.rb
65
+ - lib/daybreak/record.rb
66
+ - lib/daybreak/version.rb
67
+ - lib/daybreak/writer.rb
68
+ - test/bench.rb
69
+ - test/compare.rb
70
+ - test/prof.rb
71
+ - test/test.rb
72
+ - test/test_helper.rb
73
+ homepage: http://propublica.github.com/daybreak/
74
+ licenses: []
75
+ post_install_message:
76
+ rdoc_options: []
77
+ require_paths:
78
+ - lib
79
+ required_ruby_version: !ruby/object:Gem::Requirement
80
+ none: false
81
+ requirements:
82
+ - - ! '>='
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ required_rubygems_version: !ruby/object:Gem::Requirement
86
+ none: false
87
+ requirements:
88
+ - - ! '>='
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ requirements: []
92
+ rubyforge_project:
93
+ rubygems_version: 1.8.24
94
+ signing_key:
95
+ specification_version: 3
96
+ summary: Daybreak provides an in memory key-value store that is easily enumerable
97
+ in ruby.
98
+ test_files:
99
+ - test/bench.rb
100
+ - test/compare.rb
101
+ - test/prof.rb
102
+ - test/test.rb
103
+ - test/test_helper.rb
104
+ has_rdoc: