appbase 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 21e5c0b2f097283d8f398c5ca323f8a68c1a3f8a
4
+ data.tar.gz: 8b9a0d23b5942695c3e34b1afce107208d0d2a38
5
+ SHA512:
6
+ metadata.gz: b05fb2ef0b47302f41b5f6740a152251975b4ad451acc5d7755aa6b2695830ffee91639af82c6e7accc6f964786f6b9afe7b996289f3aacbd8e152e75f251519
7
+ data.tar.gz: 0cea345d69931905d64301d26a69137332d8213978857b3ab05a63c7453cb0a0e4fd06d29bd66569862459b8f7f1f4a2b182c3ff559d7b29b3a65dbd0ad74e88
@@ -0,0 +1,165 @@
1
+ class AppBaseController < ActionController::Base
2
+
3
+ def version
4
+ render json: AppBase::VERSION
5
+ end
6
+
7
+ def current_user
8
+ nil
9
+ end
10
+
11
+ class << self
12
+
13
+ def define_useridentity(user_identity, token_store, token_key_user, token_key_session)
14
+ self.class_eval %-
15
+ def current_user(options={})
16
+ if #{token_store}[:#{token_key_user}].nil? || #{token_store}[:#{token_key_session}].nil?
17
+ return options[:default] if options.has_key? :default
18
+ raise "unauthenticated"
19
+ end
20
+ #{user_identity}.authenticate_by_token(#{token_store}[:#{token_key_user}], #{token_store}[:#{token_key_session}])
21
+ end
22
+ -
23
+ end
24
+
25
+ def add_create_stub(model)
26
+ m = model.name
27
+ permits = model.columns.map { |item| item.name }.to_json
28
+ self.module_eval %-
29
+ def create_#{AppBase.underscore m}
30
+ obj = #{m}.new(params.except(:action, :controller, :id).permit(#{permits}))
31
+ if !#{m}.allow_create?(current_user, obj)
32
+ render json: { status: "error", msg: "unauthorized" }
33
+ else
34
+ obj.save!
35
+ render json: { status: 'ok', id: obj.id }
36
+ end
37
+ rescue Exception => e
38
+ render json: { status: 'error', msg: e.to_s }
39
+ end
40
+ -
41
+ end
42
+
43
+ def add_update_stub(model)
44
+ m = model.name
45
+ permits = model.columns.map { |item| item.name }.to_json
46
+ self.module_eval %-
47
+ def update_#{AppBase.underscore m}
48
+ obj = #{m}.find(params[:id])
49
+ if obj.nil?
50
+ return render json: { status: 'error', msg: 'not_found' }
51
+ end
52
+ obj.update_attributes(params.except(:action, :controller, :id).permit(#{permits}))
53
+ if !#{m}.allow_update?(current_user, obj)
54
+ render json: { status: "error", msg: "unauthorized" }
55
+ else
56
+ obj.save!
57
+ render json: { status: 'ok' }
58
+ end
59
+ rescue Exception => e
60
+ render json: { status: 'error', msg: e.to_s }
61
+ end
62
+ -
63
+ end
64
+
65
+ def add_delete_stub(model)
66
+ m = model.name
67
+ self.module_eval %-
68
+ def delete_#{AppBase.underscore m}
69
+ obj = #{m}.find(params[:id])
70
+ if obj.nil?
71
+ return render json: { status: 'error', msg: 'not_found' }
72
+ end
73
+ if !#{m}.allow_delete?(current_user, obj)
74
+ render json: { status: "error", msg: "unauthorized" }
75
+ else
76
+ obj.delete
77
+ render json: { status: 'ok' }
78
+ end
79
+ rescue Exception => e
80
+ render json: { status: 'error', msg: e.to_s }
81
+ end
82
+ -
83
+ end
84
+
85
+ def add_query_stub(model)
86
+ m = model.name
87
+ columns = model.columns.map{|c|c.name}
88
+ self.class_eval %-
89
+ def query_#{AppBase.underscore m}
90
+ query = #{m}.accessible_by(current_user)
91
+ params.except(:action, :controller, :p, :ps).each { |k, v|
92
+ op = 'eq'
93
+ if k.index('.') && k.split('.').count == 2
94
+ k, op = k.split('.')
95
+ end
96
+ return if #{columns}.index(k).nil?
97
+ case op
98
+ when 'eq'
99
+ query = query.where "\#{k} = ?", v
100
+ when 'lt'
101
+ query = query.where "\#{k} < ?", v
102
+ when 'le'
103
+ query = query.where "\#{k} <= ?", v
104
+ when 'gt'
105
+ query = query.where "\#{k} > ?", v
106
+ when 'ge'
107
+ query = query.where "\#{k} >= ?", v
108
+ when 'n'
109
+ query = query.where "\#{k} IS NULL"
110
+ when 'nn'
111
+ query = query.where "\#{k} IS NOT NULL"
112
+ when 'in'
113
+ values = JSON.parse v
114
+ query = query.where "\#{k} IN (?)", values
115
+ when 'nin'
116
+ values = JSON.parse v
117
+ query = query.where "\#{k} NOT IN (?)", values
118
+ else
119
+ end
120
+ }
121
+ page_size = [1, (params[:ps]||20).to_i].max
122
+ start = [0, (params[:p]||1).to_i.pred].max * page_size
123
+ render json: { status: 'ok', data: query.offset(start).limit(page_size) }
124
+ rescue Exception => e
125
+ render json: { status: 'error', msg: e.to_s }
126
+ end
127
+ -
128
+ end
129
+
130
+ def add_rpc_method_stub(bound_method, auth=false)
131
+ m = bound_method.receiver.name
132
+ mn = bound_method.name
133
+ parameters = bound_method.parameters
134
+ 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, atuh: false` to expose #{m}.#{mn} to appbase without user authentication."
136
+ end
137
+ need_params = false
138
+ if parameters.last[0] == :opt
139
+ need_params = true
140
+ parameters = parameters[(auth ? 1 : 0)..-2]
141
+ else
142
+ parameters = parameters[(auth ? 1 : 0)..-1]
143
+ end
144
+ if parameters.find{|p|p[0]!=:req}
145
+ raise "Error exposing #{m}.#{mn} to appbase engine, appbase does not support rest/optional parameters, use options instead!"
146
+ end
147
+ requires = parameters.map{|p|":#{p[1]}"}
148
+ parameters = auth ? ['current_user'] : []
149
+ requires.each { |p| parameters << "params[#{p}]" }
150
+ if need_params
151
+ parameters.push "params.except(:action, :controller#{requires.count > 0 ? ", #{requires.join(', ')}" : ""})"
152
+ end
153
+ self.class_eval %-
154
+ def rpc_#{AppBase.underscore m}_#{mn}
155
+ #{requires.map{|p|"params.require #{p}"}.join(';')}
156
+ render json: { status: 'ok', data: #{m}.#{mn}(#{parameters.join(', ')}) }
157
+ rescue Exception => e
158
+ render json: { status: 'error', msg: e.to_s }
159
+ end
160
+ -
161
+ end
162
+
163
+ end
164
+
165
+ end
@@ -0,0 +1,108 @@
1
+ require 'active_support'
2
+
3
+ module AppBase
4
+
5
+ module ModelConcern
6
+
7
+ extend ActiveSupport::Concern
8
+
9
+ module ClassMethods
10
+
11
+ def expose_to_appbase(*method_names)
12
+ return if method_names.count == 0
13
+ options = {}
14
+ if method_names.last.instance_of? Hash
15
+ *method_names, options = method_names
16
+ end
17
+ method_names.each do |method_name|
18
+ AppBase::Registry.register_rpc self, method_name, options
19
+ end
20
+ end
21
+
22
+ def appbase_allow(crud, criteria=:mine, &block)
23
+ if [:create, :update, :delete, :query].index(crud).nil?
24
+ raise "Unsupported crud operation: #{crud}, available options: create, update, delete, query"
25
+ end
26
+ model = self
27
+ if criteria == :mine
28
+ # allow_xxx :mine or simply allow_xxx
29
+ AppBase::Engine.after_initialized do
30
+ user_identity_attr = "#{AppBase::Engine::UserIdentity.underscore}_id"
31
+ model.class_eval crud == :query ? %-
32
+ def self.accessible_by(user)
33
+ #{model.name}.where(:#{user_identity_attr} => user.id)
34
+ end
35
+ - : %-
36
+ def self.allow_#{crud}?(user, obj)
37
+ user.id == obj.#{user_identity_attr}
38
+ end
39
+ -
40
+ end
41
+ elsif crud != :query && criteria == :if && block_given? && block.parameters.count == 2
42
+ # allow_xxx :if do; end
43
+ AppBase::Engine.after_initialized do
44
+ user_identity_attr = "#{AppBase::Engine::UserIdentity.underscore}_id"
45
+ model.define_singleton_method "allow_#{crud}".to_sym, &block
46
+ end
47
+ elsif crud == :query && criteria == :within && block_given? && block.parameters.count == 1
48
+ # allow_query :within {|current_user| Model.where(...)}
49
+ AppBase::Engine.after_initialized do
50
+ user_identity_attr = "#{AppBase::Engine::UserIdentity.underscore}_id"
51
+ model.define_singleton_method :accessible_by, &block
52
+ end
53
+ elsif crud != :query && riteria.instance_of?(Hash) && criteria.has_key?(:if) && criteria[:if].instance_of?(Symbol)
54
+ # :if => :a_singleton_method
55
+ AppBase::Engine.after_initialized do
56
+ user_identity_attr = "#{AppBase::Engine::UserIdentity.underscore}_id"
57
+ model.class_eval %-
58
+ def self.allow_#{crud}?(user, obj)
59
+ #{model.name}.#{criteria[:if]} user
60
+ end
61
+ -
62
+ end
63
+ elsif crud == :query && criteria.instance_of?(Hash) && criteria.has_key?(:within) && criteria[:within].instance_of?(Symbol)
64
+ # allow_query :within => :a_singleton_query_method
65
+ AppBase::Engine.after_initialized do
66
+ user_identity_attr = "#{AppBase::Engine::UserIdentity.underscore}_id"
67
+ model.class_eval %-
68
+ def self.accessible_by(user)
69
+ #{model.name}.#{criteria[:within]} user
70
+ end
71
+ -
72
+ end
73
+ else
74
+ raise %-
75
+ allow_#{crud} usage:
76
+ allow_#{crud} :mine
77
+ allow_#{crud} :#{ crud == :query ? 'within' : 'if' } => :a_singleton_method
78
+ allow_#{crud} :#{ crud == :query ? 'within' : 'if' } do |current_user_identity#{ crud == :query ? '' : ', model_instance' }|
79
+ # #{ crud == :query ? 'return fitlered query, e.g. Note.where(:user_id => current_user_identity.id)' : 'return true if allowed' }
80
+ end
81
+ -
82
+ end
83
+ AppBase::Registry.register_crud self, crud
84
+ end
85
+
86
+ def allow_create(criteria=:mine, &block)
87
+ self.appbase_allow(:create, criteria, &block)
88
+ end
89
+
90
+ def allow_update(criteria=:mine, &block)
91
+ self.appbase_allow(:update, criteria, &block)
92
+ end
93
+
94
+ def allow_delete(criteria=:mine, &block)
95
+ self.appbase_allow(:delete, criteria, &block)
96
+ end
97
+
98
+ def allow_query(criteria=:mine, &block)
99
+ self.appbase_allow(:query, criteria, &block)
100
+ end
101
+
102
+ end
103
+
104
+ end
105
+
106
+ end
107
+
108
+ ActiveRecord::Base.include AppBase::ModelConcern
@@ -0,0 +1,126 @@
1
+ require 'rails'
2
+ require_relative "registry"
3
+ require_relative "model_concern"
4
+ require_relative "controllers/app_base_controller"
5
+
6
+ module AppBase
7
+
8
+ class Engine < Rails::Engine
9
+
10
+ paths["app/controllers"] = "lib/appbase/controllers"
11
+
12
+ class << self
13
+
14
+ initialized = false
15
+ config = nil
16
+ hooks = []
17
+
18
+ define_method :bootstrap do |app_config|
19
+ return if initialized
20
+ config = app_config.appbase
21
+
22
+ # initialize user identity
23
+ if config.user_identity.nil?
24
+ raise "AppBase configuration error: please use `config.appbase.user_identity = :UserIdentity` to specify the user identity model; and implement UserIdentity.authenticate_by_token(user, token):UserIdentity method."
25
+ end
26
+ user_identity = Object.const_get config.user_identity.to_sym
27
+ if !user_identity.respond_to?(:authenticate_by_token) || user_identity.method(:authenticate_by_token).parameters.count != 2
28
+ raise "It's required to implement UserIdentity.authenticate_by_token(user, token):UserIdentity method."
29
+ end
30
+ AppBase::Engine::UserIdentity = config.user_identity.to_s.extend AppBase::StringExtension
31
+ AppBaseController.define_useridentity config.user_identity, config.token_store, config.token_key_user, config.token_key_session
32
+
33
+ # initialize crud stubs
34
+ AppBase::Registry.each_crud config.models do |model, op|
35
+ model_name_underscore = AppBase.underscore model.name
36
+ case op
37
+ when :create
38
+ AppBaseController.add_create_stub(model)
39
+ AppBase::Engine.routes.append do
40
+ put "/#{model_name_underscore}" => "app_base#create_#{model_name_underscore}"
41
+ end
42
+ when :update
43
+ AppBaseController.add_update_stub(model)
44
+ AppBase::Engine.routes.append do
45
+ put "/#{model_name_underscore}/:id" => "app_base#update_#{model_name_underscore}"
46
+ end
47
+ when :delete
48
+ AppBaseController.add_delete_stub(model)
49
+ AppBase::Engine.routes.append do
50
+ delete "/#{model_name_underscore}/:id" => "app_base#delete_#{model_name_underscore}"
51
+ end
52
+ when :query
53
+ AppBaseController.add_query_stub(model)
54
+ AppBase::Engine.routes.append do
55
+ get "/#{model_name_underscore}" => "app_base#query_#{model_name_underscore}"
56
+ end
57
+ else
58
+ raise "Unexpected crud operation: #{op}"
59
+ end
60
+ end
61
+
62
+ # initialize rpc stubs
63
+ AppBase::Registry.each_rpc do |r|
64
+ if !r[:model].respond_to? r[:method]
65
+ raise "#{r[:model].name} does not respond to #{r[:method]}."
66
+ end
67
+ bound_method = r[:model].method r[:method]
68
+ AppBaseController.add_rpc_method_stub(bound_method, r[:auth])
69
+ AppBase::Engine.routes.append do
70
+ post "/#{AppBase.underscore r[:model].name}/#{r[:method]}" => "app_base#rpc_#{AppBase.underscore r[:model].name}_#{r[:method]}"
71
+ end
72
+ end
73
+
74
+ # finalize appbase routes
75
+ AppBase::Engine.routes.draw do
76
+ get "/appbase_version" => "app_base#version"
77
+ end
78
+
79
+ # after initialized
80
+ blocks, hooks = hooks, []
81
+ blocks.each do |block|
82
+ block.call
83
+ end
84
+
85
+ end
86
+
87
+ define_method :after_initialized do |&block|
88
+ if initialized
89
+ block.call
90
+ else
91
+ hooks << block
92
+ end
93
+ end
94
+
95
+ define_method :config do
96
+ config
97
+ end
98
+
99
+ end
100
+
101
+ end
102
+
103
+ class Railtie < Rails::Railtie
104
+
105
+ # default values for appbase configuration
106
+ config.appbase = ActiveSupport::OrderedOptions.new
107
+ config.appbase.mount = "/appbase"
108
+ config.appbase.user_identity = nil
109
+ config.appbase.token_store = :cookies # :cookies, :headers, :params
110
+ config.appbase.token_key_user = :u
111
+ config.appbase.token_key_session = :s
112
+ config.appbase.models = []
113
+
114
+ initializer "appbase.configure_route", :after => :add_routing_paths do |app|
115
+
116
+ AppBase::Engine.bootstrap app.config
117
+
118
+ app.routes.append do
119
+ mount AppBase::Engine => Rails.application.config.appbase.mount
120
+ end
121
+
122
+ end
123
+
124
+ end
125
+
126
+ end
@@ -0,0 +1,60 @@
1
+ module AppBase
2
+
3
+ def self.underscore(str)
4
+ str.gsub(/::/, '/').
5
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
6
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
7
+ tr("-", "_").
8
+ downcase
9
+ end
10
+
11
+ module StringExtension
12
+
13
+ def underscore
14
+ AppBase.underscore self
15
+ end
16
+ self
17
+
18
+ end
19
+
20
+ module Registry
21
+
22
+ class << Registry
23
+
24
+ rpc_methods = []
25
+ crud_permissions = []
26
+
27
+ define_method :register_rpc do |model, method_name, options={}|
28
+ model = Object.const_get(model.to_sym) if model.instance_of?(String) || model.instance_of?(Symbol)
29
+ method_name = method_name.to_sym
30
+ auth = options.has_key?(:auth) ? options[:auth] : true
31
+ if rpc_methods.find{ |r| r[:model] == model && r[:method] == method_name }.nil?
32
+ rpc_methods << { model: model, method: method_name, auth: auth }
33
+ else
34
+ raise "#{model}.#{method_name} has already been registered"
35
+ end
36
+ end
37
+
38
+ define_method :each_rpc do |&block|
39
+ rpc_methods.each &block
40
+ end
41
+
42
+ define_method :register_crud do |model, crud|
43
+ if crud_permissions.find{ |r| r[:model] == model && r[:crud] == crud }.nil?
44
+ crud_permissions << { model: model, crud: crud }
45
+ end
46
+ end
47
+
48
+ define_method :each_crud do |*models, &block|
49
+ models = models[0] if models.count == 1 && models.instance_of?(Array)
50
+ models = models.map { |model| (model.instance_of?(Symbol) || model.instance_of?(String)) ? Object.const_get(model) : model }
51
+ crud_permissions.each do |r|
52
+ block.call r[:model], r[:crud] if models.index(r[:model])
53
+ end
54
+ end
55
+
56
+ end
57
+
58
+ end
59
+
60
+ end
@@ -0,0 +1,3 @@
1
+ module AppBase
2
+ VERSION = '0.0.1'
3
+ end
data/lib/appbase.rb ADDED
@@ -0,0 +1,3 @@
1
+ $:.unshift(File.dirname(__FILE__))
2
+
3
+ require_relative 'appbase/railtie' if defined?(Rails::Railtie)
metadata ADDED
@@ -0,0 +1,119 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: appbase
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - bestmike007
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-12-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '4.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '4.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.8.7
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
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
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
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rails
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '4.0'
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'
83
+ description: A lightweight backend for Web/iOS/Android apps.
84
+ email: i@bestmike007.com
85
+ executables: []
86
+ extensions: []
87
+ extra_rdoc_files: []
88
+ files:
89
+ - lib/appbase.rb
90
+ - lib/appbase/controllers/app_base_controller.rb
91
+ - lib/appbase/model_concern.rb
92
+ - lib/appbase/railtie.rb
93
+ - lib/appbase/registry.rb
94
+ - lib/appbase/version.rb
95
+ homepage: http://bestmike007.com/appbase
96
+ licenses:
97
+ - MIT
98
+ metadata: {}
99
+ post_install_message:
100
+ rdoc_options: []
101
+ require_paths:
102
+ - lib
103
+ required_ruby_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ required_rubygems_version: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ requirements: []
114
+ rubyforge_project:
115
+ rubygems_version: 2.4.3
116
+ signing_key:
117
+ specification_version: 4
118
+ summary: Lightweight appbase
119
+ test_files: []