appbase 0.0.1 → 0.0.2
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 +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
|
+
[](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
|
- - ">="
|