blix-rest 0.8.2 → 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,499 +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
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
259
+ end
260
+ ```
180
261
 
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.
262
+ ### Path Options
187
263
 
188
- use the following to accept requests in a special format ..
264
+ You can specify various options for your routes:
189
265
 
190
- get '/custom', :accept=>:xyz, :force=>:raw do
191
- add_headers 'Content-Type'=>'text/xyz'
192
- "xyz"
266
+ ```ruby
267
+ get '/users', :accept => [:html, :json], :default => :html do
268
+ # Handle request
193
269
  end
270
+ ```
194
271
 
272
+ Available options:
195
273
 
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:
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)
202
279
 
203
- raise RawResponse.new('xyz', 123, 'Content-Type'=>'text/xyz')
280
+ Request Handling
281
+ ----------------
204
282
 
205
- ## FORMATS
283
+ ### Request Format
206
284
 
207
- the format of a request is derived from
285
+ The format of a request is derived in the following order:
208
286
 
209
- 1. the `:force` option value if present
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)
210
293
 
211
- 2. the request query `format` parameter if the `:query` option is true
294
+ Response Handling
295
+ -----------------
212
296
 
213
- 3. the url extension unless the `:extension` option is false.
297
+ ### Setting Headers and Status
214
298
 
215
- 4. the accept header format
299
+ ```ruby
300
+ add_headers("X-Custom-Header" => "Value")
301
+ set_status(201)
302
+ ```
216
303
 
217
- 5. the format specified in the `:default` option
304
+ ### Generating Error Responses
218
305
 
219
- 6. `:json`
306
+ ```ruby
307
+ send_error("Error message", 400)
308
+ ```
220
309
 
310
+ ### Predefined and Custom Formats
221
311
 
222
- ## Controller
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.
223
315
 
224
- Blix::Rest::Controller
316
+ To register a new format:
225
317
 
226
- base class for controllers. within your block handling a particular route you
227
- have access to a number of methods
318
+ ```ruby
319
+ class MyCustomFormatParser < Blix::Rest::FormatParser
320
+ def initialize
321
+ super(:mycustom, 'application/x-mycustom')
322
+ end
228
323
 
