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.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/lib/restful_objects.rb +1 -2
  3. data/lib/restful_objects/domain_model/domain_model.rb +14 -11
  4. data/lib/restful_objects/domain_model/helpers/link_generator.rb +102 -90
  5. data/lib/restful_objects/domain_model/mixins/object.rb +10 -13
  6. data/lib/restful_objects/domain_model/mixins/object_actions.rb +106 -117
  7. data/lib/restful_objects/domain_model/mixins/object_base.rb +175 -131
  8. data/lib/restful_objects/domain_model/mixins/object_collections.rb +58 -71
  9. data/lib/restful_objects/domain_model/mixins/object_macros.rb +20 -26
  10. data/lib/restful_objects/domain_model/mixins/object_properties.rb +85 -98
  11. data/lib/restful_objects/domain_model/mixins/service.rb +9 -17
  12. data/lib/restful_objects/domain_model/types/action_description.rb +80 -82
  13. data/lib/restful_objects/domain_model/types/collection_description.rb +39 -40
  14. data/lib/restful_objects/domain_model/types/domain_type.rb +102 -105
  15. data/lib/restful_objects/domain_model/types/parameter_description.rb +42 -47
  16. data/lib/restful_objects/domain_model/types/parameter_description_list.rb +9 -12
  17. data/lib/restful_objects/domain_model/types/property_description.rb +62 -64
  18. data/lib/restful_objects/router.rb +6 -0
  19. data/lib/restful_objects/router/base.rb +23 -32
  20. data/lib/restful_objects/router/domain_object_resources.rb +89 -94
  21. data/lib/restful_objects/router/domain_type_resources.rb +24 -29
  22. data/lib/restful_objects/router/supporting_resources.rb +26 -31
  23. data/lib/restful_objects/version.rb +1 -1
  24. data/spec/integration/domain-types_actions_spec.rb +1 -3
  25. data/spec/integration/domain-types_collections_spec.rb +1 -39
  26. data/spec/integration/domain-types_properties_spec.rb +1 -78
  27. data/spec/integration/domain-types_spec.rb +39 -0
  28. data/spec/integration/{server-root_spec.rb → homepage_spec.rb} +2 -3
  29. data/spec/integration/objects_actions_spec.rb +7 -0
  30. data/spec/integration/objects_collections_spec.rb +169 -0
  31. data/spec/integration/objects_properties_spec.rb +122 -0
  32. data/spec/{acceptance/generate_json_representations_spec.rb → integration/objects_spec.rb} +12 -4
  33. data/spec/unit/object_actions_spec.rb +5 -5
  34. data/spec/unit/object_collections_spec.rb +1 -1
  35. data/spec/unit/object_properties_spec.rb +6 -6
  36. data/spec/unit/object_spec.rb +9 -9
  37. data/spec/unit/service_spec.rb +5 -5
  38. metadata +54 -35
  39. data/spec/integration/domain_model_spec.rb +0 -35
  40. data/spec/unit/type_list_spec.rb +0 -37
@@ -1,36 +1,30 @@
1
- module RestfulObjects
2
- module ObjectMacros
3
- def property(name, type, options = {})
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
- 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
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
- def collection(name, type, options = {})
24
- type = type.name if type.is_a? Class
19
+ def collection(name, type, options = {})
20
+ type = type.name if type.is_a?(Class)
25
21
 
26
- RestfulObjects::DomainModel.current.types[self.name].register_collection(name.to_s, type, options)
22
+ RestfulObjects::DomainModel.current.types[self.name].register_collection(name.to_s, type, options)
27
23
 
28
- self.class_eval { attr_reader name }
29
- end
24
+ attr_reader(name)
25
+ end
30
26
 
31
- def action(name, options = {})
32
- RestfulObjects::DomainModel.current.types[self.name].register_action(name.to_s, options)
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
- 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),
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(:details, "/objects/#{self.class.name}/#{object_id}/properties/#{name}", :object_property, property: name)
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
- if property.read_only
16
- members[name]['disabledReason'] = property.disabled_reason
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
- if self.respond_to?("#{name}_choices")
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
- members
43
+ else
44
+ representation[name]['disabledReason'] = property.disabled_reason
31
45
  end
32
46
 
