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.
- checksums.yaml +4 -4
- data/lib/restful_objects.rb +1 -2
- data/lib/restful_objects/domain_model/domain_model.rb +14 -11
- data/lib/restful_objects/domain_model/helpers/link_generator.rb +102 -90
- data/lib/restful_objects/domain_model/mixins/object.rb +10 -13
- data/lib/restful_objects/domain_model/mixins/object_actions.rb +106 -117
- data/lib/restful_objects/domain_model/mixins/object_base.rb +175 -131
- data/lib/restful_objects/domain_model/mixins/object_collections.rb +58 -71
- data/lib/restful_objects/domain_model/mixins/object_macros.rb +20 -26
- data/lib/restful_objects/domain_model/mixins/object_properties.rb +85 -98
- data/lib/restful_objects/domain_model/mixins/service.rb +9 -17
- data/lib/restful_objects/domain_model/types/action_description.rb +80 -82
- data/lib/restful_objects/domain_model/types/collection_description.rb +39 -40
- data/lib/restful_objects/domain_model/types/domain_type.rb +102 -105
- data/lib/restful_objects/domain_model/types/parameter_description.rb +42 -47
- data/lib/restful_objects/domain_model/types/parameter_description_list.rb +9 -12
- data/lib/restful_objects/domain_model/types/property_description.rb +62 -64
- data/lib/restful_objects/router.rb +6 -0
- data/lib/restful_objects/router/base.rb +23 -32
- data/lib/restful_objects/router/domain_object_resources.rb +89 -94
- data/lib/restful_objects/router/domain_type_resources.rb +24 -29
- data/lib/restful_objects/router/supporting_resources.rb +26 -31
- data/lib/restful_objects/version.rb +1 -1
- data/spec/integration/domain-types_actions_spec.rb +1 -3
- data/spec/integration/domain-types_collections_spec.rb +1 -39
- data/spec/integration/domain-types_properties_spec.rb +1 -78
- data/spec/integration/domain-types_spec.rb +39 -0
- data/spec/integration/{server-root_spec.rb → homepage_spec.rb} +2 -3
- data/spec/integration/objects_actions_spec.rb +7 -0
- data/spec/integration/objects_collections_spec.rb +169 -0
- data/spec/integration/objects_properties_spec.rb +122 -0
- data/spec/{acceptance/generate_json_representations_spec.rb → integration/objects_spec.rb} +12 -4
- data/spec/unit/object_actions_spec.rb +5 -5
- data/spec/unit/object_collections_spec.rb +1 -1
- data/spec/unit/object_properties_spec.rb +6 -6
- data/spec/unit/object_spec.rb +9 -9
- data/spec/unit/service_spec.rb +5 -5
- metadata +54 -35
- data/spec/integration/domain_model_spec.rb +0 -35
- data/spec/unit/type_list_spec.rb +0 -37
@@ -1,161 +1,205 @@
|
|
1
|
-
module RestfulObjects
|
2
|
-
|
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
|
-
|
16
|
-
rs_model.register_object(self) unless @is_service
|
17
|
-
end
|
4
|
+
HTTP_OK = 200
|
18
5
|
|
19
|
-
|
20
|
-
|
21
|
-
|
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
|
-
|
24
|
-
|
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
|
-
|
28
|
-
|
29
|
-
|
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
|
-
|
35
|
-
|
36
|
-
|
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
|
-
|
49
|
-
|
50
|
-
|
27
|
+
def ro_is_service?
|
28
|
+
@ro_is_service
|
29
|
+
end
|
51
30
|
|
52
|
-
|
53
|
-
|
54
|
-
|
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
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
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
|
-
|
71
|
-
|
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
|
-
|
74
|
-
|
75
|
-
|
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
|
-
|
78
|
-
|
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
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
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
|
-
|
88
|
-
|
89
|
-
|
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
|
-
|
95
|
-
|
96
|
-
|
104
|
+
def ro_absolute_url
|
105
|
+
"#{ro_domain_model.base_url}#{ro_relative_url}"
|
106
|
+
end
|
97
107
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
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
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
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
|
-
|
165
|
+
choices_json = choices.map { |value| decode_value(value, ro_get_property_metadata(name).return_type) }
|
136
166
|
end
|
137
|
-
|
138
|
-
|
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
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
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
|
-
|
157
|
-
|
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
|
-
|
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
|
-
|
19
|
-
|
4
|
+
def ro_get_collection_type(name)
|
5
|
+
ro_domain_type.collections[name]
|
6
|
+
end
|
20
7
|
|
21
|
-
|
22
|
-
|
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
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
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
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
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
|
-
|
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
|
-
|
55
|
-
|
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
|
-
|
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
|
-
|
67
|
-
end
|
53
|
+
send(name).push(ro_domain_model.objects[id])
|
68
54
|
|
69
|
-
|
70
|
-
|
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
|
-
|
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
|
-
|
82
|
-
|
68
|
+
send(name).delete(ro_domain_model.objects[id])
|
69
|
+
|
70
|
+
return ro_get_collection_response(name)
|
83
71
|
end
|
84
72
|
end
|
85
|
-
|