324
+ def parse(text)
325
+ # Custom parsing logic here
326
+ end
229
327
 
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
- user : the user making this request ( or nil if
240
- format : the format the response should be in :json or :html
241
- before : before hook ( opts ) - remember to add 'super' as first line !!!
242
- after : after hook (opts,response)- remember to add 'super' as first line !!!
243
- proxy : forward the call to another service (service,path, opts={}) , :include_query=>true/false
244
- session : req.session
245
- redirect : (path, status=302) redirect to another url.
246
- request_ip : the ip of the request
247
- render_erb : (template_name [,:layout=>name])
248
- server_cache : get the server cache object
249
- server_cache_get : retrieve/store value in cache
250
- path_for : (path) give the external path for an internal path
251
- url_for : (path) give the full url for an internal path
252
- h : escape html string to avoid XSS
253
- escape_javascript : escape a javascript string
254
- server_options : options as passed to server at create time
255
- logger : system logger
256
- mode_test? : test mode ?
257
- mode_production? : production mode ?
258
- mode_development? : development mode?
259
- send_data : send raw data (data, options )
260
- [:type=>mimetype]
261
- [:filename=>name]
262
- [:disposition=>inline|attachment]
263
- [:status=>234]
328
+ def format(obj)
329
+ # Custom formatting logic here
330
+ end
331
+ end
264
332
 
265
- get_session_id(session_name, opts={}) :
266
- refresh_session_id(session_name, opts={}) :
333
+ Blix::Rest::Server.register_parser(MyCustomFormatParser.new)
334
+ ```
267
335
 
268
- to accept requests other than json then set `:accept=>[:json,:html]` as options in the route
336
+ After registering your custom format parser, you can use it in your
337
+ routes:
269
338
 
270
- eg `post '/myform' :accept=>[:html] # this will only accept html requests.`
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
+ ```
271
345
 
272
- ### Hooks
273
346
 
274
- a before or after hook can be defined on a controller. Only define the hook once
275
- for a given controller per source file. A hook included from another source file
276
- is ok though.
347
+ ### Custom Responses
277
348
 
278
- class MyController < Blix::Rest::Controller
279
-
280
- before do
281
- ...
282
- end
283
-
284
- after do
285
- ...
286
- end
349
+ To return a custom response without using a registered format parser,
350
+ use the `:force => :raw` option:
287
351
 
352
+ ```ruby
353
+ get '/custom', :accept => :xyz, :force => :raw do
354
+ add_headers 'Content-Type' => 'text/xyz'
355
+ "Custom response"
288
356
  end
357
+ ```
289
358
 
359
+ This approach allows you to have full control over the response format
360
+ and headers.
290
361
 
291
- #### manipulate the route path or options
292
-
293
- the `before_route` hook can be used to modify the path or options of a route.
362
+ ### Handling All Paths and Preserving File Extensions
294
363
 
295
- the verb can not be modified
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:
296
367
 
297
- example:
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)
298
374
 
299
- class MyController < Blix::Rest::Controller
300
-
301
- before_route do |route|
302
- route.default_option(:level,:visitor)
303
- route.prefix_path('/app')
304
- end
305
- ...
375
+ add_headers 'Content-Type' => content_type
376
+ content
306
377
  end
378
+ ```
307
379
 
308
- the following methods are available on the route:
309
-
310
- verb # readonly, the 'GET','POST' etc verb of the route
311
- path # the path of the route
312
- options # the options associated with the route eg :accept
313
- path_prefix('/xx') # ensure the path has the given prefix
314
- default_option(:xxx,'foo') # ensure that option has the given default value
380
+ In this example:
315
381
 
316
- ### Sessions
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.
317
387
 
318
- the following methods are available in the controller for managing sessions.
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.
319
391
 
320
- get_session_id(session_name, opts={})
392
+ Authentication
393
+ --------------
321
394
 
322
- this will set up a session and setup the relevant cookie headers forthe browser.
395
+ Blix::Rest supports basic authentication:
323
396
 
324
- refresh_session_id(session_name, opts={})
397
+ ```ruby
398
+ login, password = get_basic_auth
399
+ auth_error("Invalid login or password") unless valid_credentials?(login, password)
400
+ ```
325
401
 
326
- this will generate a new session_id and setup the relevant headers
402
+ Sessions
403
+ --------
327
404
 
328
- options can include:
405
+ Blix::Rest provides session management:
329
406
 
330
- :secure => true # secure cookies only
331
- :http = >true # cookies for http only not javascript requests
332
- :samesite =>:strict # use strict x-site policy
333
- :samesite =>:lax # use lax x-site policy
334
-
335
- For more complete session management:
407
+ ```ruby
408
+ require 'blix/utils/redis_store'
409
+ require 'blix/rest/session'
336
410
 
337
411
  class MyController < Blix::Rest::Controller
338
412
  include Blix::Rest::Session
339
413
 
340
- session_name :xxx # optional default'blix'
341
- session_opts :http=>true # optional
342
- session_manager: MyManager.new # optional , default Blix::Redis::Store
343
-
414
+ session_name :my_session
415
+ session_opts :http => true
416
+ session_manager MySessionManager.new
344
417
 
345
- def myroutine
346
- @xxx = session['xxx']
347
-
348
- session['yyy'] = true
418
+ def my_action
419
+ session['user_id'] = 123
420
+ @user_data = session['user_data']
349
421
  end
350
-
351
422
  end
423
+ ```
352
424
 
353
- options can include:
354
-
355
- :secure # false
356
- :http # false
357
- :samesite # lax
358
- :path # '/'
359
- :expire_secs # 30 mins
360
- :cleanup_every_secs # 5 minutes
361
- :max_age # nil
362
-
363
- the following methods are available:
364
-
365
- reset_session # gen new session id
366
- session # session hash
367
- csrf_token # the session csrf token
368
-
369
- route options that affect sessions:
370
-
371
- :nosession=>true # no session will be set/retrieved
372
- :cache=>true # no session will be set/retrieved
373
- :csrf=>true # request will be validated for calid csrf token.
374
- # token must be in header X_CSRF_TOKEN field
375
-
376
- session configuration is inherited from superclass controllers unless overridden.
377
-
378
- ## Cache
379
-
380
-
381
- the server has a cache which can also be used for storing your own data.
382
-
383
- within a controller access the controller with `server_cache` which returns the
384
- cache object.
425
+ Caching
426
+ -------
385
427
 
386
- cache object methods:
428
+ Blix::Rest provides a caching mechanism:
387
429
 
388
- get(key) # return value from the cache or nil
389
- set(key,value) # set a value in the cache
390
- key?(key) # is a key present in the cache
391
- delete(key) # delete a key from the cache
392
- clear # delete all keys from the cache.
430
+ ```ruby
431
+ value = server_cache_get('my_key') { expensive_operation() }
432
+ ```
393
433
 
394
- there is also a `server_cache_get` method.
434
+ To cache responses automatically, add `:cache => true` to your route
435
+ options.
395
436
 
396
- server_cache_get(key){ action }
437
+ CORS (Cross-Origin Resource Sharing)
438
+ ------------------------------------
397
439
 
398
- get the value from the cache. If the key is missing in the cache then perform
399
- the action in the provided block and store the result in the cache.
440
+ To enable CORS for a route:
400
441
 
401
- the default cache is just a ruby hash in memory. Pass a custom cache to
402
- when creating a server with the `:cache` parameter.
403
-
404
- class MyCache < Blix::Rest::Cache
405
- def get(key)
406
- ..
407
- end
408
-
409
- def set(key,value)
410
- ..
411
- end
412
-
413
- def key?(key)
414
- ..
415
- end
416
-
417
- def delete(key)
418
- ..
419
- end
420
-
421
- def clear
422
- ..
423
- end
424
- end
425
-
426
- cache = MyCache.new
427
-
428
- app = Blix::Rest::Server.new(:cache=>cache)
429
-
430
- there is a redis cache already defined:
431
-
432
- require 'blix/rest/redis_cache'
442
+ ```ruby
443
+ get '/api/data' do
444
+ set_accept_cors
445
+ { data: 'Some data' }
446
+ end
433
447
 
434
- cache = Blix::Rest::RedisCache.new(:expire_secs=>60*60*24) # expire after 1 day
435
- 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
+ ```
436
456
 
457
+ Rate Limiting
458
+ -------------
437
459
 
438
- ### automatically cache server responses
460
+ Blix::Rest provides a rate limiting mechanism, only allow so many exceptions in a given time:
439
461
 
440
- add `:cache=>true` to your route options in order to cache this route.
462
+ the delay times are applied to:
441
463
 
442
- add `:cache_reset=>true` to your route options if the cache should be cleared when
443
- calling this route.
464
+ * 3x failure
465
+ * 10x failure
466
+ * 100x failure
444
467
 
445
- 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
+ ```
446
475
 
447
- ### 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.
448
478
 
449
- 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:
450
481
 
451
- ## 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)
452
485
 
453
- cross origin site requests
454
486
 
455
- MyController < Blix::Rest::Controller
456
487
 
457
- get '/info/crosssite' do
458
- set_accept_cors
459
- {:data=>'foo'}
460
- end
488
+ Views
489
+ -----
461
490
 
462
- options '/info/' crosssite' do
463
- set_accept_cors, :headers=>'Content-Type',
464
- end
491
+ To render views, use the `render_erb` method:
465
492
 
493
+ ```ruby
494
+ get '/users' do
495
+ @users = User.all
496
+ render_erb('users/index', layout: 'layouts/main')
466
497
  end
467
-
468
- within an `options` response the following parameters can be passes.
469
-
470
- :origin=>'www.othersite.com' # specify specific allowed origin.
471
- :methods => [:get] # allowed methods
472
- :max_age => 86400 # maximum age this auth is valid
473
- :headers => ['Content-Type','X-OTHER'] # allow additional headers
474
- :credentials => true # allow requests with credentials
475
-
476
- ## Views
498
+ ```
477
499
 
478
500
  the location of your views defaults to `app/views` otherwise set it manually with:
479
501
 
480
502
  globally eg:
481
-
503
+ ```ruby
482
504
  Blix::Rest.set_erb_root ::File.expand_path('../lib/myapp/views', __FILE__)
483
-
505
+ ```
484
506
  or per controller eg:
485
-
507
+ ```ruby
486
508
  class MyController < Blix::Rest::Controller
487
509
 
488
510
  erb_dir ::File.expand_path('../..', __FILE__)
489
511
 
490
512
  end
491
-
513
+ ```
492
514
  then within a controller render your view with.
493
-
515
+ ```ruby
494
516
  render_erb( "users/index", :layout=>'layouts/main', :locals=>{:name=>"charles"})
495
-
496
- ( locals work from ruby 2.1 )
517
+ ```
518
+ ( locals work from ruby 2.1 )
497
519
 
498
520
 
499
521
  #### directory structure
@@ -508,152 +530,116 @@ then within a controller render your view with.
508
530
  main.html.erb
509
531
 
510
532
 
511
- ## Logging
512
-
513
- Blix::Rest.logger = Logger.new('/var/log/myapp.log')
514
-
515
-
516
- ## Testing a Service with cucumber
517
-
518
-
519
- in features/support/setup.rb
520
-
521
- require 'blix/rest/cucumber'
522
-
523
- and setup your database connections etc
524
-
525
-
526
- in features/support/hooks.rb
527
-
528
- reset your database
529
-
530
-
533
+ Logging
534
+ -------
531
535
 
532
- now you can use the following in scenarios ........
536
+ Configure logging:
533
537
 
534
- Given user guest gets "/info"
535
-
536
- Given the following users exist:
537
- | name | level |
538
- | anon | guest |
539
- | bob | user |
540
- | mary | provider |
541
- | paul | user |
542
- | admin | admin |
543
-
544
- Given user mary posts "/worlds" with {"name":"narnia"} [..or gets/puts/deletes]
545
- Then store the "id" as "world_id"
546
-
547
- Given user bob posts "/worlds/:world_id" with {"the_world_id"::world_id }
548
-
549
- Then the status should be 200
550
- Then the data type should be "r_type"
551
- Then the data length should be 3
552
- Then there should be an error
553
- Then the error message should include "unique"
554
- Then the data "name" should == "bob"
555
- Then the data should include "name"
556
-
557
- And explain
558
-
559
-
560
- NOTE : if you need to set up your database with users then you can use the following hook ..
561
-
562
- in features/support/world.rb .........
563
-
564
-
565
- require 'blix/rest/cucumber'
566
-
567
- class RestWorld
568
-
569
- # add a hook to create the user in the database -
570
- #
571
- def before_user_create(user,hash)
572
- name = hash["name"]
573
- u = MyUser.new
574
- u.set(:user_wid, name)
575
- u.set(:name,name)
576
- u.set(:is_super,true) if hash["level"] == "super"
577
- u.save
578
- store["myuser_#{name}_id"] = u.id.to_s
579
- end
580
- end
581
-
582
- now you can also use eg `:myuser_foo_id` within a request path/json.
583
-
584
-
585
-
586
- ## Manage Assets
587
-
588
-
589
- require 'blix/assets'
590
-
591
-
592
- The asset manager stores a hash of the asset data and the current unique file suffix for each asset in its own file.
593
- This config file is stored in a config directory. The default is 'config/assets' but another location can be specified.
594
-
595
-
596
- Blix::AssetManager.config_dir = "myassets/config/location" # defaults to `"config/assets"`
597
-
598
-
599
- ### Compile your assets
600
-
601
- require 'blix/assets'
602
-
603
- ......
604
- ......
605
- ASSETS = ['admin.js', 'admin.css', 'standard.js']
606
- ASSETS.each do |name|
607
-
608
- compiled_asset = environment[name].to_s
609
-
610
- Blix::AssetManager.if_modified(name,compiled_asset,:rewrite=>true) do |a|
611
-
612
- filename = File.join(ROOT,"public","assets",a.newname)
613
- puts "writing #{name} to #{filename}"
614
- File.write filename,compiled_asset
615
-
616
- File.unlink File.join(ROOT,"public","assets",a.oldname) if a.oldname
617
- end
618
-
619
- end
620
-
621
-
622
- ### In your erb view
538
+ ```ruby
539
+ Blix::Rest.logger = Logger.new('/var/log/myapp.log')
540
+ ```
623
541
 
542
+ and log a message:
624
543
 
625
- eg:
544
+ ```ruby
545
+ Blix::Rest.logger.info 'my message'
546
+ ```
626
547
 
627
- require 'blix/assets'
548
+ Testing with Cucumber
549
+ ---------------------
628
550
 
629
- ........
551
+ For testing with Cucumber, install the `blix-rest-cucumber` gem and
552
+ follow the setup instructions in the README.
630
553
 
631
- <script src="<%= asset_path('assets/standard.js') %>" type="text/javascript"></script>
554
+ Asset Management
555
+ ----------------
632
556
 
633
- 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.
634
560
 
635
- <%= asset_tag('/assets/standard.js') %>
561
+ To use asset management with your Blix::Rest application:
636
562
 
563
+ 1\. Install the `blix-assets` gem:
637
564
 
638
- ### or in your controller
565
+ ```
566
+ gem install blix-assets
567
+ ```
639
568
 
640
- eg:
569
+ 2\. Require the gem in your application:
641
570
 
571
+ ```ruby
642
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')
643
644
 
644
- ........
645
-
646
- path = asset_path('assets/standard.js')
647
-
648
-
649
-
650
- #### NOTE ON ASSETS!!
651
-
652
- In production mode the compiled version of the assets will be used which will have a unique file name.
653
-
654
- In production the expiry date of your assets can be set to far in the future to take advantage of cacheing.
655
-
656
- In development or test mode the standard name will be used which then will make use of your asset pipeline ( eg sprockets )
657
645
 
658
- Asset names can contain only one extension. if there are more extensions eg: 'myfile.extra.css' then only the last
659
- extension will be used: in this case the name will be simplified to 'myfile.css' !!!