remodel 0.1.3 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.3
1
+ 0.1.4
@@ -0,0 +1,206 @@
1
+ module Remodel
2
+
3
+ # The superclass of all persistent remodel entities.
4
+ class Entity
5
+ attr_accessor :key
6
+
7
+ def initialize(attributes = {}, key = nil)
8
+ @attributes = {}
9
+ @key = key
10
+ attributes = self.class.default_values.merge(attributes) if key.nil?
11
+ attributes.each do |name, value|
12
+ send("#{name}=", value) if respond_to? "#{name}="
13
+ end
14
+ end
15
+
16
+ def id
17
+ key && key.split(':').last.to_i
18
+ end
19
+
20
+ def save
21
+ @key = self.class.next_key unless @key
22
+ Remodel.redis.set(@key, to_json)
23
+ self
24
+ end
25
+
26
+ def update(properties)
27
+ properties.each { |name, value| send("#{name}=", value) }
28
+ save
29
+ end
30
+
31
+ def reload
32
+ raise EntityNotSaved unless @key
33
+ initialize(self.class.parse(self.class.fetch(@key)), @key)
34
+ instance_variables.each do |var|
35
+ remove_instance_variable(var) if var =~ /^@association_/
36
+ end
37
+ self
38
+ end
39
+
40
+ def delete
41
+ raise EntityNotSaved unless @key
42
+ Remodel.redis.del(@key)
43
+ end
44
+
45
+ def as_json
46
+ { :id => id }.merge(@attributes)
47
+ end
48
+
49
+ def to_json
50
+ JSON.generate(self.class.pack(@attributes))
51
+ end
52
+
53
+ def inspect
54
+ properties = @attributes.map { |name, value| "#{name}: #{value.inspect}" }.join(', ')
55
+ "\#<#{self.class.name}(#{id}) #{properties}>"
56
+ end
57
+
58
+ def self.create(attributes = {})
59
+ new(attributes).save
60
+ end
61
+
62
+ def self.find(key)
63
+ key = "#{key_prefix}:#{key}" if key.kind_of? Integer
64
+ restore(key, fetch(key))
65
+ end
66
+
67
+ def self.all
68
+ keys = Remodel.redis.keys("#{key_prefix}:*").select { |k| k =~ /:[0-9]+$/ }
69
+ values = keys.empty? ? [] : Remodel.redis.mget(keys)
70
+ keys.zip(values).map do |key, json|
71
+ restore(key, json) if json
72
+ end.compact
73
+ end
74
+
75
+ def self.restore(key, json)
76
+ new(parse(json), key)
77
+ end
78
+
79
+ protected # --- DSL for subclasses ---
80
+
81
+ def self.set_key_prefix(prefix)
82
+ raise(InvalidKeyPrefix, prefix) unless prefix =~ /^[a-z]+$/
83
+ @key_prefix = prefix
84
+ end
85
+
86
+ def self.property(name, options = {})
87
+ name = name.to_sym
88
+ mapper[name] = Remodel.mapper_for(options[:class])
89
+ default_values[name] = options[:default] if options.has_key?(:default)
90
+ define_method(name) { @attributes[name] }
91
+ define_method("#{name}=") { |value| @attributes[name] = value }
92
+ end
93
+
94
+ def self.has_many(name, options)
95
+ var = "@association_#{name}".to_sym
96
+
97
+ define_method(name) do
98
+ if instance_variable_defined? var
99
+ instance_variable_get(var)
100
+ else
101
+ clazz = Class[options[:class]]
102
+ instance_variable_set(var, HasMany.new(self, clazz, "#{key}:#{name}", options[:reverse]))
103
+ end
104
+ end
105
+ end
106
+
107
+ def self.has_one(name, options)
108
+ var = "@association_#{name}".to_sym
109
+
110
+ define_method(name) do
111
+ if instance_variable_defined? var
112
+ instance_variable_get(var)
113
+ else
114
+ clazz = Class[options[:class]]
115
+ value_key = Remodel.redis.get("#{key}:#{name}")
116
+ instance_variable_set(var, clazz.find(value_key)) if value_key
117
+ end
118
+ end
119
+
120
+ define_method("#{name}=") do |value|
121
+ send("_reverse_association_of_#{name}=", value) if options[:reverse]
122
+ send("_#{name}=", value)
123
+ end
124
+
125
+ define_method("_#{name}=") do |value|
126
+ if value
127
+ instance_variable_set(var, value)
128
+ Remodel.redis.set("#{key}:#{name}", value.key)
129
+ else
130
+ remove_instance_variable(var) if instance_variable_defined? var
131
+ Remodel.redis.del("#{key}:#{name}")
132
+ end
133
+ end; private "_#{name}="
134
+
135
+ if options[:reverse]
136
+ define_method("_reverse_association_of_#{name}=") do |value|
137
+ if old_value = send(name)
138
+ association = old_value.send("#{options[:reverse]}")
139
+ if association.is_a? HasMany
140
+ association.send("_remove", self)
141
+ else
142
+ old_value.send("_#{options[:reverse]}=", nil)
143
+ end
144
+ end
145
+ if value
146
+ association = value.send("#{options[:reverse]}")
147
+ if association.is_a? HasMany
148
+ association.send("_add", self)
149
+ else
150
+ value.send("_#{options[:reverse]}=", self)
151
+ end
152
+ end
153
+ end; private "_reverse_association_of_#{name}="
154
+ end
155
+ end
156
+
157
+ private # --- Helper methods ---
158
+
159
+ def self.fetch(key)
160
+ Remodel.redis.get(key) || raise(EntityNotFound, "no #{name} with key #{key}")
161
+ end
162
+
163
+ # Each entity has its own sequence to generate unique ids.
164
+ def self.next_key
165
+ id = Remodel.redis.incr("#{key_prefix}:seq")
166
+ "#{key_prefix}:#{id}"
167
+ end
168
+
169
+ # Default key prefix is the first letter of the class name, in lowercase.
170
+ def self.key_prefix
171
+ @key_prefix ||= name.split('::').last[0,1].downcase
172
+ end
173
+
174
+ def self.parse(json)
175
+ unpack(JSON.parse(json))
176
+ end
177
+
178
+ def self.pack(attributes)
179
+ result = {}
180
+ attributes.each do |name, value|
181
+ result[name] = mapper[name].pack(value)
182
+ end
183
+ result
184
+ end
185
+
186
+ def self.unpack(attributes)
187
+ result = {}
188
+ attributes.each do |name, value|
189
+ name = name.to_sym
190
+ result[name] = mapper[name].unpack(value)
191
+ end
192
+ result
193
+ end
194
+
195
+ # Lazy init
196
+ def self.mapper
197
+ @mapper ||= {}
198
+ end
199
+
200
+ def self.default_values
201
+ @default_values ||= {}
202
+ end
203
+
204
+ end
205
+
206
+ end
@@ -0,0 +1,67 @@
1
+ module Remodel
2
+
3
+ # Represents the many-end of a many-to-one or many-to-many association.
4
+ class HasMany < Array
5
+ def initialize(this, clazz, key, reverse = nil)
6
+ super _fetch(clazz, key)
7
+ @this, @clazz, @key, @reverse = this, clazz, key, reverse
8
+ end
9
+
10
+ def create(attributes = {})
11
+ add(@clazz.create(attributes))
12
+ end
13
+
14
+ def find(id)
15
+ detect { |x| x.id == id } || raise(EntityNotFound, "no element with id #{id}")
16
+ end
17
+
18
+ def add(entity)
19
+ _add_to_reverse_association_of(entity) if @reverse
20
+ _add(entity)
21
+ end
22
+
23
+ def remove(entity)
24
+ _remove_from_reverse_association_of(entity) if @reverse
25
+ _remove(entity)
26
+ end
27
+
28
+ private
29
+
30
+ def _add(entity)
31
+ self << entity
32
+ Remodel.redis.rpush(@key, entity.key)
33
+ entity
34
+ end
35
+
36
+ def _remove(entity)
37
+ delete_if { |x| x.key == entity.key }
38
+ Remodel.redis.lrem(@key, 0, entity.key)
39
+ entity
40
+ end
41
+
42
+ def _add_to_reverse_association_of(entity)
43
+ if entity.send(@reverse).is_a? HasMany
44
+ entity.send(@reverse).send(:_add, @this)
45
+ else
46
+ entity.send("_#{@reverse}=", @this)
47
+ end
48
+ end
49
+
50
+ def _remove_from_reverse_association_of(entity)
51
+ if entity.send(@reverse).is_a? HasMany
52
+ entity.send(@reverse).send(:_remove, @this)
53
+ else
54
+ entity.send("_#{@reverse}=", nil)
55
+ end
56
+ end
57
+
58
+ def _fetch(clazz, key)
59
+ keys = Remodel.redis.lrange(key, 0, -1)
60
+ values = keys.empty? ? [] : Remodel.redis.mget(keys)
61
+ keys.zip(values).map do |key, json|
62
+ clazz.restore(key, json) if json
63
+ end.compact
64
+ end
65
+ end
66
+
67
+ end
@@ -0,0 +1,57 @@
1
+ module Remodel
2
+
3
+ # Represents the one-end of a many-to-one or one-to-one association.
4
+ class HasOne
5
+ def initialize(this, clazz, key, reverse = nil)
6
+ @this, @clazz, @key, @reverse = this, clazz, key, reverse
7
+ @value = Remodel.redis.get(@key)
8
+ end
9
+
10
+ def value
11
+ @value
12
+ end
13
+
14
+ def add(entity)
15
+ _add_to_reverse_association_of(entity) if @reverse
16
+ _add(entity)
17
+ end
18
+
19
+ def remove(entity)
20
+ _remove_from_reverse_association_of(entity) if @reverse
21
+ _remove(entity)
22
+ end
23
+
24
+ private
25
+
26
+ def _add(entity)
27
+ @value = entity
28
+ if entity.nil?
29
+ Remodel.redis.del(@key)
30
+ else
31
+ Remodel.redis.set(@key, entity.key)
32
+ end
33
+ entity
34
+ end
35
+
36
+ def _remove(entity)
37
+ _add(nil)
38
+ end
39
+
40
+ def _add_to_reverse_association_of(entity)
41
+ if entity.send(@reverse).is_a? HasMany
42
+ entity.send(@reverse).send(:_add, @this)
43
+ else
44
+ entity.send("_#{@reverse}").send., @this)
45
+ end
46
+ end
47
+
48
+ def _remove_from_reverse_association_of(entity)
49
+ if entity.send(@reverse).is_a? HasMany
50
+ entity.send(@reverse).send(:_remove, @this)
51
+ else
52
+ entity.send("_#{@reverse}=", nil)
53
+ end
54
+ end
55
+ end
56
+
57
+ end
@@ -0,0 +1,29 @@
1
+ module Remodel
2
+
3
+ # A mapper converts a given value into a native JSON value &mdash;
4
+ # *nil*, *true*, *false*, *Number*, *String*, *Hash*, *Array* &mdash;
5
+ # via `pack`, and back again via `unpack`.
6
+ #
7
+ # Without any arguments, `Mapper.new` returns the identity mapper, which
8
+ # maps every value to itself. If `clazz` is set, the mapper rejects any
9
+ # value which is not of the given type.
10
+ class Mapper
11
+ def initialize(clazz = nil, pack_method = nil, unpack_method = nil)
12
+ @clazz = clazz
13
+ @pack_method = pack_method
14
+ @unpack_method = unpack_method
15
+ end
16
+
17
+ def pack(value)
18
+ return nil if value.nil?
19
+ raise(InvalidType, "#{value.inspect} is not a #{@clazz}") if @clazz && !value.is_a?(@clazz)
20
+ @pack_method ? value.send(@pack_method) : value
21
+ end
22
+
23
+ def unpack(value)
24
+ return nil if value.nil?
25
+ @unpack_method ? @clazz.send(@unpack_method, value) : value
26
+ end
27
+ end
28
+
29
+ end
data/lib/remodel.rb CHANGED
@@ -2,42 +2,31 @@ require 'rubygems'
2
2
  require 'redis'
