fit_api 1.0.1 → 1.1.0

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 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