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.
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