fit_api 1.0.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4889af2c91cf113665016ffdc06e0cb664cb454cc8d66472db8eccef0ebb6ba9
4
- data.tar.gz: d9a95a850cc8dbbee1d1b69a4a1cecf0b169e9340fa45b13e7c498850117afc3
3
+ metadata.gz: 9625dc724aa00c2091520f8a00037e2a0deb289ff46a0531c21c45ad0d70a44e
4
+ data.tar.gz: 2270242c3c7afadaa86162c580c8b1d042e5a64a5a65807205c15b5b78e2ba67
5
5
  SHA512:
6
- metadata.gz: 28377cd6b2aa1ccc62b06a62bc60f246d2b675baa1924f934e4bcd8b2704026fa880e9068f0701655a5d3f4103141b81f88e524949bc9b2485a7102ecc60332b
7
- data.tar.gz: df8cbabaa19b0171b4621db6a5dc7dd73f2fcc19814d0d9689517d841c05243701bebf57d26f1cfc3905723b2e8f2315b32717522ddad1979c36c0ac04ed7965
6
+ metadata.gz: 3985a5d07c254aef0abd05e8e67d91d49fab2eae6c11d83a5636eca8e610f42d5a2b6b6868ab6b4bdb5f11f7b2e702c49a00509a1ee8f562790d98221752ac8b
7
+ data.tar.gz: 1a2c24f461c00ae5b78a3bef8c47fa3ded025ed567088f1c16f55ba179277e03078642bfc3ca3afc5287ba9a0285909ea2f774d93bc589db67c5b86bc2f56aca
data/.gitignore CHANGED
@@ -1,5 +1,6 @@
1
1
  .byebug_history
2
2
  *.swp
3
3
  Gemfile.lock
4
+ /pkg
4
5
  /log
5
6
  /doc
data/README.md CHANGED
@@ -4,8 +4,8 @@ Lightweight framework for building JSON API's
4
4
 
5
5
  ## Introduction
6
6
 
7
- fit-api is a 400 line dependency library based on Rack and inspired by Rails & Sinatra.
8
- The goal of this library is to provide simplicity when developing an API with ruby.
7
+ fit-api is a library based on Rack and inspired by Rails & Sinatra.
8
+ The goal of this gem is to provide simplicity when developing an API with ruby.
9
9
 
10
10
  ## Installation
11
11
 
