danielharan-mongo_mapper 0.6.5
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 +10 -0
- data/LICENSE +20 -0
- data/README.rdoc +53 -0
- data/Rakefile +55 -0
- data/VERSION +1 -0
- data/bin/mmconsole +60 -0
- data/lib/mongo_mapper.rb +134 -0
- data/lib/mongo_mapper/associations.rb +183 -0
- data/lib/mongo_mapper/associations/base.rb +110 -0
- data/lib/mongo_mapper/associations/belongs_to_polymorphic_proxy.rb +34 -0
- data/lib/mongo_mapper/associations/belongs_to_proxy.rb +22 -0
- data/lib/mongo_mapper/associations/many_documents_as_proxy.rb +25 -0
- data/lib/mongo_mapper/associations/many_documents_proxy.rb +127 -0
- data/lib/mongo_mapper/associations/many_embedded_polymorphic_proxy.rb +33 -0
- data/lib/mongo_mapper/associations/many_embedded_proxy.rb +53 -0
- data/lib/mongo_mapper/associations/many_polymorphic_proxy.rb +11 -0
- data/lib/mongo_mapper/associations/many_proxy.rb +6 -0
- data/lib/mongo_mapper/associations/proxy.rb +80 -0
- data/lib/mongo_mapper/callbacks.rb +109 -0
- data/lib/mongo_mapper/dirty.rb +136 -0
- data/lib/mongo_mapper/document.rb +481 -0
- data/lib/mongo_mapper/dynamic_finder.rb +35 -0
- data/lib/mongo_mapper/embedded_document.rb +386 -0
- data/lib/mongo_mapper/finder_options.rb +133 -0
- data/lib/mongo_mapper/key.rb +36 -0
- data/lib/mongo_mapper/observing.rb +50 -0
- data/lib/mongo_mapper/pagination.rb +53 -0
- data/lib/mongo_mapper/rails_compatibility/document.rb +15 -0
- data/lib/mongo_mapper/rails_compatibility/embedded_document.rb +27 -0
- data/lib/mongo_mapper/serialization.rb +54 -0
- data/lib/mongo_mapper/serializers/json_serializer.rb +92 -0
- data/lib/mongo_mapper/support.rb +193 -0
- data/lib/mongo_mapper/validations.rb +41 -0
- data/mongo_mapper.gemspec +171 -0
- data/specs.watchr +32 -0
- data/test/NOTE_ON_TESTING +1 -0
- data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +55 -0
- data/test/functional/associations/test_belongs_to_proxy.rb +48 -0
- data/test/functional/associations/test_many_documents_as_proxy.rb +246 -0
- data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +156 -0
- data/test/functional/associations/test_many_embedded_proxy.rb +196 -0
- data/test/functional/associations/test_many_polymorphic_proxy.rb +339 -0
- data/test/functional/associations/test_many_proxy.rb +384 -0
- data/test/functional/test_associations.rb +44 -0
- data/test/functional/test_binary.rb +18 -0
- data/test/functional/test_callbacks.rb +85 -0
- data/test/functional/test_dirty.rb +159 -0
- data/test/functional/test_document.rb +1180 -0
- data/test/functional/test_embedded_document.rb +125 -0
- data/test/functional/test_logger.rb +20 -0
- data/test/functional/test_pagination.rb +95 -0
- data/test/functional/test_rails_compatibility.rb +25 -0
- data/test/functional/test_string_id_compatibility.rb +72 -0
- data/test/functional/test_validations.rb +369 -0
- data/test/models.rb +271 -0
- data/test/support/custom_matchers.rb +55 -0
- data/test/support/timing.rb +16 -0
- data/test/test_helper.rb +27 -0
- data/test/unit/serializers/test_json_serializer.rb +189 -0
- data/test/unit/test_association_base.rb +166 -0
- data/test/unit/test_document.rb +204 -0
- data/test/unit/test_dynamic_finder.rb +125 -0
- data/test/unit/test_embedded_document.rb +718 -0
- data/test/unit/test_finder_options.rb +296 -0
- data/test/unit/test_key.rb +172 -0
- data/test/unit/test_mongo_mapper.rb +65 -0
- data/test/unit/test_observing.rb +101 -0
- data/test/unit/test_pagination.rb +113 -0
- data/test/unit/test_rails_compatibility.rb +49 -0
- data/test/unit/test_serializations.rb +52 -0
- data/test/unit/test_support.rb +342 -0
- data/test/unit/test_time_zones.rb +40 -0
- data/test/unit/test_validations.rb +503 -0
- metadata +233 -0
data/test/models.rb
ADDED
@@ -0,0 +1,271 @@
|
|
1
|
+
# custom type
|
2
|
+
class WindowSize
|
3
|
+
attr_reader :width, :height
|
4
|
+
|
5
|
+
def self.to_mongo(value)
|
6
|
+
value.to_a
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.from_mongo(value)
|
10
|
+
value.is_a?(self) ? value : WindowSize.new(value)
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(*args)
|
14
|
+
@width, @height = args.flatten
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_a
|
18
|
+
[width, height]
|
19
|
+
end
|
20
|
+
|
21
|
+
def ==(other)
|
22
|
+
other.is_a?(self.class) && other.width == width && other.height == height
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
module AccountsExtensions
|
27
|
+
def inactive
|
28
|
+
all(:last_logged_in => nil)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class Post
|
33
|
+
include MongoMapper::Document
|
34
|
+
|
35
|
+
key :title, String
|
36
|
+
key :body, String
|
37
|
+
|
38
|
+
many :comments, :as => :commentable, :class_name => 'PostComment'
|
39
|
+
|
40
|
+
timestamps!
|
41
|
+
end
|
42
|
+
|
43
|
+
class PostComment
|
44
|
+
include MongoMapper::Document
|
45
|
+
|
46
|
+
key :username, String, :default => 'Anonymous'
|
47
|
+
key :body, String
|
48
|
+
|
49
|
+
key :commentable_id, ObjectId
|
50
|
+
key :commentable_type, String
|
51
|
+
belongs_to :commentable, :polymorphic => true
|
52
|
+
|
53
|
+
timestamps!
|
54
|
+
end
|
55
|
+
|
56
|
+
class Address
|
57
|
+
include MongoMapper::EmbeddedDocument
|
58
|
+
|
59
|
+
key :address, String
|
60
|
+
key :city, String
|
61
|
+
key :state, String
|
62
|
+
key :zip, Integer
|
63
|
+
end
|
64
|
+
|
65
|
+
class Message
|
66
|
+
include MongoMapper::Document
|
67
|
+
|
68
|
+
key :body, String
|
69
|
+
key :position, Integer
|
70
|
+
key :_type, String
|
71
|
+
key :room_id, ObjectId
|
72
|
+
|
73
|
+
belongs_to :room
|
74
|
+
end
|
75
|
+
|
76
|
+
class Enter < Message; end
|
77
|
+
class Exit < Message; end
|
78
|
+
class Chat < Message; end
|
79
|
+
|
80
|
+
class Room
|
81
|
+
include MongoMapper::Document
|
82
|
+
|
83
|
+
key :name, String
|
84
|
+
many :messages, :polymorphic => true, :order => 'position' do
|
85
|
+
def older
|
86
|
+
all(:position => {'$gt' => 5})
|
87
|
+
end
|
88
|
+
end
|
89
|
+
many :latest_messages, :class_name => 'Message', :order => 'position desc', :limit => 2
|
90
|
+
|
91
|
+
many :accounts, :polymorphic => true, :extend => AccountsExtensions
|
92
|
+
end
|
93
|
+
|
94
|
+
class Account
|
95
|
+
include MongoMapper::Document
|
96
|
+
|
97
|
+
key :_type, String
|
98
|
+
key :room_id, ObjectId
|
99
|
+
key :last_logged_in, Time
|
100
|
+
|
101
|
+
belongs_to :room
|
102
|
+
end
|
103
|
+
class User < Account; end
|
104
|
+
class Bot < Account; end
|
105
|
+
|
106
|
+
class Answer
|
107
|
+
include MongoMapper::Document
|
108
|
+
|
109
|
+
key :body, String
|
110
|
+
end
|
111
|
+
|
112
|
+
module PeopleExtensions
|
113
|
+
def find_by_name(name)
|
114
|
+
detect { |p| p.name == name }
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
module CollaboratorsExtensions
|
119
|
+
def top
|
120
|
+
first
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
class Project
|
125
|
+
include MongoMapper::Document
|
126
|
+
|
127
|
+
key :name, String
|
128
|
+
|
129
|
+
many :people, :extend => PeopleExtensions
|
130
|
+
many :collaborators, :extend => CollaboratorsExtensions
|
131
|
+
|
132
|
+
many :statuses, :order => 'position' do
|
133
|
+
def open
|
134
|
+
all(:name => %w(New Assigned))
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
many :addresses do
|
139
|
+
def find_all_by_state(state)
|
140
|
+
# can't use select here for some reason
|
141
|
+
find_all { |a| a.state == state }
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
class Collaborator
|
147
|
+
include MongoMapper::Document
|
148
|
+
key :project_id, ObjectId
|
149
|
+
key :name, String
|
150
|
+
belongs_to :project
|
151
|
+
end
|
152
|
+
|
153
|
+
class Status
|
154
|
+
include MongoMapper::Document
|
155
|
+
|
156
|
+
key :project_id, ObjectId
|
157
|
+
key :target_id, ObjectId
|
158
|
+
key :target_type, String
|
159
|
+
key :name, String, :required => true
|
160
|
+
key :position, Integer
|
161
|
+
|
162
|
+
belongs_to :project
|
163
|
+
belongs_to :target, :polymorphic => true
|
164
|
+
end
|
165
|
+
|
166
|
+
class RealPerson
|
167
|
+
include MongoMapper::Document
|
168
|
+
|
169
|
+
key :room_id, ObjectId
|
170
|
+
key :name, String
|
171
|
+
|
172
|
+
belongs_to :room
|
173
|
+
|
174
|
+
many :pets
|
175
|
+
|
176
|
+
def realname=(n)
|
177
|
+
self.name = n
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
class Person
|
182
|
+
include MongoMapper::EmbeddedDocument
|
183
|
+
|
184
|
+
key :name, String
|
185
|
+
key :child, Person
|
186
|
+
|
187
|
+
many :pets
|
188
|
+
end
|
189
|
+
|
190
|
+
class Pet
|
191
|
+
include MongoMapper::EmbeddedDocument
|
192
|
+
|
193
|
+
key :name, String
|
194
|
+
key :species, String
|
195
|
+
end
|
196
|
+
|
197
|
+
class Media
|
198
|
+
include MongoMapper::EmbeddedDocument
|
199
|
+
|
200
|
+
key :_type, String
|
201
|
+
key :file, String
|
202
|
+
|
203
|
+
key :visible, Boolean
|
204
|
+
end
|
205
|
+
|
206
|
+
class Video < Media
|
207
|
+
key :length, Integer
|
208
|
+
end
|
209
|
+
|
210
|
+
class Image < Media
|
211
|
+
key :width, Integer
|
212
|
+
key :height, Integer
|
213
|
+
end
|
214
|
+
|
215
|
+
class Music < Media
|
216
|
+
key :bitrate, String
|
217
|
+
end
|
218
|
+
|
219
|
+
class Catalog
|
220
|
+
include MongoMapper::Document
|
221
|
+
|
222
|
+
many :medias, :polymorphic => true do
|
223
|
+
def visible
|
224
|
+
# for some reason we can't use select here
|
225
|
+
find_all { |m| m.visible? }
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
module TrModels
|
231
|
+
class Transport
|
232
|
+
include MongoMapper::EmbeddedDocument
|
233
|
+
|
234
|
+
key :_type, String
|
235
|
+
key :license_plate, String
|
236
|
+
key :purchased_on, Date
|
237
|
+
end
|
238
|
+
|
239
|
+
class Car < TrModels::Transport
|
240
|
+
include MongoMapper::EmbeddedDocument
|
241
|
+
|
242
|
+
key :model, String
|
243
|
+
key :year, Integer
|
244
|
+
end
|
245
|
+
|
246
|
+
class Bus < TrModels::Transport
|
247
|
+
include MongoMapper::EmbeddedDocument
|
248
|
+
|
249
|
+
key :max_passengers, Integer
|
250
|
+
end
|
251
|
+
|
252
|
+
class Ambulance < TrModels::Transport
|
253
|
+
include MongoMapper::EmbeddedDocument
|
254
|
+
|
255
|
+
key :icu, Boolean
|
256
|
+
end
|
257
|
+
|
258
|
+
class Fleet
|
259
|
+
include MongoMapper::Document
|
260
|
+
|
261
|
+
module TransportsExtension
|
262
|
+
def to_be_replaced
|
263
|
+
# for some reason we can't use select
|
264
|
+
find_all { |t| t.purchased_on < 2.years.ago.to_date }
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
many :transports, :polymorphic => true, :class_name => "TrModels::Transport", :extend => TransportsExtension
|
269
|
+
key :name, String
|
270
|
+
end
|
271
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module CustomMatchers
|
2
|
+
custom_matcher :be_nil do |receiver, matcher, args|
|
3
|
+
matcher.positive_failure_message = "Expected #{receiver} to be nil but it wasn't"
|
4
|
+
matcher.negative_failure_message = "Expected #{receiver} not to be nil but it was"
|
5
|
+
receiver.nil?
|
6
|
+
end
|
7
|
+
|
8
|
+
custom_matcher :be_blank do |receiver, matcher, args|
|
9
|
+
matcher.positive_failure_message = "Expected #{receiver} to be blank but it wasn't"
|
10
|
+
matcher.negative_failure_message = "Expected #{receiver} not to be blank but it was"
|
11
|
+
receiver.blank?
|
12
|
+
end
|
13
|
+
|
14
|
+
custom_matcher :be_true do |receiver, matcher, args|
|
15
|
+
matcher.positive_failure_message = "Expected #{receiver} to be true but it wasn't"
|
16
|
+
matcher.negative_failure_message = "Expected #{receiver} not to be true but it was"
|
17
|
+
receiver.eql?(true)
|
18
|
+
end
|
19
|
+
|
20
|
+
custom_matcher :be_false do |receiver, matcher, args|
|
21
|
+
matcher.positive_failure_message = "Expected #{receiver} to be false but it wasn't"
|
22
|
+
matcher.negative_failure_message = "Expected #{receiver} not to be false but it was"
|
23
|
+
receiver.eql?(false)
|
24
|
+
end
|
25
|
+
|
26
|
+
custom_matcher :be_valid do |receiver, matcher, args|
|
27
|
+
matcher.positive_failure_message = "Expected to be valid but it was invalid #{receiver.errors.inspect}"
|
28
|
+
matcher.negative_failure_message = "Expected to be invalid but it was valid #{receiver.errors.inspect}"
|
29
|
+
receiver.valid?
|
30
|
+
end
|
31
|
+
|
32
|
+
custom_matcher :have_error_on do |receiver, matcher, args|
|
33
|
+
receiver.valid?
|
34
|
+
attribute = args[0]
|
35
|
+
expected_message = args[1]
|
36
|
+
|
37
|
+
if expected_message.nil?
|
38
|
+
matcher.positive_failure_message = "#{receiver} had no errors on #{attribute}"
|
39
|
+
matcher.negative_failure_message = "#{receiver} had errors on #{attribute} #{receiver.errors.inspect}"
|
40
|
+
!receiver.errors.on(attribute).blank?
|
41
|
+
else
|
42
|
+
actual = receiver.errors.on(attribute)
|
43
|
+
matcher.positive_failure_message = %Q(Expected error on #{attribute} to be "#{expected_message}" but was "#{actual}")
|
44
|
+
matcher.negative_failure_message = %Q(Expected error on #{attribute} not to be "#{expected_message}" but was "#{actual}")
|
45
|
+
actual == expected_message
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
custom_matcher :have_index do |receiver, matcher, args|
|
50
|
+
index_name = args[0]
|
51
|
+
matcher.positive_failure_message = "#{receiver} does not have index named #{index_name}, but should"
|
52
|
+
matcher.negative_failure_message = "#{receiver} does have index named #{index_name}, but should not"
|
53
|
+
!receiver.collection.index_information.detect { |index| index[0] == index_name }.nil?
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class Test::Unit::TestCase
|
2
|
+
def run_with_test_timing(*args, &block)
|
3
|
+
begin_time = Time.now
|
4
|
+
run_without_test_timing(*args, &block)
|
5
|
+
end_time = Time.now
|
6
|
+
|
7
|
+
duration = end_time - begin_time
|
8
|
+
threshold = 0.5
|
9
|
+
|
10
|
+
if duration > threshold
|
11
|
+
puts "\nSLOW TEST: #{duration} - #{self.name}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
alias_method_chain :run, :test_timing unless method_defined?(:run_without_test_timing)
|
16
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../lib/mongo_mapper')
|
2
|
+
|
3
|
+
gem 'jnunemaker-matchy', '0.4.0'
|
4
|
+
gem 'shoulda', '2.10.2'
|
5
|
+
gem 'timecop', '0.3.1'
|
6
|
+
gem 'mocha', '0.9.8'
|
7
|
+
|
8
|
+
require 'matchy'
|
9
|
+
require 'shoulda'
|
10
|
+
require 'timecop'
|
11
|
+
require 'mocha'
|
12
|
+
require 'pp'
|
13
|
+
|
14
|
+
require 'support/custom_matchers'
|
15
|
+
require 'support/timing'
|
16
|
+
|
17
|
+
class Test::Unit::TestCase
|
18
|
+
include CustomMatchers
|
19
|
+
end
|
20
|
+
|
21
|
+
test_dir = File.expand_path(File.dirname(__FILE__) + '/../tmp')
|
22
|
+
FileUtils.mkdir_p(test_dir) unless File.exist?(test_dir)
|
23
|
+
|
24
|
+
MongoMapper.connection = Mongo::Connection.new('127.0.0.1', 27017, {
|
25
|
+
:logger => Logger.new(test_dir + '/test.log')
|
26
|
+
})
|
27
|
+
MongoMapper.database = 'test'
|
@@ -0,0 +1,189 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class JsonSerializationTest < Test::Unit::TestCase
|
4
|
+
class Tag
|
5
|
+
include MongoMapper::EmbeddedDocument
|
6
|
+
key :name, String
|
7
|
+
end
|
8
|
+
|
9
|
+
class Contact
|
10
|
+
include MongoMapper::Document
|
11
|
+
key :name, String
|
12
|
+
key :age, Integer
|
13
|
+
key :created_at, Time
|
14
|
+
key :awesome, Boolean
|
15
|
+
key :preferences, Hash
|
16
|
+
|
17
|
+
many :tags, :class_name => 'JsonSerializationTest::Tag'
|
18
|
+
end
|
19
|
+
|
20
|
+
def setup
|
21
|
+
Contact.include_root_in_json = false
|
22
|
+
@contact = Contact.new(
|
23
|
+
:name => 'Konata Izumi',
|
24
|
+
:age => 16,
|
25
|
+
:created_at => Time.utc(2006, 8, 1),
|
26
|
+
:awesome => true,
|
27
|
+
:preferences => { :shows => 'anime' }
|
28
|
+
)
|
29
|
+
end
|
30
|
+
|
31
|
+
should "include demodulized root" do
|
32
|
+
Contact.include_root_in_json = true
|
33
|
+
assert_match %r{^\{"contact": \{}, @contact.to_json
|
34
|
+
end
|
35
|
+
|
36
|
+
should "encode all encodable attributes" do
|
37
|
+
json = @contact.to_json
|
38
|
+
|
39
|
+
assert_no_match %r{"_id"}, json
|
40
|
+
assert_match %r{"name":"Konata Izumi"}, json
|
41
|
+
assert_match %r{"age":16}, json
|
42
|
+
assert json.include?(%("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))}))
|
43
|
+
assert_match %r{"awesome":true}, json
|
44
|
+
assert_match %r{"preferences":\{"shows":"anime"\}}, json
|
45
|
+
end
|
46
|
+
|
47
|
+
should "allow attribute filtering with only" do
|
48
|
+
json = @contact.to_json(:only => [:name, :age])
|
49
|
+
|
50
|
+
assert_no_match %r{"_id"}, json
|
51
|
+
assert_match %r{"name":"Konata Izumi"}, json
|
52
|
+
assert_match %r{"age":16}, json
|
53
|
+
assert_no_match %r{"awesome"}, json
|
54
|
+
assert_no_match %r{"created_at"}, json
|
55
|
+
assert_no_match %r{"preferences"}, json
|
56
|
+
end
|
57
|
+
|
58
|
+
should "allow attribute filtering with except" do
|
59
|
+
json = @contact.to_json(:except => [:name, :age])
|
60
|
+
|
61
|
+
assert_no_match %r{"_id"}, json
|
62
|
+
assert_no_match %r{"name"}, json
|
63
|
+
assert_no_match %r{"age"}, json
|
64
|
+
assert_match %r{"awesome"}, json
|
65
|
+
assert_match %r{"created_at"}, json
|
66
|
+
assert_match %r{"preferences"}, json
|
67
|
+
end
|
68
|
+
|
69
|
+
context "_id key" do
|
70
|
+
should "not be included by default" do
|
71
|
+
json = @contact.to_json
|
72
|
+
assert_no_match %r{"_id":}, json
|
73
|
+
end
|
74
|
+
|
75
|
+
should "not be included even if :except is used" do
|
76
|
+
json = @contact.to_json(:except => :name)
|
77
|
+
assert_no_match %r{"_id":}, json
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context "id method" do
|
82
|
+
setup do
|
83
|
+
def @contact.label; "Has cheezburger"; end
|
84
|
+
def @contact.favorite_quote; "Constraints are liberating"; end
|
85
|
+
end
|
86
|
+
|
87
|
+
should "be included by default" do
|
88
|
+
json = @contact.to_json
|
89
|
+
assert_match %r{"id"}, json
|
90
|
+
end
|
91
|
+
|
92
|
+
should "be included when single method included" do
|
93
|
+
json = @contact.to_json(:methods => :label)
|
94
|
+
assert_match %r{"id"}, json
|
95
|
+
assert_match %r{"label":"Has cheezburger"}, json
|
96
|
+
assert_match %r{"name":"Konata Izumi"}, json
|
97
|
+
assert_no_match %r{"favorite_quote":"Constraints are liberating"}, json
|
98
|
+
end
|
99
|
+
|
100
|
+
should "be included when multiple methods included" do
|
101
|
+
json = @contact.to_json(:methods => [:label, :favorite_quote])
|
102
|
+
assert_match %r{"id"}, json
|
103
|
+
assert_match %r{"label":"Has cheezburger"}, json
|
104
|
+
assert_match %r{"favorite_quote":"Constraints are liberating"}, json
|
105
|
+
assert_match %r{"name":"Konata Izumi"}, json
|
106
|
+
end
|
107
|
+
|
108
|
+
should "not be included if :only is present" do
|
109
|
+
json = @contact.to_json(:only => :name)
|
110
|
+
assert_no_match %r{"id":}, json
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
context "including methods" do
|
115
|
+
setup do
|
116
|
+
def @contact.label; "Has cheezburger"; end
|
117
|
+
def @contact.favorite_quote; "Constraints are liberating"; end
|
118
|
+
end
|
119
|
+
|
120
|
+
should "include single method" do
|
121
|
+
json = @contact.to_json(:methods => :label)
|
122
|
+
assert_match %r{"label":"Has cheezburger"}, json
|
123
|
+
end
|
124
|
+
|
125
|
+
should "include multiple methods" do
|
126
|
+
json = @contact.to_json(:only => :name, :methods => [:label, :favorite_quote])
|
127
|
+
assert_match %r{"label":"Has cheezburger"}, json
|
128
|
+
assert_match %r{"favorite_quote":"Constraints are liberating"}, json
|
129
|
+
assert_match %r{"name":"Konata Izumi"}, json
|
130
|
+
assert_no_match %r{"age":16}, json
|
131
|
+
assert_no_match %r{"awesome"}, json
|
132
|
+
assert_no_match %r{"created_at"}, json
|
133
|
+
assert_no_match %r{"preferences"}, json
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
context "array of records" do
|
138
|
+
setup do
|
139
|
+
@contacts = [
|
140
|
+
Contact.new(:name => 'David', :age => 39),
|
141
|
+
Contact.new(:name => 'Mary', :age => 14)
|
142
|
+
]
|
143
|
+
end
|
144
|
+
|
145
|
+
should "allow attribute filtering with only" do
|
146
|
+
json = @contacts.to_json(:only => :name)
|
147
|
+
assert_match %r{\{"name":"David"\}}, json
|
148
|
+
assert_match %r{\{"name":"Mary"\}}, json
|
149
|
+
end
|
150
|
+
|
151
|
+
should "allow attribute filtering with except" do
|
152
|
+
json = @contacts.to_json(:except => [:name, :preferences, :awesome, :created_at, :updated_at])
|
153
|
+
assert_match %r{"age":39}, json
|
154
|
+
assert_match %r{"age":14}, json
|
155
|
+
assert_no_match %r{"name":}, json
|
156
|
+
assert_no_match %r{"preferences":}, json
|
157
|
+
assert_no_match %r{"awesome":}, json
|
158
|
+
assert_no_match %r{"created_at":}, json
|
159
|
+
assert_no_match %r{"updated_at":}, json
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
should "allow options for hash of records" do
|
164
|
+
contacts = {
|
165
|
+
1 => Contact.new(:name => 'David', :age => 39),
|
166
|
+
2 => Contact.new(:name => 'Mary', :age => 14)
|
167
|
+
}
|
168
|
+
json = contacts.to_json(:only => [1, :name])
|
169
|
+
assert_match %r{"1":}, json
|
170
|
+
assert_match %r{\{"name":"David"\}}, json
|
171
|
+
assert_no_match %r{"2":}, json
|
172
|
+
end
|
173
|
+
|
174
|
+
should "include embedded attributes" do
|
175
|
+
contact = Contact.new(:name => 'John', :age => 27)
|
176
|
+
contact.tags = [Tag.new(:name => 'awesome'), Tag.new(:name => 'ruby')]
|
177
|
+
json = contact.to_json
|
178
|
+
assert_match %r{"tags":}, json
|
179
|
+
assert_match %r{"name":"awesome"}, json
|
180
|
+
assert_match %r{"name":"ruby"}, json
|
181
|
+
end
|
182
|
+
|
183
|
+
should "include dynamic attributes" do
|
184
|
+
contact = Contact.new(:name => 'John', :age => 27, :foo => 'bar')
|
185
|
+
contact['smell'] = 'stinky'
|
186
|
+
json = contact.to_json
|
187
|
+
assert_match %r{"smell":"stinky"}, json
|
188
|
+
end
|
189
|
+
end
|