leancloud-ruby-client 0.1.0

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.
Files changed (99) hide show
  1. checksums.yaml +7 -0
  2. data/.travis.yml +9 -0
  3. data/Gemfile +16 -0
  4. data/Gemfile.lock +86 -0
  5. data/LICENSE.txt +20 -0
  6. data/README.md +1177 -0
  7. data/Rakefile +45 -0
  8. data/VERSION +1 -0
  9. data/example.rb +35 -0
  10. data/features.md +1111 -0
  11. data/fixtures/vcr_cassettes/test_acls_arent_objects.yml +274 -0
  12. data/fixtures/vcr_cassettes/test_array_add.yml +213 -0
  13. data/fixtures/vcr_cassettes/test_array_add_pointerizing.yml +380 -0
  14. data/fixtures/vcr_cassettes/test_array_add_unique.yml +319 -0
  15. data/fixtures/vcr_cassettes/test_batch_create_object.yml +107 -0
  16. data/fixtures/vcr_cassettes/test_batch_delete_object.yml +637 -0
  17. data/fixtures/vcr_cassettes/test_batch_run.yml +109 -0
  18. data/fixtures/vcr_cassettes/test_batch_update_nils_delete_keys.yml +435 -0
  19. data/fixtures/vcr_cassettes/test_batch_update_object.yml +637 -0
  20. data/fixtures/vcr_cassettes/test_contains_all.yml +1143 -0
  21. data/fixtures/vcr_cassettes/test_cql.yml +99 -0
  22. data/fixtures/vcr_cassettes/test_created_at.yml +109 -0
  23. data/fixtures/vcr_cassettes/test_decrement.yml +213 -0
  24. data/fixtures/vcr_cassettes/test_deep_parse.yml +321 -0
  25. data/fixtures/vcr_cassettes/test_destroy.yml +213 -0
  26. data/fixtures/vcr_cassettes/test_empty_response.yml +1026 -0
  27. data/fixtures/vcr_cassettes/test_eq_pointerize.yml +427 -0
  28. data/fixtures/vcr_cassettes/test_equality.yml +321 -0
  29. data/fixtures/vcr_cassettes/test_get.yml +215 -0
  30. data/fixtures/vcr_cassettes/test_get_installation.yml +58 -0
  31. data/fixtures/vcr_cassettes/test_get_missing.yml +160 -0
  32. data/fixtures/vcr_cassettes/test_image_file_associate_with_object.yml +2089 -0
  33. data/fixtures/vcr_cassettes/test_image_file_save.yml +1928 -0
  34. data/fixtures/vcr_cassettes/test_include.yml +321 -0
  35. data/fixtures/vcr_cassettes/test_new_model.yml +109 -0
  36. data/fixtures/vcr_cassettes/test_new_object.yml +109 -0
  37. data/fixtures/vcr_cassettes/test_nils_delete_keys.yml +319 -0
  38. data/fixtures/vcr_cassettes/test_object_id.yml +56 -0
  39. data/fixtures/vcr_cassettes/test_parse_delete.yml +421 -0
  40. data/fixtures/vcr_cassettes/test_pointer.yml +109 -0
  41. data/fixtures/vcr_cassettes/test_request_sms.yml +48 -0
  42. data/fixtures/vcr_cassettes/test_reset_password.yml +109 -0
  43. data/fixtures/vcr_cassettes/test_retries.yml +4173 -0
  44. data/fixtures/vcr_cassettes/test_retries_404.yml +1026 -0
  45. data/fixtures/vcr_cassettes/test_retries_404_correct.yml +1026 -0
  46. data/fixtures/vcr_cassettes/test_retries_json_error.yml +2265 -0
  47. data/fixtures/vcr_cassettes/test_retries_server_error.yml +2265 -0
  48. data/fixtures/vcr_cassettes/test_save_installation.yml +58 -0
  49. data/fixtures/vcr_cassettes/test_save_with_sub_objects.yml +484 -0
  50. data/fixtures/vcr_cassettes/test_saving_boolean_values.yml +215 -0
  51. data/fixtures/vcr_cassettes/test_saving_nested_objects.yml +62 -0
  52. data/fixtures/vcr_cassettes/test_server_update.yml +586 -0
  53. data/fixtures/vcr_cassettes/test_simple_save.yml +109 -0
  54. data/fixtures/vcr_cassettes/test_text_file_save.yml +109 -0
  55. data/fixtures/vcr_cassettes/test_update.yml +213 -0
  56. data/fixtures/vcr_cassettes/test_updated_at.yml +213 -0
  57. data/fixtures/vcr_cassettes/test_user_login.yml +276 -0
  58. data/fixtures/vcr_cassettes/test_user_save.yml +109 -0
  59. data/fixtures/vcr_cassettes/test_xget.yml +603 -0
  60. data/leancloud-ruby-client.gemspec +166 -0
  61. data/lib/faraday/better_retry.rb +94 -0
  62. data/lib/faraday/extended_parse_json.rb +39 -0
  63. data/lib/faraday/get_method_override.rb +32 -0
  64. data/lib/leancloud-ruby-client.rb +34 -0
  65. data/lib/leancloud/application.rb +7 -0
  66. data/lib/leancloud/batch.rb +53 -0
  67. data/lib/leancloud/client.rb +149 -0
  68. data/lib/leancloud/cloud.rb +28 -0
  69. data/lib/leancloud/datatypes.rb +355 -0
  70. data/lib/leancloud/error.rb +42 -0
  71. data/lib/leancloud/installation.rb +57 -0
  72. data/lib/leancloud/model.rb +14 -0
  73. data/lib/leancloud/object.rb +252 -0
  74. data/lib/leancloud/protocol.rb +193 -0
  75. data/lib/leancloud/push.rb +48 -0
  76. data/lib/leancloud/query.rb +194 -0
  77. data/lib/leancloud/user.rb +38 -0
  78. data/lib/leancloud/util.rb +93 -0
  79. data/test/cloud_functions/MyCloudCode/cloud/main.js +4 -0
  80. data/test/config/global.json +14 -0
  81. data/test/helper.rb +108 -0
  82. data/test/middleware/better_retry_test.rb +57 -0
  83. data/test/middleware/extend_parse_json_test.rb +55 -0
  84. data/test/parsers.jpg +0 -0
  85. data/test/test_batch.rb +132 -0
  86. data/test/test_client.rb +183 -0
  87. data/test/test_cloud.rb +31 -0
  88. data/test/test_datatypes.rb +105 -0
  89. data/test/test_file.rb +67 -0
  90. data/test/test_init.rb +23 -0
  91. data/test/test_init_from_cloud_code.rb +8 -0
  92. data/test/test_installation.rb +49 -0
  93. data/test/test_model.rb +22 -0
  94. data/test/test_object.rb +295 -0
  95. data/test/test_push.rb +45 -0
  96. data/test/test_query.rb +198 -0
  97. data/test/test_throttle.rb +5 -0
  98. data/test/test_user.rb +60 -0
  99. metadata +298 -0
