restful_objects 0.0.7 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/lib/restful_objects.rb +1 -2
  3. data/lib/restful_objects/domain_model/domain_model.rb +14 -11
  4. data/lib/restful_objects/domain_model/helpers/link_generator.rb +102 -90
  5. data/lib/restful_objects/domain_model/mixins/object.rb +10 -13
  6. data/lib/restful_objects/domain_model/mixins/object_actions.rb +106 -117
  7. data/lib/restful_objects/domain_model/mixins/object_base.rb +175 -131
  8. data/lib/restful_objects/domain_model/mixins/object_collections.rb +58 -71
  9. data/lib/restful_objects/domain_model/mixins/object_macros.rb +20 -26
  10. data/lib/restful_objects/domain_model/mixins/object_properties.rb +85 -98
  11. data/lib/restful_objects/domain_model/mixins/service.rb +9 -17
  12. data/lib/restful_objects/domain_model/types/action_description.rb +80 -82
  13. data/lib/restful_objects/domain_model/types/collection_description.rb +39 -40
  14. data/lib/restful_objects/domain_model/types/domain_type.rb +102 -105
  15. data/lib/restful_objects/domain_model/types/parameter_description.rb +42 -47
  16. data/lib/restful_objects/domain_model/types/parameter_description_list.rb +9 -12
  17. data/lib/restful_objects/domain_model/types/property_description.rb +62 -64
  18. data/lib/restful_objects/router.rb +6 -0
  19. data/lib/restful_objects/router/base.rb +23 -32
  20. data/lib/restful_objects/router/domain_object_resources.rb +89 -94
  21. data/lib/restful_objects/router/domain_type_resources.rb +24 -29
  22. data/lib/restful_objects/router/supporting_resources.rb +26 -31
  23. data/lib/restful_objects/version.rb +1 -1
  24. data/spec/integration/domain-types_actions_spec.rb +1 -3
  25. data/spec/integration/domain-types_collections_spec.rb +1 -39
  26. data/spec/integration/domain-types_properties_spec.rb +1 -78
  27. data/spec/integration/domain-types_spec.rb +39 -0
  28. data/spec/integration/{server-root_spec.rb → homepage_spec.rb} +2 -3
  29. data/spec/integration/objects_actions_spec.rb +7 -0
  30. data/spec/integration/objects_collections_spec.rb +169 -0
  31. data/spec/integration/objects_properties_spec.rb +122 -0
  32. data/spec/{acceptance/generate_json_representations_spec.rb → integration/objects_spec.rb} +12 -4
  33. data/spec/unit/object_actions_spec.rb +5 -5
  34. data/spec/unit/object_collections_spec.rb +1 -1
  35. data/spec/unit/object_properties_spec.rb +6 -6
  36. data/spec/unit/object_spec.rb +9 -9
  37. data/spec/unit/service_spec.rb +5 -5
  38. metadata +54 -35
  39. data/spec/integration/domain_model_spec.rb +0 -35
  40. data/spec/unit/type_list_spec.rb +0 -37
@@ -1,161 +1,205 @@
1
- module RestfulObjects
2
- module ObjectBase
3
- attr_accessor :is_service, :title
4
-
5
- def initialize
6
- super
7
- @deleted = false
8
- @is_service = self.class.ancestors.include? RestfulObjects::Service
9
- @title = "#{self.class.name} (#{object_id})"
10
- rs_register_in_model
11
- rs_type.collections.each_value { |collection| instance_variable_set "@#{collection.id}".to_sym, Array.new }
12
- on_after_create if respond_to?(:on_after_create)
13
- end
1
+ module RestfulObjects::ObjectBase
2
+ attr_accessor :ro_title
14
3
 
15
- def rs_register_in_model
16
- rs_model.register_object(self) unless @is_service
17
- end
4
+ HTTP_OK = 200
18
5
 
19
- def rs_model
20
- RestfulObjects::DomainModel.current
21
- end
6
+ def initialize
7
+ super
8
+ @ro_deleted = false
9
+ @ro_is_service = self.class.ancestors.include?(RestfulObjects::Service)
10
+ @ro_title = "#{self.class.name} (#{object_id})"
22
11
 
23
- def rs_type
24
- rs_model.types[self.class.name]
12
+ ro_domain_model.register_object(self) unless @ro_is_service
13
+ ro_domain_type.collections.each_value do |collection|
14
+ instance_variable_set("@#{collection.id}".to_sym, Array.new)
25
15
  end
