fit_api 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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: []