remodel 0.1.3 → 0.1.4

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/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