16
+ on_after_create if respond_to?(:on_after_create)
17
+ end
26
18
 
27
- def get_representation
28
- [200,
29
- { 'Content-Type' =>
30
- "application/json;profile=\"urn:org.restfulobjects:repr-types/object\";x-ro-domain-type=\"#{rs_type.id}\"" },
31
- representation.to_json]
32
- end
19
+ def ro_domain_model
20
+ RestfulObjects::DomainModel.current
21
+ end
33
22
 
34
- def put_properties_and_get_representation(json)
35
- properties = JSON.parse(json)
36
- properties.each do |property, container|
37
- raise 'property not exists' unless rs_model.types[self.class.name].properties.include?(property)
38
- raise 'read-only property' if rs_model.types[self.class.name].properties[property].read_only
39
- set_property_value(property, container['value'])
40
- on_after_update if respond_to?(:on_after_update)
41
- end
42
- [200,
43
- { 'Content-Type' =>
44
- "application/json;profile=\"urn:org.restfulobjects:repr-types/object\";x-ro-domain-type=\"#{rs_type.id}\"" },
45
- representation(false).to_json]
46
- end
23
+ def ro_domain_type
24
+ @ro_domain_type ||= ro_domain_model.types[self.class.name]
25
+ end
47
26
 
48
- def rs_instance_id
49
- object_id
50
- end
27
+ def ro_is_service?
28
+ @ro_is_service
29
+ end
51
30
 
52
- def representation(include_self_link = true)
53
- representation = {
54
- 'instanceId' => object_id.to_s,
55
- 'serviceId' => self.class.name,
56
- 'title' => title,
57
- 'members' => generate_members,
58
- 'links' => [ link_to(:described_by, "/domain-types/#{self.class.name}", :domain_type) ],
59
- 'extensions' => rs_type.metadata
60
- }
31
+ def ro_instance_id
32
+ object_id
33
+ end
61
34
 
62
- unless is_service
63
- representation.delete('serviceId')
64
- representation['links'] << link_to(:self, "/objects/#{self.class.name}/#{object_id}", :object) if include_self_link
65
- else
66
- representation.delete('instanceId')
67
- representation['links'] << link_to(:self, "/services/#{self.class.name}", :object) if include_self_link
68
- end
35
+ def ro_delete
36
+ on_before_delete if respond_to?(:on_before_delete)
37
+ @ro_deleted = true
38
+ on_after_delete if respond_to?(:on_after_delete)
39
+ {}.to_json
40
+ end
69
41
 
70
- representation
71
- end
42
+ def ro_deleted?
43
+ @ro_deleted
44
+ end
45
+
46
+ def ro_get_representation_response
47
+ [HTTP_OK, { 'Content-Type' => ro_content_type_for_object(ro_domain_type.id) }, ro_get_representation.to_json]
48
+ end
72
49
 
73
- def generate_members
74
- if is_service
75
- actions_members
50
+ def encode_value(value, type, property_name = '')
51
+ return nil if value.nil?
52
+ case type
53
+ when :string
54
+ value.to_s
55
+ when :int
56
+ value.to_i
57
+ when :bool
58
+ value.to_s
59
+ when :decimal
60
+ value.to_f
61
+ when :date
62
+ value.strftime('%Y-%m-%d')
63
+ when :blob
64
+ Base64.encode64(value).strip
76
65
  else
77
- properties_members.merge(collections_members.merge(actions_members))
78
- end
66
+ if value.respond_to?(:ro_property_relation_representation, true)
67
+ value.ro_property_relation_representation(property_name)
68
+ else
69
+ raise "encode_value unsupported property type: #{type}"
70
+ end
79
71
  end
72
+ end
80
73
 
81
- def get_property_rel_representation(property_name)
82
- representation = link_to(:value, "/objects/#{self.class.name}/#{object_id}", :object, property: property_name)
83
- representation['title'] = @title
84
- representation
74
+ def decode_value(value, type)
75
+ return nil if value.nil?
76
+ case type
77
+ when :string
78
+ value.to_s
79
+ when :int
80
+ value.to_i
81
+ when :bool
82
+ if [true, 'true'].include?(value)
83
+ true
84
+ elsif [false, 'false'].include?(value)
85
+ false
86
+ else
87
+ raise ArgumentError.new "invalid boolean value: #{value}"
88
+ end
89
+ when :decimal
90
+ Float(value)
91
+ when :date
92
+ Date.parse(value)
93
+ when :blob
94
+ Base64.decode64(value)
95
+ else
96
+ raise "decode_value unsupported property type: #{type}"
85
97
  end
