effective_resources 1.7.4 → 1.7.5
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/app/controllers/concerns/effective/crud_controller/permitted_params.rb +8 -5
- data/app/models/concerns/effective_devise_user.rb +167 -0
- data/app/models/effective/resource.rb +8 -0
- data/app/models/effective/resources/actions.rb +27 -54
- data/app/models/effective/resources/init.rb +20 -15
- data/app/models/effective/resources/klass.rb +2 -0
- data/lib/effective_resources/engine.rb +2 -0
- data/lib/effective_resources/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2c872b773ee626165d7d0dee9d363e9375fd7702c9f0330ce2492c380fcac45b
|
4
|
+
data.tar.gz: eadd2e25503395461ea1823d8b9e719eb80cb405373fcae2253c3467631c18ea
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2a91bcaab5fe78374b9771ddfe9117bb0594202e1e91dfc9d39922dc153b0ea38d64a5643365488097efb9fd258d8915fbdea12e7b9b7098fec456f46f9bbe30
|
7
|
+
data.tar.gz: b49ff8dc969fbb45db46a4db1ed97b3f369a2734db20e2c5957b28fe0aa5928045ef73d8170e06a975170dca40d8c5a5d09d3e31600b00904783ce35dca6ea2a
|
@@ -10,25 +10,28 @@ module Effective
|
|
10
10
|
raise 'expected resource class to have effective_resource do .. end' if effective_resource.model.blank?
|
11
11
|
|
12
12
|
permitted_params = permitted_params_for(resource, effective_resource.namespaces)
|
13
|
+
permitted_name = params.key?(effective_resource.name) ? effective_resource.name : effective_resource.resource_name
|
13
14
|
|
14
15
|
if Rails.env.development?
|
15
16
|
Rails.logger.info "Effective::CrudController#resource_permitted_params:"
|
16
|
-
Rails.logger.info "params.require(:#{
|
17
|
+
Rails.logger.info "params.require(:#{permitted_name}).permit(#{permitted_params.to_s[1...-1]})"
|
17
18
|
end
|
18
19
|
|
19
|
-
params.require(
|
20
|
+
params.require(permitted_name).permit(*permitted_params)
|
20
21
|
end
|
21
22
|
|
22
23
|
# If the resource is ActiveModel, just permit all
|
23
24
|
# This can still be overridden by a controller
|
24
25
|
def resource_active_model_permitted_params
|
26
|
+
permitted_name = params.key?(effective_resource.name) ? effective_resource.name : effective_resource.resource_name
|
27
|
+
|
25
28
|
if Rails.env.development?
|
26
29
|
Rails.logger.info "Effective::CrudController#resource_permitted_params:"
|
27
|
-
Rails.logger.info "params.require(:#{
|
30
|
+
Rails.logger.info "params.require(:#{permitted_name}).permit!"
|
28
31
|
end
|
29
32
|
|
30
|
-
if params[
|
31
|
-
params.require(
|
33
|
+
if params[permitted_name].present?
|
34
|
+
params.require(permitted_name).permit!
|
32
35
|
else
|
33
36
|
params.require((effective_resource.namespaces + [effective_resource.resource_name]).join('_')).permit!
|
34
37
|
end
|
@@ -0,0 +1,167 @@
|
|
1
|
+
# EffectiveDeviseUsr
|
2
|
+
#
|
3
|
+
# Mark your user model with devise_for then effective_devise_user
|
4
|
+
|
5
|
+
module EffectiveDeviseUser
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
module Base
|
9
|
+
def effective_devise_user
|
10
|
+
include ::EffectiveDeviseUser
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
included do
|
15
|
+
effective_resource do
|
16
|
+
encrypted_password :string
|
17
|
+
reset_password_token :string
|
18
|
+
reset_password_sent_at :datetime
|
19
|
+
remember_created_at :datetime
|
20
|
+
sign_in_count :integer
|
21
|
+
current_sign_in_at :datetime
|
22
|
+
last_sign_in_at :datetime
|
23
|
+
current_sign_in_ip :inet
|
24
|
+
last_sign_in_ip :inet
|
25
|
+
|
26
|
+
# Devise invitable attributes
|
27
|
+
invitation_token :string
|
28
|
+
invitation_created_at :datetime
|
29
|
+
invitation_sent_at :datetime
|
30
|
+
invitation_accepted_at :datetime
|
31
|
+
invitation_limit :integer
|
32
|
+
invited_by_type :string
|
33
|
+
invited_by_id :integer
|
34
|
+
invitations_count :integer
|
35
|
+
|
36
|
+
# Omniauth
|
37
|
+
uid :string
|
38
|
+
provider :string
|
39
|
+
|
40
|
+
access_token :string
|
41
|
+
refresh_token :string
|
42
|
+
token_expires_at :datetime
|
43
|
+
|
44
|
+
name :string
|
45
|
+
avatar_url :string
|
46
|
+
end
|
47
|
+
|
48
|
+
# Devise invitable ignores model validations, so we manually check for duplicate email addresses.
|
49
|
+
before_save(if: -> { new_record? && invitation_sent_at.present? }) do
|
50
|
+
if email.blank?
|
51
|
+
self.errors.add(:email, "can't be blank")
|
52
|
+
raise("email can't be blank")
|
53
|
+
end
|
54
|
+
|
55
|
+
if self.class.where(email: email.downcase.strip).exists?
|
56
|
+
self.errors.add(:email, 'has already been taken')
|
57
|
+
raise("email has already been taken")
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Clear the provider if an oauth signed in user resets password
|
62
|
+
before_save(if: -> { persisted? && encrypted_password_changed? }) do
|
63
|
+
assign_attributes(provider: nil, access_token: nil, refresh_token: nil, token_expires_at: nil)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
module ClassMethods
|
68
|
+
def permitted_sign_up_params # Should contain all fields as per views/users/_sign_up_fields
|
69
|
+
[:email, :password, :password_confirmation, :first_name, :last_name, :name, :login]
|
70
|
+
end
|
71
|
+
|
72
|
+
def from_omniauth(auth, params)
|
73
|
+
invitation_token = (params.presence || {})['invitation_token']
|
74
|
+
|
75
|
+
email = (auth.info.email.presence || "#{auth.uid}@#{auth.provider}.none").downcase
|
76
|
+
image = auth.info.image
|
77
|
+
name = auth.info.name || auth.dig(:extra, :raw_info, :login)
|
78
|
+
|
79
|
+
user = if invitation_token
|
80
|
+
find_by_invitation_token(invitation_token, false) || raise(ActiveRecord::RecordNotFound)
|
81
|
+
else
|
82
|
+
where(uid: auth.uid).or(where(email: email)).first || self.new()
|
83
|
+
end
|
84
|
+
|
85
|
+
user.assign_attributes(
|
86
|
+
uid: auth.uid,
|
87
|
+
provider: auth.provider,
|
88
|
+
email: email,
|
89
|
+
avatar_url: image,
|
90
|
+
name: name,
|
91
|
+
first_name: (auth.info.first_name.presence || name.split(' ').first.presence || 'First'),
|
92
|
+
last_name: (auth.info.last_name.presence || name.split(' ').last.presence || 'Last')
|
93
|
+
)
|
94
|
+
|
95
|
+
if auth.respond_to?(:credentials)
|
96
|
+
user.assign_attributes(
|
97
|
+
access_token: auth.credentials.token,
|
98
|
+
refresh_token: auth.credentials.refresh_token,
|
99
|
+
token_expires_at: Time.zone.at(auth.credentials.expires_at), # We are given integer datetime e.g. '1549394077'
|
100
|
+
)
|
101
|
+
end
|
102
|
+
|
103
|
+
# Make a password
|
104
|
+
user.password = Devise.friendly_token[0, 20] if user.encrypted_password.blank?
|
105
|
+
|
106
|
+
# Devise Invitable
|
107
|
+
invitation_token ? user.accept_invitation! : user.save!
|
108
|
+
|
109
|
+
# Devise Confirmable
|
110
|
+
user.confirm if user.respond_to?(:confirm)
|
111
|
+
|
112
|
+
user
|
113
|
+
end
|
114
|
+
|
115
|
+
# https://github.com/heartcombo/devise/blob/master/lib/devise/models/recoverable.rb#L134
|
116
|
+
def send_reset_password_instructions(attributes = {})
|
117
|
+
recoverable = find_or_initialize_with_errors(reset_password_keys, attributes, :not_found)
|
118
|
+
return recoverable unless recoverable.persisted?
|
119
|
+
|
120
|
+
# Add custom errors and require a confirmation if previous sign in was provider
|
121
|
+
if recoverable.provider.present? && attributes[:confirm_new_password].blank?
|
122
|
+
recoverable.errors.add(:email, "previous sign in was with #{recoverable.provider}")
|
123
|
+
recoverable.errors.add(:confirm_new_password, 'please confirm to proceed')
|
124
|
+
end
|
125
|
+
|
126
|
+
recoverable.send_reset_password_instructions if recoverable.errors.blank?
|
127
|
+
recoverable
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
|
132
|
+
# EffectiveDeviseUser Instance Methods
|
133
|
+
|
134
|
+
def reinvite!
|
135
|
+
invite!
|
136
|
+
end
|
137
|
+
|
138
|
+
def active_for_authentication?
|
139
|
+
super && (respond_to?(:archived?) ? !archived? : true)
|
140
|
+
end
|
141
|
+
|
142
|
+
def inactive_message
|
143
|
+
(respond_to?(:archived?) && archived?) ? :archived : super
|
144
|
+
end
|
145
|
+
|
146
|
+
# Any password will work in development or mode
|
147
|
+
def valid_password?(password)
|
148
|
+
Rails.env.development? || Rails.env.staging? || super
|
149
|
+
end
|
150
|
+
|
151
|
+
# Send devise & devise_invitable emails via active job
|
152
|
+
def send_devise_notification(notification, *args)
|
153
|
+
raise('expected args Hash') unless args.respond_to?(:last) && args.last.kind_of?(Hash)
|
154
|
+
|
155
|
+
|
156
|
+
|
157
|
+
if defined?(Tenant)
|
158
|
+
tenant = Tenant.current || raise('expected a current tenant')
|
159
|
+
args.last[:tenant] ||= tenant
|
160
|
+
end
|
161
|
+
|
162
|
+
wait = (5 if notification == :invitation_instructions && !Rails.env.test?)
|
163
|
+
|
164
|
+
devise_mailer.send(notification, self, *args).deliver_later(wait: wait)
|
165
|
+
end
|
166
|
+
|
167
|
+
end
|
@@ -14,10 +14,18 @@ module Effective
|
|
14
14
|
include Effective::Resources::Relation
|
15
15
|
include Effective::Resources::Sql
|
16
16
|
|
17
|
+
|
18
|
+
# In practice, this is initialized two ways
|
19
|
+
# With a klass and a namespace from effective_datatables
|
20
|
+
# Or with a controller_path from crud controller
|
21
|
+
|
17
22
|
# post, Post, Admin::Post, admin::Post, admin/posts, admin/post, admin/effective::post
|
18
23
|
def initialize(input, namespace: nil, relation: nil, &block)
|
19
24
|
_initialize_input(input, namespace: namespace, relation: relation)
|
25
|
+
|
26
|
+
# This is an effective_resource do ... end block
|
20
27
|
_initialize_model(&block) if block_given?
|
28
|
+
|
21
29
|
self
|
22
30
|
end
|
23
31
|
|
@@ -7,33 +7,27 @@ module Effective
|
|
7
7
|
POST_VERBS = ['POST', 'PUT', 'PATCH']
|
8
8
|
CRUD_ACTIONS = %i(index new create show edit update destroy)
|
9
9
|
|
10
|
-
# This
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
matches = [
|
15
|
-
[namespace, route_name.pluralize].compact.join('/'),
|
16
|
-
[namespace, route_name].compact.join('/'),
|
17
|
-
]
|
18
|
-
|
19
|
-
# Check main Rails app
|
20
|
-
routes = Rails.application.routes.routes.select do |route|
|
21
|
-
(matches & [route.defaults[:controller]]).present? && !route.name.to_s.end_with?('root')
|
22
|
-
end
|
10
|
+
# This will have been set by init from crud_controller, or from a class and namespace
|
11
|
+
def controller_path
|
12
|
+
@controller_path ||= ([namespace, plural_name].compact * '/')
|
13
|
+
end
|
23
14
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
15
|
+
def routes
|
16
|
+
@routes ||= begin
|
17
|
+
routes = nil
|
18
|
+
engines = [Rails.application] + Rails::Engine.subclasses.reverse
|
19
|
+
|
20
|
+
# Check from controller_path. This is generally correct.
|
21
|
+
engines.each do |engine|
|
22
|
+
routes = engine.routes.routes.select do |route|
|
23
|
+
controller_path == route.defaults[:controller] && !route.name.to_s.end_with?('root')
|
24
|
+
end
|
29
25
|
|
30
|
-
|
31
|
-
|
32
|
-
(matches & [route.defaults[:controller]]).present? && !route.name.to_s.end_with?('root')
|
26
|
+
if routes.present?
|
27
|
+
@routes_app = engine; break
|
33
28
|
end
|
34
29
|
end
|
35
30
|
|
36
|
-
# Check engine routes
|
37
31
|
if routes.blank?
|
38
32
|
matches = [
|
39
33
|
[namespace, route_name.pluralize].compact.join('/'),
|
@@ -46,21 +40,19 @@ module Effective
|
|
46
40
|
['effective', namespace, name].compact.join('/')
|
47
41
|
]
|
48
42
|
|
49
|
-
|
43
|
+
engines.each do |engine|
|
50
44
|
routes = engine.routes.routes.select do |route|
|
51
45
|
(matches & [route.defaults[:controller]]).present? && !route.name.to_s.end_with?('root')
|
52
46
|
end
|
53
47
|
|
54
48
|
if routes.present?
|
55
|
-
@routes_app = engine
|
56
|
-
break
|
49
|
+
@routes_app = engine; break
|
57
50
|
end
|
58
|
-
|
59
51
|
end
|
60
52
|
end
|
61
53
|
|
62
54
|
Array(routes).inject({}) { |h, route| h[route.defaults[:action].to_sym] = route; h }
|
63
|
-
|
55
|
+
end
|
64
56
|
end
|
65
57
|
|
66
58
|
def routes_app
|
@@ -137,58 +129,39 @@ module Effective
|
|
137
129
|
end
|
138
130
|
|
139
131
|
def crud_actions
|
140
|
-
|
132
|
+
(actions & CRUD_ACTIONS)
|
141
133
|
end
|
142
134
|
|
143
135
|
# GET actions
|
144
136
|
def collection_actions
|
145
|
-
|
146
|
-
routes.map { |_, route| route.defaults[:action].to_sym if is_collection_route?(route) }.tap(&:compact!)
|
147
|
-
)
|
137
|
+
routes.map { |_, route| route.defaults[:action].to_sym if is_collection_route?(route) } - [nil]
|
148
138
|
end
|
149
139
|
|
150
140
|
def collection_get_actions
|
151
|
-
|
152
|
-
routes.map { |_, route| route.defaults[:action].to_sym if is_collection_route?(route) && is_get_route?(route) }.tap(&:compact!)
|
153
|
-
)
|
141
|
+
routes.map { |_, route| route.defaults[:action].to_sym if is_collection_route?(route) && is_get_route?(route) } - [nil]
|
154
142
|
end
|
155
143
|
|
156
144
|
def collection_post_actions
|
157
|
-
|
158
|
-
routes.map { |_, route| route.defaults[:action].to_sym if is_collection_route?(route) && is_post_route?(route) }.tap(&:compact!)
|
159
|
-
)
|
145
|
+
routes.map { |_, route| route.defaults[:action].to_sym if is_collection_route?(route) && is_post_route?(route) } - [nil]
|
160
146
|
end
|
161
147
|
|
162
148
|
# All actions
|
163
149
|
def member_actions
|
164
|
-
|
165
|
-
routes.map { |_, route| route.defaults[:action].to_sym if is_member_route?(route) }.tap(&:compact!)
|
166
|
-
)
|
150
|
+
routes.map { |_, route| route.defaults[:action].to_sym if is_member_route?(route) } - [nil]
|
167
151
|
end
|
168
152
|
|
169
153
|
# GET actions
|
170
154
|
def member_get_actions
|
171
|
-
|
172
|
-
routes.map { |_, route| route.defaults[:action].to_sym if is_member_route?(route) && is_get_route?(route) }.tap(&:compact!)
|
173
|
-
)
|
155
|
+
routes.map { |_, route| route.defaults[:action].to_sym if is_member_route?(route) && is_get_route?(route) } - [nil]
|
174
156
|
end
|
175
157
|
|
176
158
|
def member_delete_actions
|
177
|
-
|
178
|
-
routes.map { |_, route| route.defaults[:action].to_sym if is_member_route?(route) && is_delete_route?(route) }.tap(&:compact!)
|
179
|
-
)
|
159
|
+
routes.map { |_, route| route.defaults[:action].to_sym if is_member_route?(route) && is_delete_route?(route) } - [nil]
|
180
160
|
end
|
181
161
|
|
182
162
|
# POST/PUT/PATCH actions
|
183
163
|
def member_post_actions
|
184
|
-
|
185
|
-
routes.map { |_, route| route.defaults[:action].to_sym if is_member_route?(route) && is_post_route?(route) }.tap(&:compact!)
|
186
|
-
)
|
187
|
-
end
|
188
|
-
|
189
|
-
# Same as controller_path in the view
|
190
|
-
def controller_path
|
191
|
-
[namespace, plural_name].compact * '/'
|
164
|
+
routes.map { |_, route| route.defaults[:action].to_sym if is_member_route?(route) && is_post_route?(route) } - [nil]
|
192
165
|
end
|
193
166
|
|
194
167
|
private
|
@@ -8,6 +8,11 @@ module Effective
|
|
8
8
|
@initialized_name = input
|
9
9
|
@model_klass = (relation ? _klass_by_input(relation) : _klass_by_input(input))
|
10
10
|
|
11
|
+
# Consider controller_name
|
12
|
+
if @model_klass && input.kind_of?(String) && namespace.blank?
|
13
|
+
@controller_path = input
|
14
|
+
end
|
15
|
+
|
11
16
|
# Consider namespaces
|
12
17
|
if namespace
|
13
18
|
@namespaces = (namespace.kind_of?(String) ? namespace.split('/') : Array(namespace))
|
@@ -62,30 +67,30 @@ module Effective
|
|
62
67
|
end
|
63
68
|
end
|
64
69
|
|
70
|
+
|
71
|
+
# 'acpa/admin/shirts'
|
65
72
|
def _klass_by_name(input)
|
66
73
|
input = input.to_s
|
67
74
|
input = input[1..-1] if input.start_with?('/')
|
68
75
|
|
69
76
|
names = input.split('/')
|
70
77
|
|
71
|
-
#
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
klass
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
@model_klass = klass
|
84
|
-
return klass
|
78
|
+
# Classify based on namespace
|
79
|
+
# acpa/admin/shirts
|
80
|
+
(names.length).downto(1).each do |n|
|
81
|
+
names.combination(n).to_a.each do |pieces|
|
82
|
+
klass_pieces = pieces.map { |piece| piece.classify } * '::'
|
83
|
+
klass = klass_pieces.safe_constantize
|
84
|
+
|
85
|
+
if klass.present? && klass.class != Module
|
86
|
+
@namespaces ||= (names - pieces)
|
87
|
+
@model_klass = klass
|
88
|
+
return klass
|
89
|
+
end
|
85
90
|
end
|
86
91
|
end
|
87
92
|
|
88
|
-
# Crazy engine
|
93
|
+
# Crazy effective engine.
|
89
94
|
if names[0] == 'admin'
|
90
95
|
class_name = (['effective'] + names[1..-1]).map { |name| name.classify } * '::'
|
91
96
|
klass = class_name.safe_constantize
|
@@ -8,6 +8,8 @@ module Effective
|
|
8
8
|
|
9
9
|
def datatable_klass
|
10
10
|
if defined?(EffectiveDatatables)
|
11
|
+
"#{controller_path.classify.pluralize}Datatable".safe_constantize ||
|
12
|
+
"#{controller_path.classify}Datatable".safe_constantize ||
|
11
13
|
"#{namespaced_class_name.pluralize}Datatable".safe_constantize ||
|
12
14
|
"#{namespaced_module_name.pluralize}Datatable".safe_constantize ||
|
13
15
|
"#{namespaced_class_name.pluralize.gsub('::', '')}Datatable".safe_constantize ||
|
@@ -27,6 +27,8 @@ module EffectiveResources
|
|
27
27
|
ActiveRecord::Base.extend(ActsAsSlugged::Base)
|
28
28
|
ActiveRecord::Base.extend(ActsAsStatused::Base)
|
29
29
|
ActiveRecord::Base.extend(ActsAsWizard::Base)
|
30
|
+
|
31
|
+
ActiveRecord::Base.extend(EffectiveDeviseUser::Base)
|
30
32
|
ActiveRecord::Base.extend(EffectiveResource::Base)
|
31
33
|
end
|
32
34
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: effective_resources
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.7.
|
4
|
+
version: 1.7.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Code and Effect
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-02-
|
11
|
+
date: 2021-02-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -141,6 +141,7 @@ files:
|
|
141
141
|
- app/models/concerns/acts_as_statused.rb
|
142
142
|
- app/models/concerns/acts_as_tokened.rb
|
143
143
|
- app/models/concerns/acts_as_wizard.rb
|
144
|
+
- app/models/concerns/effective_devise_user.rb
|
144
145
|
- app/models/concerns/effective_resource.rb
|
145
146
|
- app/models/effective/access_denied.rb
|
146
147
|
- app/models/effective/action_failed.rb
|