restful_objects 0.0.1
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 +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
|