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