restful_objects 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+