blix-rest 0.1.30
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.
- checksums.yaml +7 -0
- data/LICENSE +25 -0
- data/README.md +457 -0
- data/lib/blix/rest.rb +145 -0
- data/lib/blix/rest/controller.rb +512 -0
- data/lib/blix/rest/cucumber.rb +8 -0
- data/lib/blix/rest/cucumber/hooks.rb +5 -0
- data/lib/blix/rest/cucumber/request_steps.rb +207 -0
- data/lib/blix/rest/cucumber/resource_steps.rb +28 -0
- data/lib/blix/rest/cucumber/world.rb +273 -0
- data/lib/blix/rest/format.rb +15 -0
- data/lib/blix/rest/format_parser.rb +167 -0
- data/lib/blix/rest/handlebars_assets_fix.rb +10 -0
- data/lib/blix/rest/request_mapper.rb +332 -0
- data/lib/blix/rest/resource_cache.rb +50 -0
- data/lib/blix/rest/response.rb +26 -0
- data/lib/blix/rest/server.rb +208 -0
- data/lib/blix/rest/string_hash.rb +100 -0
- data/lib/blix/rest/version.rb +5 -0
- data/lib/blix/utils.rb +2 -0
- data/lib/blix/utils/misc.rb +62 -0
- data/lib/blix/utils/redis_store.rb +173 -0
- data/lib/blix/utils/yaml_config.rb +74 -0
- metadata +126 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: c1b29a1f9ce5565c28991acebdb11f7a4e785db7760c59905936a9b2e8a6fa72
|
4
|
+
data.tar.gz: 5cc8fc070fb5fc09c3256dbc65b8087e6a17df7ed0bfc80b51fb9bd43ab38609
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: bf6e866a78e15c268c364b4a87765a05440e1b6f1f6173f442d0a82045fbae4c94df7dcb2afb46c8e27f8ef017f0dea255de9cb85321059f2015f87fc3ee0404
|
7
|
+
data.tar.gz: 7e057e8b9b1dbc9bd1f46b952d071e51fc9d805b5e3236e8c5722902c839106bbece17de8175bcbf66a3ed2159c38121a88ad00d7ef68a94a70384e331e70176
|
data/LICENSE
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2017 Clive Andrews
|
4
|
+
|
5
|
+
|
6
|
+
Permission is hereby granted, free of charge, to any person
|
7
|
+
obtaining a copy of this software and associated documentation
|
8
|
+
files (the "Software"), to deal in the Software without
|
9
|
+
restriction, including without limitation the rights to use,
|
10
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
11
|
+
copies of the Software, and to permit persons to whom the
|
12
|
+
Software is furnished to do so, subject to the following
|
13
|
+
conditions:
|
14
|
+
|
15
|
+
The above copyright notice and this permission notice shall be
|
16
|
+
included in all copies or substantial portions of the Software.
|
17
|
+
|
18
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
19
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
20
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
21
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
22
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
23
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
24
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
25
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,457 @@
|
|
1
|
+
## INSTALLATION
|
2
|
+
|
3
|
+
gem install blix-rest
|
4
|
+
|
5
|
+
|
6
|
+
## CREATE A SIMPLE WEBSERVICE
|
7
|
+
|
8
|
+
#### put the following in config.ru
|
9
|
+
|
10
|
+
|
11
|
+
require 'blix/rest'
|
12
|
+
|
13
|
+
class HomeController < Blix::Rest::Controller
|
14
|
+
|
15
|
+
get '/hello', :accept=>[:html,:json], :default=>:html do
|
16
|
+
if format == :json
|
17
|
+
{"message"=>"hello world"}
|
18
|
+
else
|
19
|
+
"<h1>hello world</h1>"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
run Blix::Rest::Server.new
|
25
|
+
|
26
|
+
|
27
|
+
|
28
|
+
|
29
|
+
#### at the command line ..
|
30
|
+
|
31
|
+
`ruby -S rackup -p3000`
|
32
|
+
|
33
|
+
|
34
|
+
#### now go to your browser and enter ..
|
35
|
+
|
36
|
+
`http://localhost:3000/hello`
|
37
|
+
|
38
|
+
or
|
39
|
+
|
40
|
+
`http://localhost:3000/hello.json`
|
41
|
+
|
42
|
+
## Note on JSON
|
43
|
+
|
44
|
+
the default json parser uses multi json. load the specific json library you need
|
45
|
+
before loading `blix/rest`.
|
46
|
+
|
47
|
+
when using oj then you may need to set some default options eg:
|
48
|
+
|
49
|
+
`MultiJson.default_options = {:mode=>:custom, :use_as_json=>true}`
|
50
|
+
|
51
|
+
## NOTE ON PATHS
|
52
|
+
|
53
|
+
`get '/user/:user_id/list`
|
54
|
+
|
55
|
+
`path_params[:user_id]` contains the content of the path at location :user_id
|
56
|
+
|
57
|
+
`get '/resource/*wildpath'`
|
58
|
+
|
59
|
+
`path_params[:wildpath]` contains the remainder of the path where the * is.
|
60
|
+
|
61
|
+
if there is a more specific path then it will be used first :
|
62
|
+
|
63
|
+
`get '/resource/aaa'` will be used before `get '/resource/*'`
|
64
|
+
|
65
|
+
`get '/*'` will be used as a default path if no other paths match.
|
66
|
+
|
67
|
+
`all '/mypath'` will accept all http_methods but if a more specific handler
|
68
|
+
is specified then it will be used first.
|
69
|
+
|
70
|
+
|
71
|
+
### Path options
|
72
|
+
|
73
|
+
:accept : the format or formats to accept eg: :html or [:png, :jpeg]
|
74
|
+
:default : default format if not derived through oher means.
|
75
|
+
:force : force response into the given format
|
76
|
+
:query : derive format from request query (default: false)
|
77
|
+
:extension : derive format from path extension (default: true)
|
78
|
+
|
79
|
+
|
80
|
+
use `:accept=>:*` in combination with `:force` to accept all request formats.
|
81
|
+
|
82
|
+
## APPLICATION MOUNT POINT
|
83
|
+
|
84
|
+
this is the path of the mount path of the application
|
85
|
+
|
86
|
+
this will be set to the environment variable `BLIX_REST_ROOT` if present
|
87
|
+
|
88
|
+
otherwise set it manually with:
|
89
|
+
|
90
|
+
`Blix::Rest.set_path_root( "/myapplication")`
|
91
|
+
|
92
|
+
|
93
|
+
|
94
|
+
|
95
|
+
## GENERATE AN ERROR RESPONSE
|
96
|
+
|
97
|
+
`send_error(message,status,headers)`
|
98
|
+
|
99
|
+
or for standard headers and status 406 just ..
|
100
|
+
|
101
|
+
`send_error "my error message`
|
102
|
+
|
103
|
+
|
104
|
+
|
105
|
+
## HEADERS && STATUS
|
106
|
+
|
107
|
+
add special headers to your response with eg:
|
108
|
+
|
109
|
+
`add_headers( "AAA"=>"xxx","BBB"=>"yyyy")`
|
110
|
+
|
111
|
+
change the status of a success response with eg:
|
112
|
+
|
113
|
+
`set_status(401)`
|
114
|
+
|
115
|
+
|
116
|
+
to specify __ALL__ the headers for a given format of response use eg:
|
117
|
+
|
118
|
+
srv = Blix::Rest::Server.new
|
119
|
+
srv.set_custom_headers(:html, 'Content-Type'=>'text/html; charset=utf-8', 'X-OTHER'=>'')
|
120
|
+
|
121
|
+
...
|
122
|
+
srv.run
|
123
|
+
|
124
|
+
remember to always set at least the content type!
|
125
|
+
|
126
|
+
## BASIC AUTH
|
127
|
+
|
128
|
+
in controller..
|
129
|
+
|
130
|
+
login,password = get_basic_auth
|
131
|
+
auth_error( "invalid login or password" ) unless .... # validate login and password
|
132
|
+
|
133
|
+
|
134
|
+
|
135
|
+
|
136
|
+
## REQUEST FORMAT
|
137
|
+
|
138
|
+
you can provide custom responses to a request format by registering a format parser
|
139
|
+
for that format. you can also override the standard html,json or xml behavior.
|
140
|
+
|
141
|
+
Note that the format for a non standard (html/json/xml) request is only taken from
|
142
|
+
the extension part ( after the .) of the url ... eg
|
143
|
+
|
144
|
+
`http://mydomain.com/mypage.jsonp` will give a format of jsonp
|
145
|
+
|
146
|
+
you can specify the :default option in your route for a fallback format other than :json
|
147
|
+
eg `:default=>:html`
|
148
|
+
|
149
|
+
|
150
|
+
class MyParser < FormatParser
|
151
|
+
|
152
|
+
def set_default_headers(headers)
|
153
|
+
headers[CACHE_CONTROL]= CACHE_NO_STORE
|
154
|
+
headers[PRAGMA] = NO_CACHE
|
155
|
+
headers[CONTENT_TYPE] = CONTENT_TYPE_JSONP
|
156
|
+
end
|
157
|
+
|
158
|
+
def format_error(message)
|
159
|
+
message.to_s
|
160
|
+
end
|
161
|
+
|
162
|
+
def format_response(value,response)
|
163
|
+
response.content = "<script>load(" +
|
164
|
+
MultiJson.dump( value) +
|
165
|
+
")</script>"
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
|
170
|
+
s = Blix::Rest::Server.new
|
171
|
+
s.register_parser(:jsonp,MyParser.new)
|
172
|
+
|
173
|
+
|
174
|
+
Then in your controller accept that format..
|
175
|
+
|
176
|
+
|
177
|
+
get "/details" :accept=>[:jsonp] do
|
178
|
+
{"id"=>12}
|
179
|
+
end
|
180
|
+
|
181
|
+
## CUSTOM RESPONSE WITHOUT CUSTOM PARSER
|
182
|
+
|
183
|
+
to force a response in a certain format use the :force option in your route.
|
184
|
+
|
185
|
+
to return a custom response use `:force=>:raw` . You will have to specify all the
|
186
|
+
headers and the body is returned as it is.
|
187
|
+
|
188
|
+
use the following to accept requests in a special format ..
|
189
|
+
|
190
|
+
get '/custom', :accept=>:xyz, :force=>:raw do
|
191
|
+
add_headers 'Content-Type'=>'text/xyz'
|
192
|
+
|
193
|
+
"xyz"
|
194
|
+
end
|
195
|
+
|
196
|
+
## FORMATS
|
197
|
+
|
198
|
+
the format of a request is derived from
|
199
|
+
|
200
|
+
1. the `:force` option value if present
|
201
|
+
|
202
|
+
2. the request query `format` parameter if the `:query` option is true
|
203
|
+
|
204
|
+
3. the url extension unless the `:extension` option is false.
|
205
|
+
|
206
|
+
4. the accept header format
|
207
|
+
|
208
|
+
5. the format specified in the `:default` option
|
209
|
+
|
210
|
+
6. `:json`
|
211
|
+
|
212
|
+
|
213
|
+
## Controller
|
214
|
+
|
215
|
+
Blix::Rest::Controller
|
216
|
+
|
217
|
+
base class for controllers. within your block handling a particular route you
|
218
|
+
have access to a number of methods
|
219
|
+
|
220
|
+
|
221
|
+
env : the request environment hash
|
222
|
+
method : the request method lowercase( 'get'/'post' ..)
|
223
|
+
req : the rack request
|
224
|
+
body : the request body as a string
|
225
|
+
query_params : a hash of parameters as passed in the url as parameters
|
226
|
+
path_params : a hash of parameters constructed from variable parts of the path
|
227
|
+
post_params : a hash of parameters passed in the body of the request
|
228
|
+
params : all the params combined
|
229
|
+
user : the user making this request ( or nil if
|
230
|
+
format : the format the response should be in :json or :html
|
231
|
+
before : before hook ( opts ) - remember to add 'super' as first line !!!
|
232
|
+
after : after hook (opts,response)- remember to add 'super' as first line !!!
|
233
|
+
proxy : forward the call to another service (service,path, opts={}) , :include_query=>true/false
|
234
|
+
session : req.session
|
235
|
+
redirect : (path, status=302) redirect to another url.
|
236
|
+
request_ip : the ip of the request
|
237
|
+
render_erb : (template_name [,:layout=>name])
|
238
|
+
path_for : (path) give the correct path for an internal path
|
239
|
+
url_for : (path) give the full url for an internal path
|
240
|
+
h : escape html string to avoid XSS
|
241
|
+
escape_javascript : escape a javascript string
|
242
|
+
server_options : options as passed to server at create time
|
243
|
+
mode_test? : test mode ?
|
244
|
+
mode_production? : production mode ?
|
245
|
+
mode_development? : development mode?
|
246
|
+
|
247
|
+
|
248
|
+
get_session_id(session_name, opts={}) :
|
249
|
+
refresh_session_id(session_name, opts={}) :
|
250
|
+
|
251
|
+
to accept requests other than json then set `:accept=>[:json,:html]` as options in the route
|
252
|
+
|
253
|
+
eg `post '/myform' :accept=>[:html] # this will only accept html requests.`
|
254
|
+
|
255
|
+
### Sessions
|
256
|
+
|
257
|
+
the following methods are available in the controller for managing sessions.
|
258
|
+
|
259
|
+
get_session_id(session_name, opts={})
|
260
|
+
|
261
|
+
this will set up a session and setup the relevant cookie headers forthe browser.
|
262
|
+
|
263
|
+
refresh_session_id(session_name, opts={})
|
264
|
+
|
265
|
+
this will generate a new session_id and setup the relevant headers
|
266
|
+
|
267
|
+
options can include:
|
268
|
+
|
269
|
+
:secure => true # secure cookies only
|
270
|
+
:http = >true # cookies for http only not javascript requests
|
271
|
+
:samesite =>:strict # use strict x-site policy
|
272
|
+
:samesite =>:lax # use lax x-site policy
|
273
|
+
|
274
|
+
|
275
|
+
|
276
|
+
|
277
|
+
## Views
|
278
|
+
|
279
|
+
the location of your views defaults to `app/views` otherwise set it manually with:
|
280
|
+
|
281
|
+
globally eg:
|
282
|
+
|
283
|
+
Blix::Rest.set_erb_root ::File.expand_path('../lib/myapp/views', __FILE__)
|
284
|
+
|
285
|
+
or per controller eg:
|
286
|
+
|
287
|
+
class MyController < Blix::Rest::Controller
|
288
|
+
|
289
|
+
erb_dir ::File.expand_path('../..', __FILE__)
|
290
|
+
|
291
|
+
end
|
292
|
+
|
293
|
+
then within a controller render your view with.
|
294
|
+
|
295
|
+
render_erb( "users/index", :layout=>'layouts/main', :locals=>{:name=>"charles"})
|
296
|
+
|
297
|
+
( locals work from ruby 2.1 )
|
298
|
+
|
299
|
+
|
300
|
+
#### directory structure
|
301
|
+
|
302
|
+
views
|
303
|
+
-----
|
304
|
+
users
|
305
|
+
-----
|
306
|
+
index.html.erb
|
307
|
+
layouts
|
308
|
+
-------
|
309
|
+
main.html.erb
|
310
|
+
|
311
|
+
|
312
|
+
## Logging
|
313
|
+
|
314
|
+
Blix::Rest.logger = Logger.new('/var/log/myapp.log')
|
315
|
+
|
316
|
+
|
317
|
+
## Testing a Service with cucumber
|
318
|
+
|
319
|
+
|
320
|
+
in features/support/setup.rb
|
321
|
+
|
322
|
+
require 'blix/rest/cucumber'
|
323
|
+
|
324
|
+
and setup your database connections etc
|
325
|
+
|
326
|
+
|
327
|
+
in features/support/hooks.rb
|
328
|
+
|
329
|
+
reset your database
|
330
|
+
|
331
|
+
|
332
|
+
|
333
|
+
now you can use the following in scenarios ........
|
334
|
+
|
335
|
+
Given user guest gets "/info"
|
336
|
+
|
337
|
+
Given the following users exist:
|
338
|
+
| name | level |
|
339
|
+
| anon | guest |
|
340
|
+
| bob | user |
|
341
|
+
| mary | provider |
|
342
|
+
| paul | user |
|
343
|
+
| admin | admin |
|
344
|
+
|
345
|
+
Given user mary posts "/worlds" with {"name":"narnia"} [..or gets/puts/deletes]
|
346
|
+
Then store the "id" as "world_id"
|
347
|
+
|
348
|
+
Given user bob posts "/worlds/:world_id" with {"the_world_id"::world_id }
|
349
|
+
|
350
|
+
Then the status should be 200
|
351
|
+
Then the data type should be "r_type"
|
352
|
+
Then the data length should be 3
|
353
|
+
Then there should be an error
|
354
|
+
Then the error message should include "unique"
|
355
|
+
Then the data "name" should == "bob"
|
356
|
+
Then the data should include "name"
|
357
|
+
|
358
|
+
And explain
|
359
|
+
|
360
|
+
|
361
|
+
NOTE : if you need to set up your database with users then you can use the following hook ..
|
362
|
+
|
363
|
+
in features/support/world.rb .........
|
364
|
+
|
365
|
+
class RestWorld
|
366
|
+
|
367
|
+
# add a hook to create the user in the database -
|
368
|
+
#
|
369
|
+
def before_user_create(user,hash)
|
370
|
+
name = hash["name"]
|
371
|
+
u = MyUser.new
|
372
|
+
u.set(:user_wid, name)
|
373
|
+
u.set(:name,name)
|
374
|
+
u.set(:is_super,true) if hash["level"] == "super"
|
375
|
+
u.save
|
376
|
+
store["myuser_#{name}_id"] = u.id.to_s
|
377
|
+
end
|
378
|
+
end
|
379
|
+
|
380
|
+
now you can also use eg `:myuser_foo_id` within a request path/json.
|
381
|
+
|
382
|
+
|
383
|
+
|
384
|
+
## Manage Assets
|
385
|
+
|
386
|
+
|
387
|
+
require 'blix/assets'
|
388
|
+
|
389
|
+
|
390
|
+
The asset manager stores a hash of the asset data and the current unique file suffix for each asset in its own file.
|
391
|
+
This config file is stored in a config directory. The default is 'config/assets' but another location can be specified.
|
392
|
+
|
393
|
+
|
394
|
+
Blix::AssetManager.config_dir = "myassets/config/location" # defaults to `"config/assets"`
|
395
|
+
|
396
|
+
|
397
|
+
### Compile your assets
|
398
|
+
|
399
|
+
require 'blix/assets'
|
400
|
+
|
401
|
+
......
|
402
|
+
......
|
403
|
+
ASSETS = ['admin.js', 'admin.css', 'standard.js']
|
404
|
+
ASSETS.each do |name|
|
405
|
+
|
406
|
+
compiled_asset = environment[name].to_s
|
407
|
+
|
408
|
+
Blix::AssetManager.if_modified(name,compiled_asset,:rewrite=>true) do |a|
|
409
|
+
|
410
|
+
filename = File.join(ROOT,"public","assets",a.newname)
|
411
|
+
puts "writing #{name} to #{filename}"
|
412
|
+
File.write filename,compiled_asset
|
413
|
+
|
414
|
+
File.unlink File.join(ROOT,"public","assets",a.oldname) if a.oldname
|
415
|
+
end
|
416
|
+
|
417
|
+
end
|
418
|
+
|
419
|
+
|
420
|
+
### In your erb view
|
421
|
+
|
422
|
+
|
423
|
+
eg:
|
424
|
+
|
425
|
+
require 'blix/assets'
|
426
|
+
|
427
|
+
........
|
428
|
+
|
429
|
+
<script src="<%= asset_path('assets/standard.js') %>" type="text/javascript"></script>
|
430
|
+
|
431
|
+
or
|
432
|
+
|
433
|
+
<%= asset_tag('/assets/standard.js') %>
|
434
|
+
|
435
|
+
|
436
|
+
### or in your controller
|
437
|
+
|
438
|
+
eg:
|
439
|
+
|
440
|
+
require 'blix/assets'
|
441
|
+
|
442
|
+
........
|
443
|
+
|
444
|
+
path = asset_path('assets/standard.js')
|
445
|
+
|
446
|
+
|
447
|
+
|
448
|
+
#### NOTE ON ASSETS!!
|
449
|
+
|
450
|
+
In production mode the compiled version of the assets will be used which will have a unique file name.
|
451
|
+
|
452
|
+
In production the expiry date of your assets can be set to far in the future to take advantage of cacheing.
|
453
|
+
|
454
|
+
In development or test mode the standard name will be used which then will make use of your asset pipeline ( eg sprockets )
|
455
|
+
|
456
|
+
Asset names can contain only one extension. if there are more extensions eg: 'myfile.extra.css' then only the last
|
457
|
+
extension will be used: in this case the name will be simplified to 'myfile.css' !!!
|