restful_objects 0.0.1 → 0.0.2

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.
@@ -0,0 +1,190 @@
1
+ # encoding: utf-8
2
+ require_relative '../spec_helper'
3
+
4
+ describe RestfulObjects::ObjectActions do
5
+ before :all do
6
+ RestfulObjects::DomainModel.current.reset
7
+
8
+ class Address
9
+ include RestfulObjects::Object
10
+
11
+ property :street, :string
12
+ property :number, :int
13
+ end
14
+
15
+ class CollectionsTest
16
+ include RestfulObjects::Object
17
+
18
+ collection :addresses, Address
19
+ collection :collection_full_metadata, Address
20
+ end
21
+ end
22
+
23
+ before :each do
24
+ @object = CollectionsTest.new
25
+ end
26
+
27
+ it 'should create a collection' do
28
+ @object.addresses.is_a?(Enumerable).should be_true
29
+ @object.addresses.empty?.should be_true
30
+
31
+ a = Address.new
32
+ a.street = 'Evergreen'
33
+ a.number = 1234
34
+
35
+ @object.addresses.push(a)
36
+ @object.addresses.empty?.should be_false
37
+ @object.addresses.count.should eq(1)
38
+ end
39
+
40
+ it 'should generate json for the collection' do
41
+ a1 = Address.new
42
+ @object.addresses.push a1
43
+ a2 = Address.new
44
+ @object.addresses.push a2
45
+ a3 = Address.new
46
+ @object.addresses.push a3
47
+
48
+ expected = { 'id' => 'addresses',
49
+ 'value' => [
50
+ { 'rel' => 'urn:org.restfulobjects:rels/value;collection="addresses"',
51
+ 'href' => "http://localhost/objects/Address/#{a1.object_id}",
52
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/object"',
53
+ 'method' => 'GET' },
54
+ { 'rel' => 'urn:org.restfulobjects:rels/value;collection="addresses"',
55
+ 'href' => "http://localhost/objects/Address/#{a2.object_id}",
56
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/object"',
57
+ 'method' => 'GET' },
58
+ { 'rel' => 'urn:org.restfulobjects:rels/value;collection="addresses"',
59
+ 'href' => "http://localhost/objects/Address/#{a3.object_id}",
60
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/object"',
61
+ 'method' => 'GET' }
62
+ ],
63
+ 'links' => [
64
+ { 'rel' => 'self',
65
+ 'href' => "http://localhost/objects/CollectionsTest/#{@object.object_id}/collections/addresses",
66
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/object-collection"',
67
+ 'method' => 'GET' },
68
+ { 'rel' => 'up',
69
+ 'href' => "http://localhost/objects/CollectionsTest/#{@object.object_id}",
70
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/object"',
71
+ 'method' => 'GET' },
72
+ { 'rel' => 'urn:org.restfulobjects:rels/add-to;collection="addresses"',
73
+ 'href' => "http://localhost/objects/CollectionsTest/#{@object.object_id}/collections/addresses",
74
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/object-collection"',
75
+ 'method' => 'PUT',
76
+ 'arguments' => { 'value' => nil } },
77
+ { 'rel' => 'urn:org.restfulobjects:rels/remove-from;collection="addresses"',
78
+ 'href' => "http://localhost/objects/CollectionsTest/#{@object.object_id}/collections/addresses",
79
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/object-collection"',
80
+ 'method' => 'DELETE',
81
+ 'arguments' => { 'value' => nil } },
82
+ ]
83
+ }
84
+
85
+ get "/objects/CollectionsTest/#{@object.object_id}/collections/addresses"
86
+
87
+ last_response.body.should match_json_expression expected
88
+ end
89
+
90
+ it 'should add an object to a collection' do
91
+ address = Address.new
92
+
93
+ json = { 'value' => { 'href' => "http://localhost/objects/Address/#{address.object_id}" } }.to_json
94
+
95
+ put "/objects/CollectionsTest/#{@object.object_id}/collections/addresses", {}, { input: json }
96
+
97
+ @object.addresses.include?(address).should be_true
98
+
99
+ expected = { 'id' => 'addresses',
100
+ 'value' => [
101
+ { 'rel' => 'urn:org.restfulobjects:rels/value;collection="addresses"',
102
+ 'href' => "http://localhost/objects/Address/#{address.object_id}",
103
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/object"',
104
+ 'method' => 'GET' } ],
105
+ 'links' => [
106
+ { 'rel' => 'self',
107
+ 'href' => "http://localhost/objects/CollectionsTest/#{@object.object_id}/collections/addresses",
108
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/object-collection"',
109
+ 'method' => 'GET' },
110
+ { 'rel' => 'up',
111
+ 'href' => "http://localhost/objects/CollectionsTest/#{@object.object_id}",
112
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/object"',
113
+ 'method' => 'GET' },
114
+ { 'rel' => 'urn:org.restfulobjects:rels/add-to;collection="addresses"',
115
+ 'href' => "http://localhost/objects/CollectionsTest/#{@object.object_id}/collections/addresses",
116
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/object-collection"',
117
+ 'method' => 'PUT',
118
+ 'arguments' => { 'value' => nil } },
119
+ { 'rel' => 'urn:org.restfulobjects:rels/remove-from;collection="addresses"',
120
+ 'href' => "http://localhost/objects/CollectionsTest/#{@object.object_id}/collections/addresses",
121
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/object-collection"',
122
+ 'method' => 'DELETE',
123
+ 'arguments' => { 'value' => nil } },
124
+ ]
125
+ }
126
+
127
+ last_response.body.should match_json_expression expected
128
+ end
129
+
130
+ it 'should remove an object from a collection' do
131
+ address = Address.new
132
+
133
+ @object.addresses.push(address)
134
+
135
+ json = { 'value' => { 'href' => "http://localhost/objects/Address/#{address.object_id}" } }.to_json
136
+
137
+ delete "/objects/CollectionsTest/#{@object.object_id}/collections/addresses", {}, { input: json }
138
+
139
+ @object.addresses.include?(address).should be_false
140
+
141
+ expected = { 'id' => 'addresses',
142
+ 'value' => [],
143
+ 'links' => [
144
+ { 'rel' => 'self',
145
+ 'href' => "http://localhost/objects/CollectionsTest/#{@object.object_id}/collections/addresses",
146
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/object-collection"',
147
+ 'method' => 'GET' },
148
+ { 'rel' => 'up',
149
+ 'href' => "http://localhost/objects/CollectionsTest/#{@object.object_id}",
150
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/object"',
151
+ 'method' => 'GET' },
152
+ { 'rel' => 'urn:org.restfulobjects:rels/add-to;collection="addresses"',
153
+ 'href' => "http://localhost/objects/CollectionsTest/#{@object.object_id}/collections/addresses",
154
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/object-collection"',
155
+ 'method' => 'PUT',
156
+ 'arguments' => { 'value' => nil } },
157
+ { 'rel' => 'urn:org.restfulobjects:rels/remove-from;collection="addresses"',
158
+ 'href' => "http://localhost/objects/CollectionsTest/#{@object.object_id}/collections/addresses",
159
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/object-collection"',
160
+ 'method' => 'DELETE',
161
+ 'arguments' => { 'value' => nil } },
162
+ ]
163
+ }
164
+
165
+ last_response.body.should match_json_expression expected
166
+ end
167
+
168
+ it 'should generate metadata in extensions' do
169
+ collection = @object.rs_type.collections['collection_full_metadata']
170
+ collection.friendly_name = 'Friendly Collection'
171
+ collection.description = 'Collection Description'
172
+ collection.plural_form = 'Collections'
173
+ expected = {
174
+ 'id' => 'collection_full_metadata',
175
+ 'extensions' => {
176
+ 'friendlyName' => 'Friendly Collection',
177
+ 'description' => 'Collection Description',
178
+ 'returnType' => 'list',
179
+ 'elementType' => 'Address',
180
+ 'pluralForm' => 'Collections',
181
+ 'memberOrder' => Fixnum
182
+ }.strict!
183
+ }
184
+
185
+ get "/objects/ActionsTest/#{@object.object_id}/collections/collection_full_metadata"
186
+
187
+ last_response.body.should match_json_expression expected
188
+ end
189
+ end
190
+
@@ -0,0 +1,206 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe RestfulObjects::ObjectProperties do
4
+ before :all do
5
+ RestfulObjects::DomainModel.current.reset
6
+
7
+ class PropertiesTest
8
+ include RestfulObjects::Object
9
+
10
+ property :id, :int, read_only: true
11
+ property :name, :string, max_length: 10
12
+ property :string_prop, :string
13
+ property :int_prop, :int
14
+ property :decimal_prop, :decimal
15
+ property :date_prop, :date
16
+ property :blob_prop, :blob
17
+
18
+ property :prop_full_metadata, :string, friendly_name: 'Meta Prop', description: 'To Test Metadata', max_length: 30,
19
+ pattern: '.*abc.*', optional: false
20
+
21
+ def initialize
22
+ super
23
+ @id = 99
24
+ end
25
+ end
26
+ end
27
+
28
+ before :each do
29
+ @object = PropertiesTest.new
30
+ end
31
+
32
+ it 'should respond to properties' do
33
+ @object.respond_to?(:string_prop).should be_true
34
+ @object.respond_to?(:string_prop=).should be_true
35
+ end
36
+
37
+ it 'should not generate writer for read-only property' do
38
+ @object.respond_to?(:id).should be_true
39
+ @object.respond_to?(:id=).should be_false
40
+ end
41
+
42
+ it 'should generate json for a read-only property' do
43
+ expected = { 'id' =>
44
+ { 'value' => 99,
45
+ 'links' => [
46
+ { 'rel' => 'self',
47
+ 'href' => "http://localhost/objects/PropertiesTest/#{@object.object_id}/properties/id",
48
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/object-property"',
49
+ 'method' => 'GET'},
50
+ { 'rel' => 'up',
51
+ 'href' => "http://localhost/objects/PropertiesTest/#{@object.object_id}",
52
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/object"',
53
+ 'method' => 'GET'}
54
+ ],
55
+ 'disabledReason' => 'read-only property'
56
+ }
57
+ }
58
+
59
+ get "/objects/PropertiesTest/#{@object.object_id}/properties/id"
60
+
61
+ last_response.body.should match_json_expression expected
62
+ end
63
+
64
+ it 'should generate json for a writable property' do
65
+ @object.name = 'Mr. John'
66
+
67
+ expected = { 'name' =>
68
+ { 'value' => 'Mr. John',
69
+ 'links' => [
70
+ { 'rel' => 'self',
71
+ 'href' => "http://localhost/objects/PropertiesTest/#{@object.object_id}/properties/name",
72
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/object-property"',
73
+ 'method' => 'GET' },
74
+ { 'rel' => 'up',
75
+ 'href' => "http://localhost/objects/PropertiesTest/#{@object.object_id}",
76
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/object"',
77
+ 'method' => 'GET' },
78
+ { 'rel' => 'urn:org.restfulobjects:rels/modify;property="name"',
79
+ 'href' => "http://localhost/objects/PropertiesTest/#{@object.object_id}/properties/name",
80
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/object-property"',
81
+ 'method' => 'PUT',
82
+ 'arguments' => { 'value' => nil } },
83
+ { 'rel' => 'urn:org.restfulobjects:rels/clear;property="name"',
84
+ 'href' => "http://localhost/objects/PropertiesTest/#{@object.object_id}/properties/name",
85
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/object-property"',
86
+ 'method' => 'DELETE' }
87
+ ]
88
+ }
89
+ }
90
+
91
+ get "/objects/PropertiesTest/#{@object.object_id}/properties/name"
92
+
93
+ last_response.body.should match_json_expression expected
94
+ end
95
+
96
+ it 'should enforce string property max_length' do
97
+ @object.name = 'x' * 10
98
+ @object.name.should eq('x' * 10)
99
+ expect { @object.name = 'x' * 11 }.to raise_error
100
+ end
101
+
102
+ it 'should process different property types get' do
103
+ @object.string_prop = 'A string'
104
+ @object.int_prop = 1234
105
+ @object.decimal_prop = 333.33
106
+ @object.date_prop = Date.new(2012, 2, 29)
107
+ @object.blob_prop = "\xE5\xA5\xB4\x30\xF2\x8C\x71\xD9"
108
+
109
+ expected = {
110
+ 'string_prop' => 'A string',
111
+ 'int_prop' => 1234,
112
+ 'decimal_prop' => 333.33,
113
+ 'date_prop' => '2012-02-29',
114
+ 'blob_prop' => '5aW0MPKMcdk=' }
115
+
116
+ expected.each do |name, value|
117
+ get "/objects/PropertiesTest/#{@object.object_id}/properties/#{name}"
118
+ JSON.parse(last_response.body)[name]['value'].should eq value
119
+ end
120
+ end
121
+
122
+ it 'should process different property types set' do
123
+ values = {
124
+ 'string_prop' => 'A string',
125
+ 'int_prop' => 1234,
126
+ 'decimal_prop' => 333.33,
127
+ 'date_prop' => '2012-02-29',
128
+ 'blob_prop' => '5aW0MPKMcdk=' }
129
+
130
+ values.each do |name, value|
131
+ put "/objects/PropertiesTest/#{@object.object_id}/properties/#{name}", { 'value' => value }.to_json
132
+ end
133
+
134
+ @object.string_prop.should eq 'A string'
135
+ @object.int_prop.should eq 1234
136
+ @object.decimal_prop.should eq 333.33
137
+ @object.date_prop.should eq Date.new(2012, 2, 29)
138
+ @object.blob_prop.should eq "\xE5\xA5\xB4\x30\xF2\x8C\x71\xD9"
139
+ end
140
+
141
+ it 'should process a property put with json' do
142
+ json = { 'value' => 'masterkey' }.to_json
143
+
144
+ expected = { 'name' =>
145
+ { 'value' => 'masterkey',
146
+ 'links' => [
147
+ { 'rel' => 'self',
148
+ 'href' => "http://localhost/objects/PropertiesTest/#{@object.object_id}/properties/name",
149
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/object-property"',
150
+ 'method' => 'GET' },
151
+ { 'rel' => 'up',
152
+ 'href' => "http://localhost/objects/PropertiesTest/#{@object.object_id}",
153
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/object"',
154
+ 'method' => 'GET' },
155
+ { 'rel' => 'urn:org.restfulobjects:rels/modify;property="name"',
156
+ 'href' => "http://localhost/objects/PropertiesTest/#{@object.object_id}/properties/name",
157
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/object-property"',
158
+ 'method' => 'PUT',
159
+ 'arguments' => { 'value' => nil } },
160
+ { 'rel' => 'urn:org.restfulobjects:rels/clear;property="name"',
161
+ 'href' => "http://localhost/objects/PropertiesTest/#{@object.object_id}/properties/name",
162
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/object-property"',
163
+ 'method' => 'DELETE' }
164
+ ]
165
+ }
166
+ }
167
+
168
+ put "/objects/PropertiesTest/#{@object.object_id}/properties/name", json
169
+
170
+ last_response.body.should match_json_expression expected
171
+ end
172
+
173
+ it 'should process a property delete' do
174
+ @object.name = 'irrelevant'
175
+
176
+ delete "/objects/PropertiesTest/#{@object.object_id}/properties/name"
177
+
178
+ @object.name.should be_nil
179
+
180
+ expected = { 'name' => { 'value' => nil } }
181
+
182
+ last_response.body.should match_json_expression expected
183
+ end
184
+
185
+ it 'should generate metadata in extensions' do
186
+ expected = {
187
+ 'prop_full_metadata' => {
188
+ 'extensions' => {
189
+ 'friendlyName' => 'Meta Prop',
190
+ 'description' => 'To Test Metadata',
191
+ 'returnType' => 'string',
192
+ 'format' => 'string',
193
+ 'optional' => false,
194
+ 'maxLength' => 30,
195
+ 'pattern' => '.*abc.*',
196
+ 'memberOrder' => Fixnum
197
+ }.strict!
198
+ }
199
+ }
200
+
201
+ get "/objects/PropertiesTest/#{@object.object_id}/properties/prop_full_metadata"
202
+
203
+ last_response.body.should match_json_expression expected
204
+ end
205
+ end
206
+
@@ -0,0 +1,228 @@
1
+ # encoding: utf-8
2
+ require_relative '../spec_helper'
3
+
4
+ describe RestfulObjects::Object do
5
+ before :all do
6
+ RestfulObjects::DomainModel.current.reset
7
+
8
+ class Address
9
+ include RestfulObjects::Object
10
+
11
+ property :street, :string
12
+ property :number, :int
13
+ end
14
+
15
+ class ObjectTest
16
+ include RestfulObjects::Object
17
+
18
+ property :id, :int, read_only: true
19
+ property :password, :string, max_length: 10
20
+
21
+ action :hash_password, :string
22
+
23
+ collection :addresses, Address
24
+
25
+ def initialize
26
+ @id = 99
27
+ super
28
+ end
29
+ end
30
+ end
31
+
32
+ before(:each) do
33
+ @object = ObjectTest.new
34
+ end
35
+
36
+ it 'module should add class macros functionality' do
37
+ @object.class.respond_to?(:property).should be_true
38
+ @object.class.respond_to?(:action).should be_true
39
+ @object.class.respond_to?(:collection).should be_true
40
+ end
41
+
42
+ it 'should add the type information to the model' do
43
+ model.types.include?('ObjectTest').should be_true
44
+
45
+ domain_type = model.types['ObjectTest']
46
+
47
+ domain_type.properties.include?('id').should be_true
48
+ domain_type.properties['id'].return_type.should eq(:int)
49
+ domain_type.properties['id'].read_only.should be_true
50
+ domain_type.properties['id'].max_length.should be_nil
51
+
52
+ domain_type.properties.include?('password').should be_true
53
+ domain_type.properties['password'].return_type.should eq(:string)
54
+ domain_type.properties['password'].read_only.should be_false
55
+ domain_type.properties['password'].max_length.should eq(10)
56
+
57
+ domain_type.actions.include?('hash_password').should be_true
58
+ domain_type.actions['hash_password'].parameters.count.should eq(0)
59
+ domain_type.actions['hash_password'].result_type.should eq(:string)
60
+ end
61
+
62
+ it 'should generate json for the object' do
63
+ @object.password = 'secret_key'
64
+ address = Address.new
65
+ @object.addresses.push(address)
66
+
67
+ expected = {
68
+ 'instanceId' => @object.object_id.to_s,
69
+ 'title' => "ObjectTest (#{@object.object_id})",
70
+ 'members' => {
71
+ 'id' =>
72
+ { 'memberType' => 'property',
73
+ 'value' => 99,
74
+ 'disabledReason' => 'read-only property',
75
+ 'links' => [
76
+ { 'rel' => 'urn:org.restfulobjects:rels/details;property="id"',
77
+ 'href' => "http://localhost/objects/ObjectTest/#{@object.object_id}/properties/id",
78
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/object-property"',
79
+ 'method' => 'GET' }
80
+ ],
81
+ 'extensions' => { }
82
+ },
83
+ 'password' => {
84
+ 'memberType' => 'property',
85
+ 'value' => 'secret_key',
86
+ 'links' => [
87
+ { 'rel' => 'urn:org.restfulobjects:rels/details;property="password"',
88
+ 'href' => "http://localhost/objects/ObjectTest/#{@object.object_id}/properties/password",
89
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/object-property"',
90
+ 'method' => 'GET' }
91
+ ],
92
+ 'extensions' => { }
93
+ },
94
+ 'addresses' => {
95
+ 'memberType' => 'collection',
96
+ 'size' => 1,
97
+ 'links' => [
98
+ { 'rel' => 'urn:org.restfulobjects:rels/details;collection="addresses"',
99
+ 'href' => "http://localhost/objects/ObjectTest/#{@object.object_id}/collections/addresses",
100
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/object-collection"',
101
+ 'method' => 'GET' }
102
+ ],
103
+ 'extensions' => { }
104
+ },
105
+ 'hash_password' => {
106
+ 'memberType' => 'action',
107
+ 'links' => [
108
+ { 'rel' => 'urn:org.restfulobjects:rels/details;action="hash_password"',
109
+ 'href' => "http://localhost/objects/ObjectTest/#{@object.object_id}/actions/hash_password",
110
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/object-action"',
111
+ 'method' => 'GET' }
112
+ ],
113
+ 'extensions' => { }
114
+ }
115
+ },
116
+ 'links' => [
117
+ { 'rel' => 'self',
118
+ 'href' => "http://localhost/objects/ObjectTest/#{@object.object_id}",
119
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/object"',
120
+ 'method' => 'GET' },
121
+ { 'rel' => 'describedby',
122
+ 'href' => "http://localhost/domain-types/ObjectTest",
123
+ 'type' => 'application/json;profile="urn:org.restfulobjects:repr-types/domain-type"',
124
+ 'method' => 'GET' }
125
+ ],
126
+ 'extensions' => wildcard_matcher
127
+ }
128
+
129
+ get "/objects/ObjectTest/#{@object.object_id}"
130
+
131
+ last_response.body.should match_json_expression expected
132
+ last_response.content_type.should eq 'application/json;profile="urn:org.restfulobjects:repr-types/object";x-ro-domain-type="ObjectTest"'
133
+ end
134
+
135
+ it 'should generate metadata in extensions' do
136
+ @object.rs_type.plural_name = 'Test Objects'
137
+ @object.rs_type.friendly_name = 'Test Object Friendly'
138
+ @object.rs_type.description = 'An object to test'
139
+
140
+ expected = {
141
+ 'instanceId' => @object.object_id.to_s,
142
+ 'extensions' => {
143
+ 'domainType' => 'ObjectTest',
144
+ 'pluralName' => 'Test Objects',
145
+ 'friendlyName' => 'Test Object Friendly',
146
+ 'description' => 'An object to test',
147
+ 'isService' => false
148
+ }.strict!
149
+ }
150
+
151
+ get "/objects/ObjectTest/#{@object.object_id}"
152
+
153
+ last_response.body.should match_json_expression expected
154
+ end
155
+
156
+ it 'should call initialize on object' do
157
+ class InitializedObject
158
+ include RestfulObjects::Object
159
+ attr_reader :init_called
160
+ def initialize
161
+ super
162
+ @init_called = true
163
+ @title = 'A title'
164
+ end
165
+ end
166
+ obj = InitializedObject.new
167
+ obj.init_called.should be_true
168
+ obj.title.should eq 'A title'
169
+ end
170
+
171
+ it 'should send on_after_create callback when object is created' do
172
+ class CreatedObject
173
+ include RestfulObjects::Object
174
+ attr_reader :called
175
+ def on_after_create
176
+ @called = true
177
+ end
178
+ end
179
+
180
+ CreatedObject.new.called.should be_true
181
+ end
182
+
183
+ it 'should send on_after_update callback when object property is updated or deleted' do
184
+ class UpdatableObject
185
+ include RestfulObjects::Object
186
+ attr_accessor :prop_updated
187
+ property :data, :string
188
+ def on_after_update
189
+ @prop_updated = true
190
+ end
191
+ end
192
+
193
+ obj = UpdatableObject.new
194
+
195
+ obj.prop_updated.should_not be_true
196
+
197
+ put "/objects/UpdatableObject/#{obj.object_id}/properties/data", {}, { input: { 'value' => 'irrelevant' }.to_json }
198
+
199
+ obj.prop_updated.should be_true
200
+
201
+ obj.prop_updated = false
202
+
203
+ delete "/objects/UpdatableObject/#{obj.object_id}/properties/data"
204
+
205
+ obj.prop_updated.should be_true
206
+ end
207
+
208
+ it 'should send on_after_delete callback when object is deleted' do
209
+ class DeletedObject
210
+ include RestfulObjects::Object
211
+ attr_reader :destroyed
212
+ def on_after_delete
213
+ @destroyed = true
214
+ end
215
+ end
216
+
217
+ obj = DeletedObject.new
218
+
219
+ obj.deleted?.should_not be_true
220
+ obj.destroyed.should_not be_true
221
+
222
+ delete "/objects/DeletedObject/#{obj.object_id}"
223
+
224
+ obj.deleted?.should be_true
225
+ obj.destroyed.should be_true
226
+ end
227
+ end
228
+