blix-rest 0.9.3 → 0.11.1

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.
data/README.md CHANGED
@@ -1,504 +1,521 @@
1
- ## INSTALLATION
2
-
1
+ Blix::Rest User Guide
2
+ =====================
3
+
4
+ Table of Contents
5
+ -----------------
6
+
7
+ - [Blix::Rest User Guide](#blixrest-user-guide)
8
+ - [Table of Contents](#table-of-contents)
9
+ - [Introduction](#introduction)
10
+ - [Installation](#installation)
11
+ - [Creating a Simple Web Service](#creating-a-simple-web-service)
12
+ - [Controllers](#controllers)
13
+ - [Hooks](#hooks)
14
+ - [Routing](#routing)
15
+ - [Path Parameters](#path-parameters)
16
+ - [Query Parameters](#query-parameters)
17
+ - [Body Values](#body-values)
18
+ - [Wildcard Paths](#wildcard-paths)
19
+ - [Path Options](#path-options)
20
+ - [Request Handling](#request-handling)
21
+ - [Request Format](#request-format)
22
+ - [Response Handling](#response-handling)
23
+ - [Setting Headers and Status](#setting-headers-and-status)
24
+ - [Generating Error Responses](#generating-error-responses)
25
+ - [Predefined and Custom Formats](#predefined-and-custom-formats)
26
+ - [Custom Responses](#custom-responses)
27
+ - [Handling All Paths and Preserving File Extensions](#handling-all-paths-and-preserving-file-extensions)
28
+ - [Authentication](#authentication)
29
+ - [Sessions](#sessions)
30
+ - [Caching](#caching)
31
+ - [CORS (Cross-Origin Resource Sharing)](#cors-cross-origin-resource-sharing)
32
+ - [Rate Limiting](#rate-limiting)
33
+ - [Views](#views)
34
+ - [directory structure](#directory-structure)
35
+ - [Logging](#logging)
36
+ - [Testing with Cucumber](#testing-with-cucumber)
37
+ - [Asset Management](#asset-management)
38
+ - [Controller Helper Methods](#controller-helper-methods)
39
+
40
+ <div style="page-break-after: always;"></div>
41
+
42
+ Introduction
43
+ ------------
44
+
45
+ Blix::Rest is a web application framework for Ruby that provides a
46
+ simple and flexible way to build web services, APIs, and web
47
+ applications. It supports RESTful routing, customizable controllers, and
48
+ various formats for responses.
49
+
50
+ Installation
51
+ ------------
52
+
53
+ To install Blix::Rest, use the following command:
54
+
55
+ ```bash
3
56
  gem install blix-rest
57
+ ```
4
58
 
59
+ Creating a Simple Web Service
60
+ -----------------------------
5
61
 
6
- ## CREATE A SIMPLE WEBSERVICE
7
-
8
- #### put the following in config.ru
62
+ To create a simple web service, follow these steps:
9
63
 
64
+ 1\. Create a new file named `config.ru` with the following content:
10
65
 
66
+ ```ruby
11
67
  require 'blix/rest'
12
68
 
13
69
  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
70
+ get '/hello', :accept => [:html, :json], :default => :html do
71
+ if format == :json
72
+ {"message" => "hello world"}
73
+ else
74
+ "<h1>hello world</h1>"
75
+ end
76
+ end
22
77
  end
23
78
 
24
79
  run Blix::Rest::Server.new
80
+ ```
25
81
 
82
+ or at its simplest, only accepting the default `:json` format:
26
83
 
84
+ ```ruby
85
+ require 'blix/rest'
27
86
 
87
+ class HomeController < Blix::Rest::Controller
88
+ get '/hello', do
89
+ {"message" => "hello world"}
90
+ end
91
+ end
28
92
 
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 other 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")`
93
+ run Blix::Rest::Server.new
94
+ ```
110
95
 
111
- change the status of a success response with eg:
112
96
 
113
- `set_status(401)`
114
97
 
98
+ 2\. Start the server:
115
99
 
116
- to specify __ALL__ the headers for a given format of response use eg:
100
+ ```
101
+ ruby -S rackup -p3000
102
+ ```
117
103
 
118
- srv = Blix::Rest::Server.new
119
- srv.set_custom_headers(:html, 'Content-Type'=>'text/html; charset=utf-8', 'X-OTHER'=>'')
104
+ 3\. Access the service:
105
+ * For HTML: `http://localhost:3000/hello`
106
+ * For JSON: `http://localhost:3000/hello.json`
120
107
 
121
- ...
122
- srv.run
108
+ Controllers
109
+ -----------
123
110
 
124
- remember to always set at least the content type!
111
+ Controllers in Blix::Rest inherit from `Blix::Rest::Controller`. They
112
+ provide various methods and hooks for handling requests and responses.
125
113
 
126
- ## BASIC AUTH
114
+ ### Hooks
127
115
 
128
- in controller..
116
+ Controllers support `before`, `before_route`, and `after` hooks:
129
117
 
130
- login,password = get_basic_auth
131
- auth_error( "invalid login or password" ) unless .... # validate login and password
118
+ ```ruby
119
+ class MyController < Blix::Rest::Controller
132
120
 
121
+ before do
122
+ # Code to run before each request
123
+ end
133
124
 
125
+ before_route do
126
+ # Code to run after 'before' but before the route is executed
127
+ end
134
128
 
129
+ after do
130
+ # Code to run after each request
131
+ end
132
+ end
133
+ ```
135
134
 
136
- ## REQUEST FORMAT
135
+ - `before`: Runs before any request processing begins. Use this for
136
+ setup operations that should occur for all routes in the controller.
137
+ - `before_route`: Runs after `before` but before the specific route
138
+ action is executed. This is useful for operations that should occur
139
+ for all routes but may depend on request-specific information.
140
+ - `after`: Runs after the request has been processed. Use this for
141
+ cleanup operations or final modifications to the response.
137
142
 
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.
143
+ These hooks allow you to execute code at different stages of the request
144
+ lifecycle, providing flexibility in how you handle requests and
145
+ responses.
140
146
 
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
147
+ ### Routing
143
148
 
144
- `http://mydomain.com/mypage.jsonp` will give a format of jsonp
149
+ Blix::Rest supports various HTTP methods for routing:
145
150
 
146
- you can specify the :default option in your route for a fallback format other than :json
147
- eg `:default=>:html`
151
+ ```ruby
152
+ class MyController < Blix::Rest::Controller
148
153
 
154
+ get '/users' do
155
+ # Handle GET request
156
+ end
149
157
 
150
- class MyParser < FormatParser
158
+ post '/users' do
159
+ # Handle POST request
160
+ end
151
161
 
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
162
+ put '/users/:id' do
163
+ # Handle PUT request
164
+ end
157
165
 
158
- def format_error(message)
159
- message.to_s
160
- end
166
+ delete '/users/:id' do
167
+ # Handle DELETE request
168
+ end
161
169
 
162
- def format_response(value,response)
163
- response.content = "<script>load(" +
164
- MultiJson.dump( value) +
165
- ")</script>"
166
- end
167
170
  end
171
+ ```
168
172
 
173
+ ### Path Parameters
169
174
 
170
- s = Blix::Rest::Server.new
171
- s.register_parser(:jsonp,MyParser.new)
172
-
173
-
174
- Then in your controller accept that format..
175
+ You can use path parameters in your routes:
175
176
 
176
-
177
- get "/details" :accept=>[:jsonp] do
178
- {"id"=>12}
177
+ ```ruby
178
+ get '/user/:user_id/list' do
179
+ user_id = path_params[:user_id]
180
+ # Use user_id
179
181
  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
- "xyz"
182
+ ```
183
+
184
+ ### Query Parameters
185
+
186
+ Query parameters are key-value pairs that appear after the question mark (?) in a URL.
187
+ You can access query parameters in your routes like this:
188
+
189
+ ```ruby
190
+ get '/search' do
191
+ query = query_params[:q]
192
+ limit = query_params[:limit]&.to_i || 10
193
+ # Use query and limit in your search logic
194
+ { results: perform_search(query, limit) }
195
+ end
196
+ ```
197
+
198
+ ### Body Values
199
+
200
+ There are several methods to access data from the request body, particularly useful for POST, PUT, and PATCH requests. Here are the main ways to access body values:
201
+
202
+ 1. `body_hash`: This method parses the request body and returns it as a hash. It's particularly useful for JSON payloads.
203
+
204
+ ```ruby
205
+ post '/users' do
206
+ user_data = body_hash
207
+ new_user = User.create(user_data)
208
+ { id: new_user.id, message: "User created successfully" }
209
+ end
210
+ ```
211
+
212
+ 2. `body`: This method returns the raw request body as a string. It's useful when you need to process the raw data yourself.
213
+
214
+ ```ruby
215
+ post '/raw-data' do
216
+ raw_data = body
217
+ # Process the raw data
218
+ { received_bytes: raw_data.bytesize }
219
+ end
220
+ ```
221
+
222
+ 3. `form_hash`: This method returns a hash of form data from POST requests. It's particularly useful for handling form submissions.
223
+
224
+ ```ruby
225
+ post '/submit-form' do
226
+ form_data = form_hash
227
+ # Process the form data
228
+ { message: "Form received", name: form_data['name'] }
229
+ end
230
+ ```
231
+
232
+ 4. `get_data(field)`: This method allows you to get a specific field from the request body's data hash.
233
+
234
+ ```ruby
235
+ put '/users/:id' do
236
+ user_id = path_params[:id]
237
+ new_name = get_data('name')
238
+ # Update user name
239
+ { message: "User #{user_id} updated with name #{new_name}" }
240
+ end
241
+ ```
242
+
243
+ When working with body values, keep these points in mind:
244
+
245
+ - The appropriate method to use depends on the `Content-Type` of the request.
246
+ - For JSON payloads, `body_hash` automatically parses the JSON into a Ruby hash.
247
+ - For form submissions, `form_hash` is the most convenient method.
248
+ - Always validate and sanitize input data before using it in your application logic.
249
+ - If the body cannot be parsed (e.g., invalid JSON), these methods may return nil or raise an error, so consider adding error handling.
250
+
251
+ ### Wildcard Paths
252
+
253
+ You can use wildcard paths:
254
+
255
+ ```ruby
256
+ get '/resource/*wildpath' do
257
+ wildpath = path_params[:wildpath]
258
+ # Use wildpath
193
259
  end
260
+ ```
194
261
 
262
+ ### Path Options
195
263
 
196
- Alternatively it is possible to raise a RawResponse:
197
-
198
- add_headers 'Content-Type'=>'text/xyz'
199
- raise RawResponse, 'xyz'
200
-
201
- or with status and headers:
202
-
203
- raise RawResponse.new('xyz', 123, 'Content-Type'=>'text/xyz')
264
+ You can specify various options for your routes:
204
265
 
205
- ## FORMATS
206
-
207
- the format of a request is derived from
208
-
209
- 1. the `:force` option value if present
210
-
211
- 2. the request query `format` parameter if the `:query` option is true
212
-
213
- 3. the url extension unless the `:extension` option is false.
214
-
215
- 4. the accept header format
266
+ ```ruby
267
+ get '/users', :accept => [:html, :json], :default => :html do
268
+ # Handle request
269
+ end
270
+ ```
216
271
 
217
- 5. the format specified in the `:default` option
272
+ Available options:
218
273
 
219
- 6. `:json`
274
+ - `:accept`: Formats to accept (e.g., `:html`, `[:png, :jpeg]`, `:*`)
275
+ - `:default`: Default format if not specified
276
+ - `:force`: Force response into a given format
277
+ - `:query`: Derive format from request query (default: false)
278
+ - `:extension`: Derive format from path extension (default: true)
220
279
 
280
+ Request Handling
281
+ ----------------
221
282
 
222
- ## Controller
283
+ ### Request Format
223
284
 
224
- Blix::Rest::Controller
285
+ The format of a request is derived in the following order:
225
286
 
226
- base class for controllers. within your block handling a particular route you
227
- have access to a number of methods
287
+ 1. The `:force` option value if present
288
+ 2. The request query `format` parameter if the `:query` option is true
289
+ 3. The URL extension unless the `:extension` option is false
290
+ 4. The accept header format
291
+ 5. The format specified in the `:default` option
292
+ 6. `:json` (default)
228
293
 
294
+ Response Handling
295
+ -----------------
229
296
 
230
- env : the request environment hash
231
- method : the request method lowercase( 'get'/'post' ..)
232
- req : the rack request
233
- body : the request body as a string
234
- path : the request path
235
- query_params : a hash of parameters as passed in the url as parameters
236
- path_params : a hash of parameters constructed from variable parts of the path
237
- post_params : a hash of parameters passed in the body of the request
238
- params : all the params combined
239
- allow_methods : allow the non standard http verbs in the controller. eg `:propfind `
240
- user : the user making this request ( or nil if
241
- format : the format the response should be in :json or :html
242
- before : before hook ( opts ) - remember to add 'super' as first line !!!
243
- after : after hook (opts,response)- remember to add 'super' as first line !!!
244
- proxy : forward the call to another service (service,path, opts={}) , :include_query=>true/false
245
- session : req.session
246
- set_status : set the http response status ( 200 )
247
- redirect : (path, status=302) redirect to another url.
248
- request_ip : the ip of the request
249
- render_erb : (template_name [,:layout=>name])
250
- server_cache : get the server cache object
251
- server_cache_get : retrieve/store value in cache
252
- path_for : (path) give the external path for an internal path
253
- url_for : (path) give the full url for an internal path
254
- h : escape html string to avoid XSS
255
- escape_javascript : escape a javascript string
256
- server_options : options as passed to server at create time
257
- logger : system logger
258
- mode_test? : test mode ?
259
- mode_production? : production mode ?
260
- mode_development? : development mode?
261
- send_data : send raw data (data, options )
262
- [:type=>mimetype]
263
- [:filename=>name]
264
- [:disposition=>inline|attachment]
265
- [:status=>234]
297
+ ### Setting Headers and Status
266
298
 
267
- get_session_id(session_name, opts={}) :
268
- refresh_session_id(session_name, opts={}) :
299
+ ```ruby
300
+ add_headers("X-Custom-Header" => "Value")
301
+ set_status(201)
302
+ ```
269
303
 
270
- to accept requests other than json then set `:accept=>[:json,:html]` as options in the route
304
+ ### Generating Error Responses
271
305
 
272
- eg `post '/myform' :accept=>[:html] # this will only accept html requests.`
306
+ ```ruby
307
+ send_error("Error message", 400)
308
+ ```
273
309
 
274
- ### Hooks
310
+ ### Predefined and Custom Formats
275
311
 
276
- a before or after hook can be defined on a controller. Only define the hook once
277
- for a given controller per source file. A hook included from another source file
278
- is ok though.
312
+ Blix::Rest comes with predefined formats such as `:json`, `:html`,
313
+ `:xml`, and others. However, you can also register custom formats to
314
+ handle specific content types.
279
315
 
280
- class MyController < Blix::Rest::Controller
316
+ To register a new format:
281
317
 
282
- before do
283
- ...
318
+ ```ruby
319
+ class MyCustomFormatParser < Blix::Rest::FormatParser
320
+ def initialize
321
+ super(:mycustom, 'application/x-mycustom')
284
322
  end
285
323
 
286
- after do
287
- ...
324
+ def parse(text)
325
+ # Custom parsing logic here
288
326
  end
289
327
 
328
+ def format(obj)
329
+ # Custom formatting logic here
330
+ end
290
331
  end
291
332
 
333
+ Blix::Rest::Server.register_parser(MyCustomFormatParser.new)
334
+ ```
292
335
 
293
- #### manipulate the route path or options
336
+ After registering your custom format parser, you can use it in your
337
+ routes:
294
338
 
295
- the `before_route` hook can be used to modify the path or options of a route.
339
+ ```ruby
340
+ get '/custom', :accept => :mycustom do
341
+ # Your custom format will be used for the response
342
+ { data: 'Some data' }
343
+ end
344
+ ```
296
345
 
297
- the verb can not be modified
298
346
 
299
- example:
347
+ ### Custom Responses
300
348
 
301
- class MyController < Blix::Rest::Controller
349
+ To return a custom response without using a registered format parser,
350
+ use the `:force => :raw` option:
302
351
 
303
- before_route do |route|
304
- route.default_option(:level,:visitor)
305
- route.path_prefix('/app')
306
- end
307
- ...
352
+ ```ruby
353
+ get '/custom', :accept => :xyz, :force => :raw do
354
+ add_headers 'Content-Type' => 'text/xyz'
355
+ "Custom response"
308
356
  end
357
+ ```
358
+
359
+ This approach allows you to have full control over the response format
360
+ and headers.
309
361
 
310
- the following methods are available on the route:
362
+ ### Handling All Paths and Preserving File Extensions
311
363
 
312
- verb # readonly, the 'GET','POST' etc verb of the route
313
- path # the path of the route
314
- options # the options associated with the route eg :accept
315
- path_prefix('/xx') # ensure the path has the given prefix
316
- default_option(:xxx,'foo') # ensure that option has the given default value
364
+ In some cases, you might want to respond to all paths and keep the file
365
+ extension as part of the path, rather than using it to determine the
366
+ response format. You can achieve this by using a combination of options:
317
367
 
318
- ### Sessions
368
+ ```ruby
369
+ get '/*path', :accept => :*, :extension => false, :force => :raw do
370
+ file_path = path_params[:path]
371
+ # Handle the request based on the full path, including any file extension
372
+ content = read_file(file_path)
373
+ content_type = determine_content_type(file_path)
319
374
 
320
- the following methods are available in the controller for managing sessions.
375
+ add_headers 'Content-Type' => content_type
376
+ content
377
+ end
378
+ ```
379
+
380
+ In this example:
321
381
 
322
- get_session_id(session_name, opts={})
382
+ - `:accept => :*` allows the route to accept any content type.
383
+ - `:extension => false` prevents Blix::Rest from using the file
384
+ extension to determine the response format.
385
+ - `:force => :raw` gives you full control over the response, including
386
+ setting the appropriate Content-Type header.
323
387
 
324
- this will set up a session and setup the relevant cookie headers forthe browser.
388
+ This configuration is particularly useful when you're serving static
389
+ files or when you need to preserve the original path structure in your
390
+ application logic.
325
391
 
326
- refresh_session_id(session_name, opts={})
392
+ Authentication
393
+ --------------
327
394
 
328
- this will generate a new session_id and setup the relevant headers
395
+ Blix::Rest supports basic authentication:
329
396
 
330
- options can include:
397
+ ```ruby
398
+ login, password = get_basic_auth
399
+ auth_error("Invalid login or password") unless valid_credentials?(login, password)
400
+ ```
331
401
 
332
- :secure => true # secure cookies only
333
- :http = >true # cookies for http only not javascript requests
334
- :samesite =>:strict # use strict x-site policy
335
- :samesite =>:lax # use lax x-site policy
402
+ Sessions
403
+ --------
336
404
 
337
- For more complete session management:
405
+ Blix::Rest provides session management:
338
406
 
407
+ ```ruby
339
408
  require 'blix/utils/redis_store'
340
409
  require 'blix/rest/session'
341
410
 
342
411
  class MyController < Blix::Rest::Controller
343
412
  include Blix::Rest::Session
344
413
 
345
- session_name :xxx # optional default'blix'
346
- session_opts :http=>true # optional
347
- session_manager: MyManager.new # optional , default Blix::Redis::Store
348
-
414
+ session_name :my_session
415
+ session_opts :http => true
416
+ session_manager MySessionManager.new
349
417
 
350
- def myroutine
351
- @xxx = session['xxx']
352
-
353
- session['yyy'] = true
418
+ def my_action
419
+ session['user_id'] = 123
420
+ @user_data = session['user_data']
354
421
  end
355
-
356
422
  end
423
+ ```
357
424
 
358
- options can include:
359
-
360
- :secure # false
361
- :http # false
362
- :samesite # lax
363
- :path # '/'
364
- :expire_secs # 30 mins
365
- :cleanup_every_secs # 5 minutes
366
- :max_age # nil
367
-
368
- the following methods are available:
369
-
370
- reset_session # gen new session id
371
- session # session hash
372
- csrf_token # the session csrf token
373
-
374
- route options that affect sessions:
375
-
376
- :nosession=>true # no session will be set/retrieved
377
- :cache=>true # no session will be set/retrieved
378
- :csrf=>true # request will be validated for calid csrf token.
379
- # token must be in header X_CSRF_TOKEN field
380
-
381
- session configuration is inherited from superclass controllers unless overridden.
382
-
383
- ## Cache
384
-
385
-
386
- the server has a cache which can also be used for storing your own data.
387
-
388
- within a controller access the controller with `server_cache` which returns the
389
- cache object.
425
+ Caching
426
+ -------
390
427
 
391
- cache object methods:
428
+ Blix::Rest provides a caching mechanism:
392
429
 
393
- get(key) # return value from the cache or nil
394
- set(key,value) # set a value in the cache
395
- key?(key) # is a key present in the cache
396
- delete(key) # delete a key from the cache
397
- clear # delete all keys from the cache.
430
+ ```ruby
431
+ value = server_cache_get('my_key') { expensive_operation() }
432
+ ```
398
433
 
399
- there is also a `server_cache_get` method.
434
+ To cache responses automatically, add `:cache => true` to your route
435
+ options.
400
436
 
401
- server_cache_get(key){ action }
437
+ CORS (Cross-Origin Resource Sharing)
438
+ ------------------------------------
402
439
 
403
- get the value from the cache. If the key is missing in the cache then perform
404
- the action in the provided block and store the result in the cache.
440
+ To enable CORS for a route:
405
441
 
406
- the default cache is just a ruby hash in memory. Pass a custom cache to
407
- when creating a server with the `:cache` parameter.
408
-
409
- class MyCache < Blix::Rest::Cache
410
- def get(key)
411
- ..
412
- end
413
-
414
- def set(key,value)
415
- ..
416
- end
417
-
418
- def key?(key)
419
- ..
420
- end
421
-
422
- def delete(key)
423
- ..
424
- end
425
-
426
- def clear
427
- ..
428
- end
429
- end
430
-
431
- cache = MyCache.new
432
-
433
- app = Blix::Rest::Server.new(:cache=>cache)
434
-
435
- there is a redis cache already defined:
436
-
437
- require 'blix/rest/redis_cache'
442
+ ```ruby
443
+ get '/api/data' do
444
+ set_accept_cors
445
+ { data: 'Some data' }
446
+ end
438
447
 
439
- cache = Blix::Rest::RedisCache.new(:expire_secs=>60*60*24) # expire after 1 day
440
- run Blix::Rest::Server.new(:cache=>cache)
448
+ options '/api/data' do
449
+ set_accept_cors(
450
+ :origin => 'https://example.com',
451
+ :methods => [:get, :post],
452
+ :headers => ['Content-Type', 'Authorization']
453
+ )
454
+ end
455
+ ```
441
456
 
457
+ Rate Limiting
458
+ -------------
442
459
 
443
- ### automatically cache server responses
460
+ Blix::Rest provides a rate limiting mechanism, only allow so many exceptions in a given time:
444
461
 
445
- add `:cache=>true` to your route options in order to cache this route.
462
+ the delay times are applied to:
446
463
 
447
- add `:cache_reset=>true` to your route options if the cache should be cleared when
448
- calling this route.
464
+ * 3x failure
465
+ * 10x failure
466
+ * 100x failure
449
467
 
450
- the cache is not used in development/testmode , only in production mode.
468
+ ```ruby
469
+ rate_limit('api_calls', times: [60, 600, 86400]) do
470
+ # Your rate-limited code here. If an exception ir raised
471
+ # then the failure count is incremented and an exception is raised
472
+ # until the corresponding delay is expired.
473
+ end
474
+ ```
451
475
 
452
- ### IMPORTANT - DO NOT CACHE SESSION KEYS AND OTHER SPECIFIC DATA IN CACHE
476
+ the `api_calls` here is a key which is used to store the failure count against.
477
+ If you were rate limiting a login then you might use the user name as part of this key.
453
478
 
454
- only cache pages with **HEADERS** and **CONTENT** that is not user specific.
479
+ The `times:` array specifies the rate limiting intervals in seconds. The default values are the
480
+ same as in this example:
455
481
 
456
- ## CORS
482
+ - `60`: Limits requests per minute (60 seconds)
483
+ - `600`: Limits requests per 10 minutes (600 seconds, which is 10 minutes)
484
+ - `86400`: Limits requests per day (86400 seconds, which is 24 hours)
457
485
 
458
- cross origin site requests
459
486
 
460
- MyController < Blix::Rest::Controller
461
487
 
462
- get '/info/crosssite' do
463
- set_accept_cors
464
- {:data=>'foo'}
465
- end
488
+ Views
489
+ -----
466
490
 
467
- options '/info/' crosssite' do
468
- set_accept_cors, :headers=>'Content-Type',
469
- end
491
+ To render views, use the `render_erb` method:
470
492
 
493
+ ```ruby
494
+ get '/users' do
495
+ @users = User.all
496
+ render_erb('users/index', layout: 'layouts/main')
471
497
  end
472
-
473
- within an `options` response the following parameters can be passes.
474
-
475
- :origin=>'www.othersite.com' # specify specific allowed origin.
476
- :methods => [:get] # allowed methods
477
- :max_age => 86400 # maximum age this auth is valid
478
- :headers => ['Content-Type','X-OTHER'] # allow additional headers
479
- :credentials => true # allow requests with credentials
480
-
481
- ## Views
498
+ ```
482
499
 
483
500
  the location of your views defaults to `app/views` otherwise set it manually with:
484
501
 
485
502
  globally eg:
486
-
503
+ ```ruby
487
504
  Blix::Rest.set_erb_root ::File.expand_path('../lib/myapp/views', __FILE__)
488
-
505
+ ```
489
506
  or per controller eg:
490
-
507
+ ```ruby
491
508
  class MyController < Blix::Rest::Controller
492
509
 
493
510
  erb_dir ::File.expand_path('../..', __FILE__)
494
511
 
495
512
  end
496
-
513
+ ```
497
514
  then within a controller render your view with.
498
-
515
+ ```ruby
499
516
  render_erb( "users/index", :layout=>'layouts/main', :locals=>{:name=>"charles"})
500
-
501
- ( locals work from ruby 2.1 )
517
+ ```
518
+ ( locals work from ruby 2.1 )
502
519
 
503
520
 
504
521
  #### directory structure
@@ -513,152 +530,116 @@ then within a controller render your view with.
513
530
  main.html.erb
514
531
 
515
532
 
516
- ## Logging
517
-
518
- Blix::Rest.logger = Logger.new('/var/log/myapp.log')
519
-
520
-
521
- ## Testing a Service with cucumber
522
-
523
-
524
- in features/support/setup.rb
525
-
526
- require 'blix/rest/cucumber'
527
-
528
- and setup your database connections etc
529
-
530
-
531
- in features/support/hooks.rb
532
-
533
- reset your database
534
-
535
-
533
+ Logging
534
+ -------
536
535
 
537
- now you can use the following in scenarios ........
536
+ Configure logging:
538
537
 
539
- Given user guest gets "/info"
540
-
541
- Given the following users exist:
542
- | name | level |
543
- | anon | guest |
544
- | bob | user |
545
- | mary | provider |
546
- | paul | user |
547
- | admin | admin |
548
-
549
- Given user mary posts "/worlds" with {"name":"narnia"} [..or gets/puts/deletes]
550
- Then store the "id" as "world_id"
551
-
552
- Given user bob posts "/worlds/:world_id" with {"the_world_id"::world_id }
553
-
554
- Then the status should be 200
555
- Then the data type should be "r_type"
556
- Then the data length should be 3
557
- Then there should be an error
558
- Then the error message should include "unique"
559
- Then the data "name" should == "bob"
560
- Then the data should include "name"
561
-
562
- And explain
563
-
564
-
565
- NOTE : if you need to set up your database with users then you can use the following hook ..
566
-
567
- in features/support/world.rb .........
568
-
569
-
570
- require 'blix/rest/cucumber'
571
-
572
- class RestWorld
573
-
574
- # add a hook to create the user in the database -
575
- #
576
- def before_user_create(user,hash)
577
- name = hash["name"]
578
- u = MyUser.new
579
- u.set(:user_wid, name)
580
- u.set(:name,name)
581
- u.set(:is_super,true) if hash["level"] == "super"
582
- u.save
583
- store["myuser_#{name}_id"] = u.id.to_s
584
- end
585
- end
586
-
587
- now you can also use eg `:myuser_foo_id` within a request path/json.
588
-
589
-
590
-
591
- ## Manage Assets
592
-
593
-
594
- require 'blix/assets'
595
-
596
-
597
- The asset manager stores a hash of the asset data and the current unique file suffix for each asset in its own file.
598
- This config file is stored in a config directory. The default is 'config/assets' but another location can be specified.
599
-
600
-
601
- Blix::AssetManager.config_dir = "myassets/config/location" # defaults to `"config/assets"`
602
-
603
-
604
- ### Compile your assets
605
-
606
- require 'blix/assets'
607
-
608
- ......
609
- ......
610
- ASSETS = ['admin.js', 'admin.css', 'standard.js']
611
- ASSETS.each do |name|
612
-
613
- compiled_asset = environment[name].to_s
614
-
615
- Blix::AssetManager.if_modified(name,compiled_asset,:rewrite=>true) do |a|
616
-
617
- filename = File.join(ROOT,"public","assets",a.newname)
618
- puts "writing #{name} to #{filename}"
619
- File.write filename,compiled_asset
620
-
621
- File.unlink File.join(ROOT,"public","assets",a.oldname) if a.oldname
622
- end
623
-
624
- end
625
-
626
-
627
- ### In your erb view
538
+ ```ruby
539
+ Blix::Rest.logger = Logger.new('/var/log/myapp.log')
540
+ ```
628
541
 
542
+ and log a message:
629
543
 
630
- eg:
544
+ ```ruby
545
+ Blix::Rest.logger.info 'my message'
546
+ ```
631
547
 
632
- require 'blix/assets'
548
+ Testing with Cucumber
549
+ ---------------------
633
550
 
634
- ........
551
+ For testing with Cucumber, install the `blix-rest-cucumber` gem and
552
+ follow the setup instructions in the README.
635
553
 
636
- <script src="<%= asset_path('assets/standard.js') %>" type="text/javascript"></script>
554
+ Asset Management
555
+ ----------------
637
556
 
638
- or
557
+ For asset management capabilities, you can use the separate
558
+ `blix-assets` gem. This gem provides tools for managing and
559
+ serving assets such as JavaScript and CSS.
639
560
 
640
- <%= asset_tag('/assets/standard.js') %>
561
+ To use asset management with your Blix::Rest application:
641
562
 
563
+ 1\. Install the `blix-assets` gem:
642
564
 
643
- ### or in your controller
565
+ ```
566
+ gem install blix-assets
567
+ ```
644
568
 
645
- eg:
569
+ 2\. Require the gem in your application:
646
570
 
571
+ ```ruby
647
572
  require 'blix/assets'
573
+ ```
574
+
575
+ 3\. Configure the asset manager:
576
+
577
+ ```ruby
578
+ Blix::AssetManager.config_dir = "config/assets"
579
+ ```
580
+
581
+ 4\. Use the asset manager in your controllers:
582
+
583
+ ```ruby
584
+ asset_path('assets/main.js')
585
+ ```
586
+
587
+ 5\. For detailed information on how to use `blix-assets`, please refer to
588
+ its documentation and README file.
589
+
590
+ Controller Helper Methods
591
+ -------------------------
592
+
593
+ Here's a comprehensive list of helper methods available in Blix::Rest controllers:
594
+
595
+ - `add_headers`: Add headers to the response
596
+ - `after`: After hook
597
+ - `allow_methods`: Allow non-standard HTTP verbs in the controller
598
+ - `asset_path`: Get the path for an asset
599
+ - `asset_tag`: Generate an HTML tag for an asset
600
+ - `auth_error`: Raise an authentication error
601
+ - `before`: Before hook
602
+ - `before_route`: Before route hook
603
+ - `body`: The request body as a string
604
+ - `body_hash`: The request body parsed as a hash
605
+ - `env`: The request environment hash
606
+ - `escape_javascript`: Escape a JavaScript string
607
+ - `format`: The response format (:json or :html)
608
+ - `form_hash`: Returns a hash of form data from POST requests
609
+ - `get_basic_auth`: Get basic authentication credentials
610
+ - `get_cookie`: Get the value of a cookie
611
+ - `get_data`: Get a field from the request body's data hash
612
+ - `get_session_id`: Get or create a session ID
613
+ - `h`: Escape HTML string to avoid XSS
614
+ - `logger`: System logger
615
+ - `method`: The request method (lowercase, e.g., 'get', 'post')
616
+ - `mode_development?`: Check if in development mode
617
+ - `mode_production?`: Check if in production mode
618
+ - `mode_test?`: Check if in test mode
619
+ - `params`: All parameters combined
620
+ - `path`: The request path
621
+ - `path_for`: Give the external path for an internal path
622
+ - `path_params`: A hash of parameters constructed from variable parts of the path
623
+ - `post_params`: A hash of parameters passed in the request body
624
+ - `proxy`: Forward the call to another service
625
+ - `query_params`: A hash of URL query parameters
626
+ - `rate_limit`: Apply rate limiting to a block of code
627
+ - `redirect`: Redirect to another URL
628
+ - `refresh_session_id`: Generate a new session ID
629
+ - `render_erb`: Render an ERB template
630
+ - `req`: The Rack request object
631
+ - `request_ip`: The IP address of the request
632
+ - `send_data`: Send raw data with various options
633
+ - `send_error`: Send an error response
634
+ - `server_cache`: Get the server cache object
635
+ - `server_cache_get`: Retrieve/store value in cache
636
+ - `server_options`: Options passed to the server at creation time
637
+ - `session`: Access to the session object
638
+ - `set_accept_cors`: Set CORS headers for the response
639
+ - `set_status`: Set the HTTP response status
640
+ - `store_cookie`: Store a cookie in the response
641
+ - `url_for`: Give the full URL for an internal path
642
+ - `user`: The user making this request (or nil if not authenticated)
643
+ - `verb`: The HTTP verb of the request (uppercase, e.g., 'GET', 'POST')
648
644
 
649
- ........
650
-
651
- path = asset_path('assets/standard.js')
652
-
653
-
654
-
655
- #### NOTE ON ASSETS!!
656
-
657
- In production mode the compiled version of the assets will be used which will have a unique file name.
658
-
659
- In production the expiry date of your assets can be set to far in the future to take advantage of cacheing.
660
-
661
- In development or test mode the standard name will be used which then will make use of your asset pipeline ( eg sprockets )
662
645
 
663
- Asset names can contain only one extension. if there are more extensions eg: 'myfile.extra.css' then only the last
664
- extension will be used: in this case the name will be simplified to 'myfile.css' !!!