33
- def get_property_as_json(property)
34
- property = property.to_s if property.is_a?(Symbol)
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
- if self.respond_to?("#{property}_choices")
55
- choices = self.send("#{property}_choices")
56
- raise "value returned by #{property}_choices method should be an Array" unless choices.is_a?(Array)
57
- if property_description(property).is_reference
58
- choices_json = choices.map { |object| object.get_property_rel_representation(property) }
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
- representation.to_json
70
- end
56
+ ro_set_property_as_json(name, JSON.parse(input)['value'])
57
+ on_after_update if respond_to?(:on_after_update)
71
58
 
72
- def put_property_as_json(property, json)
73
- property = property.to_s if property.is_a?(Symbol)
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
- value = JSON.parse(json)['value']
78
- set_property_value(property, value)
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
- def clear_property(property)
84
- raise "property not exists" if not rs_model.types[self.class.name].properties.include?(property)
85
- raise "read-only property" if rs_model.types[self.class.name].properties[property].read_only
86
-
87
- send("#{property}=".to_sym, nil)
88
- on_after_update if respond_to?(:on_after_update)
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
- def property_description(property)
93
- rs_model.types[self.class.name].properties[property]
94
- end
81
+ send("#{name}=", nil)
95
82
 
96
- def property_type(property)
97
- rs_model.types[self.class.name].properties[property].return_type
98
- end
83
+ on_after_update if respond_to?(:on_after_update)
84
+ return ro_get_property_response(name)
85
+ end
99
86
 
100
- def get_property_value(property)
101
- encode_value(send(property.to_sym), property_type(property), property)
102
- end
87
+ protected
103
88
 
104
- def set_property_value(property, value)
105
- if property_description(property).is_reference
106
- unless value.nil?
107
- href_value = value['href']
108
- match = Regexp.new(".*/objects/(?<domain-type>\\w*)/(?<object-id>\\d*)").match(href_value)
109
- raise "invalid property reference format: '#{href_value}'" if not match
110
- domain_type = match['domain-type']
111
- id = match['object-id'].to_i
112
- raise "value does not exists" if not rs_model.objects.include?(id)
113
- raise "domain-type does not exists" if not rs_model.types.include?(domain_type)
114
- send "#{property}=".to_sym, rs_model.objects[id]
115
- else
116
- send "#{property}=".to_sym, nil
117
- end
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 "#{property}=".to_sym, decode_value(value, property_type(property))
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
- module Service
3
- include LinkGenerator
1
+ module RestfulObjects::Service
2
+ include RestfulObjects::LinkGenerator
4
3
 
5
- def self.included(base)
6
- RestfulObjects::DomainModel.current.register_type(base.name)
4
+ def self.included(base)
5
+ RestfulObjects::DomainModel.current.register_type(base.name)
6
+ RestfulObjects::DomainModel.current.register_service(base)
7
7
 
8
- base.class_eval do
9
- extend ObjectMacros
10
- include ObjectBase
11
- include ObjectActions
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
- module RestfulObjects
4
- class ActionDescription
5
- include LinkGenerator
3
+ class RestfulObjects::ActionDescription
4
+ include RestfulObjects::LinkGenerator
6
5
 
7
- attr_reader :id, :kind_result_type, :result_type, :parameters
8
- attr_accessor :friendly_name, :description, :member_order, :disabled_reason
6
+ attr_reader :id, :kind_result_type, :result_type, :parameters
7
+ attr_accessor :friendly_name, :description, :member_order, :disabled_reason
9
8
 
10
- def initialize(id, domain_type, options)
11
- @id = id
12
- @domain_type = domain_type
13
- @friendly_name = options[:friendly_name] || id
14
- @description = options[:description] || id
15
- @member_order = options[:member_order] || 0
16
- @disabled_reason = options[:disabled_reason] || ''
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
- case options[:return_type]
19
- when NilClass
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 'invalid return_type: symbol or hash expected'
50
- end
51
-
52
- @parameters = ParameterDescriptionList.new
53
- options[:parameters].each { |name, definition| @parameters.add(name, definition) } if options[:parameters]
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
- def get_representation
57
- representation = {
58
- 'id' => @id,
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
- representation['friendlyName'] = friendly_name if friendly_name
71
- representation['description'] = description if description
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
- representation.to_json
74
- end
69
+ representation['friendlyName'] = friendly_name if friendly_name
70
+ representation['description'] = description if description
75
71
 
76
- def metadata
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
- def has_params
87
- not @parameters.empty?
88
- end
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
- def parameters_list
91
- result = {}
92
- parameters.each do |name, parameter|
93
- result[name] = {
94
- 'extension' => parameter.metadata
95
- }
96
- end
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