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