restful_objects 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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