restful_objects 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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,11 @@
1
+ module RestfulObjects
2
+ class HttpResponse
3
+ attr_accessor :body, :content_type, :status
4
+
5
+ def initialize(body, content_type, status = 200)
6
+ @body = body
7
+ @content_type = content_type
8
+ @status = status
9
+ end
10
+ end
11
+ 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