appbase 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.
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: []