flexible_api_server 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ module FlexibleApiServer
2
+
3
+ VERSION = '0.0.1'
4
+
5
+ end
@@ -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
@@ -0,0 +1,8 @@
1
+ require 'bundler/setup'
2
+ require 'rack/test'
3
+
4
+ require 'active_record'
5
+ ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => 'spec/test.db')
6
+
7
+ require 'flexible_api'
8
+ require File.dirname(__FILE__) + '/../lib/flexible_api_server'
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