effective_resources 1.7.3 → 1.7.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a63a4abae27e2b70ac922d2f3eca07c701842b9ef2482677d200236f05a05dc8
4
- data.tar.gz: 136206e80f42d5c019f9113d46c3260d19115f7f40ef58f0064f74b57283167f
3
+ metadata.gz: 7a4d2d6652da6f85e884ceb383ff012fda17863ad4b84c7cfaf5fec76a8632af
4
+ data.tar.gz: 155b5a31cc14908ea944978d940d550227bcb823e14bbc42e6f34108ece06c35
5
5
  SHA512:
6
- metadata.gz: 2b08dd74cfa1605c8dcf8e8082612b078ff20b93f9bfbb931f41b8d58ec2795f4a6ca0ed13f837860df4b3166c2069136ab26e0b19a46fa812af2293674bb56a
7
- data.tar.gz: a64adaf287722c737951827174f1074ad0580688540944551f406d66fb28dadf74ca7c845c57f008aea6036c355655674328c37c89cc90be36f40738753db436
6
+ metadata.gz: 4227d0379585019dafae032ac3300cc98ceed87f81bfcb45a35ab9b10c5e15f18dd7e6420b1973bef3e1567dd8ac71d78049b6ec6711153899568ed0d3398fc3
7
+ data.tar.gz: 700279f4126eefafe9b14d06b4dea16b9cf49c1f2b21fd472a17a80e3e2863d2f5d6318f481d3f862653d95f753960f5a0fbc79802301e3879422cec89866df8
data/README.md CHANGED
@@ -362,7 +362,7 @@ rails test
362
362
 
363
363
  ## License
364
364
 