98
+ end
86
99
 
87
- def rs_delete
88
- on_before_delete if respond_to?(:on_before_delete)
89
- @deleted = true
90
- on_after_delete if respond_to?(:on_after_delete)
91
- {}.to_json
92
- end
100
+ def ro_relative_url
101
+ "/objects/#{self.class.name}/#{self.object_id}"
102
+ end
93
103
 
94
- def deleted?
95
- @deleted
96
- end
104
+ def ro_absolute_url
105
+ "#{ro_domain_model.base_url}#{ro_relative_url}"
106
+ end
97
107
 
98
- def encode_value(value, type, property_name = '')
99
- return nil if value.nil?
100
- case type
101
- when :string
102
- value.to_s
103
- when :int
104
- value.to_i
105
- when :bool
106
- value.to_s
107
- when :decimal
108
- value.to_f
109
- when :date
110
- value.strftime('%Y-%m-%d')
111
- when :blob
112
- Base64.encode64(value).strip
113
- else
114
- if value.respond_to?(:get_property_rel_representation)
115
- value.get_property_rel_representation(property_name)
116
- else
117
- raise "encode_value unsupported property type: #{type}"
118
- end
119
- end
108
+ def get_self_link
109
+ link_to(:self, ro_relative_url, :object)
110
+ end
111
+
112
+ protected
113
+
114
+ def ro_get_representation(include_self_link = true)
115
+ result = {
116
+ 'title' => ro_title,
117
+ 'members' => ro_generate_members,
118
+ 'links' => [ link_to(:described_by, "/domain-types/#{ro_domain_type.id}", :domain_type) ],
119
+ 'extensions' => ro_domain_type.metadata }
120
+ if ro_is_service?
121
+ result['serviceId'] = ro_domain_type.id
122
+ result['links'] << link_to(:self, "/services/#{ro_domain_type.id}", :object) if include_self_link
123
+ else
124
+ result['instanceId'] = ro_instance_id.to_s
125
+ result['links'] << link_to(:self, "/objects/#{ro_domain_type.id}/#{ro_instance_id}", :object) if include_self_link
126
+ end
127
+ result
128
+ end
129
+
130
+ def ro_generate_members
131
+ if ro_is_service?
132
+ actions_members
133
+ else
134
+ ro_properties_members.merge(collections_members.merge(actions_members))
120
135
  end
136
+ end
121
137
 
122
- def decode_value(value, type)
123
- return nil if value.nil?
124
- case type
125
- when :string
126
- value.to_s
127
- when :int
128
- value.to_i
129
- when :bool
130
- if [true, 'true'].include?(value)
131
- true
132
- elsif [false, 'false'].include?(value)
133
- false
138
+ def ro_property_relation_representation(property_name)
139
+ representation = link_to(:value, "/objects/#{self.class.name}/#{object_id}", :object, property: property_name)
140
+ representation['title'] = ro_title
141
+ representation
142
+ end
143
+
144
+ def ro_properties_members
145
+ members = {}
146
+ ro_domain_type.properties.each do |name, property|
147
+ members[name] = {
148
+ 'memberType' => 'property',
149
+ 'value' => ro_get_property_as_json(name),
150
+ 'links' => [
151
+ link_to(:details, "/objects/#{self.class.name}/#{object_id}/properties/#{name}", :object_property, property: name)
152
+ ],
153
+ 'extensions' => property.metadata
154
+ }
155
+
156
+ if property.read_only
157
+ members[name]['disabledReason'] = property.disabled_reason
158
+ else
159
+ if self.respond_to?("#{name}_choices")
160
+ choices = self.send("#{name}_choices")
161
+ raise "value returned by #{name}_choices method should be an Array" unless choices.is_a?(Array)
162
+ if ro_get_property_metadata(name).is_reference
163
+ choices_json = choices.map { |object| object.ro_property_relation_representation(name) }
134
164
  else
135
- raise ArgumentError.new "invalid boolean value: #{value}"
165
+ choices_json = choices.map { |value| decode_value(value, ro_get_property_metadata(name).return_type) }
136
166
  end
137
- when :decimal
138
- Float(value)
139
- when :date
140
- Date.parse(value)
141
- when :blob
142
- Base64.decode64(value)
143
- else
144
- raise "decode_value unsupported property type: #{type}"
167
+ members[name]['choices'] = choices_json
168
+ end
145
169
  end
146
170
  end
