mongo_mapper-rails3 0.7.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. data/.gitignore +10 -0
  2. data/Gemfile +15 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +60 -0
  5. data/Rakefile +58 -0
  6. data/VERSION +1 -0
  7. data/bin/mmconsole +60 -0
  8. data/lib/mongo_mapper.rb +131 -0
  9. data/lib/mongo_mapper/document.rb +439 -0
  10. data/lib/mongo_mapper/embedded_document.rb +68 -0
  11. data/lib/mongo_mapper/plugins.rb +30 -0
  12. data/lib/mongo_mapper/plugins/associations.rb +106 -0
  13. data/lib/mongo_mapper/plugins/associations/base.rb +123 -0
  14. data/lib/mongo_mapper/plugins/associations/belongs_to_polymorphic_proxy.rb +30 -0
  15. data/lib/mongo_mapper/plugins/associations/belongs_to_proxy.rb +25 -0
  16. data/lib/mongo_mapper/plugins/associations/collection.rb +21 -0
  17. data/lib/mongo_mapper/plugins/associations/embedded_collection.rb +50 -0
  18. data/lib/mongo_mapper/plugins/associations/in_array_proxy.rb +141 -0
  19. data/lib/mongo_mapper/plugins/associations/many_documents_as_proxy.rb +28 -0
  20. data/lib/mongo_mapper/plugins/associations/many_documents_proxy.rb +120 -0
  21. data/lib/mongo_mapper/plugins/associations/many_embedded_polymorphic_proxy.rb +31 -0
  22. data/lib/mongo_mapper/plugins/associations/many_embedded_proxy.rb +23 -0
  23. data/lib/mongo_mapper/plugins/associations/many_polymorphic_proxy.rb +13 -0
  24. data/lib/mongo_mapper/plugins/associations/one_proxy.rb +68 -0
  25. data/lib/mongo_mapper/plugins/associations/proxy.rb +119 -0
  26. data/lib/mongo_mapper/plugins/callbacks.rb +87 -0
  27. data/lib/mongo_mapper/plugins/clone.rb +14 -0
  28. data/lib/mongo_mapper/plugins/descendants.rb +17 -0
  29. data/lib/mongo_mapper/plugins/dirty.rb +120 -0
  30. data/lib/mongo_mapper/plugins/equality.rb +24 -0
  31. data/lib/mongo_mapper/plugins/identity_map.rb +124 -0
  32. data/lib/mongo_mapper/plugins/inspect.rb +15 -0
  33. data/lib/mongo_mapper/plugins/keys.rb +310 -0
  34. data/lib/mongo_mapper/plugins/logger.rb +19 -0
  35. data/lib/mongo_mapper/plugins/pagination.rb +26 -0
  36. data/lib/mongo_mapper/plugins/pagination/proxy.rb +72 -0
  37. data/lib/mongo_mapper/plugins/protected.rb +46 -0
  38. data/lib/mongo_mapper/plugins/rails.rb +46 -0
  39. data/lib/mongo_mapper/plugins/serialization.rb +50 -0
  40. data/lib/mongo_mapper/plugins/validations.rb +88 -0
  41. data/lib/mongo_mapper/query.rb +130 -0
  42. data/lib/mongo_mapper/support.rb +217 -0
  43. data/lib/mongo_mapper/support/descendant_appends.rb +46 -0
  44. data/lib/mongo_mapper/support/find.rb +77 -0
  45. data/mongo_mapper-rails3.gemspec +208 -0
  46. data/performance/read_write.rb +52 -0
  47. data/specs.watchr +51 -0
  48. data/test/NOTE_ON_TESTING +1 -0
  49. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +63 -0
  50. data/test/functional/associations/test_belongs_to_proxy.rb +101 -0
  51. data/test/functional/associations/test_in_array_proxy.rb +321 -0
  52. data/test/functional/associations/test_many_documents_as_proxy.rb +229 -0
  53. data/test/functional/associations/test_many_documents_proxy.rb +453 -0
  54. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +176 -0
  55. data/test/functional/associations/test_many_embedded_proxy.rb +256 -0
  56. data/test/functional/associations/test_many_polymorphic_proxy.rb +302 -0
  57. data/test/functional/associations/test_one_proxy.rb +161 -0
  58. data/test/functional/test_associations.rb +44 -0
  59. data/test/functional/test_binary.rb +27 -0
  60. data/test/functional/test_callbacks.rb +81 -0
  61. data/test/functional/test_dirty.rb +163 -0
  62. data/test/functional/test_document.rb +1244 -0
  63. data/test/functional/test_embedded_document.rb +125 -0
  64. data/test/functional/test_identity_map.rb +508 -0
  65. data/test/functional/test_logger.rb +20 -0
  66. data/test/functional/test_modifiers.rb +252 -0
  67. data/test/functional/test_pagination.rb +93 -0
  68. data/test/functional/test_protected.rb +161 -0
  69. data/test/functional/test_string_id_compatibility.rb +67 -0
  70. data/test/functional/test_validations.rb +329 -0
  71. data/test/models.rb +232 -0
  72. data/test/support/custom_matchers.rb +55 -0
  73. data/test/support/timing.rb +16 -0
  74. data/test/test_helper.rb +59 -0
  75. data/test/unit/associations/test_base.rb +207 -0
  76. data/test/unit/associations/test_proxy.rb +105 -0
  77. data/test/unit/serializers/test_json_serializer.rb +189 -0
  78. data/test/unit/test_descendant_appends.rb +71 -0
  79. data/test/unit/test_document.rb +231 -0
  80. data/test/unit/test_dynamic_finder.rb +123 -0
  81. data/test/unit/test_embedded_document.rb +663 -0
  82. data/test/unit/test_keys.rb +169 -0
  83. data/test/unit/test_lint.rb +8 -0
  84. data/test/unit/test_mongo_mapper.rb +125 -0
  85. data/test/unit/test_pagination.rb +160 -0
  86. data/test/unit/test_plugins.rb +51 -0
  87. data/test/unit/test_query.rb +334 -0
  88. data/test/unit/test_rails.rb +123 -0
  89. data/test/unit/test_rails_compatibility.rb +57 -0
  90. data/test/unit/test_serialization.rb +51 -0
  91. data/test/unit/test_support.rb +362 -0
  92. data/test/unit/test_time_zones.rb +39 -0
  93. data/test/unit/test_validations.rb +557 -0
  94. metadata +344 -0
