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.
@@ -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