restful_objects 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +6 -2
  3. data/bin/restful_server +1 -1
  4. data/lib/restful_objects/domain_model/domain_model.rb +143 -0
  5. data/lib/restful_objects/{link_generator.rb → domain_model/helpers/link_generator.rb} +0 -0
  6. data/lib/restful_objects/{object.rb → domain_model/mixins/object.rb} +6 -2
  7. data/lib/restful_objects/{object_actions.rb → domain_model/mixins/object_actions.rb} +1 -0
  8. data/lib/restful_objects/{object_base.rb → domain_model/mixins/object_base.rb} +46 -13
  9. data/lib/restful_objects/{object_collections.rb → domain_model/mixins/object_collections.rb} +1 -0
  10. data/lib/restful_objects/{object_macros.rb → domain_model/mixins/object_macros.rb} +4 -3
  11. data/lib/restful_objects/{object_properties.rb → domain_model/mixins/object_properties.rb} +49 -7
  12. data/lib/restful_objects/{service.rb → domain_model/mixins/service.rb} +3 -2
  13. data/lib/restful_objects/{action_description.rb → domain_model/types/action_description.rb} +2 -0
  14. data/lib/restful_objects/{collection_description.rb → domain_model/types/collection_description.rb} +0 -0
  15. data/lib/restful_objects/{type.rb → domain_model/types/domain_type.rb} +28 -8
  16. data/lib/restful_objects/{parameter_description.rb → domain_model/types/parameter_description.rb} +0 -0
  17. data/lib/restful_objects/{parameter_description_list.rb → domain_model/types/parameter_description_list.rb} +3 -0
  18. data/lib/restful_objects/{property_description.rb → domain_model/types/property_description.rb} +23 -13
  19. data/lib/restful_objects/domain_model/user.rb +31 -0
  20. data/lib/restful_objects/router/base.rb +41 -0
  21. data/lib/restful_objects/router/domain_object_resources.rb +97 -0
  22. data/lib/restful_objects/router/domain_type_resources.rb +37 -0
  23. data/lib/restful_objects/router/supporting_resources.rb +39 -0
  24. data/lib/restful_objects/version.rb +1 -1
  25. data/lib/restful_objects.rb +3 -25
  26. data/spec/acceptance/generate_json_representations_spec.rb +102 -0
  27. data/spec/integration/domain-types_properties_spec.rb +76 -1
  28. data/spec/integration/domain_model_spec.rb +35 -0
  29. data/spec/spec_helper.rb +18 -3
  30. data/spec/unit/object_spec.rb +0 -73
  31. data/spec/unit/type_list_spec.rb +2 -2
  32. metadata +26 -26
  33. data/lib/restful_objects/action_list.rb +0 -17
  34. data/lib/restful_objects/collection_list.rb +0 -17
  35. data/lib/restful_objects/domain_model.rb +0 -83
  36. data/lib/restful_objects/http_response.rb +0 -11
  37. data/lib/restful_objects/object_list.rb +0 -16
  38. data/lib/restful_objects/property_list.rb +0 -17
  39. data/lib/restful_objects/server.rb +0 -131
  40. data/lib/restful_objects/service_list.rb +0 -55
  41. data/lib/restful_objects/type_list.rb +0 -30
  42. data/lib/restful_objects/user.rb +0 -30
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5b9d5f9ab0b9191be431dcf51c8678a83b69def4
4
- data.tar.gz: 2768a747a319c97a75d2cab86aa9728906d6d1d0
3
+ metadata.gz: acc457412078eadbb0d83af84afa695eb3abea02
4
+ data.tar.gz: 761377929fe4800901ee88b361cfe6b7e603a98e
5
5
  SHA512:
6
- metadata.gz: a0287a4cead98bedbd4cfddad1dd9d6e96361e12e528f400c43ac33639e167fbcde3acc047b9d732f570dd28f4dcc5e5a09a2dd9fb56406b2fe7d5e35a4e1f32
7
- data.tar.gz: dd0c7fe482409be9b111edc27d84bdc99072678946eadaca23a2744364b1ce5d9047e7a48b84fd4cec749ffeb1645f12f5c4aca9a73a3bad96226cd6bbc6a8a7
6
+ metadata.gz: 197315bdd45d57a1dea3ab04593561893106582eb5b7bdbd8da84116b4abc992dbb1ffc2390b568ceb12ddf5f5d5afd2d585ea578bebc16485ae4adcea9edf6d
7
+ data.tar.gz: 8e73987857c84cababd5eaa857b67b38041d7f8691f9ae7826c09f5da2bd2d24065a4a3212af13860375d827cfac358d70b050405b109b0ef507f88378451243
data/README.md CHANGED
@@ -49,13 +49,13 @@ end
49
49
  - [Topological Sort Restful Objects server](https://github.com/vizcay/ro_topological_sort)
50
50
 
51
51
  ### How to install it?
52
- Run
52
+ If you have a Ruby environment (> 1.9.3) with RubyGems installed, simply run:
53
53
 
54
54
  ```shell
55
55
  gem install restful_objects
56
56
  ```
57
57
 
58
- or add
58
+ or add,
59
59
 
60
60
  ```ruby
61
61
  gem 'restful_objects'
@@ -63,6 +63,10 @@ gem 'restful_objects'
63
63
 
64
64
  to your's project Gemfile and 'bundle install' it.
65
65
 
66
+ If not, consider using Vagrant to setup a VM with all the dependencies and the latest version of the framework following the instructions here:
67
+
68
+ [https://github.com/vizcay/restful_objects_vagrant](https://github.com/vizcay/restful_objects_vagrant)
69
+
66
70
  ### Project Status
67
71
  The source quality is at alpha state, and it was created mostly as a proof of concept. Still is has plenty of specs and a big percentage of the specification implemented, the grey areas are mostly related to errors and validations. It has been developed with MRI and hasn't been tested on other Ruby implementations.
68
72
 
data/bin/restful_server CHANGED
@@ -10,5 +10,5 @@ end
10
10
  load ARGV[0]
11
11
 
12
12
  RestfulObjects::DomainModel.current.base_url = ARGV[1] || 'http://localhost:4567'
13
- RestfulObjects::Server.run!
13
+ RestfulObjects::Router::Base.run!
14
14
 
@@ -0,0 +1,143 @@
1
+ require_relative 'helpers/link_generator'
2
+ require_relative 'user'
3
+ require_relative 'mixins/object'
4
+ require_relative 'mixins/service'
5
+ require_relative 'types/domain_type'
6
+
7
+ module RestfulObjects
8
+ class DomainModel
9
+ include LinkGenerator
10
+
11
+ attr_accessor :base_url, :compatible_mode
12
+ attr_reader :metadata_schema, :version, :user, :types, :services, :objects
13
+
14
+ def self.current
15
+ @current ||= DomainModel.new
16
+ end
17
+
18
+ def self.current=(value)
19
+ @current = value
20
+ end
21
+
22
+ def initialize
23
+ @base_url = 'http://localhost'
24
+ @metadata_schema = :selectable
25
+ @compatible_mode = false
26
+ @user = User.new(@base_url, 'anonymous')
27
+ @types = {}
28
+ @services = {}
29
+ @objects = {}
30
+ @services.instance_eval do
31
+ alias :base_accesor :[]
32
+
33
+ def [](key)
34
+ value = base_accesor(key)
35
+ if value.is_a?(Class)
36
+ value = value.new
37
+ self[key] = value
38
+ end
39
+ value
40
+ end
41
+ end
42
+ end
43
+
44
+ def register_object(instance)
45
+ @objects[instance.object_id] = instance
46
+ end
47
+
48
+ def register_service(service)
49
+ raise 'service registration should be done with a class' unless service.is_a?(Class)
50
+ @services[service.name] = service
51
+ end
52
+
53
+ def register_type(name)
54
+ @types[name] = DomainType.new(name)
55
+ end
56
+
57
+ def get_homepage
58
+ { 'links' => [
59
+ link_to(:self, '/', :homepage),
60
+ link_to(:user, '/user', :user),
61
+ link_to(:services, '/services', :list),
62
+ link_to(:version, '/version', :version),
63
+ link_to(:domain_types, '/domain-types', :type_list)
64
+ ],
65
+ 'extensions' => {}
66
+ }.to_json
67
+ end
68
+
69
+ def get_version
70
+ { 'links' => [
71
+ link_to(:self, '/version', :version),
72
+ link_to(:up, '/', :homepage),
73
+ ],
74
+ 'specVersion' => '1.0',
75
+ 'optionalCapabilities' => {
76
+ 'blobsClobs' => true,
77
+ 'deleteObjects' => true,
78
+ 'domainModel' => metadata_schema.to_s,
79
+ 'protoPersistentObjects' => true,
80
+ 'validateOnly' => false
81
+ },
82
+ 'extensions' => {}
83
+ }.to_json
84
+ end
85
+
86
+ def get_user
87
+ @user.get_as_json
88
+ end
89
+
90
+ def get_services
91
+ { 'links' => [
92
+ link_to(:self, '/services', :services),
93
+ link_to(:up, '/', :homepage),
94
+ ],
95
+ 'value' => services_generate_values,
96
+ 'extensions' => { }
97
+ }.to_json
98
+ end
99
+
100
+ def get_type_list_representation
101
+ { 'links' => [link_to(:self, '/domain-types', :type_list), link_to(:up, '/', :homepage)],
102
+ 'value' => @types.map { |name| link_to(:domain_type, "/domain-types/#{name}", :domain_type) }
103
+ }.to_json
104
+ end
105
+
106
+ def metadata_schema=(value)
107
+ if not [:simple, :formal, :selectable].include?(value)
108
+ raise "invalid metadata schema, choose :simple, :formal or :selectable"
109
+ end
110
+ @metadata_schema = value
111
+ end
112
+
113
+ def reset
114
+ @base_url = 'http://localhost'
115
+ @user = nil
116
+ @types.clear
117
+ @services.clear
118
+ @objects.clear
119
+ end
120
+
121
+ def reset_objects
122
+ @objects.clear
123
+ end
124
+
125
+ private
126
+
127
+ def services_generate_values
128
+ services_ensure_all_created
129
+ values = []
130
+ @services.each do |name, service|
131
+ element = link_to(:service, "/services/#{name}", :object, service_id: name)
132
+ element['title'] = service.title
133
+ values << element
134
+ end
135
+ values
136
+ end
137
+
138
+ def services_ensure_all_created
139
+ @services.each { |name, value| @services[name] = value.new if value.is_a? Class }
140
+ end
141
+ end
142
+ end
143
+
@@ -1,11 +1,15 @@
1
- # encoding: utf-8
1
+ require_relative 'object_macros'
2
+ require_relative 'object_base'
3
+ require_relative 'object_properties'
4
+ require_relative 'object_collections'
5
+ require_relative 'object_actions'
2
6
 
3
7
  module RestfulObjects
4
8
  module Object
5
9
  include LinkGenerator
6
10
 
7
11
  def self.included(base)
8
- RestfulObjects::DomainModel.current.types.add(base.name)
12
+ RestfulObjects::DomainModel.current.register_type(base.name)
9
13
 
10
14
  base.class_eval do
11
15
  extend ObjectMacros
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  module RestfulObjects
4
2
  module ObjectBase
5
3
  attr_accessor :is_service, :title
@@ -15,7 +13,7 @@ module RestfulObjects
15
13
  end
16
14
 
17
15
  def rs_register_in_model
18
- rs_model.objects.register(self) unless @is_service
16
+ rs_model.register_object(self) unless @is_service
19
17
  end
20
18
 
21
19
  def rs_model
@@ -27,15 +25,31 @@ module RestfulObjects
27
25
  end
28
26
 
29
27
  def get_representation
30
- HttpResponse.new(representation.to_json,
31
- 'application/json;profile="urn:org.restfulobjects:repr-types/object";x-ro-domain-type="' + rs_type.id + '"')
28
+ [200,
29
+ { 'Content-Type' =>
30
+ "application/json;profile=\"urn:org.restfulobjects:repr-types/object\";x-ro-domain-type=\"#{rs_type.id}\"" },
31
+ representation.to_json]
32
+ end
33
+
34
+ def put_properties_and_get_representation(json)
35
+ properties = JSON.parse(json)
36
+ properties.each do |property, container|
37
+ raise 'property not exists' unless rs_model.types[self.class.name].properties.include?(property)
38
+ raise 'read-only property' if rs_model.types[self.class.name].properties[property].read_only
39
+ set_property_value(property, container['value'])
40
+ on_after_update if respond_to?(:on_after_update)
41
+ end
42
+ [200,
43
+ { 'Content-Type' =>
44
+ "application/json;profile=\"urn:org.restfulobjects:repr-types/object\";x-ro-domain-type=\"#{rs_type.id}\"" },
45
+ representation(false).to_json]
32
46
  end
33
47
 
34
48
  def rs_instance_id
35
49
  object_id
36
50
  end
37
51
 
38
- def representation
52
+ def representation(include_self_link = true)
39
53
  representation = {
40
54
  'title' => title,
41
55
  'members' => generate_members,
@@ -45,10 +59,10 @@ module RestfulObjects
45
59
 
46
60
  if not is_service
47
61
  representation['instanceId'] = object_id.to_s
48
- representation['links'] << link_to(:self, "/objects/#{self.class.name}/#{object_id}", :object)
62
+ representation['links'] << link_to(:self, "/objects/#{self.class.name}/#{object_id}", :object) if include_self_link
49
63
  else
50
64
  representation['serviceId'] = self.class.name
51
- representation['links'] << link_to(:self, "/services/#{self.class.name}", :object)
65
+ representation['links'] << link_to(:self, "/services/#{self.class.name}", :object) if include_self_link
52
66
  end
53
67
 
54
68
  representation
@@ -62,17 +76,24 @@ module RestfulObjects
62
76
  end
63
77
  end
64
78
 
79
+ def get_property_rel_representation(property_name)
80
+ representation = link_to(:value, "/objects/#{self.class.name}/#{object_id}", :object, property: property_name)
81
+ representation['title'] = @title
82
+ representation
83
+ end
84
+
65
85
  def rs_delete
66
86
  on_before_delete if respond_to?(:on_before_delete)
67
87
  @deleted = true
68
88
  on_after_delete if respond_to?(:on_after_delete)
89
+ {}.to_json
69
90
  end
70
91
 
71
92
  def deleted?
72
93
  @deleted
73
94
  end
74
95
 
75
- def encode_value(value, type)
96
+ def encode_value(value, type, property_name = '')
76
97
  return nil if value.nil?
77
98
  case type
78
99
  when :string
@@ -88,7 +109,11 @@ module RestfulObjects
88
109
  when :blob
89
110
  Base64.encode64(value).strip
90
111
  else
91
- raise "encode_value unsupported property type: #{type}"
112
+ if value.respond_to?(:get_property_rel_representation)
113
+ value.get_property_rel_representation(property_name)
114
+ else
115
+ raise "encode_value unsupported property type: #{type}"
116
+ end
92
117
  end
93
118
  end
94
119
 
@@ -100,9 +125,9 @@ module RestfulObjects
100
125
  when :int
101
126
  value.to_i
102
127
  when :bool
103
- if value == 'true'
128
+ if [true, 'true'].include?(value)
104
129
  true
105
- elsif value == 'false'
130
+ elsif [false, 'false'].include?(value)
106
131
  false
107
132
  else
108
133
  raise ArgumentError.new "invalid boolean value: #{value}"
@@ -118,8 +143,16 @@ module RestfulObjects
118
143
  end
119
144
  end
120
145
 
146
+ def ro_relative_url
147
+ "/objects/#{self.class.name}/#{self.object_id}"
148
+ end
149
+
150
+ def ro_absolute_url
151
+ RestfulObjects::DomainModel.current.base_url + ro_relative_url
152
+ end
153
+
121
154
  def get_self_link
122
- link_to(:self, "/objects/#{self.class.name}/#{self.object_id}", :object)
155
+ link_to(:self, ro_relative_url, :object)
123
156
  end
124
157
  end
125
158
  end
@@ -1,7 +1,7 @@
1
1
  module RestfulObjects
2
2
  module ObjectMacros
3
3
  def property(name, type, options = {})
4
- RestfulObjects::DomainModel.current.types[self.name].properties.add(name.to_s, type, options)
4
+ RestfulObjects::DomainModel.current.types[self.name].register_property(name.to_s, type, options)
5
5
  if options[:read_only]
6
6
  self.class_eval { attr_reader name }
7
7
  else
@@ -23,13 +23,14 @@ module RestfulObjects
23
23
  def collection(name, type, options = {})
24
24
  type = type.name if type.is_a? Class
25
25
 
26
- RestfulObjects::DomainModel.current.types[self.name].collections.add(name.to_s, type, options)
26
+ RestfulObjects::DomainModel.current.types[self.name].register_collection(name.to_s, type, options)
27
27
 
28
28
  self.class_eval { attr_reader name }
29
29
  end
30
30
 
31
31
  def action(name, options = {})
32
- RestfulObjects::DomainModel.current.types[self.name].actions.add(name.to_s, options)
32
+ RestfulObjects::DomainModel.current.types[self.name].register_action(name.to_s, options)
33
33
  end
34
34
  end
35
35
  end
36
+
@@ -12,12 +12,26 @@ module RestfulObjects
12
12
  'extensions' => property.metadata
13
13
  }
14
14
 
15
- members[name]['disabledReason'] = property.disabled_reason if property.read_only
15
+ if property.read_only
16
+ members[name]['disabledReason'] = property.disabled_reason
17
+ 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
28
+ end
16
29
  end
17
30
  members
18
31
  end
19
32
 
20
33
  def get_property_as_json(property)
34
+ property = property.to_s if property.is_a?(Symbol)
21
35
  raise "Property not exists" if not rs_model.types[self.class.name].properties.include?(property)
22
36
 
23
37
  representation = {
@@ -36,6 +50,17 @@ module RestfulObjects
36
50
  { property: property, method: 'PUT', arguments: { 'value' => nil } }),
37
51
  link_to(:clear, "/objects/#{self.class.name}/#{object_id}/properties/#{property}", :object_property,
38
52
  { property: property, method: 'DELETE'} ) ]
53
+
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
39
64
  else
40
65
  representation[property]['disabledReason'] =
41
66
  rs_model.types[self.class.name].properties[property].disabled_reason
@@ -45,12 +70,13 @@ module RestfulObjects
45
70
  end
46
71
 
47
72
  def put_property_as_json(property, json)
48
- raise "property not exists" if not rs_model.types[self.class.name].properties.include?(property)
49
- raise "read-only property" if rs_model.types[self.class.name].properties[property].read_only
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
50
76
 
51
77
  value = JSON.parse(json)['value']
52
78
  set_property_value(property, value)
53
- on_after_update if respond_to? :on_after_update
79
+ on_after_update if respond_to?(:on_after_update)
54
80
  get_property_as_json(property)
55
81
  end
56
82
 
@@ -59,20 +85,36 @@ module RestfulObjects
59
85
  raise "read-only property" if rs_model.types[self.class.name].properties[property].read_only
60
86
 
61
87
  send("#{property}=".to_sym, nil)
62
- on_after_update if respond_to? :on_after_update
88
+ on_after_update if respond_to?(:on_after_update)
63
89
  get_property_as_json(property)
64
90
  end
65
91
 
92
+ def property_description(property)
93
+ rs_model.types[self.class.name].properties[property]
94
+ end
95
+
66
96
  def property_type(property)
67
97
  rs_model.types[self.class.name].properties[property].return_type
68
98
  end
69
99
 
70
100
  def get_property_value(property)
71
- encode_value(send(property.to_sym), property_type(property))
101
+ encode_value(send(property.to_sym), property_type(property), property)
72
102
  end
73
103
 
74
104
  def set_property_value(property, value)
75
- send("#{property}=".to_sym, decode_value(value, property_type(property)))
105
+ if property_description(property).is_reference
106
+ href_value = value['href']
107
+ match = Regexp.new(".*/objects/(?<domain-type>\\w*)/(?<object-id>\\d*)").match(href_value)
108
+ raise "invalid property reference format: '#{href_value}'" if not match
109
+ domain_type = match['domain-type']
110
+ id = match['object-id'].to_i
111
+ raise "value does not exists" if not rs_model.objects.include?(id)
112
+ raise "domain-type does not exists" if not rs_model.types.include?(domain_type)
113
+ send "#{property}=".to_sym, rs_model.objects[id]
114
+ else
115
+ send "#{property}=".to_sym, decode_value(value, property_type(property))
116
+ end
76
117
  end
77
118
  end
78
119
  end
120
+
@@ -3,7 +3,7 @@ module RestfulObjects
3
3
  include LinkGenerator
4
4
 
5
5
  def self.included(base)
6
- RestfulObjects::DomainModel.current.types.add(base.name)
6
+ RestfulObjects::DomainModel.current.register_type(base.name)
7
7
 
8
8
  base.class_eval do
9
9
  extend ObjectMacros
@@ -15,7 +15,8 @@ module RestfulObjects
15
15
  end
16
16
  end
17
17
 
18
- RestfulObjects::DomainModel.current.services.register(base)
18
+ RestfulObjects::DomainModel.current.register_service(base)
19
19
  end
20
20
  end
21
21
  end
22
+
@@ -1,3 +1,5 @@
1
+ require_relative 'parameter_description_list'
2
+
1
3
  module RestfulObjects
2
4
  class ActionDescription
3
5
  include LinkGenerator
@@ -1,19 +1,38 @@
1
+ require_relative 'property_description'
2
+ require_relative 'collection_description'
3
+ require_relative 'action_description'
4
+
1
5
  module RestfulObjects
2
- class Type
6
+ class DomainType
3
7
  include LinkGenerator
4
8
 
5
9
  attr_reader :id, :is_service, :properties, :collections, :actions
6
10
  attr_accessor :friendly_name, :plural_name, :description
7
11
 
8
12
  def initialize(id)
9
- @id = id
10
- @properties = PropertyList.new(id)
11
- @collections = CollectionList.new(id)
12
- @actions = ActionList.new(id)
13
- @is_service = false
13
+ @id = id
14
+ @properties = {}
15
+ @collections = {}
16
+ @actions = {}
17
+ @is_service = false
14
18
  @friendly_name = ''
15
- @plural_name = ''
16
- @description = ''
19
+ @plural_name = ''
20
+ @description = ''
21
+ end
22
+
23
+ def register_property(name, return_type, options = {})
24
+ options[:member_order] ||= @properties.count + 1
25
+ @properties[name] = PropertyDescription.new(name, @id, return_type, options)
26
+ end
27
+
28
+ def register_collection(name, type, options = {})
29
+ options[:member_order] ||= @collections.count + 1
30
+ @collections[name] = CollectionDescription.new(name, type, @id, options)
31
+ end
32
+
33
+ def register_action(name, options = {})
34
+ options[:member_order] ||= @actions.count + 1
35
+ @actions[name] = ActionDescription.new(name, @id, options)
17
36
  end
18
37
 
19
38
  def get_representation
@@ -108,3 +127,4 @@ module RestfulObjects
108
127
  end
109
128
  end
110
129
  end
130
+
@@ -1,3 +1,5 @@
1
+ require_relative 'parameter_description'
2
+
1
3
  module RestfulObjects
2
4
  class ParameterDescriptionList
3
5
  extend Forwardable
@@ -13,3 +15,4 @@ module RestfulObjects
13
15
  def_delegators :@parameters, :[], :each, :include?, :count, :empty?
14
16
  end
15
17
  end
18
+
@@ -2,23 +2,33 @@ module RestfulObjects
2
2
  class PropertyDescription
3
3
  include LinkGenerator
4
4
 
5
- attr_accessor :id, :domain_type, :return_type, :friendly_name, :description, :optional, :read_only, :member_order,
6
- :max_length, :disabled_reason, :pattern
5
+ attr_accessor :id, :domain_type, :return_type, :is_reference, :friendly_name, :description, :optional, :read_only,
6
+ :member_order, :max_length, :disabled_reason, :pattern
7
7
 
8
8
  def initialize(id, domain_type, return_type, options)
9
- raise "property type #{return_type} usupported" if not [:string, :int, :bool, :decimal, :date, :blob].include?(return_type)
9
+ if return_type.is_a?(Hash)
10
+ raise "hash with :object key expected for property reference" unless return_type.has_key?(:object)
11
+ else
12
+ raise "property type #{return_type} usupported" if not [:string, :int, :bool, :decimal, :date, :blob].include?(return_type)
13
+ end
10
14
 
11
- @id = id
12
- @domain_type = domain_type
13
- @return_type = return_type
14
- @friendly_name = options[:friendly_name] || id
15
- @description = options[:description] || id
16
- @optional = options[:optional].nil? ? true : options[:optional]
17
- @read_only = options[:read_only].nil? ? false : options[:read_only]
18
- @member_order = options[:member_order] || 1
19
- @max_length = options[:max_length]
15
+ @id = id
16
+ @domain_type = domain_type
17
+ if return_type.is_a?(Hash)
18
+ @return_type = return_type[:object]
19
+ @is_reference = true
20
+ else
21
+ @return_type = return_type
22
+ @is_reference = false
23
+ end
24
+ @friendly_name = options[:friendly_name] || id
25
+ @description = options[:description] || id
26
+ @optional = options[:optional].nil? ? true : options[:optional]
27
+ @read_only = options[:read_only].nil? ? false : options[:read_only]
28
+ @member_order = options[:member_order] || 1
29
+ @max_length = options[:max_length]
20
30
  @disabled_reason = options[:disabled_reason] || 'read-only property' if read_only
21
- @pattern = options[:pattern]
31
+ @pattern = options[:pattern]
22
32
  end
23
33
 
24
34
  def get_representation
@@ -0,0 +1,31 @@
1
+ module RestfulObjects
2
+ class User
3
+ include RestfulObjects::LinkGenerator
4
+
5
+ attr_reader :base_url, :user_name
6
+ attr_accessor :friendly_name, :email
7
+
8
+ def initialize(base_url, user_name)
9
+ @base_url = base_url
10
+ @user_name = user_name
11
+ @friendly_name = ''
12
+ @email = ''
13
+ @roles = []
14
+ end
15
+
16
+ def add_role(role)
17
+ @roles.push(role)
18
+ end
19
+
20
+ def get_as_json
21
+ { 'links' => [ gen_link('self', '/user', 'user'), gen_link('up', '/', 'homepage') ],
22
+ 'userName' => @user_name,
23
+ 'friendlyName' => @friendly_name,
24
+ 'email' => @email,
25
+ 'roles' => @roles,
26
+ 'extensions' => {}
27
+ }.to_json
28
+ end
29
+ end
30
+ end
31
+