mongo_mapper 0.5.6 → 0.5.7
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/.gitignore +3 -1
- data/README.rdoc +3 -0
- data/VERSION +1 -1
- data/lib/mongo_mapper.rb +14 -6
- data/lib/mongo_mapper/associations.rb +11 -5
- data/lib/mongo_mapper/associations/base.rb +17 -5
- data/lib/mongo_mapper/associations/many_documents_as_proxy.rb +0 -2
- data/lib/mongo_mapper/associations/many_documents_proxy.rb +15 -15
- data/lib/mongo_mapper/associations/many_embedded_polymorphic_proxy.rb +2 -2
- data/lib/mongo_mapper/associations/many_polymorphic_proxy.rb +1 -1
- data/lib/mongo_mapper/associations/proxy.rb +1 -0
- data/lib/mongo_mapper/callbacks.rb +18 -0
- data/lib/mongo_mapper/document.rb +206 -89
- data/lib/mongo_mapper/dynamic_finder.rb +1 -1
- data/lib/mongo_mapper/embedded_document.rb +7 -3
- data/lib/mongo_mapper/finder_options.rb +87 -66
- data/lib/mongo_mapper/pagination.rb +2 -0
- data/lib/mongo_mapper/serialization.rb +2 -3
- data/lib/mongo_mapper/serializers/json_serializer.rb +1 -1
- data/lib/mongo_mapper/support.rb +9 -0
- data/lib/mongo_mapper/validations.rb +3 -1
- data/mongo_mapper.gemspec +4 -4
- data/test/functional/associations/test_many_documents_as_proxy.rb +2 -2
- data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +25 -1
- data/test/functional/associations/test_many_embedded_proxy.rb +25 -0
- data/test/functional/associations/test_many_polymorphic_proxy.rb +48 -6
- data/test/functional/associations/test_many_proxy.rb +27 -6
- data/test/functional/test_document.rb +49 -29
- data/test/functional/test_pagination.rb +17 -17
- data/test/functional/test_validations.rb +35 -14
- data/test/models.rb +85 -10
- data/test/support/{test_timing.rb → timing.rb} +1 -1
- data/test/test_helper.rb +8 -8
- data/test/unit/test_association_base.rb +17 -0
- data/test/unit/test_document.rb +12 -1
- data/test/unit/test_embedded_document.rb +13 -4
- data/test/unit/test_finder_options.rb +50 -48
- data/test/unit/test_pagination.rb +4 -0
- metadata +4 -4
@@ -332,9 +332,13 @@ module MongoMapper
|
|
332
332
|
end
|
333
333
|
|
334
334
|
def read_attribute(name)
|
335
|
-
|
336
|
-
|
337
|
-
|
335
|
+
if key = _keys[name]
|
336
|
+
value = key.get(instance_variable_get("@#{name}"))
|
337
|
+
instance_variable_set "@#{name}", value if !frozen?
|
338
|
+
value
|
339
|
+
else
|
340
|
+
raise KeyNotFound, "Could not find key: #{name.inspect}"
|
341
|
+
end
|
338
342
|
end
|
339
343
|
|
340
344
|
def read_attribute_before_typecast(name)
|
@@ -1,66 +1,35 @@
|
|
1
1
|
module MongoMapper
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
{'$in' => value}
|
16
|
-
end
|
17
|
-
when Hash
|
18
|
-
criteria[field] = to_mongo_criteria(model, value, field)
|
19
|
-
else
|
20
|
-
criteria[field] = value
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
criteria
|
2
|
+
# Controls the parsing and handling of options used by finders.
|
3
|
+
#
|
4
|
+
# == Important Note
|
5
|
+
#
|
6
|
+
# This class is private to MongoMapper and should not be considered part of
|
7
|
+
# MongoMapper's public API. Some documentation herein, however, may prove
|
8
|
+
# useful for understanding how MongoMapper handles the parsing of finder
|
9
|
+
# conditions and options.
|
10
|
+
#
|
11
|
+
# @private
|
12
|
+
class FinderOperator
|
13
|
+
def initialize(field, operator)
|
14
|
+
@field, @operator = field, operator
|
25
15
|
end
|
26
16
|
|
27
|
-
|
28
|
-
|
29
|
-
if model.single_collection_inherited?
|
30
|
-
criteria[:_type] = model.to_s
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
def self.to_mongo_options(model, options)
|
35
|
-
options = options.dup
|
36
|
-
{
|
37
|
-
:fields => to_mongo_fields(options.delete(:fields) || options.delete(:select)),
|
38
|
-
:skip => (options.delete(:skip) || options.delete(:offset) || 0).to_i,
|
39
|
-
:limit => (options.delete(:limit) || 0).to_i,
|
40
|
-
:sort => options.delete(:sort) || to_mongo_sort(options.delete(:order))
|
41
|
-
}
|
17
|
+
def to_criteria(value)
|
18
|
+
{@field => {@operator => value}}
|
42
19
|
end
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
:_id
|
47
|
-
else
|
48
|
-
field
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
20
|
+
end
|
21
|
+
|
22
|
+
class FinderOptions
|
52
23
|
OptionKeys = [:fields, :select, :skip, :offset, :limit, :sort, :order]
|
53
|
-
|
54
|
-
attr_reader :model, :options
|
55
|
-
|
24
|
+
|
56
25
|
def initialize(model, options)
|
57
|
-
raise ArgumentError, "
|
58
|
-
|
59
|
-
@model = model
|
60
|
-
|
61
|
-
options = options.symbolize_keys
|
62
|
-
@options, @conditions = {}, options.delete(:conditions) || {}
|
26
|
+
raise ArgumentError, "Options must be a hash" unless options.is_a?(Hash)
|
27
|
+
options.symbolize_keys!
|
63
28
|
|
29
|
+
@model = model
|
30
|
+
@options = {}
|
31
|
+
@conditions = options.delete(:conditions) || {}
|
32
|
+
|
64
33
|
options.each_pair do |key, value|
|
65
34
|
if OptionKeys.include?(key)
|
66
35
|
@options[key] = value
|
@@ -68,42 +37,94 @@ module MongoMapper
|
|
68
37
|
@conditions[key] = value
|
69
38
|
end
|
70
39
|
end
|
40
|
+
|
41
|
+
add_sci_scope
|
71
42
|
end
|
72
43
|
|
44
|
+
# @return [Hash] Mongo compatible criteria options
|
45
|
+
#
|
46
|
+
# @see FinderOptions#to_mongo_criteria
|
73
47
|
def criteria
|
74
|
-
|
48
|
+
to_mongo_criteria(@conditions)
|
75
49
|
end
|
76
50
|
|
51
|
+
# @return [Hash] Mongo compatible options
|
77
52
|
def options
|
78
|
-
|
53
|
+
options = @options.dup
|
54
|
+
|
55
|
+
fields = options.delete(:fields) || options.delete(:select)
|
56
|
+
skip = options.delete(:skip) || options.delete(:offset) || 0
|
57
|
+
limit = options.delete(:limit) || 0
|
58
|
+
sort = options.delete(:sort) || convert_order_to_sort(options.delete(:order))
|
59
|
+
|
60
|
+
{:fields => to_mongo_fields(fields), :skip => skip.to_i, :limit => limit.to_i, :sort => sort}
|
79
61
|
end
|
80
62
|
|
63
|
+
# @return [Array<Hash>] Mongo criteria and options enclosed in an Array
|
81
64
|
def to_a
|
82
65
|
[criteria, options]
|
83
66
|
end
|
84
|
-
|
67
|
+
|
85
68
|
private
|
86
|
-
def
|
69
|
+
def to_mongo_criteria(conditions, parent_key=nil)
|
70
|
+
criteria = {}
|
71
|
+
|
72
|
+
conditions.each_pair do |field, value|
|
73
|
+
field = normalized_field(field)
|
74
|
+
if field.is_a?(FinderOperator)
|
75
|
+
criteria.merge!(field.to_criteria(value))
|
76
|
+
next
|
77
|
+
end
|
78
|
+
case value
|
79
|
+
when Array
|
80
|
+
operator_present = field.to_s =~ /^\$/
|
81
|
+
criteria[field] = operator?(field) ? value : {'$in' => value}
|
82
|
+
when Hash
|
83
|
+
criteria[field] = to_mongo_criteria(value, field)
|
84
|
+
else
|
85
|
+
criteria[field] = value
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
criteria
|
90
|
+
end
|
91
|
+
|
92
|
+
def operator?(field)
|
93
|
+
field.to_s =~ /^\$/
|
94
|
+
end
|
95
|
+
|
96
|
+
def normalized_field(field)
|
97
|
+
field.to_s == 'id' ? :_id : field
|
98
|
+
end
|
99
|
+
|
100
|
+
# adds _type single collection inheritance scope for models that need it
|
101
|
+
def add_sci_scope
|
102
|
+
if @model.single_collection_inherited?
|
103
|
+
@conditions[:_type] = @model.to_s
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def to_mongo_fields(fields)
|
87
108
|
return if fields.blank?
|
88
|
-
|
109
|
+
|
89
110
|
if fields.is_a?(String)
|
90
111
|
fields.split(',').map { |field| field.strip }
|
91
112
|
else
|
92
113
|
fields.flatten.compact
|
93
114
|
end
|
94
115
|
end
|
95
|
-
|
96
|
-
def
|
116
|
+
|
117
|
+
def convert_order_to_sort(sort)
|
97
118
|
return if sort.blank?
|
98
119
|
pieces = sort.split(',')
|
99
120
|
pieces.map { |s| to_mongo_sort_piece(s) }
|
100
121
|
end
|
101
|
-
|
102
|
-
def
|
122
|
+
|
123
|
+
def to_mongo_sort_piece(str)
|
103
124
|
field, direction = str.strip.split(' ')
|
104
125
|
direction ||= 'ASC'
|
105
126
|
direction = direction.upcase == 'ASC' ? 1 : -1
|
106
127
|
[field, direction]
|
107
128
|
end
|
108
129
|
end
|
109
|
-
end
|
130
|
+
end
|
@@ -5,7 +5,7 @@ module MongoMapper #:nodoc:
|
|
5
5
|
class Serializer #:nodoc:
|
6
6
|
attr_reader :options
|
7
7
|
|
8
|
-
def initialize(record, options
|
8
|
+
def initialize(record, options={})
|
9
9
|
@record, @options = record, options.dup
|
10
10
|
end
|
11
11
|
|
@@ -51,5 +51,4 @@ module MongoMapper #:nodoc:
|
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
|
-
|
55
|
-
require dir + 'json_serializer'
|
54
|
+
require 'mongo_mapper/serializers/json_serializer'
|
@@ -49,7 +49,7 @@ module MongoMapper #:nodoc:
|
|
49
49
|
# # => {"id": 1, "name": "Konata Izumi", "age": 16,
|
50
50
|
# "created_at": "2006/08/01", "awesome": true,
|
51
51
|
# "permalink": "1-konata-izumi"}
|
52
|
-
def to_json(options
|
52
|
+
def to_json(options={})
|
53
53
|
apply_to_json_defaults(options)
|
54
54
|
|
55
55
|
if include_root_in_json
|
data/lib/mongo_mapper/support.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
class BasicObject #:nodoc:
|
2
|
+
alias_method :proxy_extend, :extend
|
2
3
|
instance_methods.each { |m| undef_method m unless m =~ /(^__|^nil\?$|^send$|^methods$|instance_eval|proxy_|^object_id$)/ }
|
3
4
|
end unless defined?(BasicObject)
|
4
5
|
|
@@ -129,6 +130,14 @@ class String
|
|
129
130
|
end
|
130
131
|
end
|
131
132
|
|
133
|
+
class Symbol
|
134
|
+
%w{gt lt gte lte ne in nin mod size where exists}.each do |operator|
|
135
|
+
define_method operator do
|
136
|
+
MongoMapper::FinderOperator.new(self, "$#{operator}")
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
132
141
|
class Time
|
133
142
|
def self.to_mongo(value)
|
134
143
|
if value.nil? || value == ''
|
@@ -18,7 +18,9 @@ module MongoMapper
|
|
18
18
|
option :scope
|
19
19
|
|
20
20
|
def valid?(instance)
|
21
|
-
|
21
|
+
value = instance[attribute]
|
22
|
+
return true if allow_blank && value.blank?
|
23
|
+
doc = instance.class.first({self.attribute => value}.merge(scope_conditions(instance)))
|
22
24
|
doc.nil? || instance.id == doc.id
|
23
25
|
end
|
24
26
|
|
data/mongo_mapper.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{mongo_mapper}
|
8
|
-
s.version = "0.5.
|
8
|
+
s.version = "0.5.7"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["John Nunemaker"]
|
12
|
-
s.date = %q{2009-10-
|
12
|
+
s.date = %q{2009-10-28}
|
13
13
|
s.default_executable = %q{mmconsole}
|
14
14
|
s.description = %q{Awesome gem for modeling your domain and storing it in mongo}
|
15
15
|
s.email = %q{nunemaker@gmail.com}
|
@@ -74,7 +74,7 @@ Gem::Specification.new do |s|
|
|
74
74
|
"test/functional/test_validations.rb",
|
75
75
|
"test/models.rb",
|
76
76
|
"test/support/custom_matchers.rb",
|
77
|
-
"test/support/
|
77
|
+
"test/support/timing.rb",
|
78
78
|
"test/test_helper.rb",
|
79
79
|
"test/unit/serializers/test_json_serializer.rb",
|
80
80
|
"test/unit/test_association_base.rb",
|
@@ -117,7 +117,7 @@ Gem::Specification.new do |s|
|
|
117
117
|
"test/functional/test_validations.rb",
|
118
118
|
"test/models.rb",
|
119
119
|
"test/support/custom_matchers.rb",
|
120
|
-
"test/support/
|
120
|
+
"test/support/timing.rb",
|
121
121
|
"test/test_helper.rb",
|
122
122
|
"test/unit/serializers/test_json_serializer.rb",
|
123
123
|
"test/unit/test_association_base.rb",
|
@@ -137,7 +137,7 @@ class ManyDocumentsAsProxyTest < Test::Unit::TestCase
|
|
137
137
|
end
|
138
138
|
|
139
139
|
should "work with conditions" do
|
140
|
-
comments = @post.comments.find(:all, :
|
140
|
+
comments = @post.comments.find(:all, :body => 'comment1')
|
141
141
|
comments.should == [@comment1]
|
142
142
|
end
|
143
143
|
|
@@ -154,7 +154,7 @@ class ManyDocumentsAsProxyTest < Test::Unit::TestCase
|
|
154
154
|
end
|
155
155
|
|
156
156
|
should "work with conditions" do
|
157
|
-
comments = @post.comments.all(:
|
157
|
+
comments = @post.comments.all(:body => 'comment1')
|
158
158
|
comments.should == [@comment1]
|
159
159
|
end
|
160
160
|
|
@@ -129,4 +129,28 @@ class ManyEmbeddedPolymorphicProxyTest < Test::Unit::TestCase
|
|
129
129
|
from_db.transports[2].icu.should == true
|
130
130
|
end
|
131
131
|
end
|
132
|
-
|
132
|
+
|
133
|
+
context "extending the association" do
|
134
|
+
should "work using a block passed to many" do
|
135
|
+
catalog = Catalog.new
|
136
|
+
medias = catalog.medias = [
|
137
|
+
Video.new("file" => "video.mpg", "length" => 3600, :visible => true),
|
138
|
+
Music.new("file" => "music.mp3", "bitrate" => "128kbps", :visible => true),
|
139
|
+
Image.new("file" => "image.png", "width" => 800, "height" => 600, :visible => false)
|
140
|
+
]
|
141
|
+
catalog.save
|
142
|
+
catalog.medias.visible.should == [medias[0], medias[1]]
|
143
|
+
end
|
144
|
+
|
145
|
+
should "work using many's :extend option" do
|
146
|
+
fleet = TrModels::Fleet.new
|
147
|
+
transports = fleet.transports = [
|
148
|
+
TrModels::Car.new("license_plate" => "ABC1223", "model" => "Honda Civic", "year" => 2003, :purchased_on => 2.years.ago.to_date),
|
149
|
+
TrModels::Bus.new("license_plate" => "XYZ9090", "max_passengers" => 51, :purchased_on => 3.years.ago.to_date),
|
150
|
+
TrModels::Ambulance.new("license_plate" => "HDD3030", "icu" => true, :purchased_on => 1.year.ago.to_date)
|
151
|
+
]
|
152
|
+
fleet.save
|
153
|
+
fleet.transports.to_be_replaced.should == [transports[1]]
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
@@ -171,4 +171,29 @@ class ManyEmbeddedProxyTest < Test::Unit::TestCase
|
|
171
171
|
|
172
172
|
meg.pets.find(sparky.id).should == sparky
|
173
173
|
end
|
174
|
+
|
175
|
+
context "extending the association" do
|
176
|
+
should "work using a block passed to many" do
|
177
|
+
project = Project.new(:name => "Some Project")
|
178
|
+
addr1 = Address.new(:address => "Gate-3 Lankershim Blvd.", :city => "Universal City", :state => "CA", :zip => "91608")
|
179
|
+
addr2 = Address.new(:address => "3000 W. Alameda Ave.", :city => "Burbank", :state => "CA", :zip => "91523")
|
180
|
+
addr3 = Address.new(:address => "111 Some Ln", :city => "Nashville", :state => "TN", :zip => "37211")
|
181
|
+
project.addresses = [addr1, addr2, addr3]
|
182
|
+
project.save
|
183
|
+
project.addresses.find_all_by_state("CA").should == [addr1, addr2]
|
184
|
+
end
|
185
|
+
|
186
|
+
should "work using many's :extend option" do
|
187
|
+
project = Project.new(:name => "Some Project")
|
188
|
+
person1 = Person.new(:name => "Steve")
|
189
|
+
person2 = Person.new(:name => "Betty")
|
190
|
+
person3 = Person.new(:name => "Cynthia")
|
191
|
+
|
192
|
+
project.people << person1
|
193
|
+
project.people << person2
|
194
|
+
project.people << person3
|
195
|
+
project.save
|
196
|
+
project.people.find_by_name("Steve").should == person1
|
197
|
+
end
|
198
|
+
end
|
174
199
|
end
|
@@ -4,6 +4,7 @@ require 'models'
|
|
4
4
|
class ManyPolymorphicProxyTest < Test::Unit::TestCase
|
5
5
|
def setup
|
6
6
|
Room.collection.clear
|
7
|
+
Message.collection.clear
|
7
8
|
end
|
8
9
|
|
9
10
|
should "default reader to empty array" do
|
@@ -181,7 +182,7 @@ class ManyPolymorphicProxyTest < Test::Unit::TestCase
|
|
181
182
|
end
|
182
183
|
|
183
184
|
should "work with conditions" do
|
184
|
-
messages = @lounge.messages.find(:all, :
|
185
|
+
messages = @lounge.messages.find(:all, :body => 'Loungin!', :order => "position")
|
185
186
|
messages.should == [@lm1]
|
186
187
|
end
|
187
188
|
|
@@ -197,7 +198,7 @@ class ManyPolymorphicProxyTest < Test::Unit::TestCase
|
|
197
198
|
end
|
198
199
|
|
199
200
|
should "work with conditions" do
|
200
|
-
messages = @lounge.messages.all(:
|
201
|
+
messages = @lounge.messages.all(:body => 'Loungin!', :order => "position")
|
201
202
|
messages.should == [@lm1]
|
202
203
|
end
|
203
204
|
|
@@ -213,7 +214,7 @@ class ManyPolymorphicProxyTest < Test::Unit::TestCase
|
|
213
214
|
end
|
214
215
|
|
215
216
|
should "work with conditions" do
|
216
|
-
message = @lounge.messages.find(:first, :
|
217
|
+
message = @lounge.messages.find(:first, :body => 'I love loungin!', :order => "position asc")
|
217
218
|
message.should == @lm2
|
218
219
|
end
|
219
220
|
end
|
@@ -224,7 +225,7 @@ class ManyPolymorphicProxyTest < Test::Unit::TestCase
|
|
224
225
|
end
|
225
226
|
|
226
227
|
should "work with conditions" do
|
227
|
-
message = @lounge.messages.first(:
|
228
|
+
message = @lounge.messages.first(:body => 'I love loungin!', :order => "position asc")
|
228
229
|
message.should == @lm2
|
229
230
|
end
|
230
231
|
end
|
@@ -235,7 +236,7 @@ class ManyPolymorphicProxyTest < Test::Unit::TestCase
|
|
235
236
|
end
|
236
237
|
|
237
238
|
should "work with conditions" do
|
238
|
-
message = @lounge.messages.find(:last, :
|
239
|
+
message = @lounge.messages.find(:last, :body => 'Loungin!', :order => "position asc")
|
239
240
|
message.should == @lm1
|
240
241
|
end
|
241
242
|
end
|
@@ -246,7 +247,7 @@ class ManyPolymorphicProxyTest < Test::Unit::TestCase
|
|
246
247
|
end
|
247
248
|
|
248
249
|
should "work with conditions" do
|
249
|
-
message = @lounge.messages.last(:
|
250
|
+
message = @lounge.messages.last(:body => 'Loungin!', :order => "position asc")
|
250
251
|
message.should == @lm1
|
251
252
|
end
|
252
253
|
end
|
@@ -263,6 +264,20 @@ class ManyPolymorphicProxyTest < Test::Unit::TestCase
|
|
263
264
|
end
|
264
265
|
end
|
265
266
|
|
267
|
+
context "with query options/criteria" do
|
268
|
+
should "work with order on association" do
|
269
|
+
@lounge.messages.should == [@lm1, @lm2]
|
270
|
+
end
|
271
|
+
|
272
|
+
should "allow overriding the order provided to the association" do
|
273
|
+
@lounge.messages.all(:order => 'position desc').should == [@lm2, @lm1]
|
274
|
+
end
|
275
|
+
|
276
|
+
should "allow using conditions on association" do
|
277
|
+
@hall.latest_messages.should == [@hm3, @hm2]
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
266
281
|
context "with multiple ids" do
|
267
282
|
should "work for ids in association" do
|
268
283
|
messages = @lounge.messages.find(@lm1.id, @lm2.id)
|
@@ -294,4 +309,31 @@ class ManyPolymorphicProxyTest < Test::Unit::TestCase
|
|
294
309
|
end
|
295
310
|
end
|
296
311
|
end
|
312
|
+
|
313
|
+
context "extending the association" do
|
314
|
+
should "work using a block passed to many" do
|
315
|
+
room = Room.new(:name => "Amazing Room")
|
316
|
+
messages = room.messages = [
|
317
|
+
Enter.new(:body => 'John entered room', :position => 3),
|
318
|
+
Chat.new(:body => 'Heyyyoooo!', :position => 4),
|
319
|
+
Exit.new(:body => 'John exited room', :position => 5),
|
320
|
+
Enter.new(:body => 'Steve entered room', :position => 6),
|
321
|
+
Chat.new(:body => 'Anyone there?', :position => 7),
|
322
|
+
Exit.new(:body => 'Steve exited room', :position => 8)
|
323
|
+
]
|
324
|
+
room.save
|
325
|
+
room.messages.older.should == messages[3..5]
|
326
|
+
end
|
327
|
+
|
328
|
+
should "work using many's :extend option" do
|
329
|
+
room = Room.new(:name => "Amazing Room")
|
330
|
+
accounts = room.accounts = [
|
331
|
+
Bot.new(:last_logged_in => 3.weeks.ago),
|
332
|
+
User.new(:last_logged_in => nil),
|
333
|
+
Bot.new(:last_logged_in => 1.week.ago)
|
334
|
+
]
|
335
|
+
room.save
|
336
|
+
room.accounts.inactive.should == [accounts[1]]
|
337
|
+
end
|
338
|
+
end
|
297
339
|
end
|