restful_objects 0.0.7 → 0.0.8
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 +4 -4
- data/lib/restful_objects.rb +1 -2
- data/lib/restful_objects/domain_model/domain_model.rb +14 -11
- data/lib/restful_objects/domain_model/helpers/link_generator.rb +102 -90
- data/lib/restful_objects/domain_model/mixins/object.rb +10 -13
- data/lib/restful_objects/domain_model/mixins/object_actions.rb +106 -117
- data/lib/restful_objects/domain_model/mixins/object_base.rb +175 -131
- data/lib/restful_objects/domain_model/mixins/object_collections.rb +58 -71
- data/lib/restful_objects/domain_model/mixins/object_macros.rb +20 -26
- data/lib/restful_objects/domain_model/mixins/object_properties.rb +85 -98
- data/lib/restful_objects/domain_model/mixins/service.rb +9 -17
- data/lib/restful_objects/domain_model/types/action_description.rb +80 -82
- data/lib/restful_objects/domain_model/types/collection_description.rb +39 -40
- data/lib/restful_objects/domain_model/types/domain_type.rb +102 -105
- data/lib/restful_objects/domain_model/types/parameter_description.rb +42 -47
- data/lib/restful_objects/domain_model/types/parameter_description_list.rb +9 -12
- data/lib/restful_objects/domain_model/types/property_description.rb +62 -64
- data/lib/restful_objects/router.rb +6 -0
- data/lib/restful_objects/router/base.rb +23 -32
- data/lib/restful_objects/router/domain_object_resources.rb +89 -94
- data/lib/restful_objects/router/domain_type_resources.rb +24 -29
- data/lib/restful_objects/router/supporting_resources.rb +26 -31
- data/lib/restful_objects/version.rb +1 -1
- data/spec/integration/domain-types_actions_spec.rb +1 -3
- data/spec/integration/domain-types_collections_spec.rb +1 -39
- data/spec/integration/domain-types_properties_spec.rb +1 -78
- data/spec/integration/domain-types_spec.rb +39 -0
- data/spec/integration/{server-root_spec.rb → homepage_spec.rb} +2 -3
- data/spec/integration/objects_actions_spec.rb +7 -0
- data/spec/integration/objects_collections_spec.rb +169 -0
- data/spec/integration/objects_properties_spec.rb +122 -0
- data/spec/{acceptance/generate_json_representations_spec.rb → integration/objects_spec.rb} +12 -4
- data/spec/unit/object_actions_spec.rb +5 -5
- data/spec/unit/object_collections_spec.rb +1 -1
- data/spec/unit/object_properties_spec.rb +6 -6
- data/spec/unit/object_spec.rb +9 -9
- data/spec/unit/service_spec.rb +5 -5
- metadata +54 -35
- data/spec/integration/domain_model_spec.rb +0 -35
- data/spec/unit/type_list_spec.rb +0 -37
@@ -1,36 +1,30 @@
|
|
1
|
-
module RestfulObjects
|
2
|
-
|
3
|
-
|
4
|
-
RestfulObjects::DomainModel.current.types[self.name].register_property(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
|
1
|
+
module RestfulObjects::ObjectMacros
|
2
|
+
def property(name, type, options = {})
|
3
|
+
RestfulObjects::DomainModel.current.types[self.name].register_property(name.to_s, type, options)
|
13
4
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
5
|
+
define_method(name) do
|
6
|
+
instance_variable_get("@#{name}")
|
7
|
+
end
|
8
|
+
|
9
|
+
unless options[:read_only]
|
10
|
+
define_method("#{name}=") do |value|
|
11
|
+
if options[:max_length] && value && value.length > options[:max_length]
|
12
|
+
raise ArgumentError.new("string max length exceeded")
|
19
13
|
end
|
14
|
+
instance_variable_set("@#{name}", value)
|
20
15
|
end
|
21
16
|
end
|
17
|
+
end
|
22
18
|
|
23
|
-
|
24
|
-
|
19
|
+
def collection(name, type, options = {})
|
20
|
+
type = type.name if type.is_a?(Class)
|
25
21
|
|
26
|
-
|
22
|
+
RestfulObjects::DomainModel.current.types[self.name].register_collection(name.to_s, type, options)
|
27
23
|
|
28
|
-
|
29
|
-
|
24
|
+
attr_reader(name)
|
25
|
+
end
|
30
26
|
|
31
|
-
|
32
|
-
|
33
|
-
end
|
27
|
+
def action(name, options = {})
|
28
|
+
RestfulObjects::DomainModel.current.types[self.name].register_action(name.to_s, options)
|
34
29
|
end
|
35
30
|
end
|
36
|
-
|
@@ -1,124 +1,111 @@
|
|
1
|
-
module RestfulObjects
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
1
|
+
module RestfulObjects::ObjectProperties
|
2
|
+
HTTP_OK = 200
|
3
|
+
HTTP_NOT_FOUND = 404
|
4
|
+
|
5
|
+
def ro_get_property_metadata(name)
|
6
|
+
ro_domain_type.properties[name]
|
7
|
+
end
|
8
|
+
|
9
|
+
def ro_get_property_response(name)
|
10
|
+
name = String(name)
|
11
|
+
property = ro_domain_type.properties[name]
|
12
|
+
return [HTTP_NOT_FOUND, { 'Warning' => "No such property #{name}" }, ''] unless property
|
13
|
+
|
14
|
+
representation = {
|
15
|
+
name =>
|
16
|
+
{ 'value' => ro_get_property_as_json(name),
|
9
17
|
'links' => [
|
10
|
-
link_to(:
|
11
|
-
|
18
|
+
link_to(:self, "/objects/#{ro_domain_type.id}/#{ro_instance_id}/properties/#{name}", :object_property),
|
19
|
+
link_to(:up, "/objects/#{ro_domain_type.id}/#{ro_instance_id}", :object) ],
|
12
20
|
'extensions' => property.metadata
|
13
21
|
}
|
22
|
+
}
|
14
23
|
|
15
|
-
|
16
|
-
|
24
|
+
unless property.read_only
|
25
|
+
representation[name]['links'] << link_to(:modify,
|
26
|
+
"/objects/#{ro_domain_type.id}/#{ro_instance_id}/properties/#{name}",
|
27
|
+
:object_property,
|
28
|
+
{ property: name, method: 'PUT', arguments: { 'value' => nil } })
|
29
|
+
representation[name]['links'] << link_to(:clear,
|
30
|
+
"/objects/#{ro_domain_type.id}/#{ro_instance_id}/properties/#{name}",
|
31
|
+
:object_property,
|
32
|
+
{ property: name, method: 'DELETE'})
|
33
|
+
if self.respond_to?("#{name}_choices")
|
34
|
+
choices = self.send("#{name}_choices")
|
35
|
+
raise "value returned by #{name}_choices method should be an Array" unless choices.is_a?(Array)
|
36
|
+
if ro_get_property_metadata(name).is_reference
|
37
|
+
choices_json = choices.map { |object| object.ro_property_relation_representation(name) }
|
17
38
|
else
|
18
|
-
|
19
|
-
choices = self.send("#{name}_choices")
|
20
|
-
raise "value returned by #{name}_choices method should be an Array" unless choices.is_a?(Array)
|
21
|
-
if property_description(name).is_reference
|
22
|
-
choices_json = choices.map { |object| object.get_property_rel_representation(name) }
|
23
|
-
else
|
24
|
-
choices_json = choices.map { |value| decode_value(value, property_type(name)) }
|
25
|
-
end
|
26
|
-
members[name]['choices'] = choices_json
|
27
|
-
end
|
39
|
+
choices_json = choices.map { |value| decode_value(value, ro_get_property_metadata(name).return_type) }
|
28
40
|
end
|
41
|
+
representation[name]['choices'] = choices_json
|
29
42
|
end
|
30
|
-
|
43
|
+
else
|
44
|
+
representation[name]['disabledReason'] = property.disabled_reason
|
31
45
|
end
|
32
46
|
|
33
|
-
|
34
|
-
|
35
|
-
raise "Property not exists" if not rs_model.types[self.class.name].properties.include?(property)
|
36
|
-
|
37
|
-
representation = {
|
38
|
-
property =>
|
39
|
-
{ 'value' => get_property_value(property),
|
40
|
-
'links' => [
|
41
|
-
link_to(:self, "/objects/#{self.class.name}/#{object_id}/properties/#{property}", :object_property),
|
42
|
-
link_to(:up, "/objects/#{self.class.name}/#{object_id}", :object) ],
|
43
|
-
'extensions' => rs_model.types[self.class.name].properties[property].metadata
|
44
|
-
}
|
45
|
-
}
|
46
|
-
|
47
|
-
if not rs_model.types[self.class.name].properties[property].read_only then
|
48
|
-
representation[property]['links'].concat [
|
49
|
-
link_to(:modify, "/objects/#{self.class.name}/#{object_id}/properties/#{property}", :object_property,
|
50
|
-
{ property: property, method: 'PUT', arguments: { 'value' => nil } }),
|
51
|
-
link_to(:clear, "/objects/#{self.class.name}/#{object_id}/properties/#{property}", :object_property,
|
52
|
-
{ property: property, method: 'DELETE'} ) ]
|
47
|
+
[HTTP_OK, { 'Content-Type' => ro_content_type_for_property }, representation.to_json]
|
48
|
+
end
|
53
49
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
else
|
60
|
-
choices_json = choices.map { |value| decode_value(value, property_type(property)) }
|
61
|
-
end
|
62
|
-
representation[property]['choices'] = choices_json
|
63
|
-
end
|
64
|
-
else
|
65
|
-
representation[property]['disabledReason'] =
|
66
|
-
rs_model.types[self.class.name].properties[property].disabled_reason
|
67
|
-
end
|
50
|
+
def ro_put_property_and_get_response(name, input)
|
51
|
+
name = String(name)
|
52
|
+
property = ro_domain_type.properties[name]
|
53
|
+
return [HTTP_NOT_FOUND, { 'Warning' => "No such property #{name}" }, ''] unless property
|
54
|
+
return [HTTP_FORBIDDEN, { 'Warning' => "Read-only property #{name}" }, ''] if property.read_only
|
68
55
|
|
69
|
-
|
70
|
-
|
56
|
+
ro_set_property_as_json(name, JSON.parse(input)['value'])
|
57
|
+
on_after_update if respond_to?(:on_after_update)
|
71
58
|
|
72
|
-
|
73
|
-
|
74
|
-
raise 'property not exists' unless rs_model.types[self.class.name].properties.include?(property)
|
75
|
-
raise 'read-only property' if rs_model.types[self.class.name].properties[property].read_only
|
59
|
+
ro_get_property_response(name)
|
60
|
+
end
|
76
61
|
|
77
|
-
|
78
|
-
|
62
|
+
def ro_put_multiple_properties_and_get_response(input)
|
63
|
+
properties = JSON.parse(input)
|
64
|
+
properties.each do |name, value|
|
65
|
+
raise 'property not exists' unless ro_domain_type.properties.include?(name)
|
66
|
+
raise 'read-only property' if ro_domain_type.properties[name].read_only
|
67
|
+
ro_set_property_as_json(name, value['value'])
|
79
68
|
on_after_update if respond_to?(:on_after_update)
|
80
|
-
get_property_as_json(property)
|
81
69
|
end
|
70
|
+
[HTTP_OK, { 'Content-Type' => ro_content_type_for_object(ro_domain_type.id) }, ro_get_representation(false).to_json]
|
71
|
+
end
|
82
72
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
get_property_as_json(property)
|
73
|
+
def ro_clear_property_and_get_response(name)
|
74
|
+
name = String(name)
|
75
|
+
if !ro_get_property_metadata(name)
|
76
|
+
return [HTTP_NOT_FOUND, { 'Warning' => "No such property #{name}" }, '']
|
77
|
+
elsif ro_get_property_metadata(name).read_only
|
78
|
+
return [HTTP_FORBIDDEN, { 'Warning' => "Read-only property #{name}" }, '']
|
90
79
|
end
|
91
80
|
|
92
|
-
|
93
|
-
rs_model.types[self.class.name].properties[property]
|
94
|
-
end
|
81
|
+
send("#{name}=", nil)
|
95
82
|
|
96
|
-
|
97
|
-
|
98
|
-
|
83
|
+
on_after_update if respond_to?(:on_after_update)
|
84
|
+
return ro_get_property_response(name)
|
85
|
+
end
|
99
86
|
|
100
|
-
|
101
|
-
encode_value(send(property.to_sym), property_type(property), property)
|
102
|
-
end
|
87
|
+
protected
|
103
88
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
89
|
+
def ro_get_property_as_json(name)
|
90
|
+
encode_value(send(name), ro_get_property_metadata(name).return_type, name)
|
91
|
+
end
|
92
|
+
|
93
|
+
def ro_set_property_as_json(name, json)
|
94
|
+
if ro_get_property_metadata(name).is_reference
|
95
|
+
unless json.nil?
|
96
|
+
href_value = json['href']
|
97
|
+
match = Regexp.new(".*/objects/(?<domain-type>\\w*)/(?<object-id>\\d*)").match(href_value)
|
98
|
+
raise "invalid property reference format: '#{href_value}'" if not match
|
99
|
+
domain_type = match['domain-type']
|
100
|
+
id = match['object-id'].to_i
|
101
|
+
raise "value does not exists" if not ro_domain_model.objects.include?(id)
|
102
|
+
raise "domain-type does not exists" if not ro_domain_model.types.include?(domain_type)
|
103
|
+
send "#{name}=".to_sym, ro_domain_model.objects[id]
|
118
104
|
else
|
119
|
-
send "#{
|
105
|
+
send "#{name}=".to_sym, nil
|
120
106
|
end
|
107
|
+
else
|
108
|
+
send "#{name}=".to_sym, decode_value(json, ro_get_property_metadata(name).return_type)
|
121
109
|
end
|
122
110
|
end
|
123
111
|
end
|
124
|
-
|
@@ -1,22 +1,14 @@
|
|
1
|
-
module RestfulObjects
|
2
|
-
|
3
|
-
include LinkGenerator
|
1
|
+
module RestfulObjects::Service
|
2
|
+
include RestfulObjects::LinkGenerator
|
4
3
|
|
5
|
-
|
6
|
-
|
4
|
+
def self.included(base)
|
5
|
+
RestfulObjects::DomainModel.current.register_type(base.name)
|
6
|
+
RestfulObjects::DomainModel.current.register_service(base)
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
def rs_register_in_model
|
14
|
-
# do_nothing
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
RestfulObjects::DomainModel.current.register_service(base)
|
8
|
+
base.class_eval do
|
9
|
+
extend RestfulObjects::ObjectMacros
|
10
|
+
include RestfulObjects::ObjectBase
|
11
|
+
include RestfulObjects::ObjectActions
|
19
12
|
end
|
20
13
|
end
|
21
14
|
end
|
22
|
-
|
@@ -1,100 +1,98 @@
|
|
1
1
|
require_relative 'parameter_description_list'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
include LinkGenerator
|
3
|
+
class RestfulObjects::ActionDescription
|
4
|
+
include RestfulObjects::LinkGenerator
|
6
5
|
|
7
|
-
|
8
|
-
|
6
|
+
attr_reader :id, :kind_result_type, :result_type, :parameters
|
7
|
+
attr_accessor :friendly_name, :description, :member_order, :disabled_reason
|
9
8
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
9
|
+
def initialize(id, domain_type, options)
|
10
|
+
@id = id
|
11
|
+
@domain_type = domain_type
|
12
|
+
@friendly_name = options[:friendly_name] || id
|
13
|
+
@description = options[:description] || id
|
14
|
+
@member_order = options[:member_order] || 0
|
15
|
+
@disabled_reason = options[:disabled_reason] || ''
|
17
16
|
|
18
|
-
|
19
|
-
|
17
|
+
case options[:return_type]
|
18
|
+
when NilClass
|
19
|
+
@result_type = :void
|
20
|
+
@kind_result_type = :void
|
21
|
+
when Symbol
|
22
|
+
if options[:return_type] == :void
|
20
23
|
@result_type = :void
|
21
24
|
@kind_result_type = :void
|
22
|
-
when Symbol
|
23
|
-
if options[:return_type] == :void
|
24
|
-
@result_type = :void
|
25
|
-
@kind_result_type = :void
|
26
|
-
else
|
27
|
-
raise "result type for scalar '#{options[:return_type]}' unssuported" unless [:string, :int, :bool, :decimal, :date, :blob].include?(options[:return_type])
|
28
|
-
@result_type = options[:return_type]
|
29
|
-
@kind_result_type = :scalar
|
30
|
-
end
|
31
|
-
when Hash
|
32
|
-
options[:return_type]
|
33
|
-
if options[:return_type][:object]
|
34
|
-
@result_type = options[:return_type][:object]
|
35
|
-
@kind_result_type = :object
|
36
|
-
elsif options[:return_type][:proto_object]
|
37
|
-
@result_type = options[:return_type][:proto_object]
|
38
|
-
@kind_result_type = :proto_object
|
39
|
-
elsif options[:return_type][:list]
|
40
|
-
@result_type = options[:return_type][:list]
|
41
|
-
@kind_result_type = :list
|
42
|
-
else
|
43
|
-
raise 'invalid return_type: object, proto_object or list key expected'
|
44
|
-
end
|
45
|
-
unless @result_type.is_a?(Class) or @result_type.is_a?(String)
|
46
|
-
raise 'return_type object, proto_object or list value should be a class or a string'
|
47
|
-
end
|
48
25
|
else
|
49
|
-
raise '
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
26
|
+
raise "result type for scalar '#{options[:return_type]}' unssuported" unless [:string, :int, :bool, :decimal, :date, :blob].include?(options[:return_type])
|
27
|
+
@result_type = options[:return_type]
|
28
|
+
@kind_result_type = :scalar
|
29
|
+
end
|
30
|
+
when Hash
|
31
|
+
options[:return_type]
|
32
|
+
if options[:return_type][:object]
|
33
|
+
@result_type = options[:return_type][:object]
|
34
|
+
@kind_result_type = :object
|
35
|
+
elsif options[:return_type][:proto_object]
|
36
|
+
@result_type = options[:return_type][:proto_object]
|
37
|
+
@kind_result_type = :proto_object
|
38
|
+
elsif options[:return_type][:list]
|
39
|
+
@result_type = options[:return_type][:list]
|
40
|
+
@kind_result_type = :list
|
41
|
+
else
|
42
|
+
raise 'invalid return_type: object, proto_object or list key expected'
|
43
|
+
end
|
44
|
+
unless @result_type.is_a?(Class) or @result_type.is_a?(String)
|
45
|
+
raise 'return_type object, proto_object or list value should be a class or a string'
|
46
|
+
end
|
47
|
+
else
|
48
|
+
raise 'invalid return_type: symbol or hash expected'
|
54
49
|
end
|
55
50
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
'hasParams' => has_params,
|
60
|
-
'memberOrder' => @member_order,
|
61
|
-
'parameters' => parameters_list,
|
62
|
-
'links' => [
|
63
|
-
link_to(:self, "/domain-types/#{@domain_type}/actions/#{@id}", :action_description),
|
64
|
-
link_to(:up, "/domain-types/#{@domain_type}", :domain_type),
|
65
|
-
link_to(:return_type, "/domain-types/#{result_type}", :domain_type)
|
66
|
-
],
|
67
|
-
'extensions' => {}
|
68
|
-
}
|
51
|
+
@parameters = RestfulObjects::ParameterDescriptionList.new
|
52
|
+
options[:parameters].each { |name, definition| @parameters.add(name, definition) } if options[:parameters]
|
53
|
+
end
|
69
54
|
|
70
|
-
|
71
|
-
|
55
|
+
def get_representation
|
56
|
+
representation = {
|
57
|
+
'id' => @id,
|
58
|
+
'hasParams' => has_params,
|
59
|
+
'memberOrder' => @member_order,
|
60
|
+
'parameters' => parameters_list,
|
61
|
+
'links' => [
|
62
|
+
link_to(:self, "/domain-types/#{@domain_type}/actions/#{@id}", :action_description),
|
63
|
+
link_to(:up, "/domain-types/#{@domain_type}", :domain_type),
|
64
|
+
link_to(:return_type, "/domain-types/#{result_type}", :domain_type)
|
65
|
+
],
|
66
|
+
'extensions' => {}
|
67
|
+
}
|
72
68
|
|
73
|
-
|
74
|
-
|
69
|
+
representation['friendlyName'] = friendly_name if friendly_name
|
70
|
+
representation['description'] = description if description
|
75
71
|
|
76
|
-
|
77
|
-
|
78
|
-
'friendlyName' => friendly_name,
|
79
|
-
'description' => description,
|
80
|
-
'returnType' => result_type,
|
81
|
-
'hasParams' => has_params,
|
82
|
-
'memberOrder' => member_order
|
83
|
-
}
|
84
|
-
end
|
72
|
+
representation.to_json
|
73
|
+
end
|
85
74
|
|
86
|
-
|
87
|
-
|
88
|
-
|
75
|
+
def metadata
|
76
|
+
{
|
77
|
+
'friendlyName' => friendly_name,
|
78
|
+
'description' => description,
|
79
|
+
'returnType' => result_type,
|
80
|
+
'hasParams' => has_params,
|
81
|
+
'memberOrder' => member_order
|
82
|
+
}
|
83
|
+
end
|
89
84
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
result
|
85
|
+
def has_params
|
86
|
+
not @parameters.empty?
|
87
|
+
end
|
88
|
+
|
89
|
+
def parameters_list
|
90
|
+
result = {}
|
91
|
+
parameters.each do |name, parameter|
|
92
|
+
result[name] = {
|
93
|
+
'extension' => parameter.metadata
|
94
|
+
}
|
98
95
|
end
|
96
|
+
result
|
99
97
|
end
|
100
98
|
end
|