171
+ members
172
+ end
147
173
 
148
- def ro_relative_url
149
- "/objects/#{self.class.name}/#{self.object_id}"
150
- end
151
-
152
- def ro_absolute_url
153
- RestfulObjects::DomainModel.current.base_url + ro_relative_url
174
+ def collections_members
175
+ members = {}
176
+ ro_domain_type.collections.each do |name, collection|
177
+ members[name] = {
178
+ 'memberType' => 'collection',
179
+ 'size' => ro_domain_type.collections.count,
180
+ 'links' => [
181
+ link_to(:details, "/objects/#{self.class.name}/#{object_id}/collections/#{name}", :object_collection, collection: name)
182
+ ],
183
+ 'extensions' => collection.metadata
184
+ }
154
185
  end
186
+ members
187
+ end
155
188
 
156
- def get_self_link
157
- link_to(:self, ro_relative_url, :object)
189
+ def actions_members
190
+ members = {}
191
+ ro_domain_type.actions.each do |name, action|
192
+ members[name] = {
193
+ 'memberType' => 'action',
194
+ 'links' => [
195
+ !ro_is_service? ?
196
+ link_to(:details, "/objects/#{self.class.name}/#{object_id}/actions/#{name}", :object_action, action: name)
197
+ :
198
+ link_to(:details, "/services/#{self.class.name}/actions/#{name}", :object_action, action: name)
199
+ ],
200
+ 'extensions' => action.metadata
201
+ }
158
202
  end
203
+ members
159
204
  end
160
205
  end
161
-
@@ -1,85 +1,72 @@
1
- module RestfulObjects
2
- module ObjectCollections
3
- def collections_members
4
- members = {}
5
- rs_type.collections.each do |name, collection|
6
- members[name] = {
7
- 'memberType' => 'collection',
8
- 'size' => rs_type.collections.count,
9
- 'links' => [
10
- link_to(:details, "/objects/#{self.class.name}/#{object_id}/collections/#{name}", :object_collection, collection: name)
11
- ],
12
- 'extensions' => collection.metadata
13
- }
14
- end
15
- members
16
- end
1
+ module RestfulObjects::ObjectCollections
2
+ HTTP_OK = 200
17
3
 
18
- def get_collection_as_json(collection)
19
- raise "collection not exists" if not rs_model.types[self.class.name].collections.include?(collection)
4
+ def ro_get_collection_type(name)
5
+ ro_domain_type.collections[name]
6
+ end
20
7
 
21
- value = []
22
- send(collection.to_sym).each do |object|
23
- link = link_to(:value, "/objects/#{object.rs_type.id}/#{object.rs_instance_id}", :object, method: 'GET', collection: collection)
24
- link['title'] = object.title
25
- value << link
26
- end
8
+ def ro_get_collection_response(name)
9
+ raise "collection not exists" unless ro_get_collection_type(name)
27
10
 
28
- representation = {
29
- 'id' => collection,
30
- 'value' => value,
31
- 'links' => [
32
- link_to(:self, "/objects/#{rs_type.id}/#{rs_instance_id}/collections/#{collection}", :object_collection),
33
- link_to(:up, "/objects/#{rs_type.id}/#{rs_instance_id}", :object)
34
- ],
35
- 'extensions' => rs_type.collections[collection].metadata
36
- }
11
+ value = []
12
+ send(name).each do |object|
13
+ link = link_to(:value, "/objects/#{object.ro_domain_type.id}/#{object.ro_instance_id}", :object, method: 'GET', collection: name)
14
+ link['title'] = object.ro_title
15
+ value << link
16
+ end
37
17
 
38
- if not rs_model.types[self.class.name].collections[collection].read_only then
39
- add_to_link = link_to(:add_to, "/objects/#{rs_type.id}/#{rs_instance_id}/collections/#{collection}",
40
- :object_collection, method: 'PUT', collection: collection)
41
- add_to_link['arguments'] = { 'value' => nil }
42
- remove_from_link = link_to(:remove_from, "/objects/#{rs_type.id}/#{rs_instance_id}/collections/#{collection}",
43
- :object_collection, method: 'DELETE', collection: collection)
44
- remove_from_link['arguments'] = { 'value' => nil }
45
- representation['links'].concat [ add_to_link, remove_from_link ]
46
- else
47
- representation['disabledReason'] =
48
- rs_model.types[self.class.name].collections[collection].disabled_reason
49
- end
18
+ representation = {
19
+ 'id' => name,
20
+ 'value' => value,
21
+ 'links' => [
22
+ link_to(:self, "/objects/#{ro_domain_type.id}/#{ro_instance_id}/collections/#{name}", :object_collection),
23
+ link_to(:up, "/objects/#{ro_domain_type.id}/#{ro_instance_id}", :object)
24
+ ],
25
+ 'extensions' => ro_get_collection_type(name).metadata
26
+ }
50
27
 