3
3
  require 'date'
4
4
 
5
- # Use the superfast [YAJL][yajl] lib to parse [JSON][json], if available.
6
- #
7
- # [yajl]: http://github.com/brianmario/yajl-ruby
8
- # [json]: http://json.org/
5
+ # If available, use the superfast YAJL lib to parse JSON.
9
6
  begin
10
7
  require 'yajl/json_gem'
11
8
  rescue LoadError
12
9
  require 'json'
13
10
  end
14
11
 
15
- #### Monkey patches
16
-
17
- # Define `Boolean` as the superclass of `true` and `false`.
12
+ # Define `Boolean` -- the missing superclass of `true` and `false`.
18
13
  module Boolean; end
19
14
  true.extend(Boolean)
20
15
  false.extend(Boolean)
21
16
 
22
- # Find the `Class` object for a given class name, which can be a `String` or `Symbol` (or `Class`).
17
+ # Find the `Class` object for a given class name, which can be
18
+ # a `String` or a `Symbol` (or a `Class`).
23
19
  def Class.[](clazz)
24
20
  return clazz if clazz.nil? or clazz.is_a?(Class)
25
21
  clazz.to_s.split('::').inject(Kernel) { |mod, name| mod.const_get(name) }
26
22
  end
27
23
 
28
- #### Remodel
29
-
30
- module Remodel
24
+ require File.join(File.dirname(__FILE__), 'remodel', 'mapper')
25
+ require File.join(File.dirname(__FILE__), 'remodel', 'has_many')
26
+ require File.join(File.dirname(__FILE__), 'remodel', 'entity')
31
27
 
