gradesfirst 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/Gemfile +1 -2
- data/Gemfile.lock +2 -6
- data/gradesfirst.gemspec +2 -2
- data/lib/gradesfirst.rb +1 -0
- data/lib/gradesfirst/cli.rb +7 -29
- data/lib/gradesfirst/cli_helper.rb +33 -0
- data/lib/gradesfirst/command.rb +7 -0
- data/lib/gradesfirst/commit_message_command.rb +1 -1
- data/lib/gradesfirst/task_add_command.rb +51 -0
- data/lib/gradesfirst/task_cli.rb +48 -0
- data/lib/gradesfirst/task_command.rb +38 -30
- data/lib/gradesfirst/task_delete_command.rb +64 -0
- data/lib/gradesfirst/task_list_command.rb +40 -0
- data/lib/gradesfirst/task_move_command.rb +64 -0
- data/lib/gradesfirst/task_toggle_command.rb +65 -0
- data/lib/http_magic.rb +2 -258
- data/lib/http_magic/api.rb +233 -0
- data/lib/http_magic/request.rb +93 -0
- data/lib/http_magic/uri.rb +74 -0
- data/lib/pivotal_tracker.rb +1 -1
- data/test/branch_command_test.rb +8 -21
- data/test/cli_test.rb +51 -34
- data/test/command_test.rb +5 -7
- data/test/commit_message_command_test.rb +37 -45
- data/test/fixtures/task.json +10 -0
- data/test/fixtures/task_added.txt +6 -0
- data/test/fixtures/task_deleted.txt +6 -0
- data/test/fixtures/task_moved.txt +6 -0
- data/test/fixtures/task_toggled.txt +6 -0
- data/test/fixtures/tasks.txt +2 -2
- data/test/http_magic/{get_test.rb → api/get_test.rb} +1 -1
- data/test/http_magic/api/post_test.rb +34 -0
- data/test/http_magic/request_test.rb +63 -0
- data/test/http_magic/uri_test.rb +28 -55
- data/test/support/pivotal_test_helper.rb +110 -0
- data/test/support/request_expectation.rb +33 -0
- data/test/task_add_command_test.rb +54 -0
- data/test/task_delete_command_test.rb +57 -0
- data/test/task_list_command_test.rb +18 -0
- data/test/task_move_command_test.rb +66 -0
- data/test/task_toggle_command_test.rb +58 -0
- data/test/test_helper.rb +35 -19
- metadata +29 -7
- data/test/pivotal_tracker_test.rb +0 -46
- data/test/task_command_test.rb +0 -52
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'gradesfirst/task_command'
|
2
|
+
|
3
|
+
module GradesFirst
|
4
|
+
|
5
|
+
# Implementation of a Thor command for listing tasks related to a story.
|
6
|
+
class TaskListCommand < GradesFirst::TaskCommand
|
7
|
+
|
8
|
+
# Description of the gf task list Thor command that will be used in the
|
9
|
+
# command line help.
|
10
|
+
def self.description
|
11
|
+
'List the tasks related to a PivotalTracker story.'
|
12
|
+
end
|
13
|
+
|
14
|
+
# Performs the gf task list Thor command.
|
15
|
+
def execute
|
16
|
+
@story = current_story
|
17
|
+
if @story
|
18
|
+
@tasks = get_tasks(@story)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Generates the comand line output response. The output of the task command
|
23
|
+
# is a list of the tasks associated with the PivotalTracker story associated
|
24
|
+
# with the current branch.
|
25
|
+
def response
|
26
|
+
if @tasks.nil?
|
27
|
+
story_error_message
|
28
|
+
else
|
29
|
+
task_list_response(@story, @tasks)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def story_error_message
|
36
|
+
'Tasks cannot be retrieved for this branch.'
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'gradesfirst/task_command'
|
2
|
+
|
3
|
+
module GradesFirst
|
4
|
+
|
5
|
+
# Implementation of a Thor command for moving tasks on a PivotalTracker story
|
6
|
+
# from one priority position to another in the list.
|
7
|
+
class TaskMoveCommand < GradesFirst::TaskCommand
|
8
|
+
# Description of the "gf task move" Thor command that will be sued in the
|
9
|
+
# commandline help.
|
10
|
+
def self.description
|
11
|
+
'Move a task to a new position in the list for a PivotalTracker story.'
|
12
|
+
end
|
13
|
+
|
14
|
+
# Performs the gf task move Thor command.
|
15
|
+
def execute(from, to)
|
16
|
+
@story = current_story
|
17
|
+
if @story
|
18
|
+
task_id = get_task_id_by_position(@story, from)
|
19
|
+
if task_id
|
20
|
+
@success = task_move(@story, task_id, to)
|
21
|
+
else
|
22
|
+
@task_position_invalid = true
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Generates the command line output response. The output of the task move
|
28
|
+
# command is a completion status message which may be followed by the new
|
29
|
+
# list of tasks if the task was moved successfully.
|
30
|
+
def response
|
31
|
+
if @task_position_invalid
|
32
|
+
position_invalid_message
|
33
|
+
else
|
34
|
+
task_action_response(@story, @success)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def position_invalid_message
|
41
|
+
'Task "from" position given does not exist.'
|
42
|
+
end
|
43
|
+
|
44
|
+
def story_error_message
|
45
|
+
'Tasks cannnot be moved for this branch.'
|
46
|
+
end
|
47
|
+
|
48
|
+
def task_error_message
|
49
|
+
'Moving the task failed.'
|
50
|
+
end
|
51
|
+
|
52
|
+
def task_move(story, task_id, to)
|
53
|
+
PivotalTracker.
|
54
|
+
projects[story['project_id']].
|
55
|
+
stories[story['id']].
|
56
|
+
tasks[task_id].
|
57
|
+
put(position: to.to_i)
|
58
|
+
end
|
59
|
+
|
60
|
+
def task_success_message
|
61
|
+
'Task was successfully moved.'
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'gradesfirst/task_command'
|
2
|
+
|
3
|
+
module GradesFirst
|
4
|
+
|
5
|
+
# Implementation of a Thor command for toggling whether or not a task is
|
6
|
+
# complete.
|
7
|
+
class TaskToggleCommand < GradesFirst::TaskCommand
|
8
|
+
# Description of the "gf task toggle" Thor command that will be used in
|
9
|
+
# the commandline help.
|
10
|
+
def self.description
|
11
|
+
'Toggle completion status of a PivotalTracker story task.'
|
12
|
+
end
|
13
|
+
|
14
|
+
# Performs the gf task toggle POSITION Thor command.
|
15
|
+
def execute(position)
|
16
|
+
@story = current_story
|
17
|
+
if @story
|
18
|
+
task = get_task_by_position(@story, position)
|
19
|
+
if task
|
20
|
+
@success = task_toggle(@story, task)
|
21
|
+
else
|
22
|
+
@task_position_invalid = true
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Generates the command line output response. The output of the task toggle
|
28
|
+
# command is a completion status message which may be followed by the new
|
29
|
+
# list of tasks if the task was moved successfully.
|
30
|
+
def response
|
31
|
+
if @task_position_invalid
|
32
|
+
position_invalid_message
|
33
|
+
else
|
34
|
+
task_action_response(@story, @success)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def position_invalid_message
|
41
|
+
'Task position given does not exist.'
|
42
|
+
end
|
43
|
+
|
44
|
+
def story_error_message
|
45
|
+
'Tasks cannot be toggled for this branch.'
|
46
|
+
end
|
47
|
+
|
48
|
+
def task_error_message
|
49
|
+
'Toggling of the task completion status failed.'
|
50
|
+
end
|
51
|
+
|
52
|
+
def task_success_message
|
53
|
+
'Task completion status was successfully toggled.'
|
54
|
+
end
|
55
|
+
|
56
|
+
def task_toggle(story, task)
|
57
|
+
PivotalTracker.
|
58
|
+
projects[story['project_id']].
|
59
|
+
stories[story['id']].
|
60
|
+
tasks[task['id']].
|
61
|
+
put(complete: !task['complete'])
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
data/lib/http_magic.rb
CHANGED
@@ -1,260 +1,4 @@
|
|
1
|
-
require '
|
2
|
-
require 'json'
|
1
|
+
require 'http_magic/api'
|
3
2
|
|
4
|
-
|
5
|
-
# configuration.
|
6
|
-
#
|
7
|
-
# Assuming an api where the url http://www.example.com/foo/99 responds with
|
8
|
-
# the following.
|
9
|
-
#
|
10
|
-
# Header:
|
11
|
-
#
|
12
|
-
# Content-Type: application/json
|
13
|
-
#
|
14
|
-
# Body:
|
15
|
-
#
|
16
|
-
# {
|
17
|
-
# "name": "Foo Bar"
|
18
|
-
# }
|
19
|
-
#
|
20
|
-
# == Example
|
21
|
-
#
|
22
|
-
# class ExampleApi < HttpMagic
|
23
|
-
# url 'www.example.com'
|
24
|
-
# end
|
25
|
-
#
|
26
|
-
# ExampleApi.foo[99].get['name']
|
27
|
-
# => "Foo Bar"
|
28
|
-
#
|
29
|
-
class HttpMagic < BasicObject
|
30
|
-
# Makes the new method private so that instances of this class and it's
|
31
|
-
# children can only be instantiated through the class method method_missing.
|
32
|
-
# This is needed to enforce the intended usage of HttpMagic where the class
|
33
|
-
# is configured to represent an http location. Once it is configured, the
|
34
|
-
# location is interacted with dynamically with chained methods that correspond
|
35
|
-
# with parts of URNs found at the location.
|
36
|
-
#
|
37
|
-
# == Example
|
38
|
-
#
|
39
|
-
# class ExampleApi < HttpMagic
|
40
|
-
# url 'www.example.com'
|
41
|
-
# namespace 'api/v1'
|
42
|
-
# headers({'X-AuthToken' => 'token'})
|
43
|
-
# end
|
44
|
-
#
|
45
|
-
# ExampleApi.new.uri
|
46
|
-
# => "http://www.example.com/api/v1/new"
|
47
|
-
#
|
48
|
-
# ExampleApi.foo[99].uri
|
49
|
-
# => "http://www.example.com/api/v1/foo/99"
|
50
|
-
class << self
|
51
|
-
private :new
|
52
|
-
end
|
53
|
-
|
54
|
-
# Sets or returns the request headers for an HttpMagic based class. The
|
55
|
-
# headers will be passed along to each resource request being made.
|
56
|
-
#
|
57
|
-
# == Example
|
58
|
-
#
|
59
|
-
# class ExampleApi < HttpMagic
|
60
|
-
# url 'www.example.com'
|
61
|
-
# namespace 'api/v1'
|
62
|
-
# headers({'X-AuthToken' => 'token'})
|
63
|
-
# end
|
64
|
-
def self.headers(value = :not_provided)
|
65
|
-
unless value == :not_provided
|
66
|
-
@headers = value
|
67
|
-
end
|
68
|
-
@headers
|
69
|
-
end
|
70
|
-
|
71
|
-
# Sets or returns the namespace for an HttpMagic based class. The namespace
|
72
|
-
# will be prefixed to the urn for each request made to the url.
|
73
|
-
#
|
74
|
-
# Assuming an api where each resource is namespaced with 'api/v1' and where
|
75
|
-
# the url http://www.example.com/api/v1/foo/99 responds with the following.
|
76
|
-
#
|
77
|
-
# Header:
|
78
|
-
#
|
79
|
-
# Content-Type: application/json
|
80
|
-
#
|
81
|
-
# Body:
|
82
|
-
#
|
83
|
-
# {
|
84
|
-
# "name": "Foo Bar"
|
85
|
-
# }
|
86
|
-
#
|
87
|
-
# == Example
|
88
|
-
#
|
89
|
-
# class ExampleApi < HttpMagic
|
90
|
-
# url 'www.example.com'
|
91
|
-
# namespace 'api/v1'
|
92
|
-
# end
|
93
|
-
#
|
94
|
-
# ExampleApi.foo[99].get['name']
|
95
|
-
# => "Foo Bar"
|
96
|
-
def self.namespace(value = :not_provided)
|
97
|
-
unless value == :not_provided
|
98
|
-
@namespace = value
|
99
|
-
end
|
100
|
-
@namespace
|
101
|
-
end
|
102
|
-
|
103
|
-
# Sets or returns the uniform resource locator for the HTTP resource.
|
104
|
-
#
|
105
|
-
# == Example
|
106
|
-
#
|
107
|
-
# class ExampleApi < HttpMagic
|
108
|
-
# url 'www.example.com'
|
109
|
-
# end
|
110
|
-
#
|
111
|
-
# ExampleApi.url
|
112
|
-
# => 'www.example.com'
|
113
|
-
def self.url(value = :not_provided)
|
114
|
-
unless value == :not_provided
|
115
|
-
@url = value
|
116
|
-
end
|
117
|
-
@url
|
118
|
-
end
|
119
|
-
|
120
|
-
# Class scoped method_missing that starts the magic of creating urns
|
121
|
-
# through meta programming by instantiating an instance of the class
|
122
|
-
# and delegating the first part of the urn to that instance. This method
|
123
|
-
# is an implementation of the Factory Method design pattern.
|
124
|
-
def self.method_missing(name, *args, &block)
|
125
|
-
new(@url, @namespace, @headers).__send__(name, *args)
|
126
|
-
end
|
127
|
-
|
128
|
-
def initialize(url, namespace, headers)
|
129
|
-
@url = url
|
130
|
-
@namespace = namespace
|
131
|
-
@headers = headers
|
132
|
-
|
133
|
-
@urn_parts = []
|
134
|
-
end
|
135
|
-
|
136
|
-
# Resource index reference intended to allow for the use of numbers in a urn
|
137
|
-
# such as the following 'foo/99' being referenced by ExampleApi.foo[99]. It
|
138
|
-
# can also be used to allow for HttpMagic methods to be specified for a urn
|
139
|
-
# such as 'foo/get' being referenced by ExampleApi.foo[:get]. Finally, it can
|
140
|
-
# be used for urn parts that are not valid Ruby methods such as 'foo/%5B%5D'
|
141
|
-
# being referenced by ExampleApi.foo["%5B%5D"].
|
142
|
-
#
|
143
|
-
# * part - a part of a urn such that 'foo' and 'bar' would be parts of the urn
|
144
|
-
# 'foo/bar'.
|
145
|
-
#
|
146
|
-
# Returns a reference to its instance so that urn parts can be chained
|
147
|
-
# together.
|
148
|
-
def [](part)
|
149
|
-
@urn_parts << part.to_s
|
150
|
-
self
|
151
|
-
end
|
152
|
-
|
153
|
-
# Gets a resource from the URI and returns it based on its content type. JSON
|
154
|
-
# content will be parsed and returned as equivalent Ruby objects. All other
|
155
|
-
# content types will be returned as text.
|
156
|
-
#
|
157
|
-
# Assuming an api where each resource is namespaced with 'api/v1' and where
|
158
|
-
# the url http://www.example.com/api/v1/foo/99 responds with the following.
|
159
|
-
#
|
160
|
-
# Header:
|
161
|
-
#
|
162
|
-
# Content-Type: application/json
|
163
|
-
#
|
164
|
-
# Body:
|
165
|
-
#
|
166
|
-
# {
|
167
|
-
# "name": "Foo Bar"
|
168
|
-
# }
|
169
|
-
#
|
170
|
-
# == Example
|
171
|
-
#
|
172
|
-
# class ExampleApi < HttpMagic
|
173
|
-
# url 'www.example.com'
|
174
|
-
# namespace 'api/v1'
|
175
|
-
# end
|
176
|
-
#
|
177
|
-
# ExampleApi.foo[99].get
|
178
|
-
# => { "name" => "Foo Bar" }
|
179
|
-
def get
|
180
|
-
http = ::Net::HTTP.new(url, 443)
|
181
|
-
http.use_ssl = true
|
182
|
-
|
183
|
-
response = http.request_get(urn, @headers || {})
|
184
|
-
if response && response.is_a?(::Net::HTTPSuccess)
|
185
|
-
if response.content_type == 'application/json'
|
186
|
-
::JSON.parse(response.body)
|
187
|
-
else
|
188
|
-
response.body
|
189
|
-
end
|
190
|
-
else
|
191
|
-
nil
|
192
|
-
end
|
193
|
-
end
|
194
|
-
|
195
|
-
# Uniform resource identifier for a specified request.
|
196
|
-
#
|
197
|
-
# == Example
|
198
|
-
#
|
199
|
-
# class ExampleApi < HttpMagic
|
200
|
-
# url 'www.example.com'
|
201
|
-
# namespace 'api/v1'
|
202
|
-
# end
|
203
|
-
#
|
204
|
-
# ExampleApi.foo[99].uri
|
205
|
-
# => "www.example.com/api/v1/foo/99"
|
206
|
-
def uri
|
207
|
-
"#{url}#{urn}"
|
208
|
-
end
|
209
|
-
|
210
|
-
# Uniform resource locator for all the resources.
|
211
|
-
#
|
212
|
-
# == Example
|
213
|
-
#
|
214
|
-
# class ExampleApi < HttpMagic
|
215
|
-
# url 'www.example.com'
|
216
|
-
# namespace 'api/v1'
|
217
|
-
# end
|
218
|
-
#
|
219
|
-
# ExampleApi.foo[99].url
|
220
|
-
# => "www.example.com"
|
221
|
-
def url
|
222
|
-
@url
|
223
|
-
end
|
224
|
-
|
225
|
-
# Uniform resource name for a resource.
|
226
|
-
#
|
227
|
-
# == Example
|
228
|
-
#
|
229
|
-
# class ExampleApi < HttpMagic
|
230
|
-
# url 'www.example.com'
|
231
|
-
# namespace 'api/v1'
|
232
|
-
# end
|
233
|
-
#
|
234
|
-
# ExampleApi.foo[99].urn
|
235
|
-
# => "api/v1/foo/99"
|
236
|
-
def urn
|
237
|
-
resource_name = [@namespace, @urn_parts].flatten.compact.join('/')
|
238
|
-
"/#{resource_name}"
|
239
|
-
end
|
240
|
-
|
241
|
-
# Instance scoped method_missing that accumulates the URN parts used in
|
242
|
-
# forming the URI. It returns a reference to the current instance so that
|
243
|
-
# method and [] based parts can be chained together to from the URN. In the
|
244
|
-
# case of ExampleApi.foo[99] the 'foo' method is accumlated as a URN part
|
245
|
-
# that will form the resulting URI.
|
246
|
-
#
|
247
|
-
# == Example
|
248
|
-
#
|
249
|
-
# class ExampleApi < HttpMagic
|
250
|
-
# url 'www.example.com'
|
251
|
-
# namespace 'api/v1'
|
252
|
-
# end
|
253
|
-
#
|
254
|
-
# ExampleApi.foo[99].uri
|
255
|
-
# => "www.example.com/api/v1/foo/99"
|
256
|
-
def method_missing(part, *args, &block)
|
257
|
-
@urn_parts << part.to_s
|
258
|
-
self
|
259
|
-
end
|
3
|
+
module HttpMagic
|
260
4
|
end
|
@@ -0,0 +1,233 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'http_magic/uri'
|
3
|
+
require 'http_magic/request'
|
4
|
+
|
5
|
+
module HttpMagic
|
6
|
+
# A magical class that interacts with HTTP resources with a minimal amount of
|
7
|
+
# configuration.
|
8
|
+
#
|
9
|
+
# Assuming an api where the url http://www.example.com/api/v1/foo/99 responds
|
10
|
+
# with the following.
|
11
|
+
#
|
12
|
+
# Header:
|
13
|
+
#
|
14
|
+
# Content-Type: application/json
|
15
|
+
#
|
16
|
+
# Body:
|
17
|
+
#
|
18
|
+
# {
|
19
|
+
# "name": "Foo Bar"
|
20
|
+
# }
|
21
|
+
#
|
22
|
+
# == Example
|
23
|
+
#
|
24
|
+
# class ExampleApi < HttpMagic::Api
|
25
|
+
# url 'www.example.com'
|
26
|
+
# namespace 'api/v1'
|
27
|
+
# headers({'X-AuthToken' => 'token'})
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# ExampleApi.foo[99].get['name']
|
31
|
+
# => "Foo Bar"
|
32
|
+
#
|
33
|
+
# ExampleApi.foo.create.post(name: 'New Foo')
|
34
|
+
# => { 'name' => 'New Foo' }
|
35
|
+
class Api < BasicObject
|
36
|
+
# Makes the new method private so that instances of this class and it's
|
37
|
+
# children can only be instantiated through the class method method_missing.
|
38
|
+
# This is needed to enforce the intended usage of HttpMagic where the class
|
39
|
+
# is configured to represent an http location. Once it is configured, the
|
40
|
+
# location is interacted with dynamically with chained methods that correspond
|
41
|
+
# with parts of URNs found at the location.
|
42
|
+
class << self
|
43
|
+
private :new
|
44
|
+
end
|
45
|
+
|
46
|
+
# Sets or returns the request headers for an HttpMagic based class. The
|
47
|
+
# headers will be passed along to each resource request being made.
|
48
|
+
#
|
49
|
+
# == Example
|
50
|
+
#
|
51
|
+
# class ExampleApi < HttpMagic:Api
|
52
|
+
# url 'www.example.com'
|
53
|
+
# headers({'X-AuthToken' => 'token'})
|
54
|
+
# end
|
55
|
+
def self.headers(value = :not_provided)
|
56
|
+
unless value == :not_provided
|
57
|
+
@headers = value
|
58
|
+
end
|
59
|
+
@headers
|
60
|
+
end
|
61
|
+
|
62
|
+
# Sets or returns the namespace for an HttpMagic based class. The namespace
|
63
|
+
# will be prefixed to the urn for each request made to the url.
|
64
|
+
#
|
65
|
+
# Assuming an api where each resource is namespaced with 'api/v1' and where
|
66
|
+
# the url http://www.example.com/api/v1/foo/99 responds with the following.
|
67
|
+
#
|
68
|
+
# Header:
|
69
|
+
#
|
70
|
+
# Content-Type: application/json
|
71
|
+
#
|
72
|
+
# Body:
|
73
|
+
#
|
74
|
+
# {
|
75
|
+
# "name": "Foo Bar"
|
76
|
+
# }
|
77
|
+
#
|
78
|
+
# == Example
|
79
|
+
#
|
80
|
+
# class ExampleApi < HttpMagic:Api
|
81
|
+
# url 'www.example.com'
|
82
|
+
# namespace 'api/v1'
|
83
|
+
# end
|
84
|
+
#
|
85
|
+
# ExampleApi.foo[99].get['name']
|
86
|
+
# => "Foo Bar"
|
87
|
+
#
|
88
|
+
def self.namespace(value = :not_provided)
|
89
|
+
unless value == :not_provided
|
90
|
+
@namespace = value
|
91
|
+
end
|
92
|
+
@namespace
|
93
|
+
end
|
94
|
+
|
95
|
+
# Sets or returns the uniform resource locator for the HTTP resource.
|
96
|
+
#
|
97
|
+
# == Example
|
98
|
+
#
|
99
|
+
# class ExampleApi < HttpMagic::Api
|
100
|
+
# url 'www.example.com'
|
101
|
+
# end
|
102
|
+
def self.url(value = :not_provided)
|
103
|
+
unless value == :not_provided
|
104
|
+
@url = value
|
105
|
+
end
|
106
|
+
@url
|
107
|
+
end
|
108
|
+
|
109
|
+
# Class scoped method_missing that starts the magic of creating urns
|
110
|
+
# through meta programming by instantiating an instance of the class
|
111
|
+
# and delegating the first part of the urn to that instance. This method
|
112
|
+
# is an implementation of the Factory Method design pattern.
|
113
|
+
def self.method_missing(name, *args, &block)
|
114
|
+
new(@url, @namespace, @headers).__send__(name, *args)
|
115
|
+
end
|
116
|
+
|
117
|
+
def initialize(url, namespace, headers)
|
118
|
+
@uri = Uri.new(url)
|
119
|
+
@uri.namespace = namespace
|
120
|
+
@headers = headers
|
121
|
+
end
|
122
|
+
|
123
|
+
# Resource index reference intended to allow for the use of numbers in a urn
|
124
|
+
# such as the following 'foo/99' being referenced by ExampleApi.foo[99]. It
|
125
|
+
# can also be used to allow for HttpMagic methods to be specified for a urn
|
126
|
+
# such as 'foo/get' being referenced by ExampleApi.foo[:get]. Finally, it can
|
127
|
+
# be used for urn parts that are not valid Ruby methods such as 'foo/%5B%5D'
|
128
|
+
# being referenced by ExampleApi.foo["%5B%5D"].
|
129
|
+
#
|
130
|
+
# * part - a part of a urn such that 'foo' and 'bar' would be parts of the urn
|
131
|
+
# 'foo/bar'.
|
132
|
+
#
|
133
|
+
# Returns a reference to its instance so that urn parts can be chained
|
134
|
+
# together.
|
135
|
+
def [](part)
|
136
|
+
@uri.parts << part.to_s
|
137
|
+
self
|
138
|
+
end
|
139
|
+
|
140
|
+
def delete
|
141
|
+
request = Request.new(@uri,
|
142
|
+
headers: @headers
|
143
|
+
)
|
144
|
+
request.delete
|
145
|
+
end
|
146
|
+
|
147
|
+
# Gets a resource from the URI and returns it based on its content type.
|
148
|
+
# JSON content will be parsed and returned as equivalent Ruby objects. All
|
149
|
+
# other content types will be returned as text.
|
150
|
+
#
|
151
|
+
# Assuming an api where each resource is namespaced with 'api/v1' and where
|
152
|
+
# the url http://www.example.com/api/v1/foo/99 responds with the following.
|
153
|
+
#
|
154
|
+
# Header:
|
155
|
+
#
|
156
|
+
# Content-Type: application/json
|
157
|
+
#
|
158
|
+
# Body:
|
159
|
+
#
|
160
|
+
# {
|
161
|
+
# "name": "Foo Bar"
|
162
|
+
# }
|
163
|
+
#
|
164
|
+
# == Example
|
165
|
+
#
|
166
|
+
# class ExampleApi < HttpMagic::Api
|
167
|
+
# url 'www.example.com'
|
168
|
+
# namespace 'api/v1'
|
169
|
+
# end
|
170
|
+
#
|
171
|
+
# ExampleApi.foo[99].get
|
172
|
+
# => { "name" => "Foo Bar" }
|
173
|
+
def get
|
174
|
+
request = Request.new(@uri,
|
175
|
+
headers: @headers
|
176
|
+
)
|
177
|
+
request.get
|
178
|
+
end
|
179
|
+
|
180
|
+
# POST's a resource from the URI and returns the result based on its content
|
181
|
+
# type. JSON content will be parsed and returned as equivalent Ruby objects.
|
182
|
+
# All other content types will be returned as text.
|
183
|
+
#
|
184
|
+
# Assuming an api where each resource is namespaced with 'api/v1' and where
|
185
|
+
# the url http://www.example.com/api/v1/foo/create responds with the
|
186
|
+
# following when a 'name' is sent with the request.
|
187
|
+
#
|
188
|
+
# Header:
|
189
|
+
#
|
190
|
+
# Content-Type: application/json
|
191
|
+
#
|
192
|
+
# Body:
|
193
|
+
#
|
194
|
+
# {
|
195
|
+
# "name": "New Foo"
|
196
|
+
# }
|
197
|
+
#
|
198
|
+
# == Example
|
199
|
+
#
|
200
|
+
# class ExampleApi < HttpMagic::Api
|
201
|
+
# url 'www.example.com'
|
202
|
+
# namespace 'api/v1'
|
203
|
+
# end
|
204
|
+
#
|
205
|
+
# ExampleApi.foo.create.post(name: 'New Foo')
|
206
|
+
# => { "name" => "New Foo" }
|
207
|
+
def post(data = {})
|
208
|
+
request = Request.new(@uri,
|
209
|
+
headers: @headers,
|
210
|
+
data: data,
|
211
|
+
)
|
212
|
+
request.post
|
213
|
+
end
|
214
|
+
|
215
|
+
def put(data = {})
|
216
|
+
request = Request.new(@uri,
|
217
|
+
headers: @headers,
|
218
|
+
data: data,
|
219
|
+
)
|
220
|
+
request.put
|
221
|
+
end
|
222
|
+
|
223
|
+
# Instance scoped method_missing that accumulates the URN parts used in
|
224
|
+
# forming the URI. It returns a reference to the current instance so that
|
225
|
+
# method and [] based parts can be chained together to from the URN. In the
|
226
|
+
# case of ExampleApi.foo[99] the 'foo' method is accumlated as a URN part
|
227
|
+
# that will form the resulting URI.
|
228
|
+
def method_missing(part, *args, &block)
|
229
|
+
@uri.parts << part.to_s
|
230
|
+
self
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|