restful_objects 0.0.7 → 0.0.8

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 (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
-