32
- # By default, we expect to find the redis server on `localhost:6379` &mdash;
33
- # otherwise you will have to set `Remodel.redis` to a suitably initialized redis client.
34
- def self.redis
35
- @redis ||= Redis.new
36
- end
37
28
 
38
- def self.redis=(redis)
39
- @redis = redis
40
- end
29
+ module Remodel
41
30
 
42
31
  # Custom errors
43
32
  class Error < ::StandardError; end
@@ -46,35 +35,23 @@ module Remodel
46
35
  class InvalidKeyPrefix < Error; end
47
36
  class InvalidType < Error; end
48
37
 
49
- #### Mapper
50
-
51
- # A mapper converts a given value into a native JSON value &mdash;
52
- # *nil*, *true*, *false*, *Number*, *String*, *Hash*, *Array* &mdash; via `pack`,
53
- # and back again via `unpack`.
54
- #
55
- # Without any arguments, `Mapper.new` returns the identity mapper, which maps every value into itself.
56
- # If `clazz` is set, the mapper rejects any value which is not of the given type.
57
- class Mapper
58
- def initialize(clazz = nil, pack_method = nil, unpack_method = nil)
59
- @clazz = clazz
60
- @pack_method = pack_method
61
- @unpack_method = unpack_method
62
- end
38
+ # By default, the redis server is expected to listen at `localhost:6379`.
39
+ # Otherwise you will have to set `Remodel.redis` to a suitably initialized
40
+ # redis client.
41
+ def self.redis
42
+ @redis ||= Redis.new
43
+ end
63
44
 
