restful_objects 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/lib/restful_objects/action_description.rb +82 -0
- data/lib/restful_objects/action_list.rb +17 -0
- data/lib/restful_objects/collection_description.rb +47 -0
- data/lib/restful_objects/collection_list.rb +17 -0
- data/lib/restful_objects/http_response.rb +11 -0
- data/lib/restful_objects/link_generator.rb +104 -0
- data/lib/restful_objects/model.rb +79 -0
- data/lib/restful_objects/object.rb +132 -0
- data/lib/restful_objects/object_actions.rb +134 -0
- data/lib/restful_objects/object_collections.rb +84 -0
- data/lib/restful_objects/object_list.rb +16 -0
- data/lib/restful_objects/object_macros.rb +35 -0
- data/lib/restful_objects/object_properties.rb +78 -0
- data/lib/restful_objects/parameter_description.rb +60 -0
- data/lib/restful_objects/parameter_description_list.rb +15 -0
- data/lib/restful_objects/property_list.rb +17 -0
- data/lib/restful_objects/server.rb +129 -0
- data/lib/restful_objects/service_list.rb +55 -0
- data/lib/restful_objects/type.rb +110 -0
- data/lib/restful_objects/type_list.rb +30 -0
- data/lib/restful_objects/user.rb +30 -0
- data/lib/restful_objects/version.rb +3 -0
- metadata +151 -0
@@ -0,0 +1,134 @@
|
|
1
|
+
module RestfulObjects
|
2
|
+
module ObjectActions
|
3
|
+
def actions_members
|
4
|
+
members = {}
|
5
|
+
rs_type.actions.each do |name, action|
|
6
|
+
members[name] = {
|
7
|
+
'memberType' => 'action',
|
8
|
+
'links' => [
|
9
|
+
!is_service ?
|
10
|
+
link_to(:details, "/objects/#{self.class.name}/#{object_id}/actions/#{name}", :object_action, action: name)
|
11
|
+
:
|
12
|
+
link_to(:details, "/services/#{self.class.name}/actions/#{name}", :object_action, action: name)
|
13
|
+
],
|
14
|
+
'extensions' => action.metadata
|
15
|
+
}
|
16
|
+
end
|
17
|
+
members
|
18
|
+
end
|
19
|
+
|
20
|
+
def get_action(action)
|
21
|
+
{ 'id' => action,
|
22
|
+
'parameters' => generate_parameters(action),
|
23
|
+
'links' => [ rs_action_link(action), rs_action_up_link, rs_invoke_link(action) ],
|
24
|
+
'extensions' => rs_type.actions[action].metadata
|
25
|
+
}.to_json
|
26
|
+
end
|
27
|
+
|
28
|
+
def generate_parameters(action)
|
29
|
+
parameters = Hash.new
|
30
|
+
rs_type.actions[action].parameters.each do |name, parameter|
|
31
|
+
parameters[name] = {
|
32
|
+
'links' => [],
|
33
|
+
'extensions' => parameter.metadata
|
34
|
+
}
|
35
|
+
end
|
36
|
+
parameters
|
37
|
+
end
|
38
|
+
|
39
|
+
def get_action_invoke(action, json)
|
40
|
+
raise 'action does not exists' if not rs_type.actions.include?(action)
|
41
|
+
action_description = rs_type.actions[action]
|
42
|
+
json == '' ? {} : arguments = JSON.parse(json)
|
43
|
+
parameters = []
|
44
|
+
action_description.parameters.each do |name, parameter|
|
45
|
+
case parameter.type
|
46
|
+
when :int
|
47
|
+
parameters << arguments[name]['value'].to_i
|
48
|
+
else
|
49
|
+
parameters << arguments[name]['value']
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
result = send(action.to_sym, *parameters)
|
54
|
+
|
55
|
+
action_link = link_to(:self, "/objects/#{self.class.name}/#{object_id}/actions/#{action}/invoke", :action_result)
|
56
|
+
action_link['arguments'] = arguments
|
57
|
+
|
58
|
+
response = {
|
59
|
+
'links' => [ action_link ],
|
60
|
+
'resultType' => 'scalar',
|
61
|
+
'result' => {
|
62
|
+
'links' => [],
|
63
|
+
'extensions' => { }
|
64
|
+
},
|
65
|
+
'extensions' => { }
|
66
|
+
}
|
67
|
+
|
68
|
+
response['resultType'] = action_description.kind_result_type.to_s
|
69
|
+
if result.nil?
|
70
|
+
response['result'] = nil
|
71
|
+
else
|
72
|
+
case action_description.kind_result_type
|
73
|
+
when :scalar
|
74
|
+
response['resultType'] = 'scalar'
|
75
|
+
response['result']['value'] = encode_value(result, action_description.result_type)
|
76
|
+
response['result']['links'] = [ link_to(:return_type, '/domain-types/int', :domain_type) ]
|
77
|
+
when :object
|
78
|
+
response['resultType'] = 'object'
|
79
|
+
response['result'] = result.representation
|
80
|
+
when :proto_object
|
81
|
+
response['resultType'] = 'object'
|
82
|
+
response['result'] = result
|
83
|
+
when :list
|
84
|
+
response['resultType'] = 'list'
|
85
|
+
response['result']['links'] =
|
86
|
+
[ link_to(:element_type, "/domain-types/#{action_description.result_type.to_s}", :domain_type) ]
|
87
|
+
list = []
|
88
|
+
result.each do |member|
|
89
|
+
member_link = link_to(:element, "/objects/#{action_description.result_type.to_s}/#{member.rs_instance_id}", :object)
|
90
|
+
member_link['title'] = member.title
|
91
|
+
list << member_link
|
92
|
+
end
|
93
|
+
response['result']['value'] = list
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
response.to_json
|
98
|
+
end
|
99
|
+
|
100
|
+
def action_return_type(action)
|
101
|
+
RestfulObjects::DomainModel.current.types[self.class.name].actions[action].result_type
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
def rs_invoke_link(action)
|
107
|
+
invoke_link = is_service ?
|
108
|
+
link_to(:invoke, "/services/#{rs_type.id}/actions/#{action}/invoke", :action_result, action: action)
|
109
|
+
:
|
110
|
+
link_to(:invoke, "/objects/#{rs_type.id}/#{object_id}/actions/#{action}/invoke", :action_result, action: action)
|
111
|
+
invoke_link['arguments'] = {}
|
112
|
+
rs_type.actions[action].parameters.each do |name, action|
|
113
|
+
invoke_link['arguments'][name] = { 'value' => nil }
|
114
|
+
end
|
115
|
+
invoke_link
|
116
|
+
end
|
117
|
+
|
118
|
+
def rs_action_link(action)
|
119
|
+
if is_service
|
120
|
+
link_to(:self, "/services/#{self.class.name}/actions/#{action}", :object_action)
|
121
|
+
else
|
122
|
+
link_to(:self, "/objects/#{self.class.name}/#{object_id}/actions/#{action}", :object_action)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def rs_action_up_link
|
127
|
+
if is_service
|
128
|
+
link_to(:up, "/services/#{rs_type.id}", :object)
|
129
|
+
else
|
130
|
+
link_to(:up, "/objects/#{self.class.name}/#{object_id}", :object)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,84 @@
|
|
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
|
17
|
+
|
18
|
+
def get_collection_as_json(collection)
|
19
|
+
raise "collection not exists" if not rs_model.types[self.class.name].collections.include?(collection)
|
20
|
+
|
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
|
27
|
+
|
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
|
+
}
|
37
|
+
|
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
|
50
|
+
|
51
|
+
representation.to_json
|
52
|
+
end
|
53
|
+
|
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)
|
63
|
+
|
64
|
+
send(collection.to_sym).push(rs_model.objects[id])
|
65
|
+
|
66
|
+
get_collection_as_json(collection)
|
67
|
+
end
|
68
|
+
|
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)
|
78
|
+
|
79
|
+
send(collection.to_sym).delete(rs_model.objects[id])
|
80
|
+
|
81
|
+
get_collection_as_json(collection)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module RestfulObjects
|
2
|
+
class ObjectList
|
3
|
+
extend Forwardable
|
4
|
+
|
5
|
+
def initialize(base_url)
|
6
|
+
@objects = Hash.new
|
7
|
+
@base_url = base_url
|
8
|
+
end
|
9
|
+
|
10
|
+
def register(instance, service = false)
|
11
|
+
@objects[instance.object_id] = instance
|
12
|
+
end
|
13
|
+
|
14
|
+
def_delegators :@objects, :[], :each, :include?, :count, :clear, :empty?, :keys, :values
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module RestfulObjects
|
2
|
+
module ObjectMacros
|
3
|
+
def property(name, type, options = {})
|
4
|
+
RestfulObjects::DomainModel.current.types[self.name].properties.add(name.to_s, type, options)
|
5
|
+
if options[:read_only]
|
6
|
+
self.class_eval { attr_reader name }
|
7
|
+
else
|
8
|
+
if not options[:max_length]
|
9
|
+
self.class_eval { attr_accessor name }
|
10
|
+
else
|
11
|
+
self.class_eval do
|
12
|
+
attr_reader name
|
13
|
+
|
14
|
+
define_method "#{name}=".to_sym do |value|
|
15
|
+
raise "string max length exceeded" if value && value.length > options[:max_length]
|
16
|
+
instance_variable_set("@#{name}".to_sym, value)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def collection(name, type, options = {})
|
24
|
+
type = type.name if type.is_a? Class
|
25
|
+
|
26
|
+
RestfulObjects::DomainModel.current.types[self.name].collections.add(name.to_s, type, options)
|
27
|
+
|
28
|
+
self.class_eval { attr_reader name }
|
29
|
+
end
|
30
|
+
|
31
|
+
def action(name, result_type = :void, parameters = {}, options = {})
|
32
|
+
RestfulObjects::DomainModel.current.types[self.name].actions.add(name.to_s, result_type, parameters, options)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module RestfulObjects
|
2
|
+
module ObjectProperties
|
3
|
+
def properties_members
|
4
|
+
members = {}
|
5
|
+
rs_type.properties.each do |name, property|
|
6
|
+
members[name] = {
|
7
|
+
'memberType' => 'property',
|
8
|
+
'value' => get_property_value(name),
|
9
|
+
'links' => [
|
10
|
+
link_to(:details, "/objects/#{self.class.name}/#{object_id}/properties/#{name}", :object_property, property: name)
|
11
|
+
],
|
12
|
+
'extensions' => property.metadata
|
13
|
+
}
|
14
|
+
|
15
|
+
members[name]['disabledReason'] = property.disabled_reason if property.read_only
|
16
|
+
end
|
17
|
+
members
|
18
|
+
end
|
19
|
+
|
20
|
+
def get_property_as_json(property)
|
21
|
+
raise "Property not exists" if not rs_model.types[self.class.name].properties.include?(property)
|
22
|
+
|
23
|
+
representation = {
|
24
|
+
property =>
|
25
|
+
{ 'value' => get_property_value(property),
|
26
|
+
'links' => [
|
27
|
+
link_to(:self, "/objects/#{self.class.name}/#{object_id}/properties/#{property}", :object_property),
|
28
|
+
link_to(:up, "/objects/#{self.class.name}/#{object_id}", :object) ],
|
29
|
+
'extensions' => rs_model.types[self.class.name].properties[property].metadata
|
30
|
+
}
|
31
|
+
}
|
32
|
+
|
33
|
+
if not rs_model.types[self.class.name].properties[property].read_only then
|
34
|
+
representation[property]['links'].concat [
|
35
|
+
link_to(:modify, "/objects/#{self.class.name}/#{object_id}/properties/#{property}", :object_property,
|
36
|
+
{ property: property, method: 'PUT', arguments: { 'value' => nil } }),
|
37
|
+
link_to(:clear, "/objects/#{self.class.name}/#{object_id}/properties/#{property}", :object_property,
|
38
|
+
{ property: property, method: 'DELETE'} ) ]
|
39
|
+
else
|
40
|
+
representation[property]['disabledReason'] =
|
41
|
+
rs_model.types[self.class.name].properties[property].disabled_reason
|
42
|
+
end
|
43
|
+
|
44
|
+
representation.to_json
|
45
|
+
end
|
46
|
+
|
47
|
+
def put_property_as_json(property, json)
|
48
|
+
raise "property not exists" if not rs_model.types[self.class.name].properties.include?(property)
|
49
|
+
raise "read-only property" if rs_model.types[self.class.name].properties[property].read_only
|
50
|
+
|
51
|
+
value = JSON.parse(json)['value']
|
52
|
+
set_property_value(property, value)
|
53
|
+
on_after_update if respond_to? :on_after_update
|
54
|
+
get_property_as_json(property)
|
55
|
+
end
|
56
|
+
|
57
|
+
def clear_property(property)
|
58
|
+
raise "property not exists" if not rs_model.types[self.class.name].properties.include?(property)
|
59
|
+
raise "read-only property" if rs_model.types[self.class.name].properties[property].read_only
|
60
|
+
|
61
|
+
send("#{property}=".to_sym, nil)
|
62
|
+
on_after_update if respond_to? :on_after_update
|
63
|
+
get_property_as_json(property)
|
64
|
+
end
|
65
|
+
|
66
|
+
def property_type(property)
|
67
|
+
rs_model.types[self.class.name].properties[property].return_type
|
68
|
+
end
|
69
|
+
|
70
|
+
def get_property_value(property)
|
71
|
+
encode_value(send(property.to_sym), property_type(property))
|
72
|
+
end
|
73
|
+
|
74
|
+
def set_property_value(property, value)
|
75
|
+
send("#{property}=".to_sym, decode_value(value, property_type(property)))
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module RestfulObjects
|
2
|
+
class ParameterDescription
|
3
|
+
include LinkGenerator
|
4
|
+
|
5
|
+
attr_reader :id, :name, :number, :kind_type, :type
|
6
|
+
attr_accessor :friendly_name, :description, :optional, :max_length, :pattern, :format
|
7
|
+
|
8
|
+
def initialize(id, definition, number)
|
9
|
+
@id = id
|
10
|
+
@name = id
|
11
|
+
|
12
|
+
if definition.is_a? Array # [type, options]
|
13
|
+
if [:string, :int, :decimal, :date, :blob].include?(definition.first) # scalar
|
14
|
+
@kind_type = :scalar
|
15
|
+
@type = definition.first
|
16
|
+
elsif definition.first.is_a?(Class) # object type
|
17
|
+
@kind_type = :object
|
18
|
+
@type = definition.first.name
|
19
|
+
elsif definition.first.is_a?(Strign) # object type
|
20
|
+
@kind_type = :object
|
21
|
+
@type = definition.first
|
22
|
+
else
|
23
|
+
raise "unssuported parameter definition type #{definition.class}"
|
24
|
+
end
|
25
|
+
options = definition.last
|
26
|
+
elsif definition.is_a? Symbol # scalar type
|
27
|
+
@kind_type = :scalar
|
28
|
+
@type = definition
|
29
|
+
raise "result type for scalar '#{@type}' unssuported" if not [:string, :int, :decimal, :date, :blob].include?(@type)
|
30
|
+
elsif definition.is_a? String # object type
|
31
|
+
@kind_type = :object
|
32
|
+
@type = definition
|
33
|
+
elsif definition.is_a? Class # object type
|
34
|
+
@kind_type = :object
|
35
|
+
@type = definition.to_s
|
36
|
+
else
|
37
|
+
raise "unssuported parameter definition type #{definition.class}"
|
38
|
+
end
|
39
|
+
|
40
|
+
options ||= {}
|
41
|
+
@number = options[:number] || number
|
42
|
+
@friendly_name = options[:friendly_name] || id
|
43
|
+
@description = options[:description] || id
|
44
|
+
@optional = options[:optional].nil? ? true : options[:optional]
|
45
|
+
@max_length = options[:max_length]
|
46
|
+
@pattern = options[:pattern]
|
47
|
+
end
|
48
|
+
|
49
|
+
def metadata
|
50
|
+
result = { 'friendlyName' => friendly_name,
|
51
|
+
'description' => description,
|
52
|
+
'optional' => optional,
|
53
|
+
'returnType' => type }
|
54
|
+
result['maxLength'] = max_length if max_length
|
55
|
+
result['pattern'] = pattern if pattern
|
56
|
+
result['format'] = format if format
|
57
|
+
result
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module RestfulObjects
|
2
|
+
class ParameterDescriptionList
|
3
|
+
extend Forwardable
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@parameters = Hash.new
|
7
|
+
end
|
8
|
+
|
9
|
+
def add(id, definition)
|
10
|
+
@parameters[id] = ParameterDescription.new(id, definition, count + 1)
|
11
|
+
end
|
12
|
+
|
13
|
+
def_delegators :@parameters, :[], :each, :include?, :count, :empty?
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module RestfulObjects
|
2
|
+
class PropertyList
|
3
|
+
extend Forwardable
|
4
|
+
|
5
|
+
def initialize(domain_type)
|
6
|
+
@properties = Hash.new
|
7
|
+
@domain_type = domain_type
|
8
|
+
end
|
9
|
+
|
10
|
+
def add(id, return_type, options = {})
|
11
|
+
options[:member_order] ||= count + 1
|
12
|
+
@properties[id] = PropertyDescription.new(id, @domain_type, return_type, options)
|
13
|
+
end
|
14
|
+
|
15
|
+
def_delegators :@properties, :[], :each, :include?, :count, :empty?
|
16
|
+
end
|
17
|
+
end
|