appbase 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE +22 -0
- data/README.md +257 -0
- data/appbase.gemspec +17 -0
- data/lib/appbase/controllers/app_base_controller.rb +42 -29
- data/lib/appbase/model_concern.rb +92 -5
- data/lib/appbase/railtie.rb +11 -3
- data/lib/appbase/registry.rb +2 -0
- data/lib/appbase/version.rb +1 -1
- metadata +21 -48
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4287ccf943cbb1549e3524e7e20d676f0bc1fc16
|
4
|
+
data.tar.gz: 91a967f151ead8e747170cc8e4129162b2b4d7ff
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d5c3719f395cd41c315df6ae9af9efd9f8e683af7ed464eb9aa903d999e19d224cad5f9a84c859feac49f543589965ac6e258cb2c5d60e2bbaf1562c0a051ff1
|
7
|
+
data.tar.gz: 10597b8d9d43506240431a9f6e89b886e073841c90363c69cf7e26c5336dd080f5ac656e8f5380582f268f4794c6d732bf4db3476be107a1a912a7892f6f20e4
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 Mike He
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
22
|
+
|
data/README.md
ADDED
@@ -0,0 +1,257 @@
|
|
1
|
+
[![Build Status](https://travis-ci.org/bestmike007/appbase.svg?branch=master)](https://travis-ci.org/bestmike007/appbase)
|
2
|
+
|
3
|
+
Nowadays BaaS and mBaaS platforms (e.g. [firebase](https://www.firebase.com/), [parse](https://www.parse.com/), [appbase.io](https://appbase.io/)) abound. Open source solutions (e.g. [usergrid](http://usergrid.incubator.apache.org/) using LAMP, [helios](http://helios.io/) using Ruby, [deployd](http://deployd.com/) and [StrongLoop](http://strongloop.com/) using nodejs, and a lot more) are also available. And appbase is much less than those.
|
4
|
+
|
5
|
+
What is appbase? Appbase is a lightweight backend based on rails for rubyists with the following basic features:
|
6
|
+
|
7
|
+
+ User registration and authentication
|
8
|
+
+ REST model crud api
|
9
|
+
+ Expose business logic with a simple line of code
|
10
|
+
+ 3rd party authentication
|
11
|
+
+ Push notifications
|
12
|
+
+ Payment integration
|
13
|
+
+ Other basic features mostly required by apps
|
14
|
+
|
15
|
+
Appbase is/does not:
|
16
|
+
|
17
|
+
+ Use GUI to configure models and/or business logic
|
18
|
+
+ Use configuration files
|
19
|
+
|
20
|
+
Appbase is under development; and will be kept as simple as possible.
|
21
|
+
|
22
|
+
## Basic Usage
|
23
|
+
|
24
|
+
Configure in `application.rb`:
|
25
|
+
|
26
|
+
``` ruby
|
27
|
+
# enable appbase
|
28
|
+
config.appbase.enabled = true
|
29
|
+
# default: '/appbase'
|
30
|
+
# config.appbase.mount = "/_api"
|
31
|
+
config.appbase.user_identity = :User # required
|
32
|
+
config.appbase.token_store = :cookies # :cookies, :headers, :params
|
33
|
+
config.appbase.token_key_user = :u
|
34
|
+
config.appbase.token_key_session = :s
|
35
|
+
config.appbase.models.push :User, :Role, :Permission, :UserProfile, :TodoItem, :GroupTodoList
|
36
|
+
```
|
37
|
+
|
38
|
+
Implement `UserIdentityModel#authenticate_by_token(user, token)`:
|
39
|
+
|
40
|
+
``` ruby
|
41
|
+
class User < ActiveRecord::Base
|
42
|
+
def self.authenticate_by_token(user, token)
|
43
|
+
# TODO cache the result
|
44
|
+
User.find_by(user: user, token: token)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
```
|
48
|
+
|
49
|
+
Set up CRUD permissions for models:
|
50
|
+
|
51
|
+
``` ruby
|
52
|
+
class TodoItem < ActiveRecord::Base
|
53
|
+
|
54
|
+
# Allow query
|
55
|
+
allow_query :mine
|
56
|
+
# or
|
57
|
+
allow_query :within => :related_to_me
|
58
|
+
def self.related_to_me(current_user)
|
59
|
+
TodoItem.where user_id: current_user.id
|
60
|
+
end
|
61
|
+
# or
|
62
|
+
allow_query :within do |current_user|
|
63
|
+
TodoItem.where user_id: current_user.id
|
64
|
+
end
|
65
|
+
|
66
|
+
# Allow create/update/delete
|
67
|
+
allow_create :mine
|
68
|
+
# or
|
69
|
+
allow_update :if => :related_to_me?
|
70
|
+
def self.related_to_me?(current_user, obj)
|
71
|
+
obj.user_id == current_user.id
|
72
|
+
end
|
73
|
+
# or
|
74
|
+
allow_delete :if do |current_user, obj|
|
75
|
+
obj.user_id == current_user.id
|
76
|
+
end
|
77
|
+
|
78
|
+
# restrict_query_columns usage:
|
79
|
+
# restrict_query_columns <only | except>: <single_column | column_list>
|
80
|
+
# examples:
|
81
|
+
# restrict_query_columns only: [:user_id, :created_at, :updated_at]
|
82
|
+
# restrict_query_columns only: :updated_at
|
83
|
+
# restrict_query_columns except: [:content]
|
84
|
+
restrict_query_columns only: [:updated_at, :created_at]
|
85
|
+
|
86
|
+
# restrict_query_operators usage:
|
87
|
+
# restrict_query_operators :column1, :column2, <only | except>: <:equal | :compare | :in>
|
88
|
+
# examples:
|
89
|
+
# restrict_query_operators :user_id, :created_at, :updated_at, only: [:equal, :compare]
|
90
|
+
# restrict_query_operators :user_id, :created_at, :updated_at, except: :in
|
91
|
+
# restrict_query_operators :title, only: :equal
|
92
|
+
restrict_query_operators :updated_at, :created_at, except: :in
|
93
|
+
|
94
|
+
end
|
95
|
+
```
|
96
|
+
|
97
|
+
Expose business logic methods:
|
98
|
+
|
99
|
+
``` ruby
|
100
|
+
|
101
|
+
# don't have to be an active record
|
102
|
+
class GroupTodoList
|
103
|
+
|
104
|
+
include AppBase::ModelConcern
|
105
|
+
|
106
|
+
expose_to_appbase :list_group_todos, auth: true # default to true
|
107
|
+
|
108
|
+
def self.list_group_todos(current_user)
|
109
|
+
TodoItem.find_all group_id: current_user.group_id
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
# public methods, e.g. authentication, does not have the `current_user` parameter
|
115
|
+
class User < ActiveRecord::Base
|
116
|
+
|
117
|
+
expose_to_appbase :authenticate, :external_auth, auth: false
|
118
|
+
|
119
|
+
def self.authenticate(user, pass)
|
120
|
+
user = User.find_by username: user, password: pass
|
121
|
+
return nil if user.nil?
|
122
|
+
user.last_login = Time.now
|
123
|
+
user.session_token = SecureRandom.hex
|
124
|
+
user.save!
|
125
|
+
user.session_token
|
126
|
+
end
|
127
|
+
|
128
|
+
def self.external_auth(user, options={})
|
129
|
+
case options[:provider]
|
130
|
+
when 'twitter'
|
131
|
+
# do authenticate
|
132
|
+
when 'facebook'
|
133
|
+
# do authenticate
|
134
|
+
else
|
135
|
+
raise "unsupported provider"
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
140
|
+
```
|
141
|
+
|
142
|
+
And that's all.
|
143
|
+
|
144
|
+
## The Request Scheme
|
145
|
+
|
146
|
+
Apps (including iOS app, Andriod app, web app with angularjs or similar frontend framework, etc.) are communicating with appbase using HTTP/HTTPS.
|
147
|
+
|
148
|
+
### The REST API
|
149
|
+
|
150
|
+
Basic CRUD api conforms to the representational state transfer (REST) architectural style. Following sections are using model `Note (id, title, content)` as an example to illustrate how a model is created, updated, deleted, and how to perform a query on a model (Supose that the appbase engine is mount on `/_api`).
|
151
|
+
|
152
|
+
#### Create
|
153
|
+
|
154
|
+
Request to create a model with JSON serialized body:
|
155
|
+
|
156
|
+
```
|
157
|
+
PUT /_api/note HTTP/1.1
|
158
|
+
HOST xxx
|
159
|
+
Content-Type: application/json
|
160
|
+
|
161
|
+
{ "title" : "test" , "content" : "hello", "user_id" : 1 }
|
162
|
+
```
|
163
|
+
|
164
|
+
The server response on success:
|
165
|
+
|
166
|
+
```
|
167
|
+
HTTP/1.1 200 OK
|
168
|
+
|
169
|
+
{"status":"ok","id":1}
|
170
|
+
```
|
171
|
+
|
172
|
+
On failure:
|
173
|
+
|
174
|
+
```
|
175
|
+
HTTP/1.1 200 OK
|
176
|
+
|
177
|
+
{"status":"error","msg":"error_msg"}
|
178
|
+
```
|
179
|
+
|
180
|
+
#### Update
|
181
|
+
|
182
|
+
Almost the same as create except for adding the `:id` parameter (e.g. `/_api/note/:id`):
|
183
|
+
|
184
|
+
```
|
185
|
+
PUT /_api/note/1 HTTP/1.1
|
186
|
+
HOST xxx
|
187
|
+
Content-Type: application/json
|
188
|
+
|
189
|
+
{ "title" : "test" , "content" : "hello appabse!", "user_id" : 1 }
|
190
|
+
```
|
191
|
+
|
192
|
+
#### Delete
|
193
|
+
|
194
|
+
```
|
195
|
+
DELETE /_api/note/1 HTTP/1.1
|
196
|
+
HOST xxx
|
197
|
+
```
|
198
|
+
|
199
|
+
#### Query
|
200
|
+
|
201
|
+
The request:
|
202
|
+
|
203
|
+
```
|
204
|
+
GET /_api/note?p=1&ps=20 HTTP/1.1
|
205
|
+
HOST xxx
|
206
|
+
```
|
207
|
+
|
208
|
+
In the parameters, `p` indicates the page of the query; `ps` indicates the page size of the query. Except for the pagination parameters, query parameters are allowed to filter the query. Supose we need to perform a query on `Note.id`, here are some examples on how to query:
|
209
|
+
|
210
|
+
+ /_api/note?p=1&ps=20`&id=1` Equal to 1
|
211
|
+
+ /_api/note?p=1&ps=20`&id.lt=10` Less than 10
|
212
|
+
+ /_api/note?p=1&ps=20`&id.le=10` Less than or equal to 10
|
213
|
+
+ /_api/note?p=1&ps=20`&id.gt=1` Greater than 1
|
214
|
+
+ /_api/note?p=1&ps=20`&id.ge=1` Greater than or equal to 1
|
215
|
+
+ /_api/note?p=1&ps=20`&id.lt=10&id.ge=1` Greater than or equal to 1 and less than 10
|
216
|
+
+ /_api/note?p=1&ps=20`&id.in=[1,2,3]` In 1, 2, 3
|
217
|
+
+ /_api/note?p=1&ps=20`&id.nin=[1,2,3]` Not in 1, 2, 3
|
218
|
+
+ /_api/note?p=1&ps=20`&id.n=true` Is null
|
219
|
+
+ /_api/note?p=1&ps=20`&id.nn=true` Not null
|
220
|
+
|
221
|
+
`OR` conditions are not supported for now, use exposed methods instead.
|
222
|
+
|
223
|
+
### RPC Methods
|
224
|
+
|
225
|
+
Model methods with custom business logic can be exposed as rpc methods, take the method `Note.related_to_me(current_user, limit)` as example.
|
226
|
+
|
227
|
+
```
|
228
|
+
POST /_api/note/related_to_me HTTP/1.1
|
229
|
+
HOST xxx
|
230
|
+
|
231
|
+
limit=10
|
232
|
+
```
|
233
|
+
|
234
|
+
Response from the backend should be:
|
235
|
+
|
236
|
+
```
|
237
|
+
HTTP/1.1 200 OK
|
238
|
+
|
239
|
+
{"status":"ok","data":[{"id":1,"key":"value"},{"id":2,"key":"value"},{"id":3,"key":"value"}]}
|
240
|
+
```
|
241
|
+
|
242
|
+
If the method is defined with an `options` parameter, e.g. `Note.related_to_me(current_user, options={})`, then optional request arguments are passed to the method within the option hash object.
|
243
|
+
|
244
|
+
## Known Issues
|
245
|
+
|
246
|
+
+ `OR` conditions are not supported for active record query
|
247
|
+
+ Multiple accessible query base
|
248
|
+
+ Write more test cases
|
249
|
+
+ Complete the document
|
250
|
+
|
251
|
+
You're welcome to contribute to this project by creating an issue / a pull request.
|
252
|
+
|
253
|
+
---
|
254
|
+
|
255
|
+
## License
|
256
|
+
|
257
|
+
Appbase is released under the [MIT License](http://opensource.org/licenses/MIT).
|
data/appbase.gemspec
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require File.expand_path('../lib/appbase/version', __FILE__)
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = 'appbase'
|
5
|
+
s.version = AppBase::VERSION
|
6
|
+
s.summary = "Lightweight appbase"
|
7
|
+
s.description = "A lightweight backend for Web/iOS/Android apps."
|
8
|
+
s.authors = ["bestmike007"]
|
9
|
+
s.email = 'i@bestmike007.com'
|
10
|
+
s.homepage = 'http://bestmike007.com/appbase'
|
11
|
+
s.license = 'MIT'
|
12
|
+
s.files = Dir['lib/**/*'] + ['LICENSE', 'README.md', 'appbase.gemspec']
|
13
|
+
|
14
|
+
s.required_ruby_version = '>= 1.9.0'
|
15
|
+
s.add_runtime_dependency 'rails', '>= 4.0', '< 4.2'
|
16
|
+
s.add_development_dependency "rspec-rails", "~> 3.1", '>= 3.1.0'
|
17
|
+
end
|
@@ -17,7 +17,12 @@ class AppBaseController < ActionController::Base
|
|
17
17
|
return options[:default] if options.has_key? :default
|
18
18
|
raise "unauthenticated"
|
19
19
|
end
|
20
|
-
#{user_identity}.authenticate_by_token(#{token_store}[:#{token_key_user}], #{token_store}[:#{token_key_session}])
|
20
|
+
user = #{user_identity}.authenticate_by_token(#{token_store}[:#{token_key_user}], #{token_store}[:#{token_key_session}])
|
21
|
+
if user.nil?
|
22
|
+
return options[:default] if options.has_key? :default
|
23
|
+
raise "unauthenticated"
|
24
|
+
end
|
25
|
+
user
|
21
26
|
end
|
22
27
|
-
|
23
28
|
end
|
@@ -25,7 +30,7 @@ class AppBaseController < ActionController::Base
|
|
25
30
|
def add_create_stub(model)
|
26
31
|
m = model.name
|
27
32
|
permits = model.columns.map { |item| item.name }.to_json
|
28
|
-
self.
|
33
|
+
self.class_eval %-
|
29
34
|
def create_#{AppBase.underscore m}
|
30
35
|
obj = #{m}.new(params.except(:action, :controller, :id).permit(#{permits}))
|
31
36
|
if !#{m}.allow_create?(current_user, obj)
|
@@ -43,7 +48,7 @@ class AppBaseController < ActionController::Base
|
|
43
48
|
def add_update_stub(model)
|
44
49
|
m = model.name
|
45
50
|
permits = model.columns.map { |item| item.name }.to_json
|
46
|
-
self.
|
51
|
+
self.class_eval %-
|
47
52
|
def update_#{AppBase.underscore m}
|
48
53
|
obj = #{m}.find(params[:id])
|
49
54
|
if obj.nil?
|
@@ -64,7 +69,7 @@ class AppBaseController < ActionController::Base
|
|
64
69
|
|
65
70
|
def add_delete_stub(model)
|
66
71
|
m = model.name
|
67
|
-
self.
|
72
|
+
self.class_eval %-
|
68
73
|
def delete_#{AppBase.underscore m}
|
69
74
|
obj = #{m}.find(params[:id])
|
70
75
|
if obj.nil?
|
@@ -90,32 +95,40 @@ class AppBaseController < ActionController::Base
|
|
90
95
|
query = #{m}.accessible_by(current_user)
|
91
96
|
params.except(:action, :controller, :p, :ps).each { |k, v|
|
92
97
|
op = 'eq'
|
98
|
+
k = k.to_s
|
93
99
|
if k.index('.') && k.split('.').count == 2
|
94
100
|
k, op = k.split('.')
|
95
101
|
end
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
102
|
+
k = k.to_sym
|
103
|
+
operators = #{m}.appbase_queryable_operators[k]
|
104
|
+
unless #{m}.appbase_queryable_columns.index(k).nil?
|
105
|
+
case op
|
106
|
+
when 'eq'
|
107
|
+
query = query.where "\#{k} = ?", v if operators.nil? || !operators.index(:equal).nil?
|
108
|
+
when 'lt'
|
109
|
+
query = query.where "\#{k} < ?", v if operators.nil? || !operators.index(:compare).nil?
|
110
|
+
when 'le'
|
111
|
+
query = query.where "\#{k} <= ?", v if operators.nil? || !operators.index(:compare).nil?
|
112
|
+
when 'gt'
|
113
|
+
query = query.where "\#{k} > ?", v if operators.nil? || !operators.index(:compare).nil?
|
114
|
+
when 'ge'
|
115
|
+
query = query.where "\#{k} >= ?", v if operators.nil? || !operators.index(:compare).nil?
|
116
|
+
when 'n'
|
117
|
+
query = query.where "\#{k} IS NULL" if operators.nil? || !operators.index(:equal).nil?
|
118
|
+
when 'nn'
|
119
|
+
query = query.where "\#{k} IS NOT NULL" if operators.nil? || !operators.index(:equal).nil?
|
120
|
+
when 'in'
|
121
|
+
if operators.nil? || !operators.index(:in).nil?
|
122
|
+
values = JSON.parse v
|
123
|
+
query = query.where "\#{k} IN (?)", values
|
124
|
+
end
|
125
|
+
when 'nin'
|
126
|
+
if operators.nil? || !operators.index(:in).nil?
|
127
|
+
values = JSON.parse v
|
128
|
+
query = query.where "\#{k} NOT IN (?)", values
|
129
|
+
end
|
130
|
+
else
|
131
|
+
end
|
119
132
|
end
|
120
133
|
}
|
121
134
|
page_size = [1, (params[:ps]||20).to_i].max
|
@@ -132,10 +145,10 @@ class AppBaseController < ActionController::Base
|
|
132
145
|
mn = bound_method.name
|
133
146
|
parameters = bound_method.parameters
|
134
147
|
if auth && (parameters.count == 0 || parameters[0][0] != :req)
|
135
|
-
raise "#{m}.#{mn} does not accept current user identity as the first parameter. Using `expose_to_appbase :method_name,
|
148
|
+
raise "#{m}.#{mn} does not accept current user identity as the first parameter. Using `expose_to_appbase :method_name, auth: false` to expose #{m}.#{mn} to appbase without user authentication."
|
136
149
|
end
|
137
150
|
need_params = false
|
138
|
-
if parameters.last[0] == :opt
|
151
|
+
if parameters.count > 0 && parameters.last[0] == :opt
|
139
152
|
need_params = true
|
140
153
|
parameters = parameters[(auth ? 1 : 0)..-2]
|
141
154
|
else
|
@@ -84,25 +84,112 @@ module AppBase
|
|
84
84
|
end
|
85
85
|
|
86
86
|
def allow_create(criteria=:mine, &block)
|
87
|
-
|
87
|
+
appbase_allow(:create, criteria, &block)
|
88
88
|
end
|
89
89
|
|
90
90
|
def allow_update(criteria=:mine, &block)
|
91
|
-
|
91
|
+
appbase_allow(:update, criteria, &block)
|
92
92
|
end
|
93
93
|
|
94
94
|
def allow_delete(criteria=:mine, &block)
|
95
|
-
|
95
|
+
appbase_allow(:delete, criteria, &block)
|
96
96
|
end
|
97
97
|
|
98
98
|
def allow_query(criteria=:mine, &block)
|
99
|
-
|
99
|
+
appbase_allow(:query, criteria, &block)
|
100
100
|
end
|
101
101
|
|
102
|
+
def restrict_query_columns(options={})
|
103
|
+
show_usage = proc {
|
104
|
+
raise %-
|
105
|
+
restrict_query_columns usage:
|
106
|
+
restrict_query_columns <only | except>: <single_column | column_list>
|
107
|
+
examples:
|
108
|
+
restrict_query_columns only: [:user_id, :created_at, :updated_at]
|
109
|
+
restrict_query_columns only: :updated_at
|
110
|
+
restrict_query_columns except: [:content]
|
111
|
+
-
|
112
|
+
}
|
113
|
+
show_usage.call if !options || !options.instance_of?(Hash)
|
114
|
+
columns = self.columns.map{|c|c.name.to_sym}
|
115
|
+
# on columns
|
116
|
+
if options.has_key? :only
|
117
|
+
on_columns = options[:only]
|
118
|
+
on_columns = [on_columns] if on_columns.instance_of?(String) || on_columns.instance_of?(Symbol)
|
119
|
+
show_usage.call unless on_columns.instance_of?(Array)
|
120
|
+
on_columns = on_columns.map {|c|c.to_sym}
|
121
|
+
columns &= on_columns
|
122
|
+
end
|
123
|
+
# except columns
|
124
|
+
if options.has_key? :except
|
125
|
+
except_columns = options[:except]
|
126
|
+
except_columns = [except_columns] if except_columns.instance_of?(String) || except_columns.instance_of?(Symbol)
|
127
|
+
show_usage.call unless except_columns.instance_of?(Array)
|
128
|
+
except_columns = except_columns.map {|c|c.to_sym}
|
129
|
+
columns -= except_columns
|
130
|
+
end
|
131
|
+
|
132
|
+
self.define_singleton_method :appbase_queryable_columns do
|
133
|
+
columns
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
137
|
+
|
138
|
+
def appbase_queryable_operators
|
139
|
+
return {}
|
140
|
+
end
|
141
|
+
|
142
|
+
def restrict_query_operators(*columns)
|
143
|
+
show_usage = proc {
|
144
|
+
raise %-
|
145
|
+
restrict_query_operators usage:
|
146
|
+
restrict_query_operators :column1, :column2, <only | except>: <:equal | :compare | :in>
|
147
|
+
examples:
|
148
|
+
restrict_query_operators :user_id, :created_at, :updated_at, only: [:equal, :compare]
|
149
|
+
restrict_query_operators :user_id, :created_at, :updated_at, except: :in
|
150
|
+
restrict_query_operators :title, only: :equal
|
151
|
+
-
|
152
|
+
}
|
153
|
+
show_usage.call if columns.count < 2 || !columns.last.instance_of?(Hash)
|
154
|
+
*columns, options = columns
|
155
|
+
show_usage.call unless options.has_key?(:only) || options.has_key?(:except)
|
156
|
+
operators = appbase_queryable_operators
|
157
|
+
|
158
|
+
set = [:equal, :compare, :in]
|
159
|
+
# on columns
|
160
|
+
if options.has_key? :only
|
161
|
+
allows = options[:only]
|
162
|
+
allows = [allows] if allows.instance_of?(String) || allows.instance_of?(Symbol)
|
163
|
+
show_usage.call unless allows.instance_of?(Array)
|
164
|
+
allows = allows.map {|c|c.to_sym}
|
165
|
+
set &= allows
|
166
|
+
end
|
167
|
+
# except columns
|
168
|
+
if options.has_key? :except
|
169
|
+
excepts = options[:except]
|
170
|
+
excepts = [excepts] if excepts.instance_of?(String) || excepts.instance_of?(Symbol)
|
171
|
+
show_usage.call unless excepts.instance_of?(Array)
|
172
|
+
excepts = excepts.map {|c|c.to_sym}
|
173
|
+
set -= excepts
|
174
|
+
end
|
175
|
+
|
176
|
+
columns.each do |c|
|
177
|
+
operators[c.to_sym] = set
|
178
|
+
end
|
179
|
+
|
180
|
+
self.define_singleton_method :appbase_queryable_operators do
|
181
|
+
operators
|
182
|
+
end
|
183
|
+
|
184
|
+
end
|
185
|
+
|
186
|
+
|
102
187
|
end
|
103
188
|
|
104
189
|
end
|
105
190
|
|
106
191
|
end
|
107
192
|
|
108
|
-
ActiveRecord::Base
|
193
|
+
class ActiveRecord::Base
|
194
|
+
include AppBase::ModelConcern
|
195
|
+
end
|
data/lib/appbase/railtie.rb
CHANGED
@@ -81,6 +81,7 @@ module AppBase
|
|
81
81
|
blocks.each do |block|
|
82
82
|
block.call
|
83
83
|
end
|
84
|
+
initialized = true
|
84
85
|
|
85
86
|
end
|
86
87
|
|
@@ -104,6 +105,7 @@ module AppBase
|
|
104
105
|
|
105
106
|
# default values for appbase configuration
|
106
107
|
config.appbase = ActiveSupport::OrderedOptions.new
|
108
|
+
config.appbase.enabled = false
|
107
109
|
config.appbase.mount = "/appbase"
|
108
110
|
config.appbase.user_identity = nil
|
109
111
|
config.appbase.token_store = :cookies # :cookies, :headers, :params
|
@@ -113,10 +115,16 @@ module AppBase
|
|
113
115
|
|
114
116
|
initializer "appbase.configure_route", :after => :add_routing_paths do |app|
|
115
117
|
|
116
|
-
|
118
|
+
if File.basename(ENV['_']) == 'rake'
|
119
|
+
puts "Running with `rake #{$*.join(' ')}`"
|
120
|
+
end
|
117
121
|
|
118
|
-
|
119
|
-
|
122
|
+
if config.appbase.enabled && (File.basename(ENV['_']) != 'rake' || $*[0] == 'routes')
|
123
|
+
AppBase::Engine.bootstrap app.config
|
124
|
+
|
125
|
+
app.routes.append do
|
126
|
+
mount AppBase::Engine => Rails.application.config.appbase.mount
|
127
|
+
end
|
120
128
|
end
|
121
129
|
|
122
130
|
end
|
data/lib/appbase/registry.rb
CHANGED
data/lib/appbase/version.rb
CHANGED
metadata
CHANGED
@@ -1,91 +1,64 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: appbase
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- bestmike007
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-12-
|
11
|
+
date: 2014-12-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: rails
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '4.0'
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '4.2'
|
20
23
|
type: :runtime
|
21
24
|
prerelease: false
|
22
25
|
version_requirements: !ruby/object:Gem::Requirement
|
23
26
|
requirements:
|
24
|
-
- - "
|
27
|
+
- - ">="
|
25
28
|
- !ruby/object:Gem::Version
|
26
29
|
version: '4.0'
|
27
|
-
-
|
28
|
-
name: rake
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - "~>"
|
30
|
+
- - "<"
|
32
31
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
34
|
-
type: :development
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - "~>"
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: 0.8.7
|
32
|
+
version: '4.2'
|
41
33
|
- !ruby/object:Gem::Dependency
|
42
|
-
name: rspec
|
34
|
+
name: rspec-rails
|
43
35
|
requirement: !ruby/object:Gem::Requirement
|
44
36
|
requirements:
|
45
37
|
- - "~>"
|
46
38
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
48
|
-
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - "~>"
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '0'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: rack-test
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - "~>"
|
39
|
+
version: '3.1'
|
40
|
+
- - ">="
|
60
41
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
42
|
+
version: 3.1.0
|
62
43
|
type: :development
|
63
44
|
prerelease: false
|
64
45
|
version_requirements: !ruby/object:Gem::Requirement
|
65
46
|
requirements:
|
66
47
|
- - "~>"
|
67
48
|
- !ruby/object:Gem::Version
|
68
|
-
version: '
|
69
|
-
-
|
70
|
-
name: rails
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - "~>"
|
49
|
+
version: '3.1'
|
50
|
+
- - ">="
|
74
51
|
- !ruby/object:Gem::Version
|
75
|
-
version:
|
76
|
-
type: :development
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - "~>"
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: '4.0'
|
52
|
+
version: 3.1.0
|
83
53
|
description: A lightweight backend for Web/iOS/Android apps.
|
84
54
|
email: i@bestmike007.com
|
85
55
|
executables: []
|
86
56
|
extensions: []
|
87
57
|
extra_rdoc_files: []
|
88
58
|
files:
|
59
|
+
- LICENSE
|
60
|
+
- README.md
|
61
|
+
- appbase.gemspec
|
89
62
|
- lib/appbase.rb
|
90
63
|
- lib/appbase/controllers/app_base_controller.rb
|
91
64
|
- lib/appbase/model_concern.rb
|
@@ -104,7 +77,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
104
77
|
requirements:
|
105
78
|
- - ">="
|
106
79
|
- !ruby/object:Gem::Version
|
107
|
-
version:
|
80
|
+
version: 1.9.0
|
108
81
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
109
82
|
requirements:
|
110
83
|
- - ">="
|