64
- def pack(value)
65
- return nil if value.nil?
66
- raise(InvalidType, "#{value.inspect} is not a #{@clazz}") if @clazz && !value.is_a?(@clazz)
67
- @pack_method ? value.send(@pack_method) : value
68
- end
45
+ def self.redis=(redis)
46
+ @redis = redis
47
+ end
69
48
 
70
- def unpack(value)
71
- return nil if value.nil?
72
- @unpack_method ? @clazz.send(@unpack_method, value) : value
73
- end
49
+ # Returns the mapper defined for a given class, or the identity mapper.
50
+ def self.mapper_for(clazz)
51
+ mapper_by_class[Class[clazz]]
74
52
  end
75
53
 
76
- # So let's define some handy mappers for common types, and a way to look them up.
77
- # If no mapper is defined for a given class, the identity mapper is used.
54
+ # Define some mappers for common types.
78
55
  def self.mapper_by_class
79
56
  @mapper_by_class ||= Hash.new(Mapper.new).merge(
80
57
  Boolean => Mapper.new(Boolean),
@@ -88,284 +65,4 @@ module Remodel
88
65
  )
89
66
  end
90
67
 
91
- def self.mapper_for(clazz)
92
- mapper_by_class[Class[clazz]]
93
- end
94
-
95
- #### HasMany
96
-
97
- # Represents the many-end of a many-to-one or many-to-many association.
98
- class HasMany < Array
99
- def initialize(this, clazz, key, reverse = nil)
100
- super _fetch(clazz, key)
101
- @this, @clazz, @key, @reverse = this, clazz, key, reverse
102
- end
103
-
104
- def create(attributes = {})
105
- add(@clazz.create(attributes))
106
- end
107
-
108
- def find(id)
109
- detect { |x| x.id == id } || raise(EntityNotFound, "no element with id #{id}")
110
- end
111
-
112
- def add(entity)
113
- _add_to_reverse_association_of(entity) if @reverse
114
- _add(entity)
115
- end
116
-
117
- def remove(entity)
118
- _remove_from_reverse_association_of(entity) if @reverse
119
- _remove(entity)
120
- end
121
-
122
- private
123
-
124
- def _add(entity)
125
- self << entity
126
- Remodel.redis.rpush(@key, entity.key)
127
- entity
128
- end
129
-
130
- def _remove(entity)
131
- delete_if { |x| x.key == entity.key }
132
- Remodel.redis.lrem(@key, 0, entity.key)
133
- entity
134
- end
135
-
136
- def _add_to_reverse_association_of(entity)
137
- if entity.send(@reverse).is_a? HasMany
138
- entity.send(@reverse).send(:_add, @this)
139
- else
140
- entity.send("_#{@reverse}=", @this)
141
- end
142
- end
143
-
144
- def _remove_from_reverse_association_of(entity)
145
- if entity.send(@reverse).is_a? HasMany
146
- entity.send(@reverse).send(:_remove, @this)
147
- else
148
- entity.send("_#{@reverse}=", nil)
149
- end
150
- end
151
-
152
- def _fetch(clazz, key)
153
- keys = Remodel.redis.lrange(key, 0, -1)
154
- values = keys.empty? ? [] : Remodel.redis.mget(keys)
155
- keys.zip(values).map do |key, json|
156
- clazz.restore(key, json) if json
157
- end.compact
158
- end
159
- end
160
-
161
- #### Entity
162
-
163
- # The superclass of all persistent remodel entities.
164
- class Entity
165
- attr_accessor :key
166
-
167
- def initialize(attributes = {}, key = nil)
168
- @attributes = {}
169
- @key = key
170
- attributes = self.class.default_values.merge(attributes) if key.nil?
171
- attributes.each do |name, value|
172
- send("#{name}=", value) if respond_to? "#{name}="
173
- end
174
- end
175
-
176
- def id
177
- key && key.split(':').last.to_i
178
- end
179
-
180
- def save
181
- @key = self.class.next_key unless @key
182
- Remodel.redis.set(@key, to_json)
183
- self
184
- end
185
-
186
- def update(properties)
187
- properties.each { |name, value| send("#{name}=", value) }
188
- save
189
- end
190
-
191
- def reload
192
- raise EntityNotSaved unless @key
193
- initialize(self.class.parse(self.class.fetch(@key)), @key)
194
- instance_variables.each do |var|
195
- remove_instance_variable(var) if var =~ /^@association_/
196
- end
197
- self
198
- end
199
-
200
- def delete
201
- raise EntityNotSaved unless @key
202
- Remodel.redis.del(@key)
203
- end
204
-
205
- def as_json
206
- { :id => id }.merge(@attributes)
207
- end
208
-
209
- def to_json
210
- JSON.generate(self.class.pack(@attributes))
211
- end
212
-
213
- def inspect
214
- properties = @attributes.map { |name, value| "#{name}: #{value.inspect}" }.join(', ')
215
- "\#<#{self.class.name}(#{id}) #{properties}>"
216
- end
217
-
218
- def self.create(attributes = {})
219
- new(attributes).save
220
- end
221
-
222
- def self.find(key)
223
- key = "#{key_prefix}:#{key}" if key.kind_of? Integer
224
- restore(key, fetch(key))
225
- end
226
-
227
- def self.all
228
- keys = Remodel.redis.keys("#{key_prefix}:*").select { |k| k =~ /:[0-9]+$/ }
229
- values = keys.empty? ? [] : Remodel.redis.mget(keys)
230
- keys.zip(values).map do |key, json|
231
- restore(key, json) if json
232
- end.compact
233
- end
234
-
235
- def self.restore(key, json)
236
- new(parse(json), key)
237
- end
238
-
239
- #### DSL for subclasses
240
-
241
- protected
242
-
243
- def self.set_key_prefix(prefix)
244
- raise(InvalidKeyPrefix, prefix) unless prefix =~ /^[a-z]+$/
245
- @key_prefix = prefix
246
- end
247
-
248
- def self.property(name, options = {})
249
- name = name.to_sym
250
- mapper[name] = Remodel.mapper_for(options[:class])
251
- default_values[name] = options[:default] if options.has_key?(:default)
252
- define_method(name) { @attributes[name] }
253
- define_method("#{name}=") { |value| @attributes[name] = value }
254
- end
255
-
256
- def self.has_many(name, options)
257
- var = "@association_#{name}".to_sym
258
-
259
- define_method(name) do
260
- if instance_variable_defined? var
261
- instance_variable_get(var)
262
- else
263
- clazz = Class[options[:class]]
264
- instance_variable_set(var, HasMany.new(self, clazz, "#{key}:#{name}", options[:reverse]))
265
- end
266
- end
267
- end
268
-
269
- def self.has_one(name, options)
270
- var = "@association_#{name}".to_sym
271
-
272
- define_method(name) do
273
- if instance_variable_defined? var
274
- instance_variable_get(var)
275
- else
276
- clazz = Class[options[:class]]
277
- value_key = Remodel.redis.get("#{key}:#{name}")
278
- instance_variable_set(var, clazz.find(value_key)) if value_key
279
- end
280
- end
281
-
282
- define_method("#{name}=") do |value|
283
- send("_reverse_association_of_#{name}=", value) if options[:reverse]
284
- send("_#{name}=", value)
285
- end
286
-
287
- define_method("_#{name}=") do |value|
288
- if value
289
- instance_variable_set(var, value)
290
- Remodel.redis.set("#{key}:#{name}", value.key)
291
- else
292
- remove_instance_variable(var) if instance_variable_defined? var
293
- Remodel.redis.del("#{key}:#{name}")
294
- end
295
- end; private "_#{name}="
296
-
297
- if options[:reverse]
298
- define_method("_reverse_association_of_#{name}=") do |value|
299
- if value
300
- association = value.send("#{options[:reverse]}")
301
- if association.is_a? HasMany
302
- association.send("_add", self)
303
- else
304
- value.send("_#{options[:reverse]}=", self)
305
- end
306
- else
307
- if old_value = send(name)
308
- association = old_value.send("#{options[:reverse]}")
309
- if association.is_a? HasMany
310
- association.send("_remove", self)
311
- else
312
- old_value.send("_#{options[:reverse]}=", nil)
313
- end
314
- end
315
- end
316
- end; private "_reverse_association_of_#{name}="
317
- end
318
- end
319
-
320
- #### Helper methods
321
-
322
- private
323
-
324
- def self.fetch(key)
325
- Remodel.redis.get(key) || raise(EntityNotFound, "no #{name} with key #{key}")
326
- end
327
-
328
- # Each entity has its own sequence to generate unique ids.
329
- def self.next_key
330
- id = Remodel.redis.incr("#{key_prefix}:seq")
331
- "#{key_prefix}:#{id}"
332
- end
333
-
334
- # Default key prefix is the first letter of the class name, in lowercase.
335
- def self.key_prefix
336
- @key_prefix ||= name.split('::').last[0,1].downcase
337
- end
338
-
339
- def self.parse(json)
340
- unpack(JSON.parse(json))
341
- end
342
-
343
- def self.pack(attributes)
344
- result = {}
345
- attributes.each do |name, value|
346
- result[name] = mapper[name].pack(value)
347
- end
348
- result
349
- end
350
-
351
- def self.unpack(attributes)
352
- result = {}
353
- attributes.each do |name, value|
354
- name = name.to_sym
355
- result[name] = mapper[name].unpack(value)
356
- end
357
- result
358
- end
359
-
360
- # Lazy init
361
- def self.mapper
362
- @mapper ||= {}
363
- end
364
-
365
- def self.default_values
366
- @default_values ||= {}
367
- end
368
-
369
- end
370
-
371
- end
68
+ end
data/remodel.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{remodel}
8
- s.version = "0.1.3"
8
+ s.version = "0.1.4"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Tim Lossen"]
12
- s.date = %q{2010-05-03}
12
+ s.date = %q{2010-07-01}
13
13
  s.default_executable = %q{redis-monitor.rb}