51
- representation.to_json
28
+ unless ro_get_collection_type(name).read_only
29
+ add_to_link = link_to(:add_to, "/objects/#{ro_domain_type.id}/#{ro_instance_id}/collections/#{name}",
30
+ :object_collection, method: 'PUT', collection: name)
31
+ add_to_link['arguments'] = { 'value' => nil }
32
+ remove_from_link = link_to(:remove_from, "/objects/#{ro_domain_type.id}/#{ro_instance_id}/collections/#{name}",
33
+ :object_collection, method: 'DELETE', collection: name)
34
+ remove_from_link['arguments'] = { 'value' => nil }
35
+ representation['links'].concat [ add_to_link, remove_from_link ]
36
+ else
37
+ representation['disabledReason'] = ro_get_collection_type(name).disabled_reason
52
38
  end
53
39
 
54
- def add_to_collection(collection, json)
55
- raise "collection not exists" if not rs_model.types[self.class.name].collections.include?(collection)
56
- href_value = JSON.parse(json)['value']['href']
57
- match = Regexp.new(".*/objects/(?<domain-type>\\w*)/(?<object-id>\\d*)").match(href_value)
58
- raise "Invalid request format" if not match
59
- domain_type = match['domain-type']
60
- id = match['object-id'].to_i
61
- raise "Value does not exists" if not rs_model.objects.include?(id)
62
- raise "Domain-type does not exists" if not rs_model.types.include?(domain_type)
40
+ [HTTP_OK, { 'Content-Type' => ro_content_type_for_object_collection(ro_get_collection_type(name).type) }, representation.to_json]
41
+ end
63
42
 
64
- send(collection.to_sym).push(rs_model.objects[id])
43
+ def ro_add_to_collection_and_get_response(name, json)
44
+ raise "collection not exists" unless ro_get_collection_type(name)
45
+ href_value = JSON.parse(json)['value']['href']
46
+ match = Regexp.new(".*/objects/(?<domain-type>\\w*)/(?<object-id>\\d*)").match(href_value)
47
+ raise "Invalid request format" if not match
48
+ domain_type = match['domain-type']
49
+ id = match['object-id'].to_i
50
+ raise "Value does not exists" unless ro_domain_model.objects.include?(id)
51
+ raise "Domain-type does not exists" unless ro_domain_model.types.include?(domain_type)
65
52
 
66
- get_collection_as_json(collection)
67
- end
53
+ send(name).push(ro_domain_model.objects[id])
68
54
 
69
- def delete_from_collection(collection, json)
70
- raise "collection not exists" if not rs_model.types[self.class.name].collections.include?(collection)
71
- href_value = JSON.parse(json)['value']['href']
72
- match = Regexp.new(".*/objects/(?<domain-type>\\w*)/(?<object-id>\\d*)").match(href_value)
73
- raise "Invalid request format" if not match
74
- domain_type = match['domain-type']
75
- id = match['object-id'].to_i
76
- raise "Value does not exists" if not rs_model.objects.include?(id)
77
- raise "Domain-type does not exists" if not rs_model.types.include?(domain_type)
55
+ return ro_get_collection_response(name)
56
+ end
78
57
 
79
- send(collection.to_sym).delete(rs_model.objects[id])
58
+ def ro_delete_from_collection_and_get_response(name, json)
59
+ raise "collection not exists" unless ro_get_collection_type(name)
60
+ href_value = JSON.parse(json)['value']['href']
61
+ match = Regexp.new(".*/objects/(?<domain-type>\\w*)/(?<object-id>\\d*)").match(href_value)
62
+ raise "Invalid request format" if not match
63
+ domain_type = match['domain-type']
64
+ id = match['object-id'].to_i
65
+ raise "Value does not exists" unless ro_domain_model.objects.include?(id)
66
+ raise "Domain-type does not exists" unless ro_domain_model.types.include?(domain_type)
80
67
 
81
- get_collection_as_json(collection)
82
- end
68
+ send(name).delete(ro_domain_model.objects[id])
69
+
70
+ return ro_get_collection_response(name)
83
71
  end
84
72
  end
85
-