flexible_api_server 0.0.1
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.
- data/lib/flexible_api_server/version.rb +5 -0
- data/lib/flexible_api_server.rb +219 -0
- data/spec/spec_helper.rb +8 -0
- metadata +168 -0
@@ -0,0 +1,219 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'active_record'
|
3
|
+
require 'sinatra'
|
4
|
+
require 'sinatra/respond_to'
|
5
|
+
require 'json'
|
6
|
+
|
7
|
+
require 'flexible_api'
|
8
|
+
|
9
|
+
class ActiveRecord::Base
|
10
|
+
named_scope :limit, lambda { |l| { :limit => l } }
|
11
|
+
named_scope :offset, lambda { |o| { :offset => o } }
|
12
|
+
end
|
13
|
+
|
14
|
+
module FlexibleApiServer
|
15
|
+
|
16
|
+
class App < Sinatra::Base
|
17
|
+
|
18
|
+
register Sinatra::RespondTo
|
19
|
+
set :assume_xhr_is_js, true
|
20
|
+
set :default_content, :js
|
21
|
+
set :raise_errors, false
|
22
|
+
set :show_exceptions, false
|
23
|
+
|
24
|
+
set :views, File.dirname(__FILE__) + '/../views'
|
25
|
+
|
26
|
+
FILTERED_COLUMNS = [:password, :password_confirmation]
|
27
|
+
|
28
|
+
def assign(k, v)
|
29
|
+
@assign ||= {}
|
30
|
+
@assign[k] = v
|
31
|
+
nil
|
32
|
+
end
|
33
|
+
|
34
|
+
def free_render(code, hash)
|
35
|
+
status code
|
36
|
+
# Filter some things out
|
37
|
+
FILTERED_COLUMNS.each do |column|
|
38
|
+
hash[column] = '[filtered]' if hash.has_key?(column)
|
39
|
+
end if hash.is_a?(Hash)
|
40
|
+
# and respond
|
41
|
+
respond_to do |wants|
|
42
|
+
wants.js { hash.to_json }
|
43
|
+
wants.xml { hash.to_xml }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# TODO too generic
|
48
|
+
error NameError do
|
49
|
+
free_render 404, :message => "No such type: #{params[:model]}"
|
50
|
+
end
|
51
|
+
|
52
|
+
error FlexibleApi::NoSuchRequestLevelError do
|
53
|
+
free_render 400, :message => request.env['sinatra.error'].message
|
54
|
+
end
|
55
|
+
|
56
|
+
# TODO too generic
|
57
|
+
error ActiveRecord::RecordNotFound do
|
58
|
+
free_render 404, :message => request.env['sinatra.error'].message
|
59
|
+
end
|
60
|
+
|
61
|
+
# TODO too generic
|
62
|
+
error NoMethodError do
|
63
|
+
free_render 404, :message => "Unknown method '#{request.env['sinatra.error'].name}' on #{params[:model]}"
|
64
|
+
end
|
65
|
+
|
66
|
+
post '/:model' do
|
67
|
+
model_klass = params[:model].singularize.camelize.constantize
|
68
|
+
|
69
|
+
record = model_klass.new(request.POST)
|
70
|
+
@assign.each { |k, v| record.send(:"#{k}=", v) } unless @assign.nil?
|
71
|
+
if record.save
|
72
|
+
free_render 200, record.to_hash(requested_level)
|
73
|
+
else
|
74
|
+
free_render 422, :message => 'Validation error', :errors => record.errors
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
get '/:model' do
|
79
|
+
model_param, scope_param = params[:model].split ':', 2
|
80
|
+
query = model_param.singularize.camelize.constantize
|
81
|
+
|
82
|
+
query = add_scopes(query, scope_param)
|
83
|
+
|
84
|
+
if params[:count_only] == 'true'
|
85
|
+
free_render 200, :count => query.count
|
86
|
+
else
|
87
|
+
query = add_limit_and_offset query
|
88
|
+
records = query.find_all_hash(:request_level => requested_level)
|
89
|
+
free_render 200, records
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
delete '/:model' do
|
94
|
+
free_render 404, :message => 'not implemented'
|
95
|
+
end
|
96
|
+
|
97
|
+
get '/:model/:id' do
|
98
|
+
model_klass = params[:model].singularize.camelize.constantize
|
99
|
+
record = model_klass.find_hash(params[:id], :request_level => requested_level)
|
100
|
+
free_render 200, record
|
101
|
+
end
|
102
|
+
|
103
|
+
put '/:model/:id' do
|
104
|
+
model_klass = params[:model].singularize.camelize.constantize
|
105
|
+
record = model_klass.find(params[:id])
|
106
|
+
|
107
|
+
record.attributes = request.POST
|
108
|
+
@assign.each { |k, v| record.send(:"#{k}=", v) } unless @assign.nil?
|
109
|
+
if record.save
|
110
|
+
free_render 200, record.to_hash(requested_level)
|
111
|
+
else
|
112
|
+
free_render 422, :message => 'Validation error', :errors => record.errors
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
delete '/:model/:id' do
|
117
|
+
free_render 404, :message => 'not implemented'
|
118
|
+
end
|
119
|
+
|
120
|
+
post '/:model/:id/:relation' do
|
121
|
+
|
122
|
+
model_klass = params[:model].singularize.camelize.constantize
|
123
|
+
|
124
|
+
model_instance = model_klass.find(params[:id])
|
125
|
+
relation = model_instance.send(params[:relation].to_sym)
|
126
|
+
|
127
|
+
record = relation.new(request.POST)
|
128
|
+
@assign.each { |k, v| record.send(:"#{k}=", v) } unless @assign.nil?
|
129
|
+
|
130
|
+
if record.save
|
131
|
+
free_render 200, record.to_hash(requested_level)
|
132
|
+
else
|
133
|
+
free_render 422, :message => 'Validation error', :errors => record.errors
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
get '/:model/:id/:relation' do
|
138
|
+
|
139
|
+
model_klass = params[:model].singularize.camelize.constantize
|
140
|
+
relation_param, scope_param = params[:relation].split ':', 2
|
141
|
+
|
142
|
+
record = model_klass.find(params[:id]) # TODO only select the thing needed for the join
|
143
|
+
query = record.send(relation_param)
|
144
|
+
|
145
|
+
return free_render 200, nil if query.nil?
|
146
|
+
|
147
|
+
options = { :request_level => requested_level }
|
148
|
+
if query.is_a?(ActiveRecord::Base)
|
149
|
+
free_render 200, query.to_hash(options)
|
150
|
+
else
|
151
|
+
# Scope it
|
152
|
+
query = add_scopes(query, scope_param)
|
153
|
+
# Render the result
|
154
|
+
if params[:count_only] == 'true'
|
155
|
+
free_render 200, :count => query.count
|
156
|
+
else
|
157
|
+
query = add_limit_and_offset(query)
|
158
|
+
free_render 200, query.find_all_hash(options)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
put '/:model/:id/:relation' do
|
164
|
+
free_render 404, :message => 'not implemented'
|
165
|
+
end
|
166
|
+
|
167
|
+
delete '/:model/:id/:relation' do
|
168
|
+
free_render 404, :message => 'not implemented'
|
169
|
+
end
|
170
|
+
|
171
|
+
get '/' do
|
172
|
+
format 'html'
|
173
|
+
respond_to do |wants|
|
174
|
+
wants.html do
|
175
|
+
@model_details = FlexibleApi.flexible_models.sort_by { |m| m.name }
|
176
|
+
path = "./config/locales/#{I18n.locale}/descriptions.yml"
|
177
|
+
@copy = File.exists?(path) ? YAML::load(File.open(path, 'r')) : {}
|
178
|
+
erb :doc_index
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
private
|
184
|
+
|
185
|
+
DEFAULT_LIMIT = 50
|
186
|
+
|
187
|
+
def add_scopes(query, scope_param)
|
188
|
+
scopes = []
|
189
|
+
scopes.concat scope_param.split ':' unless scope_param.nil?
|
190
|
+
scopes.concat params[:scopes] if params[:scopes].is_a?(Array)
|
191
|
+
scopes.each do |scope|
|
192
|
+
# TODO remove this ugliness
|
193
|
+
method, arg_string = scope.split '('
|
194
|
+
if !arg_string.nil? && args = arg_string.chop.split(',').map(&:strip)
|
195
|
+
query = query.send method.to_sym, *args
|
196
|
+
else
|
197
|
+
query = query.send method.to_sym
|
198
|
+
end
|
199
|
+
end
|
200
|
+
query
|
201
|
+
end
|
202
|
+
|
203
|
+
def add_limit_and_offset(query)
|
204
|
+
limit = params[:limit].to_i
|
205
|
+
limit = DEFAULT_LIMIT if limit <= 0
|
206
|
+
|
207
|
+
offset = params[:offset].to_i
|
208
|
+
offset = 0 if offset < 0
|
209
|
+
|
210
|
+
query.limit(limit).offset(offset)
|
211
|
+
end
|
212
|
+
|
213
|
+
def requested_level
|
214
|
+
params[:request_level] || params[:level]
|
215
|
+
end
|
216
|
+
|
217
|
+
end
|
218
|
+
|
219
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,168 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: flexible_api_server
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- John Crepezzi
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-03-28 00:00:00 +01:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: rspec
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :development
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: sqlite3
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 3
|
44
|
+
segments:
|
45
|
+
- 0
|
46
|
+
version: "0"
|
47
|
+
type: :development
|
48
|
+
version_requirements: *id002
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: rack-test
|
51
|
+
prerelease: false
|
52
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
hash: 3
|
58
|
+
segments:
|
59
|
+
- 0
|
60
|
+
version: "0"
|
61
|
+
type: :development
|
62
|
+
version_requirements: *id003
|
63
|
+
- !ruby/object:Gem::Dependency
|
64
|
+
name: flexible_api
|
65
|
+
prerelease: false
|
66
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
67
|
+
none: false
|
68
|
+
requirements:
|
69
|
+
- - "="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
hash: 29
|
72
|
+
segments:
|
73
|
+
- 0
|
74
|
+
- 0
|
75
|
+
- 1
|
76
|
+
version: 0.0.1
|
77
|
+
type: :runtime
|
78
|
+
version_requirements: *id004
|
79
|
+
- !ruby/object:Gem::Dependency
|
80
|
+
name: activerecord
|
81
|
+
prerelease: false
|
82
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
83
|
+
none: false
|
84
|
+
requirements:
|
85
|
+
- - ">="
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
hash: 3
|
88
|
+
segments:
|
89
|
+
- 0
|
90
|
+
version: "0"
|
91
|
+
type: :runtime
|
92
|
+
version_requirements: *id005
|
93
|
+
- !ruby/object:Gem::Dependency
|
94
|
+
name: sinatra
|
95
|
+
prerelease: false
|
96
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ">="
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
hash: 3
|
102
|
+
segments:
|
103
|
+
- 0
|
104
|
+
version: "0"
|
105
|
+
type: :runtime
|
106
|
+
version_requirements: *id006
|
107
|
+
- !ruby/object:Gem::Dependency
|
108
|
+
name: sinatra-respond_to
|
109
|
+
prerelease: false
|
110
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
111
|
+
none: false
|
112
|
+
requirements:
|
113
|
+
- - ">="
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
hash: 3
|
116
|
+
segments:
|
117
|
+
- 0
|
118
|
+
version: "0"
|
119
|
+
type: :runtime
|
120
|
+
version_requirements: *id007
|
121
|
+
description: Server for exposing Flexible APIs
|
122
|
+
email: john.crepezzi@gmail.com
|
123
|
+
executables: []
|
124
|
+
|
125
|
+
extensions: []
|
126
|
+
|
127
|
+
extra_rdoc_files: []
|
128
|
+
|
129
|
+
files:
|
130
|
+
- lib/flexible_api_server/version.rb
|
131
|
+
- lib/flexible_api_server.rb
|
132
|
+
- spec/spec_helper.rb
|
133
|
+
has_rdoc: true
|
134
|
+
homepage: http://github.com/seejohnrun/flexible_api_server
|
135
|
+
licenses: []
|
136
|
+
|
137
|
+
post_install_message:
|
138
|
+
rdoc_options: []
|
139
|
+
|
140
|
+
require_paths:
|
141
|
+
- lib
|
142
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
143
|
+
none: false
|
144
|
+
requirements:
|
145
|
+
- - ">="
|
146
|
+
- !ruby/object:Gem::Version
|
147
|
+
hash: 3
|
148
|
+
segments:
|
149
|
+
- 0
|
150
|
+
version: "0"
|
151
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
152
|
+
none: false
|
153
|
+
requirements:
|
154
|
+
- - ">="
|
155
|
+
- !ruby/object:Gem::Version
|
156
|
+
hash: 3
|
157
|
+
segments:
|
158
|
+
- 0
|
159
|
+
version: "0"
|
160
|
+
requirements: []
|
161
|
+
|
162
|
+
rubyforge_project: flexible_api_server
|
163
|
+
rubygems_version: 1.6.2
|
164
|
+
signing_key:
|
165
|
+
specification_version: 3
|
166
|
+
summary: Server for exposing Flexible APIs created by FlexibleAPI
|
167
|
+
test_files:
|
168
|
+
- spec/spec_helper.rb
|