@@ -28,41 +28,28 @@ $ gem install fit_api
28
28
  * [404](#customize-error-404-message)
29
29
  * [Controllers](#controllers)
30
30
  * [Request](#request)
31
+ * [Halt](#halt)
31
32
  * [Params](#params)
32
33
  * [Headers](#request-headers)
33
34
  * [Callbacks](#callbacks)
34
- * [Rack Middlewares](#rack-middlewares)
35
35
 
36
36
 
37
37
  ## Usage
38
38
 
39
39
  This is a basic example showing how it works... you can check the demo app from this repository:
40
- [fit-api-demo](/bermanya/fit-api-demo)
40
+ [fit-api-demo](http://github.com/bermanya/fit-api-demo)
41
41
 
42
- **my_app.rb**
42
+ **api.rb**
43
43
 
44
44
  ```ruby
45
45
  require 'fit_api'
46
46
 
47
- require_relative 'routes'
48
- require_relative 'app_controller'
49
-
50
- Rack::Handler::WEBrick.run FitApi::App.new
51
- ```
52
-
53
- **routes.rb**
54
-
55
- ```ruby
56
47
  FitApi::Router.define do
57
48
  get '/:name', to: 'app#show'
58
49
 
59
50
  root to: 'app#index'
60
51
  end
61
- ```
62
52
 
63
- **app_controller.rb**
64
-
65
- ```ruby
66
53
  class AppController < FitApi::Controller
67
54
  def index
68
55
  json({ message: 'Hello world' })
@@ -72,15 +59,21 @@ class AppController < FitApi::Controller
72
59
  json({ message: "Welcome #{params.name}" })
73
60
  end
74
61
  end
62
+
63
+ # You can setup any Rack Middleware
64
+
65
+ FitApi.use Rack::CommonLogger, Logger.new('log/app.log')
66
+
67
+ Rack::Handler::WEBrick.run FitApi.app
75
68
  ```
76
69
 
77
70
  ```bash
78
- ruby my_app.rb
71
+ ruby api.rb
79
72
  ```
80
73
 
81
74
  ## Router
82
75
 
83
- It recognizes URLs and invoke the controller's action... the DSL is pretty similar to Rails (obviously not to so powerful):
76
+ It recognizes URLs and invoke the controller's action... the DSL is pretty similar to Rails (obviously not so powerful):
84
77
 
85
78
  ### HTTP methods:
86
79
 
@@ -91,6 +84,8 @@ put '/test', to: 'app#test_put'
91
84
  delete '/test/:id', to: 'app#test_delete'
92
85
  ```
93
86
 
87
+ ----
88
+
94
89
  ### Resources
95
90
 
96
91
  **Nested:**
@@ -208,12 +203,12 @@ root to: 'app#index'
208
203
  ### Customize error 404 message
209
204
 
210
205
  ```ruby
211
- not_found 'app#error_404'
206
+ not_found to: 'app#error_404'
212
207
  ```
213
208
 
214
209
  ## Controllers
215
210
 
216
- The library provides one father class `FitApi::Controller` that should be inherited from your controllers.
211
+ The library provides one class `FitApi::Controller` which should be inherited from your controllers.
217
212
  One limitation is the class name of your controller must end with "Controller", i.e: AppController, UsersController...
218
213
 
219
214
  ```ruby
@@ -228,82 +223,108 @@ class AppController < FitApi::Controller
228
223
  end
229
224
  ```
230
225
 
231
- You have the method `#json` available, basically, it sets the Response body.
226
+ You have the method `#json` available, which basically sets the response body.
227
+
228
+ ----
232
229
 
233
230
  ### Request
234
231
 
235
- You can access the Request object like this: `request`
232
+ You can access the Request object like this:
236
233
 
237
- ### Params
234
+ `request`
238
235
 
239
- Assuming the following requests:
236
+ ----
240
237
 
241
- **GET /users/:id?name=Berna&age=28&height=180**
238
+ ### Halt
239
+
240
+ You can exit the current action throwing an exception... the default status code is 400
242
241
 
243
242
  ```ruby
244
- params.id # 1
245
- params.name # "Berna"
246
- params[:age] # 28
247
- params['height'] # 180
243
+ halt
244
+ halt 500
245
+ halt 404, 'Not found'
246
+ halt 'Error message'
248
247
  ```
249
248
 
250
- **POST /test**
249
+ ----
250
+
251
+ ### Params
251
252
 
252
- With Params:
253
+ #### GET /users
253
254
 
254
255
  ```bash
255
- curl -i -X POST -d 'user[name]=Berna&user[age]=28' http://server:1337/test
256
+ curl -i http://localhost:1337/users/:id?name=Berna&age=28&height=180
256
257
  ```
257
258
 
258
- With JSON:
259
+ ```ruby
260
+ params.id # 1
261
+ params.name # "Berna"
262
+ params[:age] # 28
263
+ params['height'] # 180
264
+ ```
265
+
266
+ #### POST with params:
259
267
 
260
268
  ```bash
261
- curl -i -X POST -H "Content-Type: application/json" -d '{ "user": { "name": "Berna", "age": 28 } }' http://server:1337/test
269
+ curl -i -X POST -d 'user[name]=Berna&user[age]=28' http://localhost:1337/users
262
270
  ```
263
271
 
264
- Then we have the following data in our `params` object:
272
+ #### POST with JSON:
265
273
 
266
- ```ruby
267
- params.user # > Params
268
- params.user.name # "Berna"
269
- params[:user][:age] # 28
274
+ ```bash
275
+ curl -i -X POST -H "Content-Type: application/json" -d '{ "user": { "name": "Berna", "age": 28 } }' http://localhost:1337/users
270
276
  ```
271
277
 
272
- ### Request Headers
278
+ Result:
273
279
 
274
280
  ```ruby
275
- request.headers['Authorization']
281
+ params.user.name # "Berna"
282
+ params[:user][:age] # "28"
276
283
  ```
277
284
 
278
- ### Response Headers
285
+ ----
286
+
287
+ #### #permit
279
288
 
280
289
  ```ruby
281
- headers['Header-Key'] = 'Header Value'
290
+ params.user.permit(:name, :age)
282
291
  ```
283
292
 
284
- ### Callbacks
293
+ ----
294
+
295
+ #### #except
285
296
 
286
297
  ```ruby
287
- before_action *actions
288
- after_action *actions, only: %i(index show)
298
+ params.user.except(:height)
289
299
  ```
290
300
 
291
- ## Rack Middlewares
301
+ ----
302
+
303
+ ### Request Headers
304
+
305
+ ```ruby
306
+ request.headers['Authorization']
307
+ ```
292
308
 
293
- You can set up any rack middleware you want, i.e:
309
+ ----
294
310
 
295
- **config.ru**
311
+ ### Response Headers
296
312
 
297
313
  ```ruby
298
- require 'fit_api'
314
+ headers['Header-Name'] = 'Header Value'
315
+ ```
299
316
 
300
- use Rack::CommonLogger, Logger.new('log/app.log')
317
+ ----
301
318
 
302
- run FitApi::App.new
319
+ ### Callbacks
320
+
321
+ ```ruby
322
+ before_action *actions
323
+ after_action *actions, only: %i(index show)
303
324
  ```
304
325
 
305
326
  ## TODO:
306
327
  - [ ] Implement tests
307
328
  - [ ] Allow websockets -> `FitApi::Controller#stream`
308
329
 
309
- Any contribution would be appreciated =)
330
+ Any contribution would be appreciated =)
@@ -29,8 +29,8 @@ module FitApi
29
29
  end
30
30
 
31
31
  def set_response_headers
32
- response.add_header 'Content-Type', 'application/json'
33
- response.add_header 'Date', Rack::Utils.rfc2822(Time.now)
32
+ headers['Date'] = Rack::Utils.rfc2822(Time.now)
33
+ headers['Content-Type'] = 'application/json'
34
34
 
35
35
  headers.each &response.method(:add_header)
36
36
  end
@@ -38,11 +38,13 @@ module FitApi
38
38
  private
39
39
 
40
40
  def json(hash, status: 200)
41
- self.response =
42
- Rack::Response.new(hash.to_json, status)
41
+ self.response = Rack::Response.new(hash.to_json, status)
43
42
  end
44
43
 
45
- def halt(status = 400, error)
44
+ def halt(*args)
45
+ is_integer = args.first.is_a?(Integer)
46
+ status = is_integer ? args.first : 400
47
+ error = is_integer ? (args.count > 1 ? args.last : '') : args.first
46
48
  json(error, status: status)
47
49
  raise Halt
48
50
  end
@@ -28,7 +28,7 @@ module FitApi
28
28
  get '', to: to
29
29
  end
30
30
 
31
- def not_found(to)
31
+ def not_found(to:)
32
32
  get '/404', to: to
33
33
  end
34
34
 
@@ -60,10 +60,12 @@ module FitApi
60
60
 
61
61
  def set_resource(type, resource, options, &block)
62
62
  options[:only] ||= %i(index show create update destroy)
63
+ options[:controller] ||= resource
64
+
63
65
  path = get_path(type, resource)
64
66
 
65
67
  @parent = [ type, resource ]
66
- @controller = options[:controller] || resource
68
+ @controller = options[:controller]
67
69
 
68
70
  namespace path, options do
69
71
  set_actions type, resource, options[:only]
@@ -97,7 +99,7 @@ module FitApi
97
99
  def get_path(type, resource)
98
100
  return "/:#{s(@parent.last)}_id/#{resource}" if type == :resources && parent_is?(:resources)
99
101
  return "/:#{s(@parent.last)}_id/#{s(resource)}" if type == :resource && parent_is?(:resources)
100
- return "/#{s(resource)}" if type == :resource && parent_is?(:resource)
102
+ return "/#{s(resource)}" if type == :resource && parent_is?(:resource)
101
103
  return "/#{resource}"
102
104
  end
103
105
 
@@ -110,11 +112,9 @@ module FitApi
110
112
  end
111
113
 
112
114
  def fix_path(path)
113
- if path.is_a?(Symbol) || path[0] != '/' && path != ''
114
- "/#{path}"
115
- else
116
- path
117
- end
115
+ fix = path.is_a?(Symbol) || path[0] != '/' && path != ''
116
+ path = fix ? "/#{path}" : path
117
+ path.gsub(/\/$/, '')
118
118
  end
119
119
 
120
120
  def s(word)
@@ -18,7 +18,7 @@ module FitApi
18
18
  route_params = parse(request.path).params
19
19
  json_params = JSON.parse(request.body.read) rescue {}
20
20
  params = Params.new(request.params.merge(route_params).merge(json_params))
21
- controller = Object.const_get("#{@controller.capitalize}Controller").new(request, params)
21
+ controller = Object.const_get("#{@controller.to_s.capitalize}Controller").new(request, params)
22
22
 
23
23
  run! controller
24
24
  rescue Halt
@@ -4,22 +4,25 @@ module FitApi
4
4
  module Router
5
5
  def self.call(env)
6
6
  method, path = env['REQUEST_METHOD'], env['PATH_INFO']
7
- route = find method, path, path != '/'
7
+ is_root = path == '/'
8
8
 
9
- return route.invoke(env) if route
9
+ if route = Router.find(method, path, !is_root)
10
+ route.invoke(env)
11
+ else
12
+ status = is_root ? 200 : 404
13
+ res = is_root ? 'fit-api is working!' : 'Action not found'
10
14
 
11
- res = path == '/' ? { message: 'fit-api is working!' } : { error: 'action not found' }
12
-
13
- [ res[:error] ? 404 : 200, { 'Content-Type' => 'application/json'}, [ res.to_json ] ]
15
+ [ status, { 'Content-Type' => 'application/json'}, [ res.to_json ] ]
16
+ end
14
17
  end
15
18
 
16
- def self.find(method, path, find_error = true)
19
+ def self.find(method, path, find_not_found_action = true)
17
20
  routes = mapper.routes[method.downcase]
18
21
  route = routes.find { |route| route.match? path }
19
22
 
20
23
  return route if route
21
24
 
22
- if find_error
25
+ if find_not_found_action
23
26
  not_found = find('get', '/404', false)
24
27
  return not_found if not_found
25
28
  end
@@ -1,3 +1,5 @@
1
1
  module FitApi
2
- def self.version; '1.0.1' end
2
+ def self.version
3
+ '1.1.0'
4
+ end
3
5
  end
data/lib/fit_api.rb CHANGED
@@ -2,10 +2,24 @@ require 'rack'
2
2
  require 'json/ext'
3
3
  require 'dry/inflector'
4
4
 
5
- require 'fit_api/app'
5
+ require 'fit_api/version'
6
+ require 'fit_api/router'
6
7
  require 'fit_api/controller'
7
8
 
8
9
  module FitApi
10
+ def self.builder
11
+ @builder ||= Rack::Builder.new
12
+ end
13
+
14
+ def self.use(middleware, *args, &block)
15
+ builder.use(middleware, *args, &block)
16
+ end
17
+
18
+ def self.app
19
+ builder.run Router.method(:call)
20
+ builder.to_app
21
+ end
22
+
9
23
  def self.inflector
10
24
  @inflector ||= Dry::Inflector.new
11
25
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fit_api
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bernardo Castro
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-07-23 00:00:00.000000000 Z
11
+ date: 2019-07-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -51,7 +51,6 @@ files:
51
51
  - Rakefile
52
52
  - fit_api.gemspec
53
53
  - lib/fit_api.rb
54
- - lib/fit_api/app.rb
55
54
  - lib/fit_api/controller.rb
56
55
  - lib/fit_api/router.rb
57
56
  - lib/fit_api/router/mapper.rb
data/lib/fit_api/app.rb DELETED
@@ -1,9 +0,0 @@
1
- require 'fit_api/router'
2
-
3
- module FitApi
4
- class App
5
- def call(env)
6
- Router.call(env)
7
- end
8
- end
9
- end