365
- MIT License. Copyright [Code and Effect Inc.](http://www.codeandeffect.com/)
365
+ MIT License. Copyright [Code and Effect Inc.](http://www.codeandeffect.com/)
366
366
 
367
367
  ## Contributing
368
368
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Effective
2
3
  module CrudController
3
4
  extend ActiveSupport::Concern
@@ -12,6 +13,7 @@ module Effective
12
13
  included do
13
14
  define_actions_from_routes
14
15
  define_callbacks :resource_render, :resource_before_save, :resource_after_save, :resource_after_commit, :resource_error
16
+ layout -> { resource_layout }
15
17
  end
16
18
 
17
19
  module ClassMethods
@@ -111,6 +113,11 @@ module Effective
111
113
  datatable
112
114
  end
113
115
 
116
+ def resource_layout
117
+ namespace = controller_path.include?('admin/') ? 'admin' : 'application'
118
+ defined?(Tenant) ? "#{Tenant.current}/#{namespace}" : namespace
119
+ end
120
+
114
121
  def resource_params_method_name
115
122
  ['permitted_params', "#{resource_name}_params", "#{resource_plural_name}_params"].each do |name|
116
123
  return name if respond_to?(name, true)
@@ -53,7 +53,7 @@ module Effective
53
53
 
54
54
  respond_to do |format|
55
55
  format.html { }
56
- format.js { render('new.js') }
56
+ format.js { render('new', formats: :js) }
57
57
  end
58
58
  end
59
59
 
@@ -95,7 +95,7 @@ module Effective
95
95
 
96
96
  respond_to do |format|
97
97
  format.html { }
98
- format.js { render('show.js') }
98
+ format.js { render('show', formats: :js) }
99
99
  end
100
100
 
101
101
  end
@@ -112,7 +112,7 @@ module Effective
112
112
 
113
113
  respond_to do |format|
114
114
  format.html { }
115
- format.js { render('edit.js') }
115
+ format.js { render('edit', formats: :js) }
116
116
  end
117
117
 
118
118
  end
@@ -176,7 +176,10 @@ module Effective
176
176
 
177
177
  respond_to do |format|
178
178
  format.html { }
179
- format.js { render(template_present?(action) ? action : 'member_action.js', locals: { action: action }) }
179
+ format.js do
180
+ template = template_present?(action) ? action : 'member_action'
181
+ render(template, formats: :js, locals: { action: action })
182
+ end
180
183
  end
181
184
 
182
185
  return
@@ -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(:#{effective_resource.resource_name}).permit(#{permitted_params.to_s[1...-1]})"
17
+ Rails.logger.info "params.require(:#{permitted_name}).permit(#{permitted_params.to_s[1...-1]})"
17
18
  end
18
19
 
19
- params.require(effective_resource.resource_name).permit(*permitted_params)
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(:#{effective_resource.resource_name}).permit!"
30
+ Rails.logger.info "params.require(:#{permitted_name}).permit!"
28
31
  end
29
32
 
30
- if params[effective_resource.resource_name].present?
31
- params.require(effective_resource.resource_name).permit!
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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Effective
2
4
  module CrudController
3
5
  module Respond
@@ -94,7 +96,10 @@ module Effective
94
96
  end
95
97
 
96
98
  def template_present?(action)
97
- lookup_context.template_exists?("#{action}.#{request.format.symbol.to_s.sub('json', 'js').presence || 'html'}", _prefixes)
99
+ #lookup_context.template_exists?("#{action}.#{request.format.symbol.to_s.sub('json', 'js').presence || 'html'}", _prefixes)
100
+
101
+ formats = [request.format.symbol.to_s.sub('json', 'js').presence || 'html']
102
+ lookup_context.template_exists?(action, _prefixes, formats: formats)
98
103
  end
99
104
 
100
105
  end
@@ -76,6 +76,7 @@ module Effective
76
76
  def resource_flash(status, resource, action, e: nil)
77
77
  submit = commit_action(action)
78
78
  message = submit[status].respond_to?(:call) ? instance_exec(&submit[status]) : submit[status]
79
+
79
80
  return message.gsub('@resource', resource.to_s) if message.present?
80
81
  return nil if message.blank? && submit.key?(status)
81
82
 
@@ -93,6 +94,9 @@ module Effective
93
94
 
94
95
  # Should return a new resource based on the passed one
95
96
  def duplicate_resource(resource)
97
+ return resource.duplicate if resource.respond_to?(:duplicate)
98
+ return resource.duplicate! if resource.respond_to?(:duplicate!)
99
+ return resource.deep_dup if resource.respond_to?(:deep_dup)
96
100
  resource.dup
97
101
  end
98
102
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Effective
2
4
  module FlashMessages
3
5
  extend ActiveSupport::Concern
@@ -6,7 +8,11 @@ module Effective
6
8
  def flash_success(resource, action = nil, name: nil)
7
9
  raise 'expected an ActiveRecord resource' unless (name || resource.class.respond_to?(:model_name))
8
10
 
9
- "Successfully #{action_verb(action)} #{name || resource}".html_safe
11
+ name ||= begin
12
+ resource.destroyed? ? resource.class.model_name.to_s.downcase.split('::').last : resource.to_s.presence
13
+ end
14
+
15
+ "Successfully #{action_verb(action)} #{name || 'resource'}".html_safe
10
16
  end
11
17
 
12
18
  # flash.now[:danger] = flash_danger(@post)
@@ -18,7 +24,9 @@ module Effective
18
24
 
19
25
  messages = flash_errors(resource, e: e)
20
26
 
21
- name ||= resource.to_s.presence
27
+ name ||= begin
28
+ resource.destroyed? ? resource.class.model_name.to_s.downcase.split('::').last : resource.to_s.presence
29
+ end
22
30
 
23
31
  ["Unable to #{action}", (" #{name}" if name), (": #{messages}" if messages)].compact.join.html_safe
24
32
  end
@@ -27,7 +35,10 @@ module Effective
27
35
  def flash_errors(resource, e: nil)
28
36
  raise 'expected an ActiveRecord resource' unless resource.respond_to?(:errors)
29
37
 
30
- messages = resource.errors.map do |attribute, message|
38
+ messages = resource.errors.map do |error|
39
+ attribute = error.respond_to?(:attribute) ? error.attribute : error.first
40
+ message = error.respond_to?(:message) ? error.message : error.last
41
+
31
42
  if message[0] == message[0].upcase # If the error begins with a capital letter
32
43
  message
33
44
  elsif attribute == :base
@@ -52,6 +63,8 @@ module Effective
52
63
  'deleted'
53
64
  elsif word == 'undo'
54
65
  'undid'
66
+ elsif word == 'run'
67
+ 'ran'
55
68
  elsif word.end_with?('e')
56
69
  action.sub(word, word + 'd')
57
70
  elsif ['a', 'i', 'o', 'u'].include?(word[-1])
@@ -107,15 +107,16 @@ module EffectiveResourcesHelper
107
107
 
108
108
  # Select Partial
109
109
  partial = if partial.kind_of?(Symbol)
110
- "effective/resource/actions_#{partial}.html"
110
+ "effective/resource/actions_#{partial}"
111
111
  else
112
- "#{partial.presence || 'effective/resource/actions'}.html"
112
+ partial.presence || 'effective/resource/actions'
113
113
  end
114
114
 
115
115
  # Assign Locals
116
116
  locals = {
117
117
  resource: resource,
118
118
  effective_resource: effective_resource,
119
+ formats: [:html],
119
120
  format_block: (block if block_given?),
120
121
  namespace: namespace,
121
122
  actions: actions,
@@ -123,7 +124,14 @@ module EffectiveResourcesHelper
123
124
  }.compact.merge(locals)
124
125
 
125
126
  if resource.kind_of?(Array)
126
- render(partial: partial, collection: resource, as: :resource, locals: locals.except(:resource), spacer_template: spacer_template)
127
+ render(
128
+ partial: partial,
129
+ formats: [:html],
130
+ collection: resource,
131
+ as: :resource,
132
+ locals: locals.except(:resource),
133
+ spacer_template: spacer_template
134
+ )
127
135
  else
128
136
  render(partial, locals)
129
137
  end
@@ -143,16 +151,24 @@ module EffectiveResourcesHelper
143
151
  atts = { :namespace => (effective_resource.namespace.to_sym if effective_resource.namespace), effective_resource.name.to_sym => resource }.compact.merge(atts)
144
152
 
145
153
  if lookup_context.template_exists?("form_#{action}", controller._prefixes, :partial)
146
- render "form_#{action}", atts
147
- elsif lookup_context.template_exists?('form', controller._prefixes, :partial)
148
- render 'form', atts
149
- elsif lookup_context.template_exists?('form', effective_resource.plural_name, :partial)
150
- render "#{effective_resource.plural_name}/form", atts
151
- elsif lookup_context.template_exists?('form', effective_resource.name, :partial)
152
- render "#{effective_resource.name}/form", atts
153
- else
154
- render 'form', atts # Will raise the regular error
154
+ return render("form_#{action}", atts)
155
+ end
156
+
157
+ if lookup_context.template_exists?('form', controller._prefixes, :partial)
158
+ return render('form', atts)
159
+ end
160
+
161
+ effective_resource.view_paths.each do |view_path|
162
+ if lookup_context.template_exists?("form_#{action}", [view_path], :partial)
163
+ return render(view_path + '/' + "form_#{action}", atts)
164
+ end
165
+
166
+ if lookup_context.template_exists?('form', [view_path], :partial)
167
+ return render(view_path + '/' + 'form', atts)
168
+ end
155
169
  end
170
+
171
+ render('form', atts) # Will raise the regular error
156
172
  end
157
173
 
158
174
  # Similar to render_resource_form
@@ -169,14 +185,16 @@ module EffectiveResourcesHelper
169
185
  atts = { :namespace => (effective_resource.namespace.to_sym if effective_resource.namespace), effective_resource.name.to_sym => resource }.compact.merge(atts)
170
186
 
171
187
  if lookup_context.template_exists?(effective_resource.name, controller._prefixes, :partial)
172
- render(effective_resource.name, atts)
173
- elsif lookup_context.template_exists?(effective_resource.name, [effective_resource.plural_name], :partial)
174
- render(effective_resource.plural_name + '/' + effective_resource.name, atts)
175
- elsif lookup_context.template_exists?(effective_resource.name, [effective_resource.name], :partial)
176
- render(effective_resource.name + '/' + effective_resource.name, atts)
177
- else
178
- render(resource, atts) # Will raise the regular error
188
+ return render(effective_resource.name, atts)
179
189
  end
190
+
191
+ effective_resource.view_paths.each do |view_path|
192
+ if lookup_context.template_exists?(effective_resource.name, [view_path], :partial)
193
+ return render(view_path + '/' + effective_resource.name, atts)
194
+ end
195
+ end
196
+
197
+ render(resource, atts) # Will raise the regular error
180
198
  end
181
199
 
182
200
  # Tableize attributes
@@ -16,7 +16,9 @@ module ActsAsSlugged
16
16
  included do
17
17
  extend FinderMethods
18
18
 
19
- before_validation { self.slug ||= build_slug }
19
+ before_validation do
20
+ assign_attributes(slug: build_slug) if slug.blank?
21
+ end
20
22
 
21
23
  validates :slug,
22
24
  presence: true, uniqueness: true, exclusion: { in: excluded_slugs }, length: { maximum: 255 },
@@ -44,7 +46,7 @@ module ActsAsSlugged
44
46
 
45
47
  # Instance Methods
46
48
  def build_slug
47
- slug = self.to_s.parameterize.downcase[0, 250]
49
+ slug = to_s.parameterize.downcase[0, 250]
48
50
 
49
51
  if self.class.excluded_slugs.include?(slug)
50
52
  slug = "#{slug}-#{self.class.name.demodulize.parameterize}"
@@ -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
@@ -27,6 +27,7 @@ module Effective
27
27
  @type ||= (
28
28
  case obj
29
29
  when :boolean ; :boolean
30
+ when :config ; :config
30
31
  when :currency ; :currency
31
32
  when :date ; :date
32
33
  when :datetime ; :datetime
@@ -73,6 +74,9 @@ module Effective
73
74
  case type
74
75
  when :boolean
75
76
  [true, 'true', 't', '1'].include?(value)
77
+ when :config
78
+ raise('expected an ActiveSupport::OrderedOptions') unless value.kind_of?(ActiveSupport::OrderedOptions)
79
+ parse_ordered_options(value)
76
80
  when :date, :datetime
77
81
  if (digits = value.to_s.scan(/(\d+)/).flatten).present?
78
82
  date = if digits.first.length == 4 # 2017-01-10
@@ -169,5 +173,14 @@ module Effective
169
173
  name == other.name && type == other.type
170
174
  end
171
175
 
176
+ # This returns a nested ActiveSupport::OrderedOptions.new config
177
+ def parse_ordered_options(obj)
178
+ return obj unless obj.kind_of?(Hash)
179
+
180
+ ActiveSupport::OrderedOptions.new.tap do |config|
181
+ obj.each { |key, value| config[key] = parse_ordered_options(value) }
182
+ end
183
+ end
184
+
172
185
  end
173
186
  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,60 +7,43 @@ 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 was written for the Edit actions fallback templates and Datatables
11
- # Effective::Resource.new('admin/posts').routes[:index]
10
+ # This will have been set by init from crud_controller, or from a class and namespace
11
+ def controller_path
12
+ @controller_path ||= route_name #[namespace, plural_name].compact * '/')
13
+ end
14
+
12
15
  def routes
13
- @routes ||= (
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
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 || '').end_with?('root')
24
+ end
23
25
 
24
- if routes.blank?
25
- matches = [
26
- [namespace, plural_name].compact.join('/'),
27
- [namespace, name].compact.join('/')
28
- ]
29
-
30
- # Check main Rails app
31
- routes = Rails.application.routes.routes.select do |route|
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
- matches = [
39
- [namespace, route_name.pluralize].compact.join('/'),
40
- [namespace, route_name].compact.join('/'),
41
- ['effective', namespace, route_name.pluralize].compact.join('/'),
42
- ['effective', namespace, route_name].compact.join('/'),
43
- [namespace, plural_name].compact.join('/'),
44
- [namespace, name].compact.join('/'),
45
- ['effective', namespace, plural_name].compact.join('/'),
46
- ['effective', namespace, name].compact.join('/')
47
- ]
48
-
49
- (Rails::Engine.subclasses.reverse + [Rails.application]).each do |engine|
32
+ matches = route_name_fallbacks()
33
+
34
+ engines.each do |engine|
50
35
  routes = engine.routes.routes.select do |route|
51
- (matches & [route.defaults[:controller]]).present? && !route.name.to_s.end_with?('root')
36
+ (matches & [route.defaults[:controller]]).present? && !(route.name || '').end_with?('root')
52
37
  end
53
38
 
54
39
  if routes.present?
55
- @routes_app = engine
56
- break
40
+ @routes_app = engine; break
57
41
  end
58
-
59
42
  end
60
43
  end
61
44
 
62
45
  Array(routes).inject({}) { |h, route| h[route.defaults[:action].to_sym] = route; h }
63
- )
46
+ end
64
47
  end
65
48
 
66
49
  def routes_app
@@ -137,58 +120,39 @@ module Effective
137
120
  end
138
121
 
139
122
  def crud_actions
140
- @crud_actions ||= (actions & CRUD_ACTIONS)
123
+ (actions & CRUD_ACTIONS)
141
124
  end
142
125
 
143
126
  # GET actions
144
127
  def collection_actions
145
- @collection_actions ||= (
146
- routes.map { |_, route| route.defaults[:action].to_sym if is_collection_route?(route) }.tap(&:compact!)
147
- )
128
+ routes.map { |_, route| route.defaults[:action].to_sym if is_collection_route?(route) } - [nil]
148
129
  end
149
130
 
150
131
  def collection_get_actions
151
- @collection_get_actions ||= (
152
- routes.map { |_, route| route.defaults[:action].to_sym if is_collection_route?(route) && is_get_route?(route) }.tap(&:compact!)
153
- )
132
+ routes.map { |_, route| route.defaults[:action].to_sym if is_collection_route?(route) && is_get_route?(route) } - [nil]
154
133
  end
155
134
 
156
135
  def collection_post_actions
157
- @collection_post_actions ||= (
158
- routes.map { |_, route| route.defaults[:action].to_sym if is_collection_route?(route) && is_post_route?(route) }.tap(&:compact!)
159
- )
136
+ routes.map { |_, route| route.defaults[:action].to_sym if is_collection_route?(route) && is_post_route?(route) } - [nil]
160
137
  end
161
138
 
162
139
  # All actions
163
140
  def member_actions
164
- @member_actions ||= (
165
- routes.map { |_, route| route.defaults[:action].to_sym if is_member_route?(route) }.tap(&:compact!)
166
- )
141
+ routes.map { |_, route| route.defaults[:action].to_sym if is_member_route?(route) } - [nil]
167
142
  end
168
143
 
169
144
  # GET actions
170
145
  def member_get_actions
171
- @member_get_actions ||= (
172
- routes.map { |_, route| route.defaults[:action].to_sym if is_member_route?(route) && is_get_route?(route) }.tap(&:compact!)
173
- )
146
+ routes.map { |_, route| route.defaults[:action].to_sym if is_member_route?(route) && is_get_route?(route) } - [nil]
174
147
  end
175
148
 
176
149
  def member_delete_actions
177
- @member_delete_actions ||= (
178
- routes.map { |_, route| route.defaults[:action].to_sym if is_member_route?(route) && is_delete_route?(route) }.tap(&:compact!)
179
- )
150
+ routes.map { |_, route| route.defaults[:action].to_sym if is_member_route?(route) && is_delete_route?(route) } - [nil]
180
151
  end
181
152
 
182
153
  # POST/PUT/PATCH actions
183
154
  def member_post_actions
184
- @member_post_actions ||= (
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 * '/'
155
+ routes.map { |_, route| route.defaults[:action].to_sym if is_member_route?(route) && is_post_route?(route) } - [nil]
192
156
  end
193
157
 
194
158
  private
@@ -7,6 +7,7 @@ module Effective
7
7
  case (type || sql_type(name))
8
8
  when :belongs_to
9
9
  { as: :select }.merge(search_form_field_collection(belongs_to(name)))
10
+
10
11
  when :belongs_to_polymorphic
11
12
  constant_pluralized = name.to_s.upcase
12
13
  constant = name.to_s.pluralize.upcase
@@ -18,7 +19,7 @@ module Effective
18
19
  collection ||= (klass.const_get(constant_pluralized) rescue nil) if defined?("#{klass.name}::#{constant_pluralized}")
19
20
  end
20
21
 
21
- { as: :select, polymorphic: true, collection: collection }.compact
22
+ { as: :select, polymorphic: true, collection: (collection || []) }.compact
22
23
  when :has_and_belongs_to_many
23
24
  { as: :select }.merge(search_form_field_collection(has_and_belongs_to_many(name)))
24
25
  when :has_many
@@ -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
- # Crazy classify
72
- 0.upto(names.length-1) do |index|
73
- class_name = names[index..-1].map { |name| name.classify } * '::'
74
- klass = class_name.safe_constantize
75
-
76
- if klass.blank? && index > 0
77
- class_name = (names[0..index-1].map { |name| name.classify.pluralize } + names[index..-1].map { |name| name.classify }) * '::'
78
- klass = class_name.safe_constantize
79
- end
80
-
81
- if klass.present?
82
- @namespaces ||= names[0...index]
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 ||
@@ -19,8 +19,27 @@ module Effective
19
19
  @initialized_name
20
20
  end
21
21
 
22
+ # There could be a few, this is the best guess.
22
23
  def route_name # 'post' initialized from the controller_path/initialized_name and not the class
23
- @route_name ||= (initialized_name.to_s.split(SPLIT).last || '').singularize.underscore
24
+ names = class_name.split('::')
25
+
26
+ if names.length > 1
27
+ Array(names[0]) + namespaces + Array(names[1..-1])
28
+ else
29
+ namespaces + names
30
+ end.compact.map(&:downcase).join('/').pluralize
31
+ end
32
+
33
+ def route_name_fallbacks
34
+ mod = class_name.split('::').first.downcase
35
+
36
+ matches = [
37
+ route_name.singularize,
38
+ [*namespace, plural_name].join('/'),
39
+ [*namespace, name].join('/'),
40
+ [mod, *namespace, plural_name].join('/'),
41
+ [mod, *namespace, name].join('/')
42
+ ]
24
43
  end
25
44
 
26
45
  def class_name # 'Effective::Post'
@@ -22,6 +22,22 @@ module Effective
22
22
  File.join('app/views', plural_name, "#{'_' if partial}#{action}.html.haml")
23
23
  end
24
24
 
25
+ # Used by render_resource_partial and render_resource_form to guess the view path
26
+ def view_paths
27
+ mod = class_name.split('::').first.downcase
28
+
29
+ [
30
+ [mod, *namespace, plural_name].join('/'),
31
+ [mod, *namespace, name].join('/'),
32
+ [*namespace, mod, plural_name].join('/'),
33
+ [*namespace, mod, name].join('/'),
34
+ [mod, plural_name].join('/'),
35
+ [mod, name].join('/'),
36
+ [*namespace, plural_name].join('/'),
37
+ [*namespace, name].join('/')
38
+ ]
39
+ end
40
+
25
41
  end
26
42
  end
27
43
  end
@@ -1,25 +1,24 @@
1
1
  require 'effective_resources/engine'
2
2
  require 'effective_resources/version'
3
+ require 'effective_resources/effective_gem'
3
4
 
4
5
  module EffectiveResources
5
6
 
6
- # The following are all valid config keys
7
- mattr_accessor :authorization_method
8
- mattr_accessor :default_submits
9
-
10
- def self.setup
11
- yield self
7
+ def self.config_keys
8
+ [:authorization_method, :default_submits]
12
9
  end
13
10
 
11
+ include EffectiveGem
12
+
14
13
  def self.authorized?(controller, action, resource)
15
- @_exceptions ||= [Effective::AccessDenied, (CanCan::AccessDenied if defined?(CanCan)), (Pundit::NotAuthorizedError if defined?(Pundit))].compact
14
+ @exceptions ||= [Effective::AccessDenied, (CanCan::AccessDenied if defined?(CanCan)), (Pundit::NotAuthorizedError if defined?(Pundit))].compact
16
15
 
17
16
  return !!authorization_method unless authorization_method.respond_to?(:call)
18
17
  controller = controller.controller if controller.respond_to?(:controller)
19
18
 
20
19
  begin
21
20
  !!(controller || self).instance_exec((controller || self), action, resource, &authorization_method)
22
- rescue *@_exceptions
21
+ rescue *@exceptions
23
22
  false
24
23
  end
25
24
  end
@@ -29,9 +28,7 @@ module EffectiveResources
29
28
  end
30
29
 
31
30
  def self.default_submits
32
- @_default_submits ||= begin
33
- (['Save', 'Continue', 'Add New'] & Array(@@default_submits)).inject({}) { |h, v| h[v] = true; h }
34
- end
31
+ (['Save', 'Continue', 'Add New'] & Array(config.default_submits)).inject({}) { |h, v| h[v] = true; h }
35
32
  end
36
33
 
37
34
  end
@@ -0,0 +1,42 @@
1
+ # Effective Engine concern
2
+
3
+ module EffectiveGem
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ raise("expected self.config_keys method") unless respond_to?(:config_keys)
8
+
9
+ config_keys.each do |key|
10
+ self.class.define_method(key) { config()[key] }
11
+ end
12
+ end
13
+
14
+ module ClassMethods
15
+ def config(namespace = nil)
16
+ namespace ||= Tenant.current if defined?(Tenant)
17
+ @config.dig(namespace) || @config
18
+ end
19
+
20
+ def setup(namespace = nil, &block)
21
+ @config ||= ActiveSupport::OrderedOptions.new
22
+ namespace ||= Tenant.current if defined?(Tenant)
23
+
24
+ if namespace
25
+ @config[namespace] ||= ActiveSupport::OrderedOptions.new
26
+ end
27
+
28
+ yield(config(namespace))
29
+
30
+ if(unsupported = (config(namespace).keys - config_keys)).present?
31
+ if unsupported.include?(:authorization_method)
32
+ raise("config.authorization_method has been removed. This gem will call EffectiveResources.authorization_method instead. Please double check the config.authorization_method setting in config/initializers/effective_resources.rb and remove it from this file.")
33
+ end
34
+
35
+ raise("unsupported config keys: #{unsupported}\n supported keys: #{config_keys}")
36
+ end
37
+
38
+ true
39
+ end
40
+ end
41
+
42
+ end
@@ -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
@@ -1,3 +1,3 @@
1
1
  module EffectiveResources
2
- VERSION = '1.7.3'.freeze
2
+ VERSION = '1.7.8'.freeze
3
3
  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.3
4
+ version: 1.7.8
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-02 00:00:00.000000000 Z
11
+ date: 2021-02-22 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
@@ -179,6 +180,7 @@ files:
179
180
  - app/views/effective/resource/_actions_glyphicons.html.haml
180
181
  - config/effective_resources.rb
181
182
  - lib/effective_resources.rb
183
+ - lib/effective_resources/effective_gem.rb
182
184
  - lib/effective_resources/engine.rb
183
185
  - lib/effective_resources/version.rb
184
186
  - lib/generators/effective_resources/install_generator.rb