14
14
  s.description = %q{build your domain model in ruby, persist your objects to redis.}
15
15
  s.email = %q{tim@lossen.de}
@@ -29,6 +29,10 @@ Gem::Specification.new do |s|
29
29
  "docs/remodel.html",
30
30
  "example/book.rb",
31
31
  "lib/remodel.rb",
32
+ "lib/remodel/entity.rb",
33
+ "lib/remodel/has_many.rb",
34
+ "lib/remodel/has_one.rb",
35
+ "lib/remodel/mapper.rb",
32
36
  "remodel.gemspec",
33
37
  "test/helper.rb",
34
38
  "test/test_entity.rb",
@@ -9,7 +9,7 @@ class TestOneToMany < Test::Unit::TestCase
9
9
  end
10
10
 
11
11
  class Puzzle < Remodel::Entity
12
- has_many :pieces, :class => 'TestOneToMany::Piece'
12
+ has_many :pieces, :class => 'TestOneToMany::Piece', :reverse => 'puzzle'
13
13
  property :topic
14
14
  end
15
15
 
@@ -48,6 +48,15 @@ class TestOneToMany < Test::Unit::TestCase
48
48
  piece = Piece.create
49
49
  piece.puzzle = puzzle
50
50
  assert_equal 1, puzzle.pieces.size
51
+ assert_equal piece.id, puzzle.pieces.first.id
52
+ end
53
+
54
+ should "remove the entity from the old reverse association" do
55
+ puzzle = Puzzle.create
56
+ piece = puzzle.pieces.create
57
+ new_puzzle = Puzzle.create
58
+ piece.puzzle = new_puzzle
59
+ assert_equal [], puzzle.reload.pieces
51
60
  end
52
61
 
53
62
  should "be settable to nil" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: remodel
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tim Lossen
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-05-03 00:00:00 +02:00
12
+ date: 2010-07-01 00:00:00 +02:00
13
13
  default_executable: redis-monitor.rb
14
14
  dependencies: []
15
15
 
@@ -33,6 +33,10 @@ files:
33
33
  - docs/remodel.html
34
34
  - example/book.rb
35
35
  - lib/remodel.rb
36
+ - lib/remodel/entity.rb
37
+ - lib/remodel/has_many.rb
38
+ - lib/remodel/has_one.rb
39
+ - lib/remodel/mapper.rb
36
40
  - remodel.gemspec
37
41
  - test/helper.rb
38
42
  - test/test_entity.rb