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
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
YmE0NjFmNDUzNmFiODQ4MGM5YTNkZDUxZmNlM2EwOGQyZjExZmRiYg==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
MDk2MDgzNTRkNWU5OGQ0YTlhYmJjMmNmNjIyOTBhZTc2NzJlYjFmMQ==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
NTc2M2UyZGRiOTVmMzA5ZjU3M2Q4MGFiY2M3MzNiNTM2MWMzNDJkNDNkMDJl
|
10
|
+
ODU1ZjUwZGQ1MzFlMGQ2ZDIwZGY3NGY2Mzk4YjI4N2RiZDdjY2M3N2I1MDY5
|
11
|
+
ZmViODRjMjk0M2ZkMzEwMzUxMmIzODgzMmE4OGQ2YTQwMTNlOTQ=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
NjA5OGE5YzYwYzFiMWNjYTBjYmEyMjZjYzljNzE0OGRiYTNmMjY1MGY5OTE0
|
14
|
+
ZDg5ZGJmMWE3MWQ2MzM0NTNmZDBmMTM3ZGE5NGZlMzQyMWNhZmJhNjQxYTAw
|
15
|
+
MTU4MDI5YzIyZDJjN2Y5NWE0OTE3OGIwMGM2NGZlM2YyOGU4MWE=
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module RestfulObjects
|
2
|
+
class ActionDescription
|
3
|
+
include LinkGenerator
|
4
|
+
|
5
|
+
attr_reader :id, :kind_result_type, :result_type, :parameters
|
6
|
+
attr_accessor :friendly_name, :description, :member_order, :disabled_reason
|
7
|
+
|
8
|
+
def initialize(id, result_type, domain_type, parameters = {}, options = {})
|
9
|
+
@id = id
|
10
|
+
|
11
|
+
if result_type == :void
|
12
|
+
result_type = [:void, :void]
|
13
|
+
elsif result_type.is_a?(Symbol)
|
14
|
+
result_type = [:scalar, result_type]
|
15
|
+
end
|
16
|
+
raise "result type should be a symbol or an array" if not result_type.is_a?(Array)
|
17
|
+
raise "result type kind '#{result_type.last}' unssuported" if
|
18
|
+
not [:void, :scalar, :object, :proto_object, :list].include?(result_type.first)
|
19
|
+
@kind_result_type = result_type.first
|
20
|
+
@result_type = result_type.last
|
21
|
+
case @kind_result_type
|
22
|
+
when :scalar
|
23
|
+
raise "result type for scalar '#{result_type.last}' unssuported" if
|
24
|
+
not [:string, :int, :decimal, :date, :blob].include?(@result_type)
|
25
|
+
when :object, :proto_object, :list
|
26
|
+
raise "result type should be a class or a string with a class name" if
|
27
|
+
not (@result_type.is_a?(Class) || @result_type.is_a?(String))
|
28
|
+
end
|
29
|
+
|
30
|
+
@parameters = ParameterDescriptionList.new
|
31
|
+
parameters.each { |name, definition| @parameters.add(name, definition) }
|
32
|
+
|
33
|
+
@domain_type = domain_type
|
34
|
+
@friendly_name = options[:friendly_name] || id
|
35
|
+
@description = options[:description] || id
|
36
|
+
@member_order = options[:member_order] || 0
|
37
|
+
@disabled_reason = options[:disabled_reason] || ''
|
38
|
+
end
|
39
|
+
|
40
|
+
def get_representation
|
41
|
+
representation = {
|
42
|
+
'id' => @id,
|
43
|
+
'hasParams' => has_params,
|
44
|
+
'memberOrder' => @member_order,
|
45
|
+
'parameters' => parameters_list,
|
46
|
+
'links' => [
|
47
|
+
link_to(:self, "/domain-types/#{@domain_type}/actions/#{@id}", :action_description),
|
48
|
+
link_to(:up, "/domain-types/#{@domain_type}", :domain_type),
|
49
|
+
link_to(:return_type, "/domain-types/#{result_type}", :domain_type)
|
50
|
+
],
|
51
|
+
'extensions' => {}
|
52
|
+
}
|
53
|
+
|
54
|
+
representation['friendlyName'] = friendly_name if friendly_name
|
55
|
+
representation['description'] = description if description
|
56
|
+
|
57
|
+
representation.to_json
|
58
|
+
end
|
59
|
+
|
60
|
+
def metadata
|
61
|
+
result = { 'friendlyName' => friendly_name,
|
62
|
+
'description' => description,
|
63
|
+
'returnType' => result_type,
|
64
|
+
'hasParams' => has_params,
|
65
|
+
'memberOrder' => member_order }
|
66
|
+
end
|
67
|
+
|
68
|
+
def has_params
|
69
|
+
not @parameters.empty?
|
70
|
+
end
|
71
|
+
|
72
|
+
def parameters_list
|
73
|
+
result = {}
|
74
|
+
parameters.each do |name, parameter|
|
75
|
+
result[name] = {
|
76
|
+
'extension' => parameter.metadata
|
77
|
+
}
|
78
|
+
end
|
79
|
+
result
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module RestfulObjects
|
2
|
+
class ActionList
|
3
|
+
extend Forwardable
|
4
|
+
|
5
|
+
def initialize(domain_type)
|
6
|
+
@actions = Hash.new
|
7
|
+
@domain_type = domain_type
|
8
|
+
end
|
9
|
+
|
10
|
+
def add(id, result_type, parameters = {}, options = {})
|
11
|
+
options[:member_order] ||= count + 1
|
12
|
+
@actions[id] = ActionDescription.new(id, result_type, @domain_type, parameters, options)
|
13
|
+
end
|
14
|
+
|
15
|
+
def_delegators :@actions, :[], :each, :include?, :count, :empty?
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module RestfulObjects
|
2
|
+
class CollectionDescription
|
3
|
+
include LinkGenerator
|
4
|
+
attr_reader :id, :type, :read_only
|
5
|
+
attr_accessor :friendly_name, :description, :plural_form, :member_order, :disabled_reason
|
6
|
+
|
7
|
+
def initialize(id, type, domain_type, options = {})
|
8
|
+
@id = id
|
9
|
+
@type = type
|
10
|
+
@domain_type = domain_type
|
11
|
+
@read_only = options[:read_only].nil? ? false : options[:read_only]
|
12
|
+
@disabled_reason = options[:disabled_reason] || 'read only collection' if read_only
|
13
|
+
@friendly_name = options[:friendly_name] || id
|
14
|
+
@description = options[:description] || id
|
15
|
+
@plural_form = options[:plural_form]
|
16
|
+
@member_order = options[:member_order]
|
17
|
+
end
|
18
|
+
|
19
|
+
def get_representation
|
20
|
+
representation = {
|
21
|
+
'id' => id,
|
22
|
+
'memberOrder' => member_order,
|
23
|
+
'links' => [
|
24
|
+
link_to(:self, "/domain-types/#{@domain_type}/collections/#{@id}", :collection_description),
|
25
|
+
link_to(:up, "/domain-types/#{@domain_type}", :domain_type),
|
26
|
+
link_to(:return_type, "/domain-types/list", :domain_type),
|
27
|
+
link_to(:element_type, "/domain-types/#{@type}", :domain_type)
|
28
|
+
],
|
29
|
+
'extensions' => metadata
|
30
|
+
}
|
31
|
+
|
32
|
+
representation['friendlyName'] = friendly_name if friendly_name
|
33
|
+
representation['description'] = description if description
|
34
|
+
|
35
|
+
representation.to_json
|
36
|
+
end
|
37
|
+
|
38
|
+
def metadata
|
39
|
+
{ 'friendlyName' => friendly_name,
|
40
|
+
'description' => description,
|
41
|
+
'returnType' => 'list',
|
42
|
+
'elementType' => type,
|
43
|
+
'memberOrder' => member_order,
|
44
|
+
'pluralForm' => plural_form }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module RestfulObjects
|
2
|
+
class CollectionList
|
3
|
+
extend Forwardable
|
4
|
+
|
5
|
+
def initialize(domain_type)
|
6
|
+
@domain_type = domain_type
|
7
|
+
@collections = Hash.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def add(name, type, options = {})
|
11
|
+
options[:member_order] ||= count + 1
|
12
|
+
@collections[name] = CollectionDescription.new(name, type, @domain_type, options)
|
13
|
+
end
|
14
|
+
|
15
|
+
def_delegators :@collections, :[], :each, :each_key, :each_value, :include?, :count, :empty?, :clear
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
module RestfulObjects
|
2
|
+
module LinkGenerator
|
3
|
+
def link_to(rel, href, type, options = {})
|
4
|
+
link = {
|
5
|
+
'rel' => generate_rel(rel, options),
|
6
|
+
'href' => RestfulObjects::DomainModel.current.base_url + href,
|
7
|
+
'type' => generate_repr_type(type, options[:domain_type], options[:element_type]),
|
8
|
+
'method' => options[:method] || 'GET' }
|
9
|
+
|
10
|
+
link['arguments'] = options[:arguments] if options[:arguments]
|
11
|
+
|
12
|
+
link
|
13
|
+
end
|
14
|
+
|
15
|
+
def generate_rel(rel, options = {})
|
16
|
+
if [:self, :up, :next, :previous, :icon, :help].include?(rel)
|
17
|
+
rel.to_s
|
18
|
+
elsif rel == :described_by
|
19
|
+
'describedby'
|
20
|
+
else
|
21
|
+
if [:action, :action_param, :collection, :delete, :domain_type, :domain_types, :element, :element_type, :persist,
|
22
|
+
:property, :return_type, :services, :update, :user, :version].include?(rel)
|
23
|
+
'urn:org.restfulobjects:rels/' + underscore_to_hyphen_string(rel)
|
24
|
+
else
|
25
|
+
'urn:org.restfulobjects:rels/' +
|
26
|
+
case rel
|
27
|
+
when :add_to
|
28
|
+
'add-to;collection="' + options[:collection] + '"'
|
29
|
+
when :attachment
|
30
|
+
'attachment;property="' + options[:property] + '"'
|
31
|
+
when :choice
|
32
|
+
if options[:property]
|
33
|
+
'choice;property="' + options[:property] + '"'
|
34
|
+
elsif options[:action]
|
35
|
+
'choice;action="' + options[:action] + '"'
|
36
|
+
elsif options[:param]
|
37
|
+
'choice;param="' + options[:param] + '"'
|
38
|
+
else
|
39
|
+
raise 'option not found for choice rel'
|
40
|
+
end
|
41
|
+
when :clear
|
42
|
+
'clear;property="' + options[:property] + '"'
|
43
|
+
when :details
|
44
|
+
if options[:property]
|
45
|
+
'details;property="' + options[:property] + '"'
|
46
|
+
elsif options[:collection]
|
47
|
+
'details;collection="' + options[:collection] + '"'
|
48
|
+
elsif options[:action]
|
49
|
+
'details;action="' + options[:action] + '"'
|
50
|
+
else
|
51
|
+
raise 'option not found for details rel'
|
52
|
+
end
|
53
|
+
when :invoke
|
54
|
+
if options[:action]
|
55
|
+
'invoke;action="' + options[:action] + '"'
|
56
|
+
elsif options[:type_action]
|
57
|
+
'invoke;typeaction="' + options[:type_action] + '"'
|
58
|
+
else
|
59
|
+
raise 'option not found for invoke rel'
|
60
|
+
end
|
61
|
+
when :modify
|
62
|
+
'modify;property="' + options[:property] + '"'
|
63
|
+
when :remove_from
|
64
|
+
'remove-from;collection="' + options[:collection] + '"'
|
65
|
+
when :service
|
66
|
+
'service;serviceId="' + options[:service_id] + '"'
|
67
|
+
when :value
|
68
|
+
if options[:property]
|
69
|
+
'value;property="' + options[:property] + '"'
|
70
|
+
elsif options[:collection]
|
71
|
+
'value;collection="' + options[:collection] + '"'
|
72
|
+
else
|
73
|
+
raise 'option not found for value rel'
|
74
|
+
end
|
75
|
+
else
|
76
|
+
raise "rel invalid: #{rel}"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def generate_repr_type(type, domain_type = nil, element_type = nil)
|
83
|
+
valid_repr = [:homepage, :user, :version, :list, :object, :object_property, :object_collection, :object_action,
|
84
|
+
:object_result, :action_result, :type_list, :domain_type, :property_description, :collection_description,
|
85
|
+
:action_description, :action_param_description, :type_action_result, :error, :services]
|
86
|
+
|
87
|
+
raise "repr-type invalid: #{type.to_s}" if not valid_repr.include?(type)
|
88
|
+
|
89
|
+
repr = 'application/json;profile="urn:org.restfulobjects:repr-types/' + underscore_to_hyphen_string(type) + '"'
|
90
|
+
|
91
|
+
if domain_type && [:object, :action].include?(type)
|
92
|
+
repr += ';x-ro-domain-type="' + domain_type + '"'
|
93
|
+
elsif element_type && [:object_collection, :action].include?(type)
|
94
|
+
repr +=';x-ro-element-type="' + element_type + '"'
|
95
|
+
end
|
96
|
+
|
97
|
+
repr
|
98
|
+
end
|
99
|
+
|
100
|
+
def underscore_to_hyphen_string(symbol)
|
101
|
+
symbol.to_s.sub('_', '-')
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module RestfulObjects
|
2
|
+
class DomainModel
|
3
|
+
include LinkGenerator
|
4
|
+
|
5
|
+
attr_accessor :base_url, :compatible_mode
|
6
|
+
attr_reader :metadata_schema, :version, :user, :types, :services, :objects
|
7
|
+
|
8
|
+
def self.current
|
9
|
+
@current ||= DomainModel.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.current=(value)
|
13
|
+
@current = value
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize
|
17
|
+
@base_url = 'http://localhost'
|
18
|
+
@metadata_schema = :selectable
|
19
|
+
@compatible_mode = false
|
20
|
+
@user = User.new(@base_url, 'anonymous')
|
21
|
+
@types = TypeList.new
|
22
|
+
@services = ServiceList.new(@base_url)
|
23
|
+
@objects = ObjectList.new(@base_url)
|
24
|
+
end
|
25
|
+
|
26
|
+
def get_homepage
|
27
|
+
{ 'links' => [
|
28
|
+
link_to(:self, '/', :homepage),
|
29
|
+
link_to(:user, '/user', :user),
|
30
|
+
link_to(:services, '/services', :list),
|
31
|
+
link_to(:version, '/version', :version),
|
32
|
+
link_to(:domain_types, '/domain-types', :type_list)
|
33
|
+
],
|
34
|
+
'extensions' => {}
|
35
|
+
}.to_json
|
36
|
+
end
|
37
|
+
|
38
|
+
def get_version
|
39
|
+
{ 'links' => [
|
40
|
+
link_to(:self, '/version', :version),
|
41
|
+
link_to(:up, '/', :homepage),
|
42
|
+
],
|
43
|
+
'specVersion' => '1.0',
|
44
|
+
'optionalCapabilities' => {
|
45
|
+
'blobsClobs' => true,
|
46
|
+
'deleteObjects' => true,
|
47
|
+
'domainModel' => metadata_schema.to_s,
|
48
|
+
'protoPersistentObjects' => true,
|
49
|
+
'validateOnly' => false
|
50
|
+
},
|
51
|
+
'extensions' => {}
|
52
|
+
}.to_json
|
53
|
+
end
|
54
|
+
|
55
|
+
def get_user
|
56
|
+
@user.get_as_json
|
57
|
+
end
|
58
|
+
|
59
|
+
def get_services
|
60
|
+
services.get_list
|
61
|
+
end
|
62
|
+
|
63
|
+
def metadata_schema=(value)
|
64
|
+
if not [:simple, :formal, :selectable].include?(value)
|
65
|
+
raise "invalid metadata schema, choose :simple, :formal or :selectable"
|
66
|
+
end
|
67
|
+
@metadata_schema = value
|
68
|
+
end
|
69
|
+
|
70
|
+
def reset
|
71
|
+
@base_url = 'http://localhost'
|
72
|
+
@user = nil
|
73
|
+
@types.clear
|
74
|
+
@services.clear
|
75
|
+
@objects.clear
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
@@ -0,0 +1,132 @@
|
|
1
|
+
require 'bigdecimal'
|
2
|
+
require 'base64'
|
3
|
+
|
4
|
+
module RestfulObjects
|
5
|
+
module Object
|
6
|
+
include LinkGenerator
|
7
|
+
|
8
|
+
def self.included(base)
|
9
|
+
RestfulObjects::DomainModel.current.types.add(base.name)
|
10
|
+
|
11
|
+
base.class_eval do
|
12
|
+
extend ObjectMacros
|
13
|
+
include ObjectBase
|
14
|
+
include ObjectProperties
|
15
|
+
include ObjectCollections
|
16
|
+
include ObjectActions
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module ObjectBase
|
22
|
+
attr_accessor :is_service, :title
|
23
|
+
|
24
|
+
def initialize
|
25
|
+
super
|
26
|
+
@deleted = false
|
27
|
+
@is_service = self.class.ancestors.include? RestfulObjects::Service
|
28
|
+
@title = "#{self.class.name} (#{object_id})"
|
29
|
+
rs_register_in_model
|
30
|
+
rs_type.collections.each_value { |collection| instance_variable_set "@#{collection.id}".to_sym, Array.new }
|
31
|
+
on_after_create if respond_to? :on_after_create
|
32
|
+
end
|
33
|
+
|
34
|
+
def rs_register_in_model
|
35
|
+
rs_model.objects.register(self) if not @is_service
|
36
|
+
end
|
37
|
+
|
38
|
+
def rs_model
|
39
|
+
RestfulObjects::DomainModel.current
|
40
|
+
end
|
41
|
+
|
42
|
+
def rs_type
|
43
|
+
rs_model.types[self.class.name]
|
44
|
+
end
|
45
|
+
|
46
|
+
def get_representation
|
47
|
+
HttpResponse.new(representation.to_json,
|
48
|
+
'application/json;profile="urn:org.restfulobjects:repr-types/object";x-ro-domain-type="' + rs_type.id + '"')
|
49
|
+
end
|
50
|
+
|
51
|
+
def rs_instance_id
|
52
|
+
object_id
|
53
|
+
end
|
54
|
+
|
55
|
+
def representation
|
56
|
+
representation = {
|
57
|
+
'title' => title,
|
58
|
+
'members' => generate_members,
|
59
|
+
'links' => [ link_to(:described_by, "/domain-types/#{self.class.name}", :domain_type) ],
|
60
|
+
'extensions' => rs_type.metadata
|
61
|
+
}
|
62
|
+
|
63
|
+
if not is_service
|
64
|
+
representation['instanceId'] = object_id.to_s
|
65
|
+
representation['links'] << link_to(:self, "/objects/#{self.class.name}/#{object_id}", :object)
|
66
|
+
else
|
67
|
+
representation['serviceId'] = self.class.name
|
68
|
+
representation['links'] << link_to(:self, "/services/#{self.class.name}", :object)
|
69
|
+
end
|
70
|
+
|
71
|
+
representation
|
72
|
+
end
|
73
|
+
|
74
|
+
def generate_members
|
75
|
+
if is_service
|
76
|
+
actions_members
|
77
|
+
else
|
78
|
+
properties_members.merge(collections_members.merge(actions_members))
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def rs_delete
|
83
|
+
on_before_delete if respond_to? :on_before_delete
|
84
|
+
@deleted = true
|
85
|
+
on_after_delete if respond_to? :on_after_delete
|
86
|
+
end
|
87
|
+
|
88
|
+
def deleted?
|
89
|
+
@deleted
|
90
|
+
end
|
91
|
+
|
92
|
+
def encode_value(value, type)
|
93
|
+
return nil if value.nil?
|
94
|
+
case type
|
95
|
+
when :string
|
96
|
+
value.to_s
|
97
|
+
when :int
|
98
|
+
value.to_i
|
99
|
+
when :decimal
|
100
|
+
value.to_f
|
101
|
+
when :date
|
102
|
+
value.strftime('%Y-%m-%d')
|
103
|
+
when :blob
|
104
|
+
Base64.encode64(value).strip
|
105
|
+
else
|
106
|
+
raise "encode_value unsupported property type: #{type}"
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def decode_value(value, type)
|
111
|
+
return nil if value.nil?
|
112
|
+
case type
|
113
|
+
when :string
|
114
|
+
value.to_s
|
115
|
+
when :int
|
116
|
+
value.to_i
|
117
|
+
when :decimal
|
118
|
+
Float(value)
|
119
|
+
when :date
|
120
|
+
Date.parse(value)
|
121
|
+
when :blob
|
122
|
+
Base64.decode64(value)
|
123
|
+
else
|
124
|
+
raise "decode_value unsupported property type: #{type}"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def get_self_link
|
129
|
+
link_to(:self, "/objects/#{self.class.name}/#{self.object_id}", :object)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|