@@ -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[attribute].size > 0
41
+ else
42
+ actual = receiver.errors[attribute].first
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 = 1.0
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
@@ -0,0 +1,59 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../lib/mongo_mapper')
2
+
3
+ require 'active_support/core_ext/logger'
4
+ require 'active_support/core_ext/module/aliasing'
5
+
6
+ require 'matchy'
7
+ require 'shoulda'
8
+ require 'timecop'
9
+ require 'mocha'
10
+ require 'pp'
11
+
12
+ require 'support/custom_matchers'
13
+ require 'support/timing'
14
+
15
+ class Test::Unit::TestCase
16
+ include CustomMatchers
17
+
18
+ def Doc(name='Unnamed', &block)
19
+ klass = Class.new do
20
+ include MongoMapper::Document
21
+ set_collection_name "test#{rand(20)}"
22
+
23
+ if name
24
+ class_eval "def self.name; '#{name}' end"
25
+ class_eval "def self.to_s; '#{name}' end"
26
+ end
27
+ end
28
+
29
+ klass.class_eval(&block) if block_given?
30
+ klass.collection.remove
31
+ klass
32
+ end
33
+
34
+ def EDoc(name='Unnamed', &block)
35
+ klass = Class.new do
36
+ include MongoMapper::EmbeddedDocument
37
+
38
+ if name
39
+ class_eval "def self.name; '#{name}' end"
40
+ class_eval "def self.to_s; '#{name}' end"
41
+ end
42
+ end
43
+
44
+ klass.class_eval(&block) if block_given?
45
+ klass
46
+ end
47
+
48
+ def drop_indexes(klass)
49
+ if klass.database.collection_names.include?(klass.collection.name)
50
+ klass.collection.drop_indexes
51
+ end
52
+ end
53
+ end
54
+
55
+ test_dir = File.expand_path(File.dirname(__FILE__) + '/../tmp')
56
+ FileUtils.mkdir_p(test_dir) unless File.exist?(test_dir)
57
+
58
+ MongoMapper.connection = Mongo::Connection.new('127.0.0.1', 27017, {:logger => Logger.new(test_dir + '/test.log')})
59
+ MongoMapper.database = 'test'
@@ -0,0 +1,207 @@
1
+ require 'test_helper'
2
+ require 'models'
3
+
4
+ class FooMonster; end
5
+
6
+ class AssociationBaseTest < Test::Unit::TestCase
7
+ include MongoMapper::Plugins::Associations
8
+
9
+ should "initialize with type and name" do
10
+ base = Base.new(:many, :foos)
11
+ base.type.should == :many
12
+ base.name.should == :foos
13
+ end
14
+
15
+ should "also allow options when initializing" do
16
+ base = Base.new(:many, :foos, :polymorphic => true)
17
+ base.options[:polymorphic].should be_true
18
+ end
19
+
20
+ context "class_name" do
21
+ should "work for belongs_to" do
22
+ Base.new(:belongs_to, :user).class_name.should == 'User'
23
+ end
24
+
25
+ should "work for many" do
26
+ Base.new(:many, :smart_people).class_name.should == 'SmartPerson'
27
+ end
28
+
29
+ should "be changeable using class_name option" do
30
+ base = Base.new(:many, :smart_people, :class_name => 'IntelligentPerson')
31
+ base.class_name.should == 'IntelligentPerson'
32
+ end
33
+ end
34
+
35
+ context "klass" do
36
+ should "default to class_name constantized" do
37
+ Base.new(:belongs_to, :foo_monster).klass.should == FooMonster
38
+ end
39
+
40
+ should "be the specified class" do
41
+ anonnymous_class = Class.new
42
+ Base.new(:belongs_to, :foo_monster, :class => anonnymous_class).klass.should == anonnymous_class
43
+ end
44
+ end
45
+
46
+ context "many?" do
47
+ should "be true if many" do
48
+ Base.new(:many, :foos).many?.should be_true
49
+ end
50
+
51
+ should "be false if not many" do
52
+ Base.new(:belongs_to, :foo).many?.should be_false
53
+ Base.new(:one, :foo).many?.should be_false
54
+ end
55
+ end
56
+
57
+ context "one?" do
58
+ should "be true if one" do
59
+ Base.new(:one, :foo).one?.should be_true
60
+ end
61
+
62
+ should "be false if not one" do
63
+ Base.new(:many, :foo).one?.should be_false
64
+ end
65
+ end
66
+
67
+ context "belongs_to?" do
68
+ should "be true if belongs_to" do
69
+ Base.new(:belongs_to, :foo).belongs_to?.should be_true
70
+ end
71
+
72
+ should "be false if not belongs_to" do
73
+ Base.new(:many, :foos).belongs_to?.should be_false
74
+ end
75
+ end
76
+
77
+ context "polymorphic?" do
78
+ should "be true if polymorphic" do
79
+ Base.new(:many, :foos, :polymorphic => true).polymorphic?.should be_true
80
+ end
81
+
82
+ should "be false if not polymorphic" do
83
+ Base.new(:many, :bars).polymorphic?.should be_false
84
+ end
85
+ end
86
+
87
+ context "as?" do
88
+ should "be true if one" do
89
+ Base.new(:one, :foo, :as => :commentable).as?.should be_true
90
+ end
91
+
92
+ should "be false if not one" do
93
+ Base.new(:many, :foo).as?.should be_false
94
+ end
95
+ end
96
+
97
+ context "in_array?" do
98
+ should "be true if one" do
99
+ Base.new(:one, :foo, :in => :list_ids).in_array?.should be_true
100
+ end
101
+
102
+ should "be false if not one" do
103
+ Base.new(:many, :foo).in_array?.should be_false
104
+ end
105
+ end
106
+
107
+ context "query_options" do
108
+ should "default to empty hash" do
109
+ base = Base.new(:many, :foos)
110
+ base.query_options.should == {}
111
+ end
112
+
113
+ should "work with order" do
114
+ base = Base.new(:many, :foos, :order => 'position')
115
+ base.query_options.should == {:order => 'position'}
116
+ end
117
+
118
+ should "correctly parse from options" do
119
+ base = Base.new(:many, :foos, :order => 'position', :somekey => 'somevalue')
120
+ base.query_options.should == {:order => 'position', :somekey => 'somevalue'}
121
+ end
122
+ end
123
+
124
+ context "type_key_name" do
125
+ should "be _type for many" do
126
+ Base.new(:many, :foos).type_key_name.should == '_type'
127
+ end
128
+
129
+ should "be association name _ type for belongs_to" do
130
+ Base.new(:belongs_to, :foo).type_key_name.should == 'foo_type'
131
+ end
132
+ end
133
+
134
+ context "foreign_key" do
135
+ should "default to assocation name _id for belongs to" do
136
+ base = Base.new(:belongs_to, :foo)
137
+ base.foreign_key.should == 'foo_id'
138
+ end
139
+
140
+ should "be overridable with :foreign_key option" do
141
+ base = Base.new(:belongs_to, :foo, :foreign_key => 'foobar_id')
142
+ base.foreign_key.should == 'foobar_id'
143
+ end
144
+ end
145
+
146
+ should "have ivar that is association name" do
147
+ Base.new(:belongs_to, :foo).ivar.should == '@_foo'
148
+ end
149
+
150
+ context "embeddable?" do
151
+ should "be true if class is embeddable" do
152
+ base = Base.new(:many, :medias)
153
+ base.embeddable?.should be_true
154
+ end
155
+
156
+ should "be false if class is not embeddable" do
157
+ base = Base.new(:many, :statuses)
158
+ base.embeddable?.should be_false
159
+
160
+ base = Base.new(:belongs_to, :project)
161
+ base.embeddable?.should be_false
162
+ end
163
+ end
164
+
165
+ context "proxy_class" do
166
+ should "be ManyDocumentsProxy for many" do
167
+ base = Base.new(:many, :statuses)
168
+ base.proxy_class.should == ManyDocumentsProxy
169
+ end
170
+
171
+ should "be ManyPolymorphicProxy for polymorphic many" do
172
+ base = Base.new(:many, :messages, :polymorphic => true)
173
+ base.proxy_class.should == ManyPolymorphicProxy
174
+ end
175
+
176
+ should "be ManyEmbeddedProxy for many embedded" do
177
+ base = Base.new(:many, :medias)
178
+ base.proxy_class.should == ManyEmbeddedProxy
179
+ end
180
+
181
+ should "be ManyEmbeddedPolymorphicProxy for polymorphic many embedded" do
182
+ base = Base.new(:many, :medias, :polymorphic => true)
183
+ base.proxy_class.should == ManyEmbeddedPolymorphicProxy
184
+ end
185
+
186
+ should "be BelongsToProxy for belongs_to" do
187
+ base = Base.new(:belongs_to, :project)
188
+ base.proxy_class.should == BelongsToProxy
189
+ end
190
+
191
+ should "be BelongsToPolymorphicProxy for polymorphic belongs_to" do
192
+ base = Base.new(:belongs_to, :target, :polymorphic => true)
193
+ base.proxy_class.should == BelongsToPolymorphicProxy
194
+ end
195
+
196
+ should "be OneProxy for one" do
197
+ base = Base.new(:one, :target, :polymorphic => true)
198
+ base.proxy_class.should == OneProxy
199
+ end
200
+
201
+ should "be InArrayProxy for many with :in option" do
202
+ base = Base.new(:many, :messages, :in => :message_ids)
203
+ base.proxy_class.should == InArrayProxy
204
+ end
205
+ end
206
+
207
+ end
@@ -0,0 +1,105 @@
1
+ require 'test_helper'
2
+
3
+ class FakeNilProxy < MongoMapper::Plugins::Associations::Proxy
4
+ def find_target; nil end
5
+ end
6
+
7
+ class FakeBlankProxy < MongoMapper::Plugins::Associations::Proxy
8
+ def find_target; '' end
9
+ end
10
+
11
+ class FakeNumberProxy < MongoMapper::Plugins::Associations::Proxy
12
+ def find_target; 17 end
13
+ end
14
+
15
+ class FakeProxy < MongoMapper::Plugins::Associations::Proxy
16
+ def find_target; [1, 2] end
17
+ end
18
+
19
+ class ProxyTest < Test::Unit::TestCase
20
+ def setup
21
+ @owner = mock('owner')
22
+ @owner.stubs(:new?).returns(false)
23
+ @association = mock('association')
24
+ @association.stubs(:options).returns({:extend => []})
25
+
26
+ @proxy = FakeProxy.new(@owner, @association)
27
+ @nil_proxy = FakeNilProxy.new(@owner, @association)
28
+ @blank_proxy = FakeBlankProxy.new(@owner, @association)
29
+ end
30
+
31
+ should 'return true for === target' do
32
+ @proxy = FakeProxy.new(@owner, @association)
33
+ @proxy.should === Array
34
+ end
35
+
36
+ should "set target to nil when reset is called" do
37
+ @proxy.reset
38
+ @proxy.target.should be_nil
39
+ end
40
+
41
+ should "be able to inspect the proxy" do
42
+ @proxy.inspect.should == '[1, 2]'
43
+ end
44
+
45
+ context "nil?" do
46
+ should "be true if nil" do
47
+ @nil_proxy.nil?.should be_true
48
+ end
49
+
50
+ should "be false if not nil" do
51
+ @proxy.nil?.should be_false
52
+ end
53
+ end
54
+
55
+ context "blank?" do
56
+ should "be true if blank" do
57
+ @blank_proxy.blank?.should be_true
58
+ @nil_proxy.blank?.should be_true
59
+ end
60
+
61
+ should "be false if not blank" do
62
+ @proxy.blank?.should be_false
63
+ end
64
+ end
65
+
66
+ context "present?" do
67
+ should "be true if present" do
68
+ @proxy.present?.should be_true
69
+ end
70
+
71
+ should "be false if not present" do
72
+ @blank_proxy.present?.should be_false
73
+ @nil_proxy.present?.should be_false
74
+ end
75
+ end
76
+
77
+ should "delegate respond_to? to target" do
78
+ @proxy.respond_to?(:each).should be_true
79
+ @proxy.respond_to?(:size).should be_true
80
+ @proxy.respond_to?(:gsub).should be_false
81
+ end
82
+
83
+ should "alias proxy owner to owner" do
84
+ @proxy.proxy_owner.should == @owner
85
+ end
86
+
87
+ should "alias proxy target to target" do
88
+ @proxy.proxy_target.should == @target
89
+ end
90
+
91
+ context "send" do
92
+ should "work if proxy responds to method" do
93
+ @proxy.send(:reset)
94
+ @proxy.target.should be_nil
95
+ end
96
+
97
+ should "work if the target responds to the method" do
98
+ @proxy.send(:size).should == 2
99
+ end
100
+
101
+ should "not work if neither the proxy or target respond to method" do
102
+ lambda { @proxy.send(:gsub) }.should raise_error
103
+ end
104
+ end
105
+ end
@@ -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":[ ]?\{}, ActiveSupport::JSON.encode(@contact)
34
+ end
35
+
36
+ should "encode all encodable attributes" do
37
+ json = ActiveSupport::JSON.encode(@contact)
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 = ActiveSupport::JSON.encode(@contact, :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 = ActiveSupport::JSON.encode(@contact, :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 = ActiveSupport::JSON.encode(@contact)
72
+ assert_no_match %r{"_id":}, json
73
+ end
74
+
75
+ should "not be included even if :except is used" do
76
+ json = ActiveSupport::JSON.encode(@contact, :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 = ActiveSupport::JSON.encode(@contact)
89
+ assert_match %r{"id"}, json
90
+ end
91
+
92
+ should "be included when single method included" do
93
+ json = ActiveSupport::JSON.encode(@contact, :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 = ActiveSupport::JSON.encode(@contact, :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 = ActiveSupport::JSON.encode(@contact, :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 = ActiveSupport::JSON.encode(@contact, :methods => :label)
122
+ assert_match %r{"label":"Has cheezburger"}, json
123
+ end
124
+
125
+ should "include multiple methods" do
126
+ json = ActiveSupport::JSON.encode(@contact, :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 = ActiveSupport::JSON.encode(@contacts, :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 = ActiveSupport::JSON.encode(@contacts, :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 = ActiveSupport::JSON.encode(contacts, :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 = ActiveSupport::JSON.encode(contact)
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 = ActiveSupport::JSON.encode(contact)
187
+ assert_match %r{"smell":"stinky"}, json
188
+ end
189
+ end