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