repositories 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,238 @@
1
+ require 'active_record'
2
+ require 'repository/base'
3
+
4
+ module Repository
5
+ class ActiveRecord < Base
6
+
7
+ def initialize(model_klass, options={})
8
+ @model_klass = model_klass
9
+ @domain_model_klass = options[:domain_model_klass]
10
+ end
11
+
12
+ def create!(model_or_hash={})
13
+ attrs = model_or_hash_as_attrs(model_or_hash)
14
+ build_domain_model(model_klass.create!(attrs))
15
+ end
16
+
17
+ def update!(model_or_id, attrs={})
18
+ attributes = attrs || {}
19
+ case
20
+ when model_or_id.is_a?(model_klass)
21
+ if model_or_id.persisted?
22
+ model_or_id.update_attributes(attributes)
23
+ build_domain_model(model_or_id)
24
+ else
25
+ raise ArgumentError.new("Could not update record with id: #{model_or_id.send(primary_key)} because it does not exist")
26
+ end
27
+ when model_or_id.is_a?(domain_model_klass)
28
+ id = model_or_id.send(primary_key)
29
+ if model = model_klass.where(primary_key => id).first
30
+ update!(model, model_or_id.attributes.merge(attributes))
31
+ else
32
+ raise ArgumentError.new("Could not update record with id: #{id} because it does not exist")
33
+ end
34
+ else
35
+ if model = model_klass.where(primary_key => model_or_id).first
36
+ update!(model, attributes)
37
+ else
38
+ raise ArgumentError.new("Could not update record with id: #{model_or_id} because it does not exist")
39
+ end
40
+ end
41
+ end
42
+
43
+ def find_by_id(id)
44
+ self.find.eq(model_klass.primary_key, id).first
45
+ end
46
+
47
+ def execute_find(query)
48
+ scope = apply_filters model_klass, query[:filters]
49
+ scope = apply_sorts scope, query[:sorts]
50
+ scope = apply_limit scope, query[:limit]
51
+ scope = apply_offset scope, query[:offset]
52
+ scope.all.map { |m| build_domain_model(m) }
53
+ end
54
+
55
+ def execute_count(query)
56
+ apply_filters(model_klass, query[:filters]).count
57
+ end
58
+
59
+ def execute_remove!(query)
60
+ apply_filters(model_klass, query[:filters]).delete_all
61
+ end
62
+
63
+ private
64
+
65
+ attr_reader :domain_model_klass
66
+
67
+ def update_data(model_or_id)
68
+ case
69
+ when model_or_id.is_a?(model_klass)
70
+ [model_or_id.send(primary_key), model_or_id.attributes.slice(*model_or_id.changed)]
71
+ when model_or_id.is_a?(domain_model_klass)
72
+ [model_or_id.send(primary_key), model_or_id.attributes]
73
+ else
74
+ [model_or_id, {}]
75
+ end
76
+ end
77
+
78
+ def build_domain_model(model)
79
+ if domain_model_klass
80
+ domain_model_klass.new(model.attributes)
81
+ else
82
+ model
83
+ end
84
+ end
85
+
86
+ def apply_filters(scope, filters)
87
+ apply_filter(scope, Filter.new(nil, 'and', filters))
88
+ end
89
+
90
+ class FakeScope
91
+
92
+ attr_reader :wheres, :variables
93
+
94
+ def initialize
95
+ @wheres = []
96
+ @variables = []
97
+ end
98
+
99
+ def where(sql, *varaiables)
100
+ @wheres << sql
101
+ @variables += varaiables
102
+ self
103
+ end
104
+
105
+ end
106
+
107
+ def apply_filter(scope, filter)
108
+ column = quoted_column(filter.field)
109
+ case filter.operator
110
+ when '='
111
+ if filter.value
112
+ scope.where("#{column} = ?", filter.value)
113
+ else
114
+ scope.where("#{column} IS NULL")
115
+ end
116
+ when '!='
117
+ if filter.value
118
+ scope.where("#{column} <> ? OR #{column} IS NULL", filter.value)
119
+ else
120
+ scope.where("#{column} IS NOT NULL")
121
+ end
122
+ when '<'
123
+ scope.where("#{column} < ?", filter.value)
124
+ when '<='
125
+ scope.where("#{column} <= ?", filter.value)
126
+ when '>'
127
+ scope.where("#{column} > ?", filter.value)
128
+ when '>='
129
+ scope.where("#{column} >= ?", filter.value)
130
+ when 'in'
131
+ apply_contains_filter(scope, column, filter.value, '', lambda { |str|
132
+ "(#{str} AND #{column} IS NOT NULL)"
133
+ }, lambda { |str|
134
+ "(#{str} OR #{column} IS NULL)"
135
+ })
136
+ when '!in'
137
+ non_nil_values, nil_values = filter.value.uniq.partition { |val| !val.nil? }
138
+ case
139
+ when non_nil_values.empty? && nil_values.empty?
140
+ scope
141
+ when non_nil_values.empty? && !nil_values.empty?
142
+ scope.where("#{column} IS NOT NULL")
143
+ when !non_nil_values.empty? && nil_values.empty?
144
+ scope.where("(#{column} NOT IN (?) OR #{column} IS NULL)", non_nil_values)
145
+ when !non_nil_values.empty? && !nil_values.empty?
146
+ scope.where("(#{column} NOT IN (?) AND #{column} IS NOT NULL)", non_nil_values)
147
+ end
148
+ when 'like'
149
+ scope.where("#{column} LIKE #{glob(filter.value)}")
150
+ when 'or'
151
+ apply_and_join_filters(scope, filter.value, ' OR ')
152
+ when 'and'
153
+ apply_and_join_filters(scope, filter.value, ' AND ')
154
+ else
155
+ scope
156
+ end
157
+ end
158
+
159
+ def apply_and_join_filters(scope, filters, join)
160
+ fake_scope = FakeScope.new
161
+ filters.each do |filter|
162
+ apply_filter(fake_scope, filter)
163
+ end
164
+ sql = fake_scope.wheres.join(join)
165
+ sql = "(#{sql})" if fake_scope.wheres.size > 1
166
+ scope.where(sql, *fake_scope.variables)
167
+ end
168
+
169
+ def apply_contains_filter(scope, column, values, _not, if_nil, if_not_nil)
170
+ non_nil_values, nil_values = values.uniq.partition { |val| !val.nil? }
171
+ str = "#{column} #{_not} IN (?)"
172
+ if nil_values.empty?
173
+ str = if_nil.call(str)
174
+ else
175
+ str = if_not_nil.call(str)
176
+ end
177
+ scope.where(str, non_nil_values)
178
+ end
179
+
180
+ def apply_sorts(scope, sorts)
181
+ if sorts.empty?
182
+ scope
183
+ else
184
+ ar_sorts = sorts.reduce([]) do |ar_sorts, sort|
185
+ ar_sorts << "#{quoted_column(sort.field)} #{sort.order}"
186
+ ar_sorts
187
+ end.join(', ')
188
+ scope.order ar_sorts
189
+ end
190
+ end
191
+
192
+ def apply_limit(scope, limit)
193
+ if limit
194
+ scope.limit(limit)
195
+ else
196
+ scope
197
+ end
198
+ end
199
+
200
+ def apply_offset(scope, offset)
201
+ if offset
202
+ scope.offset(offset)
203
+ else
204
+ scope
205
+ end
206
+ end
207
+
208
+ def quoted_column(column)
209
+ ::ActiveRecord::Base.connection.quote_column_name(column)
210
+ end
211
+
212
+ def quoted_value(value)
213
+ ::ActiveRecord::Base.connection.quote(value)
214
+ end
215
+
216
+ def glob(k)
217
+ quoted_value("%#{k}%")
218
+ end
219
+
220
+ def store_record!(attrs)
221
+ record = attrs.dup
222
+ if id = record.delete(primary_key)
223
+ model_klass.update_all(record, {primary_key => id}, {limit: 1})
224
+ else
225
+ model_klass.create!(record)
226
+ end
227
+ end
228
+
229
+ def primary_key
230
+ model_klass.primary_key
231
+ end
232
+
233
+ def remove_model!(model)
234
+ model_klass.delete_all(id: model.id)
235
+ end
236
+
237
+ end
238
+ end
@@ -0,0 +1,55 @@
1
+ require 'time'
2
+ require 'repository/cursor'
3
+ require 'repository/filter_factory'
4
+
5
+ module Repository
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 'repository/filter'
2
+ require 'repository/sort'
3
+ require 'repository/filter_factory'
4
+
5
+ module Repository
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
@@ -0,0 +1,13 @@
1
+ module Repository
2
+ class Filter
3
+
4
+ attr_reader :field, :operator, :value
5
+
6
+ def initialize(field, operator, value)
7
+ @field = field
8
+ @operator = operator
9
+ @value = value
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,87 @@
1
+ require 'repository/filter'
2
+
3
+ module Repository
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
@@ -0,0 +1,206 @@
1
+ require 'multi_json'
2
+ require 'repository/base'
3
+ require 'repository/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 Repository
27
+ class Memory < 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,12 @@
1
+ module Repository
2
+ class Sort
3
+
4
+ attr_reader :field, :order
5
+
6
+ def initialize(field, order)
7
+ @field = field
8
+ @order = order
9
+ end
10
+
11
+ end
12
+ end
metadata ADDED
@@ -0,0 +1,196 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: repositories
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-25 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activerecord
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 3.2.11
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: 3.2.11
30
+ - !ruby/object:Gem::Dependency
31
+ name: bson_ext
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 1.8.6
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.8.6
46
+ - !ruby/object:Gem::Dependency
47
+ name: database_cleaner
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: 0.9.1
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: 0.9.1
62
+ - !ruby/object:Gem::Dependency
63
+ name: mongo
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: 1.8.1
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: 1.8.1
78
+ - !ruby/object:Gem::Dependency
79
+ name: multi_json
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ~>
84
+ - !ruby/object:Gem::Version
85
+ version: 1.5.0
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: 1.5.0
94
+ - !ruby/object:Gem::Dependency
95
+ name: rake
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ~>
100
+ - !ruby/object:Gem::Version
101
+ version: 10.0.3
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ~>
108
+ - !ruby/object:Gem::Version
109
+ version: 10.0.3
110
+ - !ruby/object:Gem::Dependency
111
+ name: rspec
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ~>
116
+ - !ruby/object:Gem::Version
117
+ version: 2.12.0
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ~>
124
+ - !ruby/object:Gem::Version
125
+ version: 2.12.0
126
+ - !ruby/object:Gem::Dependency
127
+ name: sqlite3
128
+ requirement: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ~>
132
+ - !ruby/object:Gem::Version
133
+ version: 1.3.6
134
+ type: :development
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ~>
140
+ - !ruby/object:Gem::Version
141
+ version: 1.3.6
142
+ - !ruby/object:Gem::Dependency
143
+ name: virtus
144
+ requirement: !ruby/object:Gem::Requirement
145
+ none: false
146
+ requirements:
147
+ - - ~>
148
+ - !ruby/object:Gem::Version
149
+ version: 0.5.3
150
+ type: :development
151
+ prerelease: false
152
+ version_requirements: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ~>
156
+ - !ruby/object:Gem::Version
157
+ version: 0.5.3
158
+ description: Repository?
159
+ email:
160
+ - myles.megyesi@gmail.com
161
+ executables: []
162
+ extensions: []
163
+ extra_rdoc_files: []
164
+ files:
165
+ - lib/repository/memory.rb
166
+ - lib/repository/base.rb
167
+ - lib/repository/filter_factory.rb
168
+ - lib/repository/sort.rb
169
+ - lib/repository/filter.rb
170
+ - lib/repository/active_record.rb
171
+ - lib/repository/cursor.rb
172
+ homepage:
173
+ licenses: []
174
+ post_install_message:
175
+ rdoc_options: []
176
+ require_paths:
177
+ - lib
178
+ required_ruby_version: !ruby/object:Gem::Requirement
179
+ none: false
180
+ requirements:
181
+ - - ! '>='
182
+ - !ruby/object:Gem::Version
183
+ version: '0'
184
+ required_rubygems_version: !ruby/object:Gem::Requirement
185
+ none: false
186
+ requirements:
187
+ - - ! '>='
188
+ - !ruby/object:Gem::Version
189
+ version: '0'
190
+ requirements: []
191
+ rubyforge_project:
192
+ rubygems_version: 1.8.24
193
+ signing_key:
194
+ specification_version: 3
195
+ summary: Store some data?
196
+ test_files: []