daybreak 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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: