cybercoach 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +45 -0
  3. data/.rspec +2 -0
  4. data/CHANGELOG.md +8 -0
  5. data/Gemfile +23 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +31 -0
  8. data/Rakefile +2 -0
  9. data/cybercoach.gemspec +27 -0
  10. data/lib/cybercoach/abstract_resource.rb +162 -0
  11. data/lib/cybercoach/entry.rb +166 -0
  12. data/lib/cybercoach/format_not_supported_error.rb +7 -0
  13. data/lib/cybercoach/http_error.rb +27 -0
  14. data/lib/cybercoach/pageable.rb +38 -0
  15. data/lib/cybercoach/partnership.rb +151 -0
  16. data/lib/cybercoach/post_createable.rb +49 -0
  17. data/lib/cybercoach/privacy_level.rb +21 -0
  18. data/lib/cybercoach/put_createable.rb +48 -0
  19. data/lib/cybercoach/resource.rb +94 -0
  20. data/lib/cybercoach/resource_page.rb +191 -0
  21. data/lib/cybercoach/settings.rb +16 -0
  22. data/lib/cybercoach/sport.rb +104 -0
  23. data/lib/cybercoach/subclass_responsibility_error.rb +7 -0
  24. data/lib/cybercoach/subscription.rb +145 -0
  25. data/lib/cybercoach/user.rb +159 -0
  26. data/lib/cybercoach/version.rb +10 -0
  27. data/lib/cybercoach.rb +19 -0
  28. data/spec/lib/cybercoach/entry_spec.rb +95 -0
  29. data/spec/lib/cybercoach/partnership_spec.rb +79 -0
  30. data/spec/lib/cybercoach/privacy_level_spec.rb +15 -0
  31. data/spec/lib/cybercoach/resource_helper.rb +11 -0
  32. data/spec/lib/cybercoach/resource_page_spec.rb +44 -0
  33. data/spec/lib/cybercoach/settings_spec.rb +10 -0
  34. data/spec/lib/cybercoach/sport_spec.rb +29 -0
  35. data/spec/lib/cybercoach/subscription_spec.rb +129 -0
  36. data/spec/lib/cybercoach/user_helper.rb +22 -0
  37. data/spec/lib/cybercoach/user_spec.rb +82 -0
  38. data/spec/lib/cybercoach_helper.rb +2 -0
  39. data/spec/lib/integration_spec.rb +56 -0
  40. data/spec/spec_helper.rb +19 -0
  41. metadata +56 -4
@@ -0,0 +1,151 @@
1
+ module CyberCoach
2
+ #
3
+ # A Partnership consists of two Users, which participate in different Sport
4
+ # with Subscriptions, to which Entries are submitted.
5
+ #
6
+ class Partnership < Resource
7
+ #
8
+ # It is pageable.
9
+ #
10
+ include Pageable
11
+
12
+ #
13
+ # It is creatable by PUT.
14
+ #
15
+ include PutCreateable
16
+
17
+ #
18
+ # :attr: proposer
19
+ # The User who proposed it.
20
+ #
21
+
22
+ #
23
+ # :attr: proposed
24
+ # The User it is proposed to.
25
+ #
26
+
27
+ #
28
+ # :attr: subscriptions
29
+ # The Subscriptions.
30
+ #
31
+
32
+ #
33
+ # :attr: confirmed_by_proposer
34
+ # True if the proposing User has confirmed it, false otherwise.
35
+ #
36
+
37
+ #
38
+ # :attr: confirmed_by_proposed
39
+ # True if the proposed User has confirmed it, false otherwise.
40
+ #
41
+
42
+ #
43
+ # :attr: privacy_level
44
+ # The privacy level, see PrivacyLevel constants.
45
+ #
46
+
47
+ attr_accessor :proposer,
48
+ :proposed,
49
+ :subscriptions,
50
+ :confirmed_by_proposer,
51
+ :confirmed_by_proposed,
52
+ :privacy_level
53
+
54
+ #
55
+ # :category: Serialization
56
+ #
57
+ # Creates itself from a serializable representation, which only contains
58
+ # simple data types.
59
+ # serializable:: A hash with the keys:
60
+ # * uri:: The URI.
61
+ # * id:: The identifier.
62
+ # * user1:: A User serializable of the proposer.
63
+ # * user2:: A User serializable of the proposed.
64
+ # * subscriptions:: Subscription serializables.
65
+ # * userconfirmed1:: True if the proposing User has confirmed it, false otherwise.
66
+ # * userconfirmed2:: True if the proposed User has confirmed it, false otherwise.
67
+ # * publicvisible:: The privacy level, see PrivacyLevel constants.
68
+ #
69
+ def from_serializable(serializable)
70
+ super(serializable)
71
+ @proposer = nil
72
+ unless serializable['user1'].nil?
73
+ @proposer = User.new
74
+ @proposer.from_serializable(serializable['user1'])
75
+ end
76
+ @proposed = nil
77
+ unless serializable['user2'].nil?
78
+ @proposed = User.new
79
+ @proposed.from_serializable(serializable['user2'])
80
+ end
81
+ @subscriptions = []
82
+ unless serializable['subscriptions'].nil?
83
+ @subscriptions = serializable['subscriptions'].map do
84
+ |subscription_serializable|
85
+ subscription = Subscription.new
86
+ subscription.from_serializable(subscription_serializable)
87
+ subscription
88
+ end
89
+ end
90
+ @confirmed_by_proposer = serializable['userconfirmed1']
91
+ @confirmed_by_proposed = serializable['userconfirmed2']
92
+ @privacy_level = serializable['publicvisible']
93
+ end
94
+
95
+ #
96
+ # :category: Serialization
97
+ #
98
+ # Returns a serializable representation, which only contains simple data
99
+ # types.
100
+ # The hash has the keys:
101
+ # * uri:: The URI.
102
+ # * id:: The identifier.
103
+ # * user1:: A User serializable of the proposer.
104
+ # * user2:: A User serializable of the proposed.
105
+ # * publicvisible:: The privacy level, see PrivacyLevel constants.
106
+ #
107
+ def to_serializable
108
+ serializable = super
109
+ unless @proposer.nil?
110
+ serializable['user1'] = @proposer.to_serializable
111
+ end
112
+ unless @proposed.nil?
113
+ serializable['user2'] = @proposed.to_serializable
114
+ end
115
+ serializable['publicvisible'] = @privacy_level
116
+ serializable
117
+ end
118
+
119
+ #
120
+ # :category: Configuration
121
+ #
122
+ # Returns 'partnership'.
123
+ #
124
+ def singular_name
125
+ 'partnership'
126
+ end
127
+
128
+ #
129
+ # :category: Configuration
130
+ #
131
+ # Returns 'partnerships'.
132
+ #
133
+ def plural_name
134
+ 'partnerships'
135
+ end
136
+
137
+ protected
138
+
139
+ #
140
+ # :category: Invalidation
141
+ #
142
+ # Sets the uri to the base uri and the proposer's and proposed's username if
143
+ # neither of them is nil.
144
+ #
145
+ def invalidate_uri
146
+ unless @proposer.nil? || @proposed.nil?
147
+ @uri = "#{resource_base_uri}#{@proposer.username};#{@proposed.username}/"
148
+ end
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,49 @@
1
+ module CyberCoach
2
+ #
3
+ # Mixin for a resource that gets its URI assigned from the server.
4
+ # Include it in a class to use it.
5
+ #
6
+ module PostCreateable
7
+ #
8
+ # Installs class and instance methods in the class it is included in.
9
+ #
10
+ def self.included(base)
11
+ base.extend ClassMethods
12
+ base.send :include, InstanceMethods
13
+ end
14
+
15
+ #
16
+ # The class methods to install.
17
+ #
18
+ module ClassMethods
19
+ end
20
+
21
+ #
22
+ # The instance methods to install.
23
+ #
24
+ module InstanceMethods
25
+ #
26
+ # :category: CRUD
27
+ #
28
+ # Creates it.
29
+ # Gets the URI from the response and reads itself again.
30
+ # Raises HttpError if the request is unsuccessful.
31
+ # options:: A hash of options to send with the request.
32
+ #
33
+ def create(options = {})
34
+ invalidate_uri
35
+ invalidate_options
36
+ options = @options.merge(options).merge(
37
+ body: serialize
38
+ )
39
+ response = self.class.post(resource_base_uri, options)
40
+ if response.success?
41
+ @uri = response.headers['location']
42
+ read(options)
43
+ else
44
+ raise HttpError.new(response.response)
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,21 @@
1
+ module CyberCoach
2
+ #
3
+ # The CyberCoach privacy levels.
4
+ #
5
+ module PrivacyLevel
6
+ #
7
+ # Only the owner of the resource can access its properties.
8
+ #
9
+ OWNER = 0
10
+
11
+ #
12
+ # Only registered users can access the resource's properties.
13
+ #
14
+ REGISTERED_USER = 1
15
+
16
+ #
17
+ # Everybody can access the resource's properties.
18
+ #
19
+ EVERYBODY = 2
20
+ end
21
+ end
@@ -0,0 +1,48 @@
1
+ module CyberCoach
2
+ #
3
+ # Mixin for a resource that gets its URI assigned from the client.
4
+ # Include it in a class to use it.
5
+ #
6
+ module PutCreateable
7
+ #
8
+ # Installs class and instance methods in the class it is included in.
9
+ #
10
+ def self.included(base)
11
+ base.extend ClassMethods
12
+ base.send :include, InstanceMethods
13
+ end
14
+
15
+ #
16
+ # The class methods to install.
17
+ #
18
+ module ClassMethods
19
+ end
20
+
21
+ #
22
+ # The instance methods to install.
23
+ #
24
+ module InstanceMethods
25
+ #
26
+ # :category: CRUD
27
+ #
28
+ # Creates it.
29
+ # Reads itself from the response.
30
+ # Raises HttpError if the request is unsuccessful.
31
+ # options:: A hash of options to send with the request.
32
+ #
33
+ def create(options = {})
34
+ invalidate_uri
35
+ invalidate_options
36
+ options = @options.merge(options).merge(
37
+ body: serialize
38
+ )
39
+ response = self.class.put(@uri, options)
40
+ if response.success?
41
+ deserialize(response)
42
+ else
43
+ raise HttpError.new(response.response)
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,94 @@
1
+ module CyberCoach
2
+ #
3
+ # A Resource can be created, read, updated and deleted.
4
+ #
5
+ class Resource < AbstractResource
6
+ #
7
+ # The identifier.
8
+ #
9
+ attr_accessor :id
10
+
11
+ #
12
+ # :category: CRUD
13
+ #
14
+ # Creates it.
15
+ # Must be overridden in a subclass.
16
+ # May use PutCreateable or PostCreateable.
17
+ # options:: A hash of options to send with the request.
18
+ #
19
+ def create(_options = {})
20
+ raise SubclassResponsibilityError.new
21
+ end
22
+
23
+ #
24
+ # :category: CRUD
25
+ #
26
+ # Updates it.
27
+ # Reads itself from the response.
28
+ # Raises HttpError if the request is unsuccessful.
29
+ # options:: A hash of options to send with the request.
30
+ #
31
+ def update(options = {})
32
+ invalidate_uri
33
+ invalidate_options
34
+ options = @options.merge(options).merge(
35
+ body: serialize
36
+ )
37
+ response = self.class.put(@uri, options)
38
+ if response.success?
39
+ deserialize(response)
40
+ else
41
+ raise HttpError.new(response.response)
42
+ end
43
+ end
44
+
45
+ #
46
+ # :category: CRUD
47
+ #
48
+ # Deletes it.
49
+ # Reads itself from the response.
50
+ # Raises HttpError if the request is unsuccessful.
51
+ # options:: A hash of options to send with the request.
52
+ #
53
+ def delete(options = {})
54
+ invalidate_uri
55
+ invalidate_options
56
+ options = @options.merge(options)
57
+ response = self.class.delete(@uri, options)
58
+ if response.success?
59
+ deserialize(response)
60
+ else
61
+ raise HttpError.new(response.response)
62
+ end
63
+ end
64
+
65
+ #
66
+ # :category: Serialization
67
+ #
68
+ # Creates itself from a serializable representation, which only contains
69
+ # simple data types.
70
+ # serializable:: A hash with the keys:
71
+ # * uri:: The URI.
72
+ # * id:: The identifier.
73
+ #
74
+ def from_serializable(serializable)
75
+ super(serializable)
76
+ @id = serializable['id']
77
+ end
78
+
79
+ #
80
+ # :category: Serialization
81
+ #
82
+ # Returns a serializable representation, which only contains simple data
83
+ # types.
84
+ # The hash has the keys:
85
+ # * uri:: The URI.
86
+ # * id:: The identifier.
87
+ #
88
+ def to_serializable
89
+ serializable = super
90
+ serializable['id'] = @id
91
+ serializable
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,191 @@
1
+ module CyberCoach
2
+ #
3
+ # A ResourcePage can be used to navigate through many resources of a type.
4
+ #
5
+ class ResourcePage < AbstractResource
6
+ #
7
+ # Raised when trying to get the next page from the last page.
8
+ #
9
+ class NoNextPageError < StandardError
10
+ end
11
+
12
+ #
13
+ # Raised when trying to get the previous page from the first page.
14
+ #
15
+ class NoPreviousPageError < StandardError
16
+ end
17
+
18
+ #
19
+ # :attr: start
20
+ # The start index.
21
+ #
22
+
23
+ #
24
+ # :attr: end
25
+ # The end index.
26
+ #
27
+
28
+ #
29
+ # :attr: size
30
+ # The size.
31
+ #
32
+
33
+ #
34
+ # :attr: available
35
+ # The resources available.
36
+ #
37
+
38
+ #
39
+ # :attr: type
40
+ # The class of the resource to page.
41
+ #
42
+
43
+ #
44
+ # :attr: resources
45
+ # The resources.
46
+ #
47
+
48
+ attr_accessor :start, :end, :size, :available, :type, :resources
49
+
50
+ #
51
+ # Create a ResourcePage of the specified type.
52
+ # type:: The class of resource to page.
53
+ #
54
+ def initialize(type)
55
+ super()
56
+ @type = type
57
+ end
58
+
59
+ #
60
+ # :category: CRUD
61
+ #
62
+ # Returns the next page.
63
+ # Raises NoNextPageError if the is none.
64
+ # options:: A hash of options to send with the request.
65
+ #
66
+ def next(options = {})
67
+ if @next.nil?
68
+ raise NoNextPageError.new
69
+ end
70
+ invalidate_options
71
+ options = @options.merge(options)
72
+ response = self.class.get(@next['href'], options)
73
+ if response.success?
74
+ page = self.class.new(@type)
75
+ page.deserialize(response)
76
+ page
77
+ else
78
+ raise HttpError.new(response.response)
79
+ end
80
+ end
81
+
82
+ #
83
+ # :category: CRUD
84
+ #
85
+ # Returns the previous page.
86
+ # Raises NoPreviousPageError if the is none.
87
+ # options:: A hash of options to send with the request.
88
+ #
89
+ def previous(options = {})
90
+ if @previous.nil?
91
+ raise NoPreviousPageError.new
92
+ end
93
+ invalidate_options
94
+ options = @options.merge(options)
95
+ response = self.class.get(@previous['href'], options)
96
+ if response.success?
97
+ page = self.class.new(@type)
98
+ page.deserialize(response)
99
+ page
100
+ else
101
+ raise HttpError.new(response.response)
102
+ end
103
+ end
104
+
105
+ #
106
+ # :category: Serialization
107
+ #
108
+ # Creates itself from a serializable representation, which only contains
109
+ # simple data types.
110
+ # serializable:: A hash with the keys:
111
+ # * uri:: The URI.
112
+ # * start:: The start index.
113
+ # * end:: The end index.
114
+ # * size:: The size.
115
+ # * available:: The resources available.
116
+ # * links:: Links as hashes with the keys:
117
+ # * description:: May be 'next' or 'previous'.
118
+ # * href:: The URI of the referenced page.
119
+ # * @plural_name:: Items mapped to the plural name of the @type.
120
+ #
121
+ def from_serializable(serializable)
122
+ super(serializable)
123
+ @start = serializable['start']
124
+ @end = serializable['end']
125
+ @size = serializable['size']
126
+ @available = serializable['available']
127
+ if serializable['links'].nil?
128
+ @next = nil
129
+ @previous = nil
130
+ else
131
+ @next = serializable['links'].find { |link| link['description'] == 'next' }
132
+ @previous = serializable['links'].find { |link| link['description'] == 'previous' }
133
+ end
134
+ @resources = serializable[plural_name].map do |resource_serializable|
135
+ resource = @type.new
136
+ resource.from_serializable(resource_serializable)
137
+ resource
138
+ end
139
+ end
140
+
141
+ #
142
+ # :category: Configuration
143
+ #
144
+ # Return the singular name of the type.
145
+ #
146
+ def singular_name
147
+ @type.new.singular_name
148
+ end
149
+
150
+ #
151
+ # :category: Configuration
152
+ #
153
+ # Return the plural name of the type.
154
+ #
155
+ def plural_name
156
+ @type.new.plural_name
157
+ end
158
+
159
+ #
160
+ # :category: Configuration
161
+ #
162
+ # Return the resource's base URI of the type.
163
+ #
164
+ def resource_base_uri
165
+ @type.new.resource_base_uri
166
+ end
167
+
168
+ protected
169
+
170
+ #
171
+ # :category: Invalidation
172
+ #
173
+ # Sets the start and size attributes as query parameters.
174
+ #
175
+ def invalidate_options
176
+ @options[:query] = {
177
+ start: @start,
178
+ size: @size
179
+ }
180
+ end
181
+
182
+ #
183
+ # :category: Invalidation
184
+ #
185
+ # Sets the URI to the types' base URI.
186
+ #
187
+ def invalidate_uri
188
+ @uri = resource_base_uri
189
+ end
190
+ end
191
+ end
@@ -0,0 +1,16 @@
1
+ module CyberCoach
2
+ #
3
+ # Settings to access CyberCoach.
4
+ #
5
+ class Settings
6
+ #
7
+ # The URI to the server.
8
+ #
9
+ SERVER_URI = 'diufvm31.unifr.ch:8090'
10
+
11
+ #
12
+ # The URI to the resources, relative to the server URI.
13
+ #
14
+ BASE_URI = '/CyberCoachServer/resources'
15
+ end
16
+ end
@@ -0,0 +1,104 @@
1
+ module CyberCoach
2
+ #
3
+ # A Sport has Subscriptions to which Entries are submitted.
4
+ #
5
+ class Sport < Resource
6
+ #
7
+ # It is pageable.
8
+ #
9
+ include Pageable
10
+
11
+ #
12
+ # :attr: name
13
+ # The name.
14
+ #
15
+
16
+ #
17
+ # :attr: description
18
+ # The description.
19
+ #
20
+
21
+ #
22
+ # :attr: subscriptions
23
+ # The Subscriptions to it.
24
+ #
25
+
26
+ attr_accessor :name, :description, :subscriptions
27
+
28
+ #
29
+ # :category: Serialization
30
+ #
31
+ # Creates itself from a serializable representation, which only contains
32
+ # simple data types.
33
+ # serializable:: A hash with the keys:
34
+ # * uri:: The URI.
35
+ # * id:: The identifier.
36
+ # * name:: The name.
37
+ # * description:: The description.
38
+ # * subscriptions:: Subscription serializables.
39
+ #
40
+ def from_serializable(serializable)
41
+ super(serializable)
42
+ @name = serializable['name']
43
+ @description = serializable['description']
44
+ @subscriptions = []
45
+ unless serializable['subscriptions'].nil?
46
+ @subscriptions = serializable['subscriptions'].map do
47
+ |subscription_serializable|
48
+ subscription = Subscription.new
49
+ subscription.from_serializable(subscription_serializable)
50
+ subscription
51
+ end
52
+ end
53
+ end
54
+
55
+ #
56
+ # :category: Serialization
57
+ #
58
+ # Returns a serializable representation, which only contains simple data
59
+ # types.
60
+ # The hash has the keys:
61
+ # * uri:: The URI.
62
+ # * id:: The identifier.
63
+ # * name:: The name.
64
+ # * description:: The description.
65
+ #
66
+ def to_serializable
67
+ serializable = super
68
+ serializable['name'] = @name
69
+ serializable['description'] = @description
70
+ serializable
71
+ end
72
+
73
+ #
74
+ # :category: Configuration
75
+ #
76
+ # Returns 'sport'.
77
+ #
78
+ def singular_name
79
+ 'sport'
80
+ end
81
+
82
+ #
83
+ # :category: Configuration
84
+ #
85
+ # Returns 'sports'.
86
+ #
87
+ def plural_name
88
+ 'sports'
89
+ end
90
+
91
+ protected
92
+
93
+ #
94
+ # :category: Invalidation
95
+ #
96
+ # Sets the uri to the base uri and the name.
97
+ #
98
+ def invalidate_uri
99
+ unless @name.nil?
100
+ @uri = "#{resource_base_uri}#{@name}/"
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,7 @@
1
+ module CyberCoach
2
+ #
3
+ # Raised when a method is abstract and should be implemented in a subclass.
4
+ #
5
+ class SubclassResponsibilityError < RuntimeError
6
+ end
7
+ end