memorydb 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/memorydb.rb +9 -0
- data/lib/memorydb/base.rb +55 -0
- data/lib/memorydb/cursor.rb +168 -0
- data/lib/memorydb/db.rb +206 -0
- data/lib/memorydb/filter.rb +13 -0
- data/lib/memorydb/filter_factory.rb +87 -0
- data/lib/memorydb/sort.rb +12 -0
- metadata +132 -0
data/lib/memorydb.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'time'
|
2
|
+
require 'memorydb/cursor'
|
3
|
+
require 'memorydb/filter_factory'
|
4
|
+
|
5
|
+
module MemoryDb
|
6
|
+
class Base
|
7
|
+
|
8
|
+
def find
|
9
|
+
cursor
|
10
|
+
end
|
11
|
+
|
12
|
+
def filter
|
13
|
+
@filter_factory ||= FilterFactory.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def remove_by_id!(id)
|
17
|
+
if model = find_by_id(id)
|
18
|
+
remove_model!(model)
|
19
|
+
else
|
20
|
+
raise ArgumentError.new("Could not remove record with id: #{id} because it does not exist")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def remove!(model=nil)
|
25
|
+
if model
|
26
|
+
remove_by_id!(model.send(primary_key))
|
27
|
+
else
|
28
|
+
cursor.remove!
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
attr_reader :model_klass
|
35
|
+
|
36
|
+
def cursor
|
37
|
+
Cursor.new(self)
|
38
|
+
end
|
39
|
+
|
40
|
+
def model_or_hash_as_attrs(model_or_hash)
|
41
|
+
if model_or_hash
|
42
|
+
if model_or_hash.is_a?(Hash)
|
43
|
+
model_or_hash
|
44
|
+
elsif model_or_hash.is_a?(model_klass)
|
45
|
+
model_or_hash.attributes
|
46
|
+
else
|
47
|
+
raise ArgumentError.new("A hash or a #{model_klass} must be given to create a record")
|
48
|
+
end
|
49
|
+
else
|
50
|
+
{}
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,168 @@
|
|
1
|
+
require 'memorydb/filter'
|
2
|
+
require 'memorydb/sort'
|
3
|
+
require 'memorydb/filter_factory'
|
4
|
+
|
5
|
+
module MemoryDb
|
6
|
+
class Cursor
|
7
|
+
|
8
|
+
def initialize(query_executor)
|
9
|
+
@query_executor = query_executor
|
10
|
+
@filters = []
|
11
|
+
@sorts = []
|
12
|
+
end
|
13
|
+
|
14
|
+
def eq(field, value)
|
15
|
+
@filters << filter_factory.eq(field, value)
|
16
|
+
self
|
17
|
+
end
|
18
|
+
|
19
|
+
def not_eq(field, value)
|
20
|
+
@filters << filter_factory.not_eq(field, value)
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
def lt(field, value)
|
25
|
+
@filters << filter_factory.lt(field, value)
|
26
|
+
self
|
27
|
+
end
|
28
|
+
|
29
|
+
def lte(field, value)
|
30
|
+
@filters << filter_factory.lte(field, value)
|
31
|
+
self
|
32
|
+
end
|
33
|
+
|
34
|
+
def gt(field, value)
|
35
|
+
@filters << filter_factory.gt(field, value)
|
36
|
+
self
|
37
|
+
end
|
38
|
+
|
39
|
+
def gte(field, value)
|
40
|
+
@filters << filter_factory.gte(field, value)
|
41
|
+
self
|
42
|
+
end
|
43
|
+
|
44
|
+
def in(field, value)
|
45
|
+
@filters << filter_factory.in(field, value)
|
46
|
+
self
|
47
|
+
end
|
48
|
+
|
49
|
+
def not_in(field, value)
|
50
|
+
@filters << filter_factory.not_in(field, value)
|
51
|
+
self
|
52
|
+
end
|
53
|
+
|
54
|
+
def like(field, value)
|
55
|
+
@filters << filter_factory.like(field, value)
|
56
|
+
self
|
57
|
+
end
|
58
|
+
|
59
|
+
def or(*filters)
|
60
|
+
@filters << filter_factory.or(*filters)
|
61
|
+
self
|
62
|
+
end
|
63
|
+
|
64
|
+
def sort(field, order)
|
65
|
+
assert_field!(field)
|
66
|
+
assert_order!(order)
|
67
|
+
@sorts << Sort.new(field, order.to_sym)
|
68
|
+
self
|
69
|
+
end
|
70
|
+
|
71
|
+
def limit(limit)
|
72
|
+
if limit
|
73
|
+
assert_int!('Limit', limit)
|
74
|
+
@limit = limit.to_i
|
75
|
+
end
|
76
|
+
self
|
77
|
+
end
|
78
|
+
|
79
|
+
def offset(offset)
|
80
|
+
if offset
|
81
|
+
assert_int!('Offset', offset)
|
82
|
+
@offset = offset.to_i
|
83
|
+
end
|
84
|
+
self
|
85
|
+
end
|
86
|
+
|
87
|
+
def count
|
88
|
+
query_executor.execute_count(query)
|
89
|
+
end
|
90
|
+
|
91
|
+
def all
|
92
|
+
query_executor.execute_find(query)
|
93
|
+
end
|
94
|
+
|
95
|
+
def first
|
96
|
+
query_executor.execute_find(
|
97
|
+
query.merge(
|
98
|
+
sorts: sorts_for_first,
|
99
|
+
limit: 1
|
100
|
+
)).first
|
101
|
+
end
|
102
|
+
|
103
|
+
def last
|
104
|
+
query_executor.execute_find(
|
105
|
+
query.merge(
|
106
|
+
sorts: sorts_for_last,
|
107
|
+
limit: 1
|
108
|
+
)).first
|
109
|
+
end
|
110
|
+
|
111
|
+
def remove!
|
112
|
+
query_executor.execute_remove!(query)
|
113
|
+
end
|
114
|
+
|
115
|
+
def first
|
116
|
+
query_executor.execute_find(
|
117
|
+
query.merge(
|
118
|
+
limit: 1
|
119
|
+
)).first
|
120
|
+
end
|
121
|
+
|
122
|
+
def query
|
123
|
+
{
|
124
|
+
:type => @type,
|
125
|
+
:filters => @filters,
|
126
|
+
:sorts => @sorts,
|
127
|
+
:offset => @offset,
|
128
|
+
:limit => @limit
|
129
|
+
}
|
130
|
+
end
|
131
|
+
|
132
|
+
private
|
133
|
+
|
134
|
+
attr_reader :query_executor
|
135
|
+
|
136
|
+
def filter_factory
|
137
|
+
@filter_factory ||= FilterFactory.new
|
138
|
+
end
|
139
|
+
|
140
|
+
def sorts_for_first
|
141
|
+
@sorts
|
142
|
+
end
|
143
|
+
|
144
|
+
def sorts_for_last
|
145
|
+
sorts_for_first.map do |sort|
|
146
|
+
Sort.new(sort.field, sort.order == :asc ? :desc : :asc)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def assert_field!(field)
|
151
|
+
unless field.is_a?(String) || field.is_a?(Symbol)
|
152
|
+
raise ArgumentError.new "Field name must be a String or Symbol but you gave #{PP.pp(field, '')}"
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def assert_order!(order)
|
157
|
+
unless [:asc, :desc, 'asc', 'desc'].include?(order)
|
158
|
+
raise ArgumentError.new "Sort order must be 'asc' or 'desc' but you gave #{PP.pp(order, '')}"
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def assert_int!(name, num)
|
163
|
+
unless num.is_a?(Integer) || (num.is_a?(String) && num =~ /\d+/)
|
164
|
+
raise ArgumentError.new "#{name} must be an integer but you gave #{PP.pp(num, '')}"
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
data/lib/memorydb/db.rb
ADDED
@@ -0,0 +1,206 @@
|
|
1
|
+
require 'multi_json'
|
2
|
+
require 'memorydb/base'
|
3
|
+
require 'memorydb/cursor'
|
4
|
+
|
5
|
+
# for sorting on booleans
|
6
|
+
class FalseClass
|
7
|
+
def <(other)
|
8
|
+
other
|
9
|
+
end
|
10
|
+
|
11
|
+
def >(other)
|
12
|
+
false
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class TrueClass
|
17
|
+
def <(other)
|
18
|
+
other
|
19
|
+
end
|
20
|
+
|
21
|
+
def >(other)
|
22
|
+
true
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
module MemoryDb
|
27
|
+
class Db < Base
|
28
|
+
|
29
|
+
def initialize(model_klass, options={})
|
30
|
+
@model_klass = model_klass
|
31
|
+
@id = 0
|
32
|
+
@store = {}
|
33
|
+
@primary_key = options[:primary_key]
|
34
|
+
end
|
35
|
+
|
36
|
+
def create!(attrs={})
|
37
|
+
_id = id
|
38
|
+
attrs = model_or_hash_as_attrs(attrs)
|
39
|
+
verify_attributes!(attrs)
|
40
|
+
attributes = attrs.merge(primary_key => _id)
|
41
|
+
store!(_id, attributes)
|
42
|
+
model_klass.new(attributes)
|
43
|
+
end
|
44
|
+
|
45
|
+
def update!(model_or_id, attributes={})
|
46
|
+
attributes ||= {}
|
47
|
+
model = case
|
48
|
+
when model_or_id.is_a?(model_klass)
|
49
|
+
if model = find_by_id(model_or_id.send(primary_key))
|
50
|
+
model_or_id
|
51
|
+
else
|
52
|
+
raise ArgumentError.new("Could not update record with id: #{model_or_id.send(primary_key)} because it does not exist") unless model
|
53
|
+
end
|
54
|
+
|
55
|
+
else
|
56
|
+
if model = find_by_id(model_or_id)
|
57
|
+
model
|
58
|
+
else
|
59
|
+
raise ArgumentError.new("Could not update record with id: #{model_or_id} because it does not exist") unless model
|
60
|
+
end
|
61
|
+
end
|
62
|
+
updated_attrs = model.attributes.merge(attributes_without_pkey(attributes))
|
63
|
+
store!(model.send(primary_key), updated_attrs)
|
64
|
+
model_klass.new(updated_attrs)
|
65
|
+
end
|
66
|
+
|
67
|
+
def find_by_id(id)
|
68
|
+
find.eq(primary_key, id).first
|
69
|
+
end
|
70
|
+
|
71
|
+
###########################
|
72
|
+
# These methods are called by the cursor. Do not call them directly. Or call them. Whatever. Either one.
|
73
|
+
|
74
|
+
def raw_find(query)
|
75
|
+
models = all_models
|
76
|
+
models = apply_transforms models, query[:transforms]
|
77
|
+
models = filter_models models, query[:filters]
|
78
|
+
models = sort_models models, query[:sorts]
|
79
|
+
models = offset_models models, query[:offset]
|
80
|
+
models = limit_models models, query[:limit]
|
81
|
+
end
|
82
|
+
|
83
|
+
def execute_find(query)
|
84
|
+
models = raw_find(query)
|
85
|
+
models.map { |h| model_klass.new(h) }
|
86
|
+
end
|
87
|
+
|
88
|
+
def execute_count(query)
|
89
|
+
execute_find(query).size
|
90
|
+
end
|
91
|
+
|
92
|
+
def execute_remove!(query)
|
93
|
+
execute_find(query).each do |model|
|
94
|
+
remove_model!(model)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
###########################
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
attr_reader :primary_key
|
103
|
+
|
104
|
+
def verify_attributes!(attrs)
|
105
|
+
null_model = model_klass.new
|
106
|
+
allowed_attributes = null_model.attributes.keys.map(&:to_sym)
|
107
|
+
attrs.each do |key, value|
|
108
|
+
unless allowed_attributes.include?(key.to_sym)
|
109
|
+
raise ArgumentError.new("Unknown attribute: #{key}")
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def attributes_without_pkey(attributes)
|
115
|
+
attributes.reject { |k, v| k == primary_key }
|
116
|
+
end
|
117
|
+
|
118
|
+
def apply_transforms(models, transforms)
|
119
|
+
models.map do |model|
|
120
|
+
(transforms || []).reduce(model) do |model, transform|
|
121
|
+
transform.call(model)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def filter_models(models, filters)
|
127
|
+
models.select do |model|
|
128
|
+
(filters || []).all? do |filter|
|
129
|
+
filter_matches?(filter, model)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def filter_matches?(filter, model)
|
135
|
+
value = model[filter.field]
|
136
|
+
case filter.operator
|
137
|
+
when '='; value == filter.value
|
138
|
+
when '!='; value != filter.value
|
139
|
+
when '<'; value && value < filter.value
|
140
|
+
when '<='; value && value <= filter.value
|
141
|
+
when '>'; value && value > filter.value
|
142
|
+
when '>='; value && value >= filter.value
|
143
|
+
when 'in'; filter.value.include?(value)
|
144
|
+
when '!in'; !filter.value.include?(value)
|
145
|
+
when 'or'; filter.value.any? do |sub_filter|
|
146
|
+
filter_matches?(sub_filter, model)
|
147
|
+
end
|
148
|
+
when 'like'; value =~ /#{filter.value}/i
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def sort_models(models, sorts)
|
153
|
+
models.sort { |model1, model2| compare_models(model1, model2, sorts) }
|
154
|
+
end
|
155
|
+
|
156
|
+
def compare_models(model1, model2, sorts)
|
157
|
+
sorts.each do |sort|
|
158
|
+
result = compare_model(model1, model2, sort)
|
159
|
+
return result if result
|
160
|
+
end
|
161
|
+
0
|
162
|
+
end
|
163
|
+
|
164
|
+
def compare_model(model1, model2, sort)
|
165
|
+
field1, field2 = model1[sort.field], model2[sort.field]
|
166
|
+
field1 == field2 ? nil :
|
167
|
+
field1 < field2 && sort.order == :asc ? -1 :
|
168
|
+
field1 > field2 && sort.order == :desc ? -1 : 1
|
169
|
+
end
|
170
|
+
|
171
|
+
def limit_models(models, limit)
|
172
|
+
if limit
|
173
|
+
models.take(limit)
|
174
|
+
else
|
175
|
+
models
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def offset_models(models, offset)
|
180
|
+
if offset
|
181
|
+
models.drop(offset)
|
182
|
+
else
|
183
|
+
models
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
def all_models
|
188
|
+
@store.values.map do |raw_record|
|
189
|
+
model_klass.new(MultiJson.load(raw_record)).attributes
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def store!(id, attributes)
|
194
|
+
@store[id] = MultiJson.dump(attributes)
|
195
|
+
end
|
196
|
+
|
197
|
+
def remove_model!(model)
|
198
|
+
@store.delete(model.send(primary_key))
|
199
|
+
nil
|
200
|
+
end
|
201
|
+
|
202
|
+
def id
|
203
|
+
@id += 1
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'memorydb/filter'
|
2
|
+
|
3
|
+
module MemoryDb
|
4
|
+
class FilterFactory
|
5
|
+
|
6
|
+
def eq(field, value)
|
7
|
+
assert_field!(field)
|
8
|
+
Filter.new(field, '=', value)
|
9
|
+
end
|
10
|
+
|
11
|
+
def not_eq(field, value)
|
12
|
+
assert_field!(field)
|
13
|
+
Filter.new(field, '!=', value)
|
14
|
+
end
|
15
|
+
|
16
|
+
def lt(field, value)
|
17
|
+
assert_field!(field)
|
18
|
+
assert_not_nil!('Less than', value)
|
19
|
+
Filter.new(field, '<', value)
|
20
|
+
end
|
21
|
+
|
22
|
+
def lte(field, value)
|
23
|
+
assert_field!(field)
|
24
|
+
assert_not_nil!('Less than or equal to', value)
|
25
|
+
Filter.new(field, '<=', value)
|
26
|
+
end
|
27
|
+
|
28
|
+
def gt(field, value)
|
29
|
+
assert_field!(field)
|
30
|
+
assert_not_nil!('Greater than', value)
|
31
|
+
Filter.new(field, '>', value)
|
32
|
+
end
|
33
|
+
|
34
|
+
def gte(field, value)
|
35
|
+
assert_field!(field)
|
36
|
+
assert_not_nil!('Greater than or equal to', value)
|
37
|
+
Filter.new(field, '>=', value)
|
38
|
+
end
|
39
|
+
|
40
|
+
def in(field, value)
|
41
|
+
assert_field!(field)
|
42
|
+
assert_to_a!('Inclusion', value)
|
43
|
+
Filter.new(field, 'in', value.to_a)
|
44
|
+
end
|
45
|
+
|
46
|
+
def not_in(field, value)
|
47
|
+
assert_field!(field)
|
48
|
+
assert_to_a!('Exclusion', value)
|
49
|
+
Filter.new(field, '!in', value.to_a)
|
50
|
+
end
|
51
|
+
|
52
|
+
def like(field, value)
|
53
|
+
assert_field!(field)
|
54
|
+
assert_str_or_sym!(value, 'Value')
|
55
|
+
Filter.new(field, 'like', value)
|
56
|
+
end
|
57
|
+
|
58
|
+
def or(*filters)
|
59
|
+
Filter.new(nil, 'or', filters)
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def assert_to_a!(name, value)
|
65
|
+
unless value.respond_to?(:to_a)
|
66
|
+
raise ArgumentError.new "#{name} filter value must respond to to_a but #{PP.pp(value, '')} does not"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def assert_not_nil!(name, value)
|
71
|
+
if value.nil?
|
72
|
+
raise ArgumentError.new "#{name} filter value cannot be nil"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def assert_field!(field)
|
77
|
+
assert_str_or_sym!(field, 'Field name')
|
78
|
+
end
|
79
|
+
|
80
|
+
def assert_str_or_sym!(value, name)
|
81
|
+
unless value.is_a?(String) || value.is_a?(Symbol)
|
82
|
+
raise ArgumentError.new "#{name} must be a String or Symbol but you gave #{PP.pp(value, '')}"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
metadata
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: memorydb
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Myles Megyesi
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-06-19 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: multi_json
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.7.6
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 1.7.6
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: multi_json
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 1.5.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: 1.5.0
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rake
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 10.0.3
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 10.0.3
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: rspec
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 2.12.0
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 2.12.0
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: virtus
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ~>
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: 0.5.3
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ~>
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: 0.5.3
|
94
|
+
description: An in memory database
|
95
|
+
email:
|
96
|
+
- myles.megyesi@gmail.com
|
97
|
+
executables: []
|
98
|
+
extensions: []
|
99
|
+
extra_rdoc_files: []
|
100
|
+
files:
|
101
|
+
- lib/memorydb/db.rb
|
102
|
+
- lib/memorydb/base.rb
|
103
|
+
- lib/memorydb/filter_factory.rb
|
104
|
+
- lib/memorydb/sort.rb
|
105
|
+
- lib/memorydb/filter.rb
|
106
|
+
- lib/memorydb/cursor.rb
|
107
|
+
- lib/memorydb.rb
|
108
|
+
homepage:
|
109
|
+
licenses: []
|
110
|
+
post_install_message:
|
111
|
+
rdoc_options: []
|
112
|
+
require_paths:
|
113
|
+
- lib
|
114
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
115
|
+
none: false
|
116
|
+
requirements:
|
117
|
+
- - ! '>='
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: '0'
|
120
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
requirements: []
|
127
|
+
rubyforge_project:
|
128
|
+
rubygems_version: 1.8.24
|
129
|
+
signing_key:
|
130
|
+
specification_version: 3
|
131
|
+
summary: An in memory database
|
132
|
+
test_files: []
|