fit_api 1.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 47c9ea656a0413aed2e76fdbdd586c4df827a71fde3346eb1829526cf2d5fe72
4
+ data.tar.gz: 3105d64111f3d3bdb9fb1fc7882afe62498a0e06b40a363d7380e9a5713dd1d4
5
+ SHA512:
6
+ metadata.gz: f02633bd1e2ce741ab55dd3e959827de5323458eeb3614be57759578537a2b64a52f43f1b51be4f55acf5566a138d89aa3307939cb421611a1e6039a4f120995
7
+ data.tar.gz: 4362fa551cf76e494f5408912da387c0553bbe469d574cd85123c56e022f25ab5539c48348870391e32d4b4f764488cd25673158eb83434e2759c0d09bbf5fc3
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ .byebug_history
2
+ *.swp
3
+ Gemfile.lock
4
+ /log
5
+ /doc
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+ ruby '2.5.3'
3
+
4
+ gem 'rack', git: 'https://github.com/rack/rack.git'
5
+ gem 'rack-test', '~> 1.1.0'
6
+ gem 'dry-inflector', '~> 0.1.2'
data/LICENSE.md ADDED
@@ -0,0 +1,22 @@
1
+ Copyright © 2014-2017 Luca Guidi
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,309 @@
1
+ # fit-api
2
+
3
+ Lightweight framework for building JSON API's
4
+
5
+ ## Introduction
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.
9
+
10
+ ## Installation
11
+
12
+ Install the latest version from RubyGems
13
+
14
+ ```
15
+ $ gem install fit_api
16
+ ```
17
+
18
+ ## Table of Contents
19
+
20
+ * [fit-api](#fit-api)
21
+ * [Table of Contents](#table-of-contents)
22
+ * [Usage](#usage)
23
+ * [Router](#router)
24
+ * [Resource/s](#resources)
25
+ * [Namespace](#namespace)
26
+ * [Controller](#controller)
27
+ * [Root](#root)
28
+ * [404](#customize-error-404-message)
29
+ * [Controllers](#controllers)
30
+ * [Request](#request)
31
+ * [Params](#params)
32
+ * [Headers](#request-headers)
33
+ * [Callbacks](#callbacks)
34
+ * [Rack Middlewares](#rack-middlewares)
35
+
36
+
37
+ ## Usage
38
+
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)
41
+
42
+ **my_app.rb**
43
+
44
+ ```ruby
45
+ require 'fit_api'
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
+ FitApi::Router.define do
57
+ get '/:name', to: 'app#show'
58
+
59
+ root to: 'app#index'
60
+ end
61
+ ```
62
+
63
+ **app_controller.rb**
64
+
65
+ ```ruby
66
+ class AppController < FitApi::Controller
67
+ def index
68
+ json({ message: 'Hello world' })
69
+ end
70
+
71
+ def show
72
+ json({ message: "Welcome #{params.name}" })
73
+ end
74
+ end
75
+ ```
76
+
77
+ ```bash
78
+ ruby my_app.rb
79
+ ```
80
+
81
+ ## Router
82
+
83
+ It recognizes URLs and invoke the controller's action... the DSL is pretty similar to Rails (obviously not to so powerful):
84
+
85
+ ### HTTP methods:
86
+
87
+ ```ruby
88
+ get '/test/:name', to: 'app#test_show'
89
+ post '/test', to: 'app#test_post'
90
+ put '/test', to: 'app#test_put'
91
+ delete '/test/:id', to: 'app#test_delete'
92
+ ```
93
+
94
+ ### Resources
95
+
96
+ **Nested:**
97
+
98
+ ```ruby
99
+ resources :users do
100
+ resource :avatar do
101
+ get :comments
102
+ post :add_comment
103
+ end
104
+ end
105
+ ```
106
+
107
+ **Endpoints for users:**
108
+
109
+ | Method | Path | Controller & action |
110
+ |-------------|--------------------|-----------------------|
111
+ | **GET** | /users | users#index |
112
+ | **GET** | /users/:id | users#show |
113
+ | **POST** | /users | users#create |
114
+ | **PATCH** | /users/:id | users#update |
115
+ | **DELETE** | /users/:id | users#destroy |
116
+
117
+ **Endpoints for avatar:**
118
+
119
+ | Method | Path | Controller & action |
120
+ |-------------|------------------------------------|-----------------------|
121
+ | **GET** | /users/:user_id/avatar | avatar#show |
122
+ | **POST** | /users/:user_id/avatar | avatar#create |
123
+ | **PATCH** | /users/:user_id/avatar | avatar#update |
124
+ | **DELETE** | /users/:user_id/avatar | avatar#destroy |
125
+ | **GET** | /users/:user_id/avatar/comments | avatar#comments |
126
+ | **POST** | /users/:user_id/avatar/add_comment | avatar#add_comment |
127
+
128
+ -----
129
+
130
+ **Member & Collection:**
131
+
132
+ ```ruby
133
+ resources :contacts, only: %i(index) do
134
+ member do
135
+ post :add_activity
136
+ end
137
+
138
+ collection do
139
+ get :search
140
+ end
141
+ end
142
+ ```
143
+
144
+ | Method | Path | Controller & action |
145
+ |-------------|----------------------------|-----------------------|
146
+ | **GET** | /contacts | contacts#index |
147
+ | **GET** | /contacts/search | contacts#search |
148
+ | **POST** | /contacts/:id/add_activity | contacts#add_activity |
149
+
150
+ -----
151
+
152
+ ### Namespace
153
+
154
+ Only for paths
155
+
156
+ ```ruby
157
+ namespace :test do
158
+ get :hello_world
159
+ post :hello_world, action: :post_hello_world
160
+ end
161
+ ```
162
+
163
+ | Method | Path | Controller & action |
164
+ |------------|--------------------|-----------------------|
165
+ | **GET** | /test/hello_world | test#hello_world |
166
+ | **POST** | /test/hello_world | test#post_hello_world |
167
+
168
+ -----
169
+
170
+ ```ruby
171
+ namespace '/hello/world', controller: :test do
172
+ get :test
173
+ end
174
+ ```
175
+
176
+ | Method | Path | Controller & action |
177
+ |----------|-------------------|-----------------------|
178
+ | **GET** | /test/world/test | test#test |
179
+
180
+ -----
181
+
182
+ ### Controller
183
+
184
+ ```ruby
185
+ controller :app do
186
+ get :another_action
187
+ end
188
+ ```
189
+
190
+ | Method | Path | Controller & action |
191
+ |------------|-------------------|-----------------------|
192
+ | **GET** | /another_action | app#another_action |
193
+
194
+ -----
195
+
196
+ ### Root
197
+
198
+ ```ruby
199
+ root to: 'app#index'
200
+ ```
201
+
202
+ | Method | Path | Controller & action |
203
+ |----------|--------|-----------------------|
204
+ | **GET** | / | app#index |
205
+
206
+ -----
207
+
208
+ ### Customize error 404 message
209
+
210
+ ```ruby
211
+ not_found 'app#error_404'
212
+ ```
213
+
214
+ ## Controllers
215
+
216
+ The library provides one father class `FitApi::Controller` that should be inherited from your controllers.
217
+ One limitation is the class name of your controller must end with "Controller", i.e: AppController, UsersController...
218
+
219
+ ```ruby
220
+ class AppController < FitApi::Controller
221
+ def index
222
+ json 'hello world'
223
+ end
224
+
225
+ def process_post
226
+ json params
227
+ end
228
+ end
229
+ ```
230
+
231
+ You have the method `#json` available, basically, it sets the Response body.
232
+
233
+ ### Request
234
+
235
+ You can access the Request object like this: `request`
236
+
237
+ ### Params
238
+
239
+ Assuming the following requests:
240
+
241
+ **GET /users/:id?name=Berna&age=28&height=180**
242
+
243
+ ```ruby
244
+ params.id # 1
245
+ params.name # "Berna"
246
+ params[:age] # 28
247
+ params['height'] # 180
248
+ ```
249
+
250
+ **POST /test**
251
+
252
+ With Params:
253
+
254
+ ```bash
255
+ curl -i -X POST -d 'user[name]=Berna&user[age]=28' http://server:1337/test
256
+ ```
257
+
258
+ With JSON:
259
+
260
+ ```bash
261
+ curl -i -X POST -H "Content-Type: application/json" -d '{ "user": { "name": "Berna", "age": 28 } }' http://server:1337/test
262
+ ```
263
+
264
+ Then we have the following data in our `params` object:
265
+
266
+ ```ruby
267
+ params.user # > Params
268
+ params.user.name # "Berna"
269
+ params[:user][:age] # 28
270
+ ```
271
+
272
+ ### Request Headers
273
+
274
+ ```ruby
275
+ request.headers['Authorization']
276
+ ```
277
+
278
+ ### Response Headers
279
+
280
+ ```ruby
281
+ headers['Header-Key'] = 'Header Value'
282
+ ```
283
+
284
+ ### Callbacks
285
+
286
+ ```ruby
287
+ before_action *actions
288
+ after_action *actions, only: %i(index show)
289
+ ```
290
+
291
+ ## Rack Middlewares
292
+
293
+ You can set up any rack middleware you want, i.e:
294
+
295
+ **config.ru**
296
+
297
+ ```ruby
298
+ require 'fit_api'
299
+
300
+ use Rack::CommonLogger, Logger.new('log/app.log')
301
+
302
+ run FitApi::App.new
303
+ ```
304
+
305
+ ## TODO:
306
+ - [ ] Implement tests
307
+ - [ ] Allow websockets -> `FitApi::Controller#stream`
308
+
309
+ Any contribution would be appreciated =)
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/fit_api.gemspec ADDED
@@ -0,0 +1,17 @@
1
+ require File.expand_path("../lib/fit_api/version.rb", __FILE__)
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = 'fit_api'
5
+ s.summary = 'Lightweight framework for building APIs'
6
+ s.description = 'fit-api is a Rack based framework for building JSON APIs'
7
+ s.author = 'Bernardo Castro'
8
+ s.email = 'bernacas@gmail.com'
9
+ s.version = FitApi.version
10
+ s.date = Time.now.strftime("%Y-%m-%d")
11
+ s.license = 'MIT'
12
+ s.files = `git ls-files`.split("\n")
13
+ s.test_files = `git ls-files -- spec/*`.split("\n")
14
+ s.required_ruby_version = '>= 2.2.0'
15
+ s.add_dependency 'rack', '~> 2.0'
16
+ s.add_dependency 'dry-inflector', '~> 0.1'
17
+ end
data/lib/fit_api.rb ADDED
@@ -0,0 +1,12 @@
1
+ require 'rack'
2
+ require 'json/ext'
3
+ require 'dry/inflector'
4
+
5
+ require 'fit_api/app'
6
+ require 'fit_api/controller'
7
+
8
+ module FitApi
9
+ def self.inflector
10
+ @inflector ||= Dry::Inflector.new
11
+ end
12
+ end
@@ -0,0 +1,9 @@
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
@@ -0,0 +1,48 @@
1
+ module FitApi
2
+ class Controller
3
+ attr_accessor :response
4
+ attr_reader :request, :params, :headers
5
+
6
+ def initialize(request, params)
7
+ @request = request
8
+ @params = params
9
+ @headers = {}
10
+ end
11
+
12
+ class << self
13
+ def actions
14
+ @actions ||= Hash.new { |h,k| h[k] = [] }
15
+ end
16
+
17
+ %i(before after).each do |callback_type|
18
+ define_method "#{callback_type}_action" do |*methods|
19
+ only = methods.last.is_a?(Hash) ? methods.last[:only] : nil
20
+ methods.each do |method|
21
+ unless method.is_a?(Hash)
22
+ actions[callback_type] << { method: method, only: only }
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ def set_response_headers
30
+ response.add_header 'Content-Type', 'application/json'
31
+ response.add_header 'Date', Rack::Utils.rfc2822(Time.now)
32
+
33
+ headers.each &response.method(:add_header)
34
+ end
35
+
36
+ private
37
+
38
+ def json(hash, status: 200)
39
+ self.response =
40
+ Rack::Response.new(hash.to_json, status)
41
+ end
42
+
43
+ def halt(status = 400, error)
44
+ json(error, status: status)
45
+ raise Halt
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,36 @@
1
+ require 'fit_api/router/mapper'
2
+
3
+ module FitApi
4
+ module Router
5
+ def self.call(env)
6
+ method, path = env['REQUEST_METHOD'], env['PATH_INFO']
7
+ route = find method, path, path != '/'
8
+
9
+ return route.invoke(env) if route
10
+
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 ] ]
14
+ end
15
+
16
+ def self.find(method, path, find_error = true)
17
+ routes = mapper.routes[method.downcase]
18
+ route = routes.find { |route| route.match? path }
19
+
20
+ return route if route
21
+
22
+ if find_error
23
+ not_found = find('get', '/404', false)
24
+ return not_found if not_found
25
+ end
26
+ end
27
+
28
+ def self.define(&block)
29
+ mapper.instance_eval &block
30
+ end
31
+
32
+ def self.mapper
33
+ @mapper ||= Mapper.new
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,125 @@
1
+ require 'fit_api/router/route'
2
+
3
+ module FitApi
4
+ module Router
5
+ class Mapper
6
+ attr_reader :routes
7
+
8
+ def initialize
9
+ @routes = Hash.new { |h,k| h[k] = [] }
10
+ @namespaces = []
11
+ end
12
+
13
+ %w(get post put delete patch).each do |verb|
14
+ define_method verb do |path, options = {}|
15
+ options[:controller] ||= @controller
16
+ route = Route.new(verb, "#{@namespaces.join}#{fix_path(path)}", options)
17
+ @routes[verb] << route
18
+ end
19
+ end
20
+
21
+ %i(resource resources).each do |resource_type|
22
+ define_method resource_type do |resource, options = {}, &block|
23
+ set_resource resource_type, resource, options, &block
24
+ end
25
+ end
26
+
27
+ def root(to:)
28
+ get '', to: to
29
+ end
30
+
31
+ def not_found(to)
32
+ get '/404', to: to
33
+ end
34
+
35
+ def member(&block)
36
+ namespace '/:id', &block
37
+ end
38
+
39
+ def collection(&block)
40
+ instance_eval &block
41
+ end
42
+
43
+ def namespace(path, options = {}, &block)
44
+ @namespaces << fix_path(path)
45
+ if controller = options[:controller] || path
46
+ previous, @controller = @controller, controller
47
+ end
48
+ instance_eval &block
49
+ @controller = previous if controller
50
+ @namespaces.pop
51
+ end
52
+
53
+ def controller(controller, &block)
54
+ @controller = controller
55
+ instance_eval &block
56
+ @controller = nil
57
+ end
58
+
59
+ private
60
+
61
+ def set_resource(type, resource, options, &block)
62
+ options[:only] ||= %i(index show create update destroy)
63
+ path = get_path(type, resource)
64
+
65
+ @parent = [ type, resource ]
66
+ @controller = options[:controller] || resource
67
+
68
+ namespace path, options do
69
+ set_actions type, resource, options[:only]
70
+ instance_eval &block if block
71
+ end
72
+
73
+ @parent, @controller = nil, nil
74
+ end
75
+
76
+ def set_actions(type, resource, actions)
77
+ path = get_resource_path(type)
78
+
79
+ actions.delete(:index) if type == :resource
80
+
81
+ actions.each do |action|
82
+ case action
83
+ when :index
84
+ get '', to: "#{resource}#index"
85
+ when :create
86
+ post '', to: "#{resource}#create"
87
+ when :show
88
+ get path, to: "#{resource}#show"
89
+ when :update
90
+ patch path, to: "#{resource}#update"
91
+ when :destroy
92
+ delete path, to: "#{resource}#destroy"
93
+ end
94
+ end
95
+ end
96
+
97
+ def get_path(type, resource)
98
+ return "/:#{s(@parent.last)}_id/#{resource}" if type == :resources && parent_is?(:resources)
99
+ return "/:#{s(@parent.last)}_id/#{s(resource)}" if type == :resource && parent_is?(:resources)
100
+ return "/#{s(resource)}" if type == :resource && parent_is?(:resource)
101
+ return "/#{resource}"
102
+ end
103
+
104
+ def parent_is?(type)
105
+ @parent && @parent.first == type
106
+ end
107
+
108
+ def get_resource_path(type)
109
+ return type == :resources ? '/:id' : ''
110
+ end
111
+
112
+ def fix_path(path)
113
+ if path.is_a?(Symbol) || path[0] != '/' && path != ''
114
+ "/#{path}"
115
+ else
116
+ path
117
+ end
118
+ end
119
+
120
+ def s(word)
121
+ FitApi.inflector.singularize(word)
122
+ end
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,51 @@
1
+ module FitApi
2
+ module Router
3
+ class Params
4
+ def initialize(hash)
5
+ @hash = hash
6
+ end
7
+
8
+ def to_json
9
+ @hash.to_json
10
+ end
11
+
12
+ def [](key)
13
+ value = @hash[key.to_s]
14
+
15
+ if value.is_a?(Hash)
16
+ self.class.new(value)
17
+ else
18
+ value
19
+ end
20
+ end
21
+
22
+ def []=(key, value)
23
+ @hash[key.to_s] = value
24
+ end
25
+
26
+ def method_missing(method_sym, *arguments, &block)
27
+ if @hash.include? method_sym.to_s
28
+ send('[]', method_sym.to_s)
29
+ else
30
+ nil
31
+ end
32
+ end
33
+
34
+ def except(*blacklist)
35
+ Params.new(
36
+ {}.tap do |h|
37
+ (@hash.keys - blacklist.map(&:to_s)).each { |k| h[k] = @hash[k] }
38
+ end
39
+ )
40
+ end
41
+
42
+ def permit(*whitelist)
43
+ Params.new(
44
+ {}.tap do |h|
45
+ (@hash.keys & whitelist.map(&:to_s)).each { |k| h[k] = @hash[k] }
46
+ end
47
+ )
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,41 @@
1
+ module FitApi
2
+ module Router
3
+ class Parser
4
+ attr_reader :params
5
+
6
+ def initialize(route, path)
7
+ @route = route
8
+ @path = path.gsub(/\/$/, '')
9
+ @match = false
10
+ @params = {}
11
+
12
+ parse
13
+ end
14
+
15
+ def match?
16
+ @match
17
+ end
18
+
19
+ private
20
+
21
+ def parse
22
+ result = @path.scan(/^#{regexp}$/)
23
+ set_params(result.flatten) if result.any?
24
+ end
25
+
26
+ def set_params(result)
27
+ @match = true
28
+ params = @route.scan(/\/\:+(\w+)/).flatten
29
+
30
+ params.each_index do |i|
31
+ @params[params[i]] =
32
+ result[i].match(/^\d+$/) ? result[i].to_i : URI.decode(result[i])
33
+ end
34
+ end
35
+
36
+ def regexp
37
+ @route.gsub(/\:\w+/, '([^\/]*)')
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,74 @@
1
+ require 'fit_api/router/parser'
2
+ require 'fit_api/router/params'
3
+
4
+ module FitApi
5
+ module Router
6
+ class Route
7
+ attr_reader :verb, :path, :controller, :action
8
+
9
+ def initialize(verb, path, options = {})
10
+ @verb = verb
11
+ @path = path
12
+ @controller = get_controller(options)
13
+ @action = get_action(options)
14
+ end
15
+
16
+ def invoke(env)
17
+ request = Request.new(env)
18
+ route_params = parse(request.path).params
19
+ json_params = JSON.parse(request.body.read) rescue {}
20
+ params = Params.new(request.params.merge(route_params).merge(json_params))
21
+ controller = Object.const_get("#{@controller.capitalize}Controller").new(request, params)
22
+
23
+ run! controller
24
+ rescue Halt
25
+ controller.response
26
+ end
27
+
28
+ def match?(path)
29
+ parse(path).match?
30
+ end
31
+
32
+ private
33
+
34
+ def get_controller(options)
35
+ return options[:controller] if options[:controller]
36
+ options[:to].split('#').first
37
+ end
38
+
39
+ def get_action(options)
40
+ return options[:action] if options[:action]
41
+ return options[:to].split('#').last if options[:to]
42
+ path[/\w+$/]
43
+ end
44
+
45
+ def parse(path)
46
+ Parser.new(@path, path)
47
+ end
48
+
49
+ def run!(controller)
50
+ run_callbacks! controller, :before
51
+ controller.send action
52
+ run_callbacks! controller, :after
53
+ controller.response
54
+ ensure
55
+ controller.set_response_headers
56
+ end
57
+
58
+ def run_callbacks!(controller, type)
59
+ controller.class.actions[type].each do |action|
60
+ if action[:only].nil? || action[:only].include?(@action.to_sym)
61
+ controller.send action[:method]
62
+ end
63
+ end
64
+ end
65
+ end
66
+
67
+ class Request < Rack::Request
68
+ def headers
69
+ env.select { |k,v| k.start_with? 'HTTP_'}.
70
+ transform_keys { |k| k.sub(/^HTTP_/, '').split('_').map(&:capitalize).join('-') }
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,3 @@
1
+ module FitApi
2
+ def self.version; '1.0.0' end
3
+ end
metadata ADDED
@@ -0,0 +1,86 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fit_api
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Bernardo Castro
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-07-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rack
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: dry-inflector
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.1'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.1'
41
+ description: fit-api is a Rack based framework for building JSON APIs
42
+ email: bernacas@gmail.com
43
+ executables: []
44
+ extensions: []
45
+ extra_rdoc_files: []
46
+ files:
47
+ - ".gitignore"
48
+ - Gemfile
49
+ - LICENSE.md
50
+ - README.md
51
+ - Rakefile
52
+ - fit_api.gemspec
53
+ - lib/fit_api.rb
54
+ - lib/fit_api/app.rb
55
+ - lib/fit_api/controller.rb
56
+ - lib/fit_api/router.rb
57
+ - lib/fit_api/router/mapper.rb
58
+ - lib/fit_api/router/params.rb
59
+ - lib/fit_api/router/parser.rb
60
+ - lib/fit_api/router/route.rb
61
+ - lib/fit_api/version.rb
62
+ homepage:
63
+ licenses:
64
+ - MIT
65
+ metadata: {}
66
+ post_install_message:
67
+ rdoc_options: []
68
+ require_paths:
69
+ - lib
70
+ required_ruby_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: 2.2.0
75
+ required_rubygems_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: '0'
80
+ requirements: []
81
+ rubyforge_project:
82
+ rubygems_version: 2.7.6
83
+ signing_key:
84
+ specification_version: 4
85
+ summary: Lightweight framework for building APIs
86
+ test_files: []