@@ -0,0 +1,105 @@
1
+ require 'helper'
2
+
3
+ class TestDatatypes < Test::Unit::TestCase
4
+ def test_pointer
5
+ data = {
6
+ AV::Protocol::KEY_CLASS_NAME => "DatatypeTestClass",
7
+ AV::Protocol::KEY_OBJECT_ID => "12345abcd"
8
+ }
9
+ p = AV::Pointer.new data
10
+
11
+ assert_equal p.to_json, "{\"__type\":\"Pointer\",\"#{AV::Protocol::KEY_CLASS_NAME}\":\"DatatypeTestClass\",\"#{AV::Protocol::KEY_OBJECT_ID}\":\"12345abcd\"}"
12
+ end
13
+
14
+ def test_pointer_make
15
+ p = AV::Pointer.make("SomeClass", "someId")
16
+ assert_equal "SomeClass", p.class_name
17
+ assert_equal "someId", p.id
18
+ end
19
+
20
+ def test_date
21
+ date_time = Time.at(0).to_datetime
22
+ parse_date = AV::Date.new(date_time)
23
+
24
+ assert_equal date_time, parse_date.value
25
+ assert_equal "1970-01-01T00:00:00.000Z", JSON.parse(parse_date.to_json)["iso"]
26
+ assert_equal 0, parse_date <=> parse_date
27
+ assert_equal 0, AV::Date.new(date_time) <=> AV::Date.new(date_time)
28
+
29
+ post = AV::Object.new("Post")
30
+ post["time"] = parse_date
31
+ post.save
32
+ q = AV.get("Post", post.id)
33
+
34
+ # time zone from parse is utc so string formats don't compare equal,
35
+ # also floating points vary a bit so only equality after rounding to millis is guaranteed
36
+ assert_equal parse_date.to_time.utc.to_datetime.iso8601(3), q["time"].to_time.utc.to_datetime.iso8601(3)
37
+ end
38
+
39
+ def test_date_with_bad_data
40
+ assert_raise do
41
+ AV::Date.new(2014)
42
+ end
43
+ assert_raise do
44
+ AV::Date.new(nil)
45
+ end
46
+ end
47
+
48
+ def test_date_with_time
49
+ time = Time.parse("01/01/2012 23:59:59")
50
+ assert_equal time, AV::Date.new(time).to_time
51
+ end
52
+
53
+ def test_bytes
54
+ data = {
55
+ "base64" => Base64.encode64("testing bytes!")
56
+ }
57
+ byte = AV::Bytes.new data
58
+
59
+ assert_equal byte.value, "testing bytes!"
60
+ assert_equal JSON.parse(byte.to_json)[AV::Protocol::KEY_TYPE], AV::Protocol::TYPE_BYTES
61
+ assert_equal JSON.parse(byte.to_json)["base64"], Base64.encode64("testing bytes!")
62
+ end
63
+
64
+ def test_increment
65
+ amount = 5
66
+ increment = AV::Increment.new amount
67
+
68
+ assert_equal increment.to_json, "{\"__op\":\"Increment\",\"amount\":#{amount}}"
69
+ end
70
+
71
+ def test_geopoint
72
+ # '{"location": {"__type":"GeoPoint", "latitude":40.0, "longitude":-30.0}}'
73
+ data = {
74
+ "longitude" => 40.0,
75
+ "latitude" => -30.0
76
+ }
77
+ gp = AV::GeoPoint.new data
78
+
79
+ assert_equal JSON.parse(gp.to_json)["longitude"], data["longitude"]
80
+ assert_equal JSON.parse(gp.to_json)["latitude"], data["latitude"]
81
+ assert_equal JSON.parse(gp.to_json)[AV::Protocol::KEY_TYPE], AV::Protocol::TYPE_GEOPOINT
82
+
83
+ post = AV::Object.new("Post")
84
+ post["location"] = gp
85
+ post.save
86
+ q = AV.get("Post", post.id)
87
+ assert_equal gp, q["location"]
88
+ end
89
+
90
+
91
+ # deprecating - see test_file.rb for new implementation
92
+ # ----------------------------
93
+ #def test_file
94
+ # data = {"name" => "test/parsers.png"}
95
+ # file = AV::File.new(data)
96
+ # assert_equal JSON.parse(file.to_json)["name"], data["name"]
97
+ # assert_equal JSON.parse(file.to_json)[AV::Protocol::KEY_TYPE], AV::Protocol::TYPE_FILE
98
+
99
+ # post = AV::Object.new("Post")
100
+ # post["avatar"] = file
101
+ # post.save
102
+ # q = AV.get("Post", post.id)
103
+ # assert_equal file.parse_filename, q["avatar"].parse_filename
104
+ #end
105
+ end
data/test/test_file.rb ADDED
@@ -0,0 +1,67 @@
1
+ require 'helper'
2
+
3
+ class TestFile < AVTestCase
4
+
5
+ def test_file_save
6
+ VCR.use_cassette('test_text_file_save', :record => :new_episodes) do
7
+ tf = AV::File.new({
8
+ :body => "Hello World!",
9
+ :local_filename => "hello.txt",
10
+ :content_type => "text/plain"
11
+ })
12
+ tf.save
13
+
14
+ assert tf.local_filename
15
+ assert tf.url
16
+ assert tf.parse_filename
17
+ assert tf.body
18
+ assert tf.to_json
19
+ assert_equal String, tf.body.class
20
+ end
21
+ end
22
+
23
+ def test_image_save
24
+ #VCR.use_cassette('test_image_file_save', :record => :new_episodes) do
25
+ tf = AV::File.new({
26
+ :body => IO.read("test/parsers.jpg"),
27
+ :local_filename => "parsers.jpg",
28
+ :content_type => "image/jpeg"
29
+ })
30
+ tf.save
31
+
32
+ assert tf.local_filename
33
+ assert tf.url
34
+ assert tf.parse_filename
35
+ assert tf.body
36
+ assert tf.to_json
37
+ #end
38
+ end
39
+
40
+ def test_associate_with_object
41
+ #VCR.use_cassette('test_image_file_associate_with_object', :record => :new_episodes) do
42
+ tf = AV::File.new({
43
+ :body => IO.read("test/parsers.jpg"),
44
+ :local_filename => "parsers.jpg",
45
+ :content_type => "image/jpeg"
46
+ })
47
+ tf.save
48
+
49
+ assert tf.local_filename
50
+ assert tf.url
51
+ assert tf.parse_filename
52
+ assert tf.body
53
+ assert tf.to_json
54
+
55
+ object = AV::Object.new("ShouldHaveFile")
56
+ object["photo"] = tf
57
+ object.save
58
+
59
+ assert object["photo"]
60
+ assert object["objectId"]
61
+
62
+ object.refresh.save
63
+ #end
64
+ end
65
+
66
+
67
+ end
data/test/test_init.rb ADDED
@@ -0,0 +1,23 @@
1
+ require 'helper'
2
+
3
+ #AV.init :application_id => $LC_APPLICATION_ID, :api_key => $LC_APPLICATION_KEY
4
+
5
+ class TestInit < Test::Unit::TestCase
6
+ def setup
7
+ AV.destroy
8
+ end
9
+
10
+ def test_no_api_keys_error
11
+ fake = AV::Object.new "shouldNeverExist"
12
+ fake["foo"] = "bar"
13
+
14
+ begin
15
+ fake.save
16
+ rescue
17
+ error_triggered = true
18
+ end
19
+
20
+ assert_equal error_triggered, true
21
+ assert_equal fake[AV::Protocol::KEY_OBJECT_ID], nil
22
+ end
23
+ end
@@ -0,0 +1,8 @@
1
+ require 'helper'
2
+
3
+ class TestInitFromCloudCode < Test::Unit::TestCase
4
+ def test_init
5
+ client = AV.init_from_cloud_code("test/config/global.json")
6
+ assert_equal AV::Client, client.class
7
+ end
8
+ end
@@ -0,0 +1,49 @@
1
+ require 'helper'
2
+
3
+ class TestInstallation < AVTestCase
4
+ def test_retrieving_installation_data
5
+ installation_data = {
6
+ "appIdentifier"=>"net.project_name",
7
+ "appName"=>"AV Project",
8
+ "appVersion"=>"35",
9
+ "badge"=>9,
10
+ "channels"=>["", "channel1"],
11
+ "deviceToken"=> "123",
12
+ "deviceType"=>"ios",
13
+ "installationId"=>"345",
14
+ "parseVersion"=>"1.3.0",
15
+ "timeZone"=>"Europe/Chisinau",
16
+ "createdAt"=>"2014-09-18T15:04:18.602Z",
17
+ "updatedAt"=>"2014-09-19T12:17:48.509Z",
18
+ "objectId"=>"987"
19
+ }
20
+
21
+ VCR.use_cassette('test_get_installation') do
22
+ installation = AV::Installation.get "987"
23
+ assert_equal installation_data, installation
24
+ end
25
+ end
26
+
27
+ def test_changing_channels
28
+ installation = AV::Installation.new "987"
29
+ installation.channels = ["", "my-channel"]
30
+ assert_equal ["", "my-channel"], installation["channels"]
31
+ end
32
+
33
+ def test_changing_badges
34
+ installation = AV::Installation.new "987"
35
+ installation.badge = 5
36
+ assert_equal 5, installation["badge"]
37
+ end
38
+
39
+ def test_updating_installation_data
40
+ installation = AV::Installation.new "987"
41
+ installation.channels = ["", "my-channel"]
42
+ installation.badge = 5
43
+
44
+ VCR.use_cassette('test_save_installation') do
45
+ result = installation.save
46
+ assert_not_empty result["updatedAt"]
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,22 @@
1
+ require 'helper'
2
+
3
+ class TestModelObject < AV::Model
4
+ end
5
+
6
+ class TestModel < AVTestCase
7
+
8
+ def test_new
9
+ VCR.use_cassette('test_new_model', :record => :new_episodes) do
10
+ tmo = TestModelObject.new
11
+ assert_equal tmo.new?, true
12
+ tmo.save
13
+ assert_equal tmo.new?, false
14
+ end
15
+ end
16
+
17
+ def test_superclass
18
+ tmo = TestModelObject
19
+ assert_equal tmo.superclass, AV::Model
20
+ assert_equal tmo.superclass.superclass, AV::Object
21
+ end
22
+ end
@@ -0,0 +1,295 @@
1
+ require 'helper'
2
+
3
+ class TestObject < AVTestCase
4
+
5
+ def test_new?
6
+ VCR.use_cassette('test_new_object', :record => :new_episodes) do
7
+ post = AV::Object.new "Post"
8
+ assert_equal post.new?, true
9
+ post.save
10
+ assert_equal post.new?, false
11
+ end
12
+ end
13
+
14
+ def test_object_id
15
+ #VCR.use_cassette('test_object_id', :record => :new_episodes) do
16
+ post = AV::Object.new "Post"
17
+ assert_equal post.id, nil
18
+ post["title"] = "hello world"
19
+ post.save
20
+ assert_equal post.id.class, String
21
+ #end
22
+ end
23
+
24
+ def test_pointer
25
+ VCR.use_cassette('test_pointer', :record => :new_episodes) do
26
+ post = AV::Object.new "Post"
27
+ assert_nil post.pointer
28
+
29
+ post.save
30
+ pointer = post.pointer
31
+ assert_equal pointer.class_name, post.class_name
32
+ assert_equal pointer.parse_object_id, post.parse_object_id
33
+ end
34
+ end
35
+
36
+ def test_equality
37
+ VCR.use_cassette('test_equality', :record => :new_episodes) do
38
+ foo_1 = AV::Object.new("Foo")
39
+ foo_2 = AV::Object.new("Foo")
40
+
41
+ assert foo_1 != foo_2
42
+ assert foo_1 == foo_1
43
+
44
+ foo_1.save
45
+ assert foo_1 != foo_2
46
+ assert foo_2 != foo_1
47
+ assert foo_1.pointer != foo_2
48
+ assert foo_2 != foo_1.pointer
49
+ foo_2.save
50
+
51
+ assert foo_1 == foo_1
52
+ assert foo_1 != foo_2
53
+
54
+ assert foo_1 == foo_1.pointer
55
+ assert foo_1.pointer == foo_1
56
+
57
+ other_foo_1 = AV.get("Foo", foo_1.id)
58
+ assert foo_1 == other_foo_1
59
+ end
60
+ end
61
+
62
+ def test_created_at
63
+ VCR.use_cassette('test_created_at', :record => :new_episodes) do
64
+ post = AV::Object.new "Post"
65
+ assert_equal post.created_at, nil
66
+ post.save
67
+ assert_equal post.created_at.class, DateTime
68
+ end
69
+ end
70
+
71
+ def test_updated_at
72
+ VCR.use_cassette('test_updated_at', :record => :new_episodes) do
73
+ post = AV::Object.new "Post"
74
+ assert_equal post.updated_at, nil
75
+ post["title"] = "hello"
76
+ post.save
77
+ assert_equal post.updated_at, nil
78
+ post["title"] = "hello 2"
79
+ post.save
80
+ assert_equal post.updated_at.class, DateTime
81
+ end
82
+ end
83
+
84
+ def test_parse_delete
85
+ VCR.use_cassette('test_parse_delete', :record => :new_episodes) do
86
+ post = AV::Object.new "Post"
87
+ post.save
88
+ assert_equal post.id.class, String
89
+
90
+ q = AV.get("Post", post.id)
91
+ assert_equal q.id, post.id
92
+
93
+ post.parse_delete
94
+
95
+ q = AV.get("Post", post.id)
96
+ assert_true q.empty?
97
+ end
98
+ end
99
+
100
+ def test_deep_parse
101
+ VCR.use_cassette('test_deep_parse', :record => :new_episodes) do
102
+ other = AV::Object.new "Post"
103
+ other.save
104
+ post = AV::Object.new "Post"
105
+ post["other"] = other.pointer
106
+ post.save
107
+
108
+ q = AV.get("Post", post.id)
109
+ assert_equal AV::Pointer, q["other"].class
110
+ assert_equal other.pointer, q["other"]
111
+ end
112
+ end
113
+
114
+ def test_acls_arent_objects
115
+ VCR.use_cassette('test_acls_arent_objects', :record => :new_episodes) do
116
+ post = AV::Object.new("Post", "ACL" => {"*" => {"read"=>true}})
117
+ assert_equal Hash, post['ACL'].class
118
+ post.save
119
+ #assert_equal Hash, post.refresh['ACL'].class
120
+
121
+ post = AV.get("Post", post.id)
122
+ #assert_equal Hash, post['ACL'].class
123
+ end
124
+ end
125
+
126
+ def test_to_json_uses_rest_api_hash
127
+ post = AV::Object.new "Post"
128
+ hash = { 'post' => [post] }
129
+ parsed = JSON.parse(hash.to_json)
130
+ assert_equal "Post", parsed['post'][0][AV::Protocol::KEY_CLASS_NAME]
131
+ end
132
+
133
+ def test_deep_as_json
134
+ VCR.use_cassette('test_deep_as_json', :record => :new_episodes) do
135
+ other = AV::Object.new "Post"
136
+ other['date'] = AV::Date.new(DateTime.now)
137
+ assert other.as_json['date']['iso']
138
+ end
139
+ end
140
+
141
+ def test_deep_as_json_with_array
142
+ VCR.use_cassette('test_deep_as_json', :record => :new_episodes) do
143
+ other = AV::Object.new "Post"
144
+ other['date'] = AV::Date.new(DateTime.now)
145
+ other['array'] = [1, 2]
146
+ assert other.as_json['date']['iso']
147
+ end
148
+ end
149
+
150
+ def test_nils_delete_keys
151
+ VCR.use_cassette('test_nils_delete_keys', :record => :new_episodes) do
152
+ post = AV::Object.new "Post"
153
+ post["title"] = "hello"
154
+ post.save
155
+ post["title"] = nil
156
+ post.save
157
+ assert_false post.refresh.keys.include?("title")
158
+ end
159
+ end
160
+
161
+ def test_saving_nested_objects
162
+ VCR.use_cassette('test_saving_nested_objects', :record => :new_episodes) do
163
+ post = AV::Object.new "Post"
164
+ post["comment"] = AV::Object.new("Comment", "text" => "testing")
165
+ assert_raise{post.save}
166
+ end
167
+ end
168
+
169
+ def test_boolean_values_as_json
170
+ post = AV::Object.new "Post"
171
+ post["read"] = false
172
+ post["published"] = true
173
+ safe_json_hash = JSON.parse post.safe_hash.to_json
174
+ assert_equal false, safe_json_hash["read"]
175
+ assert_equal true, safe_json_hash["published"]
176
+ end
177
+
178
+ def test_saving_boolean_values
179
+ VCR.use_cassette('test_saving_boolean_values', :record => :new_episodes) do
180
+ post = AV::Object.new "Post"
181
+ post["read"] = false
182
+ post["published"] = true
183
+ post.save
184
+ retrieved_post = AV::Query.new("Post")
185
+ .eq("objectId", post["objectId"]).get.first
186
+ assert_equal false, retrieved_post["read"]
187
+ assert_equal true, retrieved_post["published"]
188
+ end
189
+ end
190
+
191
+ def test_array_add
192
+ VCR.use_cassette('test_array_add', :record => :new_episodes) do
193
+ post = AV::Object.new "Post"
194
+ post.array_add("chapters", "hello")
195
+ assert_equal ["hello"], post["chapters"]
196
+ post.save
197
+ assert_equal ["hello"], post["chapters"]
198
+
199
+ post.array_add("chapters", "goodbye")
200
+ assert_equal ["hello", "goodbye"], post["chapters"]
201
+ post.save
202
+ assert_equal ["hello", "goodbye"], post["chapters"]
203
+ end
204
+ end
205
+
206
+ def test_array_add_pointerizing
207
+ VCR.use_cassette('test_array_add_pointerizing', :record => :new_episodes) do
208
+ post = AV::Object.new "Post"
209
+
210
+ comment = AV::Object.new("Comment", "text" => "great post!")
211
+ comment.save
212
+ post.array_add("comments", comment)
213
+ assert_equal "great post!", post['comments'][0]['text']
214
+ post.save
215
+ assert_equal "great post!", post['comments'][0]['text']
216
+
217
+ post = AV::Query.new("Post").eq("objectId", post.id).tap { |q| q.include = 'comments' }.get.first
218
+ assert_equal "great post!", post['comments'][0]['text']
219
+ #post.save
220
+ assert_equal "great post!", post['comments'][0]['text']
221
+ end
222
+ end
223
+
224
+ def test_array_add_unique
225
+ VCR.use_cassette('test_array_add_unique', :record => :new_episodes) do
226
+ post = AV::Object.new "Post"
227
+ post.save
228
+
229
+ comment = AV::Object.new("Comment", "text" => "great post!")
230
+ comment.save
231
+
232
+ post.array_add_unique("comments", comment)
233
+ assert_equal "great post!", post['comments'][0]['text']
234
+ post.save
235
+ assert_equal comment, post['comments'][0]
236
+ assert post['comments'][0].instance_of?(AV::Pointer) # save returns array pointerized
237
+ end
238
+ end
239
+
240
+ def test_decrement
241
+ VCR.use_cassette('test_decrement', :record => :new_episodes) do
242
+ post = AV::Object.new "Post", 'count' => 1
243
+ post.save
244
+
245
+ post.decrement('count')
246
+ assert_equal 0, post['count']
247
+ end
248
+ end
249
+
250
+ def test_array_add_relation
251
+ omit("broken test, saving Post results in AVProtocolError: 111: can't add a relation to an non-relation field")
252
+
253
+ VCR.use_cassette('test_array_add_relation', :record => :new_episodes) do
254
+ post = AV::Object.new "Post"
255
+ post.save
256
+
257
+ comment = AV::Object.new "Comment"
258
+ comment.save
259
+
260
+ post.array_add_relation("comments", comment.pointer)
261
+ post.save
262
+
263
+ q = AV::Query.new("Comment")
264
+ q.related_to("comments", post.pointer)
265
+ comments = q.get
266
+ assert_equal comments.first["objectId"], comment["objectId"]
267
+ end
268
+ end
269
+
270
+ def test_save_with_sub_objects
271
+ VCR.use_cassette('test_save_with_sub_objects', :record => :new_episodes) do
272
+ bar = AV::Object.new("Bar", "foobar" => "foobar")
273
+ bar.save
274
+
275
+ foo = AV::Object.new("Foo", "bar" => bar, "bars" => [bar])
276
+ foo.save
277
+
278
+ assert_equal "foobar", foo['bar']['foobar']
279
+ assert_equal "foobar", foo['bars'][0]['foobar']
280
+
281
+ foo = AV::Query.new("Foo").eq("objectId", foo.id).tap { |q| q.include = 'bar,bars' }.get.first
282
+
283
+ #foo.save
284
+
285
+ assert_equal "foobar", foo['bar']['foobar']
286
+ assert_equal "foobar", foo['bars'][0]['foobar']
287
+
288
+ bar = foo['bar']
289
+ bar['baz'] = 'baz'
290
+ bar.save
291
+
292
+ assert_equal 'baz', bar['baz']
293
+ end
294
+ end
295
+ end