sinatra-rest-api 0.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 +7 -0
- data/lib/sinatra-rest-api.rb +22 -0
- data/lib/sinatra-rest-api/actions.rb +90 -0
- data/lib/sinatra-rest-api/adapter.rb +168 -0
- data/lib/sinatra-rest-api/provider.rb +77 -0
- data/lib/sinatra-rest-api/router.rb +116 -0
- metadata +63 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 31a424922bc37c8f72631ef6c2991dd3cedb9da4
|
4
|
+
data.tar.gz: bf71346a4cc7b8ac29d1f3b898641d167efd9c52
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: dab82f312bd1357305af0d0f7a3e46f495ab55cc682862a8377d33063def1e6fa8d019887429ed7118218173a98c7eeb6b08913def902845c51370e7befa95db
|
7
|
+
data.tar.gz: 948816326d8837873e9c30f36992da5c7dc6ee7595759c921cd5a617fac40991db871ebdad17de2eee6bfaf55b2d16107c41c8f1660cdf15f8be636c40b9c1ab
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# sinatra-rest-api
|
2
|
+
$LOAD_PATH << File.join(File.dirname(__FILE__))
|
3
|
+
|
4
|
+
require 'sinatra/base'
|
5
|
+
|
6
|
+
require 'sinatra-rest-api/actions'
|
7
|
+
require 'sinatra-rest-api/adapter'
|
8
|
+
require 'sinatra-rest-api/provider'
|
9
|
+
require 'sinatra-rest-api/router'
|
10
|
+
|
11
|
+
# Sinatra namespace
|
12
|
+
module Sinatra
|
13
|
+
# RestApi module definition
|
14
|
+
module RestApi
|
15
|
+
def resource( klass, options = {}, &block )
|
16
|
+
Provider.new( klass, options, self, &block )
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# helpers RestApi
|
21
|
+
register RestApi
|
22
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module Sinatra
|
2
|
+
module RestApi
|
3
|
+
# Action
|
4
|
+
class Actions
|
5
|
+
DONE = { message: :ok }.freeze
|
6
|
+
# REF: https://en.wikipedia.org/wiki/Representational_state_transfer#Relationship_between_URL_and_HTTP_methods
|
7
|
+
SCHEMA = {
|
8
|
+
# Member actions
|
9
|
+
# list: { verb: :get, path: '/?:format?', fields: [ :id, :title, :author_id, :category_id ] },
|
10
|
+
read: { verb: :get, path: '/:id.?:format?' },
|
11
|
+
update: { verb: :put, path: '/:id.?:format?' },
|
12
|
+
delete: { verb: :delete, path: '/:id.?:format?' },
|
13
|
+
# Collection actions
|
14
|
+
list: { verb: :get, path: '/?.?:format?' },
|
15
|
+
replace: { verb: :put, path: '/?.?:format?' },
|
16
|
+
create: { verb: :post, path: '/?.?:format?' },
|
17
|
+
truncate: { verb: :delete, path: '/?.?:format?' },
|
18
|
+
# Member actions
|
19
|
+
# update: { verb: [ :post, :put ], path: '/:id.?:format?' },
|
20
|
+
}.freeze
|
21
|
+
|
22
|
+
def self.create( route_args, params, mapping )
|
23
|
+
unless route_args[:request].form_data?
|
24
|
+
route_args[:request].body.rewind
|
25
|
+
params.merge!( Provider.settings[:request_type].eql?( :json ) ? JSON.parse( route_args[:request].body.read ) : route_args[:request].body.read )
|
26
|
+
end
|
27
|
+
params[:_data] = {}
|
28
|
+
resource = route_args[:resource]
|
29
|
+
if !params[resource].nil? && params[resource].is_a?( Hash )
|
30
|
+
cols = mapping[:columns].call( nil ) + mapping[:relations].call( nil ).map { |rel| "#{rel}_attributes" } + mapping[:extra_fields].call( nil )
|
31
|
+
cols.delete( 'id' ) # TODO: option to set id field name
|
32
|
+
params[:_data] = params[resource].select { |key, _valye| cols.include? key }
|
33
|
+
# params[:_data] = params[resource]
|
34
|
+
params.delete( resource )
|
35
|
+
end
|
36
|
+
# params[:_data]['created_at'] = Time.now.strftime( '%F %T' ).to_s
|
37
|
+
# params[:_data]['updated_at'] = Time.now.strftime( '%F %T' ).to_s
|
38
|
+
result = mapping[:create].call( params )
|
39
|
+
# route_args[:response].headers['Location'] = '/' # TODO: todo
|
40
|
+
[ 201, result.to_json ]
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.delete( _route_args, params, mapping )
|
44
|
+
mapping[:delete].call( params )
|
45
|
+
[ 200, DONE.to_json ]
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.list( route_args, params, mapping )
|
49
|
+
# TODO: option to enable X-Total-Count ?
|
50
|
+
params[:_where] = params[:_where].nil? ? '1=1' : JSON.parse( params[:_where] )
|
51
|
+
# params[:_where] = '1=1' unless params[:_where].present?
|
52
|
+
route_args[:response].headers['X-Total-Count'] = mapping[:count].call( params ).to_s
|
53
|
+
result = mapping[:list].call( params, route_args[:fields] )
|
54
|
+
[ 200, result.to_json( include: mapping[:relations].call( nil ) ) ]
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.other( _route_args, params, mapping )
|
58
|
+
action = params[:_action]
|
59
|
+
raise( APIError, 'Action not implemented' ) if mapping[action].nil?
|
60
|
+
ret = mapping[action].call( params )
|
61
|
+
[ 200, ret.nil? ? DONE.to_json : ret.to_json ]
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.read( _route_args, params, mapping )
|
65
|
+
result = mapping[:read].call( params )
|
66
|
+
[ 200, result.to_json( include: mapping[:relations].call( nil ) ) ]
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.update( route_args, params, mapping )
|
70
|
+
unless route_args[:request].form_data?
|
71
|
+
route_args[:request].body.rewind
|
72
|
+
params.merge!( Provider.settings[:request_type].eql?( :json ) ? JSON.parse( route_args[:request].body.read ) : route_args[:request].body.read )
|
73
|
+
end
|
74
|
+
params[:_data] = {}
|
75
|
+
resource = route_args[:resource]
|
76
|
+
if !params[resource].nil? && params[resource].is_a?( Hash )
|
77
|
+
# params[resource].delete( 'id' ) # TODO: option to set id field name
|
78
|
+
# params[:_data] = OpenStruct.new( params[resource] )
|
79
|
+
cols = mapping[:columns].call( nil ) + mapping[:relations].call( nil ).map { |rel| "#{rel}_attributes" } + mapping[:extra_fields].call( nil )
|
80
|
+
# TODO: consider relations _ids
|
81
|
+
cols.delete( 'id' ) # TODO: option to set id field name
|
82
|
+
params[:_data] = params[resource].select { |key, _valye| cols.include? key }
|
83
|
+
params.delete( resource )
|
84
|
+
end
|
85
|
+
mapping[:update].call( params )
|
86
|
+
[ 200, DONE.to_json ]
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,168 @@
|
|
1
|
+
require 'pry'
|
2
|
+
|
3
|
+
module Sinatra
|
4
|
+
module RestApi
|
5
|
+
# ORM Mapping
|
6
|
+
class Adapter
|
7
|
+
TYPES = %w(ActiveRecord Mongoid Sequel).freeze
|
8
|
+
NOT_FOUND = [ 'ActiveRecord::RecordNotFound', 'Mongoid::Errors::DocumentNotFound', 'Sequel::NoMatchingRow' ].freeze
|
9
|
+
|
10
|
+
attr_reader :mapping, :model_singular
|
11
|
+
|
12
|
+
def initialize( provider )
|
13
|
+
@provider = provider
|
14
|
+
@klass = @provider.klass
|
15
|
+
parents = @klass.ancestors.map( &:to_s ) # List of super classes
|
16
|
+
if parents.include? 'ActiveRecord::Base'
|
17
|
+
@type = 'ActiveRecord'
|
18
|
+
@mapping = setup_activerecord
|
19
|
+
elsif parents.include? 'Mongoid::Document'
|
20
|
+
@type = 'Mongoid'
|
21
|
+
@mapping = setup_mongoid
|
22
|
+
elsif parents.include? 'Sequel::Model'
|
23
|
+
@type = 'Sequel'
|
24
|
+
@mapping = setup_sequel
|
25
|
+
else
|
26
|
+
@type = nil
|
27
|
+
@mapping = {}
|
28
|
+
end
|
29
|
+
@model_singular = provider.klass.to_s.split( '::' ).last.gsub( /([A-Z]+)([A-Z][a-z])/, '\1_\2' ).gsub( /([a-z\d])([A-Z])/, '\1_\2' ).tr( '-', '_' ).downcase
|
30
|
+
end
|
31
|
+
|
32
|
+
protected
|
33
|
+
|
34
|
+
## Sample setup method
|
35
|
+
# def setup
|
36
|
+
# {
|
37
|
+
# # Collection actions
|
38
|
+
# list: ->( _params ) { '' },
|
39
|
+
# create: ->( _params ) { { id: 0 } },
|
40
|
+
# truncate: ->( _params ) { '' },
|
41
|
+
# # Member actions
|
42
|
+
# read: ->( _params ) { '' },
|
43
|
+
# update: ->( _params ) { '' },
|
44
|
+
# delete: ->( _params ) { '' },
|
45
|
+
# # Other actions
|
46
|
+
# columns: ->( _params ) { [] },
|
47
|
+
# count: ->( _params ) { 0 },
|
48
|
+
# extra_fields: ->( _params ) { [] },
|
49
|
+
# relations: ->( _params ) { [] }
|
50
|
+
# }
|
51
|
+
# end
|
52
|
+
|
53
|
+
def setup_activerecord
|
54
|
+
{
|
55
|
+
# Collection actions
|
56
|
+
list: ->( params, fields ) { @klass.select( fields ).where( params[:_where] ).offset( params[:offset].to_i ).limit( params[:limit].nil? ? -1 : params[:limit].to_i ) },
|
57
|
+
# replace: { verb: :put, path: '/?' },
|
58
|
+
create: lambda do |params|
|
59
|
+
item = @klass.new( params[:_data] )
|
60
|
+
item.save!
|
61
|
+
{ id: item.id }
|
62
|
+
end,
|
63
|
+
truncate: ->( _params ) { @klass.delete_all },
|
64
|
+
# Member actions
|
65
|
+
read: ->( params ) { @klass.find( params[:id] ) },
|
66
|
+
update: lambda do |params|
|
67
|
+
row = @klass.find( params[:id] ) # Same as read
|
68
|
+
row.update!( params[:_data] )
|
69
|
+
end,
|
70
|
+
delete: lambda do |params|
|
71
|
+
row = @klass.find( params[:id] ) # Same as read
|
72
|
+
row.destroy
|
73
|
+
end,
|
74
|
+
# Other actions
|
75
|
+
columns: ->( _params ) { @columns ||= @klass.column_names },
|
76
|
+
count: ->( params ) { @klass.where( params[:_where] ).count },
|
77
|
+
extra_fields: lambda do |_params|
|
78
|
+
@extra_fields ||= @klass.reflections.map do |_key, value|
|
79
|
+
Provider.klasses[value.class_name][:model_singular] + '_ids' if value.class.to_s == 'ActiveRecord::Reflection::HasManyReflection' || value.class.to_s == 'ActiveRecord::Reflection::HasAndBelongsToManyReflection'
|
80
|
+
end.compact
|
81
|
+
end,
|
82
|
+
relations: ->( _params ) { @relations ||= @klass.reflections.keys }
|
83
|
+
}
|
84
|
+
end
|
85
|
+
|
86
|
+
def setup_mongoid
|
87
|
+
{
|
88
|
+
# Collection actions
|
89
|
+
# list: ->( _params ) { @klass.all },
|
90
|
+
list: lambda do |params, fields|
|
91
|
+
if fields.nil?
|
92
|
+
@klass.all.skip( params[:offset].to_i ).limit( params[:limit].nil? ? 0 : params[:limit].to_i )
|
93
|
+
else
|
94
|
+
@klass.all.only( fields ).skip( params[:offset].to_i ).limit( params[:limit].nil? ? 0 : params[:limit].to_i )
|
95
|
+
end
|
96
|
+
end,
|
97
|
+
create: lambda do |params|
|
98
|
+
row = @klass.create!( params[:_data] )
|
99
|
+
{ id: row.id }
|
100
|
+
end,
|
101
|
+
truncate: ->( _params ) { @klass.delete_all },
|
102
|
+
# Member actions
|
103
|
+
read: ->( params ) { @klass.find( params[:id] ) },
|
104
|
+
update: lambda do |params|
|
105
|
+
row = @klass.find( params[:id] ) # Same as read
|
106
|
+
row.update!( params[:_data] )
|
107
|
+
end,
|
108
|
+
delete: lambda do |params|
|
109
|
+
row = @klass.find( params[:id] ) # Same as read
|
110
|
+
row.destroy
|
111
|
+
end,
|
112
|
+
# Other actions
|
113
|
+
columns: ->( _params ) { @columns ||= @klass.attribute_names },
|
114
|
+
count: ->( _params ) { @klass.count },
|
115
|
+
extra_fields: ->( _params ) { [] },
|
116
|
+
relations: ->( _params ) { @relations ||= @klass.relations.keys }
|
117
|
+
# params: ->( _params ) { @params ||= @klass.attribute_names }, # TODO: try this way
|
118
|
+
}
|
119
|
+
end
|
120
|
+
|
121
|
+
def setup_sequel
|
122
|
+
{
|
123
|
+
# Collection actions
|
124
|
+
list: ->( params, fields ) { @klass.select( *fields ).where( params[:_where] ).offset( params[:offset].to_i ).limit( params[:limit].nil? ? nil : params[:limit].to_i ) },
|
125
|
+
# create: ->( params ) { @klass.insert( params[:_data] ) },
|
126
|
+
create: lambda do |params|
|
127
|
+
# Search nested ids
|
128
|
+
ids = {}
|
129
|
+
params[:_data].keys.reject { |k| !k.end_with?( '_ids' ) }.each do |k|
|
130
|
+
nk = k.sub( /_ids$/, '_pks' )
|
131
|
+
ids[nk] = params[:_data].delete k
|
132
|
+
end
|
133
|
+
# Create a new record
|
134
|
+
row = @klass.new( params[:_data] )
|
135
|
+
row.save
|
136
|
+
# Update ids
|
137
|
+
row.update( ids ) unless ids.empty?
|
138
|
+
{ id: row.id }
|
139
|
+
end,
|
140
|
+
truncate: ->( _params ) { @klass.truncate },
|
141
|
+
# Member actions
|
142
|
+
read: ->( params ) { @klass.with_pk!( params[:id] ) },
|
143
|
+
update: lambda do |params|
|
144
|
+
row = @klass.with_pk!( params[:id] ) # Same as read
|
145
|
+
# Adjust nested ids
|
146
|
+
params[:_data].keys.reject { |k| !k.end_with?( '_ids' ) }.each do |k|
|
147
|
+
nk = k.sub( /_ids$/, '_pks' )
|
148
|
+
params[:_data][nk] = params[:_data].delete k
|
149
|
+
end
|
150
|
+
row.update( params[:_data] )
|
151
|
+
end,
|
152
|
+
delete: lambda do |params|
|
153
|
+
row = @klass.with_pk!( params[:id] ) # Same as read
|
154
|
+
row.delete
|
155
|
+
end,
|
156
|
+
# Other actions
|
157
|
+
columns: ->( _params ) { @columns ||= @klass.columns.map( &:to_s ) },
|
158
|
+
count: ->( params ) { @klass.where( params[:_where] ).count },
|
159
|
+
extra_fields: lambda do |_params|
|
160
|
+
@extra_fields ||= @klass.association_reflections.map { |_key, value| Provider.klasses[value[:class_name].split( '::' ).last][:model_singular] + '_ids' if value[:type] == :one_to_many || value[:type] == :many_to_many }.compact
|
161
|
+
end,
|
162
|
+
# extra_fields: ->( _params ) { [] },
|
163
|
+
relations: ->( _params ) { @relations ||= @klass.associations }
|
164
|
+
}
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module Sinatra
|
2
|
+
module RestApi
|
3
|
+
# Prodiver
|
4
|
+
class Provider
|
5
|
+
OPTIONS = [ :actions, :plural, :singular ].freeze
|
6
|
+
REQUEST = {
|
7
|
+
# content_types: [ :formdata, :json, :multipart, :www_form ]
|
8
|
+
content_types: [ :json, :www_form ]
|
9
|
+
}.freeze
|
10
|
+
RESPONSE = {}.freeze
|
11
|
+
|
12
|
+
attr_reader :adapter, :app, :klass, :options, :router
|
13
|
+
|
14
|
+
@@klasses = {}
|
15
|
+
@@settings = {
|
16
|
+
request_type: :www_form
|
17
|
+
}
|
18
|
+
|
19
|
+
def initialize( klass, opts, app, &block )
|
20
|
+
@klass = klass
|
21
|
+
@options = opts
|
22
|
+
@app = app
|
23
|
+
instance_eval( &block ) if block_given?
|
24
|
+
init_settings
|
25
|
+
@adapter = Adapter.new( self )
|
26
|
+
@router = Router.new( self )
|
27
|
+
@router.generate_routes
|
28
|
+
klass_name = klass.to_s.split( '::' ).last
|
29
|
+
@@klasses[klass_name] = { class: klass, model_singular: @adapter.model_singular }
|
30
|
+
decorate_model
|
31
|
+
end
|
32
|
+
|
33
|
+
def method_missing( name, *args, &block )
|
34
|
+
super unless OPTIONS.include? name
|
35
|
+
@options[name] = args[0]
|
36
|
+
end
|
37
|
+
|
38
|
+
def respond_to_missing?( name, include_all = false )
|
39
|
+
super unless OPTIONS.include? name
|
40
|
+
true
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.klasses
|
44
|
+
@@klasses
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.settings
|
48
|
+
@@settings
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def decorate_model
|
54
|
+
# Attach meta data to model
|
55
|
+
@klass.class.module_eval { attr_accessor :restapi }
|
56
|
+
@klass.restapi = {}
|
57
|
+
@klass.restapi[:model_singular] = @adapter.model_singular
|
58
|
+
# @klass.restapi[:model_plural] = @router.plural
|
59
|
+
@klass.restapi[:path_singular] = @router.path_singular
|
60
|
+
@klass.restapi[:path_plural] = @router.plural
|
61
|
+
# @klass.restapi_routes = routes
|
62
|
+
# @klass.association_reflections[:chapters][:class_name].split( '::' ).last
|
63
|
+
# => Chapter
|
64
|
+
# @@klasses['Book'][:model_singular]
|
65
|
+
# => book
|
66
|
+
end
|
67
|
+
|
68
|
+
def init_settings
|
69
|
+
@@settings[:request_type] = @app.restapi_request_type if defined?( @app.restapi_request_type ) && REQUEST[:content_types].include?( @app.restapi_request_type )
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# API Exception class
|
74
|
+
class APIError < StandardError
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
module Sinatra
|
2
|
+
module RestApi
|
3
|
+
# Router
|
4
|
+
class Router
|
5
|
+
EXTS = [ 'json' ].freeze # TODO: load valid exts from an option
|
6
|
+
VERBS = [ :get, :post, :put, :patch, :delete, :options, :link, :unlink ].freeze
|
7
|
+
|
8
|
+
attr_reader :path_singular, :plural
|
9
|
+
|
10
|
+
@@all_routes = []
|
11
|
+
|
12
|
+
def initialize( provider )
|
13
|
+
@provider = provider
|
14
|
+
options = @provider.options
|
15
|
+
@path_singular = options[:singular].nil? ? provider.adapter.model_singular : options[:singular].downcase
|
16
|
+
@plural = options[:plural].nil? ? Router.pluralize( @path_singular ) : options[:plural].downcase
|
17
|
+
@routes = {}
|
18
|
+
routes = options[:actions].nil? ? Actions::SCHEMA.keys : options[:actions]
|
19
|
+
list = routes.delete :list
|
20
|
+
if routes.is_a?( Array )
|
21
|
+
# Move list action to the end to lower its priority
|
22
|
+
routes.push list unless list.nil?
|
23
|
+
routes.each { |route| init_route route, Actions::SCHEMA[route] if Actions::SCHEMA.include? route }
|
24
|
+
elsif routes.is_a?( Hash )
|
25
|
+
routes.each { |route, data| init_route route, data }
|
26
|
+
# Process list action in the end
|
27
|
+
init_route :list, list unless list.nil?
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def generate_routes
|
32
|
+
@routes.each do |route, data|
|
33
|
+
path = "/#{@plural}#{data[:path]}"
|
34
|
+
if data[:verb].is_a?( Array )
|
35
|
+
data[:verb].each do |verb|
|
36
|
+
prepare_route( route: route, resource: @path_singular, verb: verb, path: path, fields: data[:fields], mapping: @provider.adapter.mapping )
|
37
|
+
end
|
38
|
+
else
|
39
|
+
prepare_route( route: route, resource: @path_singular, verb: data[:verb], path: path, fields: data[:fields], mapping: @provider.adapter.mapping )
|
40
|
+
end
|
41
|
+
end
|
42
|
+
@routes
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.pluralize( string )
|
46
|
+
case string
|
47
|
+
when /(s|x|z|ch)$/
|
48
|
+
"#{string}es"
|
49
|
+
when /(a|e|i|o|u)y$/
|
50
|
+
"#{string}s"
|
51
|
+
when /y$/
|
52
|
+
"#{string[0..-2]}ies"
|
53
|
+
when /f$/
|
54
|
+
"#{string[0..-2]}ves"
|
55
|
+
when /fe$/
|
56
|
+
"#{string[0..-3]}ves"
|
57
|
+
else
|
58
|
+
"#{string}s"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.list_routes
|
63
|
+
@@all_routes
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.on_error( e )
|
67
|
+
ret = 500
|
68
|
+
if e.class == APIError
|
69
|
+
ret = 400 # API error
|
70
|
+
elsif Adapter::NOT_FOUND.include? e.class.to_s
|
71
|
+
ret = 404 # Item not found
|
72
|
+
elsif Adapter::TYPES.include?( e.class.to_s.split( '::' )[0] ) || e.class == JSON::ParserError
|
73
|
+
ret = 400 # Invalid request
|
74
|
+
else
|
75
|
+
raise e
|
76
|
+
end
|
77
|
+
[ ret, { error: e.class.to_s, message: e.message }.to_json ]
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def init_route( route, data )
|
83
|
+
return unless Actions::SCHEMA.include? route # Invalid action
|
84
|
+
@routes[route] = Actions::SCHEMA[route] unless data.is_a?( FalseClass )
|
85
|
+
return unless data.is_a?( Hash )
|
86
|
+
@routes[route][:verb] = data[:verb] unless data[:verb].nil?
|
87
|
+
@routes[route][:path] = data[:path] unless data[:path].nil?
|
88
|
+
end
|
89
|
+
|
90
|
+
def prepare_route( route_args )
|
91
|
+
if VERBS.include? route_args[:verb]
|
92
|
+
@@all_routes.push "#{route_args[:verb].upcase}: #{route_args[:path]}"
|
93
|
+
@provider.app.send( route_args[:verb], route_args[:path] ) do
|
94
|
+
# app = self
|
95
|
+
begin
|
96
|
+
raise( APIError, 'Invalid request format' ) if !params[:format].nil? && !EXTS.include?( params[:format] )
|
97
|
+
route_args[:request] = request
|
98
|
+
route_args[:response] = response
|
99
|
+
if Actions.respond_to? route_args[:route]
|
100
|
+
Actions.send( route_args[:route], route_args, params, route_args[:mapping] )
|
101
|
+
else
|
102
|
+
params[:_action] = route_args[:route]
|
103
|
+
Actions.other( route_args, params, route_args[:mapping] )
|
104
|
+
end
|
105
|
+
rescue StandardError => e
|
106
|
+
Router.on_error e
|
107
|
+
end
|
108
|
+
end
|
109
|
+
else
|
110
|
+
# logger.error "Invalid verb: #{verb}"
|
111
|
+
puts "Invalid verb: #{verb}" # TODO: use logger
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
metadata
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sinatra-rest-api
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mattia Roccoberton
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-11-20 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: sinatra
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.4.7
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.4.7
|
27
|
+
description: 'Sinatra REST API generator: CRUD actions, nested resources, supports
|
28
|
+
ActiveRecord, Sequel and Mongoid'
|
29
|
+
email: mat@blocknot.es
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- lib/sinatra-rest-api.rb
|
35
|
+
- lib/sinatra-rest-api/actions.rb
|
36
|
+
- lib/sinatra-rest-api/adapter.rb
|
37
|
+
- lib/sinatra-rest-api/provider.rb
|
38
|
+
- lib/sinatra-rest-api/router.rb
|
39
|
+
homepage: https://github.com/blocknotes/sinatra-rest-api
|
40
|
+
licenses:
|
41
|
+
- ISC
|
42
|
+
metadata: {}
|
43
|
+
post_install_message:
|
44
|
+
rdoc_options: []
|
45
|
+
require_paths:
|
46
|
+
- lib
|
47
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: 2.0.0
|
52
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '0'
|
57
|
+
requirements: []
|
58
|
+
rubyforge_project:
|
59
|
+
rubygems_version: 2.4.8
|
60
|
+
signing_key:
|
61
|
+
specification_version: 4
|
62
|
+
summary: Sinatra REST API generator
|
63
|
+
test_files: []
|