effective_resources 1.5.1 → 1.6.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '0088b5cd07643454e1be8f7c9e01ccd0f5c1b7f93711b9cb86f90e03b74fa463'
4
- data.tar.gz: e1e1fc6e1e3f3f2d025112681a0e868252f176e447ee9245800994f9b308e41e
3
+ metadata.gz: 2318bd8d0a93c63c8287583dd80a6b6e067bb5677f330b930d58588677d2cb55
4
+ data.tar.gz: 82af0a3fdbb5c27bd2498bf82017c398485b755d729674e8963796b5b72f85c9
5
5
  SHA512:
6
- metadata.gz: 6a8171a67e5006edbe0b7e3fe17e9e826f8657325314d285fc866328306d54ae14865f5126024f378e77612846aa7d7b8498f102443cfb5e5b7392210dda8deb
7
- data.tar.gz: b02c0e21f4263200e8cccec56e4948466a889028ea2ce8d3eb24386776e4e5549255befc769dcb1c60f3c3a2870a02faee3218e0fd74bdae103e5a576f638475
6
+ metadata.gz: 5bd4c614b11beea8c848996a541b2e729092164543d9b69727ae346b6146783b0024c8ebeb428615c5806c87107146b10a7055800cf7ad0a7f42a80fea0f54bf
7
+ data.tar.gz: d942166674ede76968161547a5eef54c1f3e9db015c2878d67ebaaa930117ad53f19beeaeea5632e4d69d39282cadf848c0da5c2cf151b3079d6288d4daeadab
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Effective Resources
2
2
 
3
3
  This gem looks at the current `routes.rb`, authorization `ability.rb`, `current_user` and controller context
4
- to metaprogram an effective CRUD website.
4
+ to metaprogram an effective CRUD website.
5
5
 
6
6
  It automates linking to resource edit, show, delete pages, as well as member and collection actions.
7
7
 
@@ -256,6 +256,14 @@ end
256
256
 
257
257
  and include Effective::CrudController in your resource controller.
258
258
 
259
+ ## Testing
260
+
261
+ Run tests by:
262
+
263
+ ```ruby
264
+ rails test
265
+ ```
266
+
259
267
  ## License
260
268
 
261
269
  MIT License. Copyright [Code and Effect Inc.](http://www.codeandeffect.com/)
@@ -268,4 +276,3 @@ MIT License. Copyright [Code and Effect Inc.](http://www.codeandeffect.com/)
268
276
  4. Push to the branch (`git push origin my-new-feature`)
269
277
  5. Bonus points for test coverage
270
278
  6. Create new Pull Request
271
-
@@ -118,7 +118,9 @@ module Effective
118
118
 
119
119
  return unless datatable_klass.present?
120
120
 
121
- datatable_klass.new(resource_datatable_attributes)
121
+ datatable = datatable_klass.new(resource_datatable_attributes)
122
+ datatable.effective_resource = effective_resource if datatable.respond_to?(:effective_resource=)
123
+ datatable
122
124
  end
123
125
 
124
126
  def resource_params_method_name
@@ -80,7 +80,7 @@ module Effective
80
80
  raise 'expected a proc or block' unless (obj.respond_to?(:call) || block_given?)
81
81
 
82
82
  instance_exec do
83
- prepend_before_action(opts) { @_effective_resource_scope ||= instance_exec(&(block_given? ? block : obj)) }
83
+ before_action(opts) { @_effective_resource_scope ||= instance_exec(&(block_given? ? block : obj)) }
84
84
  end
85
85
  end
86
86
 
@@ -5,7 +5,7 @@ module Effective
5
5
 
6
6
  # This is only available to models that use the effective_resource do ... end attributes block
7
7
  # It will be called last, and only for those resources
8
- # params.require(effective_resource.name).permit!
8
+ # params.require(effective_resource.resource_name).permit!
9
9
  def resource_permitted_params
10
10
  raise 'expected resource class to have effective_resource do .. end' if effective_resource.model.blank?
11
11
 
@@ -13,10 +13,10 @@ module Effective
13
13
 
14
14
  if Rails.env.development?
15
15
  Rails.logger.info "Effective::CrudController#resource_permitted_params:"
16
- Rails.logger.info "params.require(:#{effective_resource.name}).permit(#{permitted_params.to_s[1...-1]})"
16
+ Rails.logger.info "params.require(:#{effective_resource.resource_name}).permit(#{permitted_params.to_s[1...-1]})"
17
17
  end
18
18
 
19
- params.require(effective_resource.name).permit(*permitted_params)
19
+ params.require(effective_resource.resource_name).permit(*permitted_params)
20
20
  end
21
21
 
22
22
  # If the resource is ActiveModel, just permit all
@@ -24,13 +24,13 @@ module Effective
24
24
  def resource_active_model_permitted_params
25
25
  if Rails.env.development?
26
26
  Rails.logger.info "Effective::CrudController#resource_permitted_params:"
27
- Rails.logger.info "params.require(:#{effective_resource.name}).permit!"
27
+ Rails.logger.info "params.require(:#{effective_resource.resource_name}).permit!"
28
28
  end
29
29
 
30
- if params[effective_resource.name].present?
31
- params.require(effective_resource.name).permit!
30
+ if params[effective_resource.resource_name].present?
31
+ params.require(effective_resource.resource_name).permit!
32
32
  else
33
- params.require((effective_resource.namespaces + [effective_resource.name]).join('_')).permit!
33
+ params.require((effective_resource.namespaces + [effective_resource.resource_name]).join('_')).permit!
34
34
  end
35
35
  end
36
36
 
@@ -96,7 +96,7 @@ module Effective
96
96
 
97
97
  if (nested_params = permitted_params_for(nested.klass, namespaces)).present?
98
98
  nested_params.insert(nested_params.rindex { |obj| !obj.kind_of?(Hash)} + 1, :_destroy)
99
- permitted_params << { "#{nested.name}_attributes".to_sym => nested_params }
99
+ permitted_params << { "#{nested.resource_name}_attributes".to_sym => nested_params }
100
100
  end
101
101
  end
102
102
 
@@ -64,10 +64,14 @@ module Effective
64
64
  else
65
65
  if template_present?(action)
66
66
  format.html { render(action, locals: { action: action }) }
67
+ elsif request.referer.to_s.end_with?('/edit')
68
+ format.html { render :edit }
69
+ elsif request.referer.to_s.end_with?('/new')
70
+ format.html { render :new }
67
71
  else
68
72
  format.html do
69
73
  redirect_flash
70
- redirect_to(from_path.presence || resource_redirect_path(action))
74
+ redirect_to(resource_redirect_path(action))
71
75
  end
72
76
  end
73
77
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module EffectiveResourcesHelper
2
4
 
3
5
  # effective_bootstrap
@@ -74,18 +76,22 @@ module EffectiveResourcesHelper
74
76
  namespace ||= (effective_resource.namespace.to_sym if effective_resource.namespace)
75
77
 
76
78
  # Assign actions
77
- actions = if atts.key?(:actions) # We filter out any actions passed to us that aren't supported
78
- available = effective_resource.actions + atts[:actions].map { |k, v| v[:action] if v[:path] }.compact
79
- atts[:actions].inject({}) { |h, (commit, opts)| h[commit] = opts if available.include?(opts[:action]); h }
79
+ # We filter out any actions passed to us that aren't supported
80
+ actions = if atts.key?(:actions)
81
+ {}.tap do |actions|
82
+ atts[:actions].each do |commit, opts|
83
+ actions[commit] = opts if (effective_resource.actions.include?(opts[:action]) || opts[:path]).present?
84
+ end
85
+ end
80
86
  else
81
87
  (resource.kind_of?(Class) ? effective_resource.resource_klass_actions : effective_resource.resource_actions)
82
88
  end
83
89
 
84
90
  # Consider only, except, false and proc false
85
- only = Array(atts[:only]).compact
86
- except = Array(atts[:except]).compact
91
+ only = Array(atts[:only]) if atts[:only].present?
92
+ except = Array(atts[:except]) if atts[:except].present?
87
93
 
88
- actions = actions.select do |_, opts|
94
+ actions.select! do |_, opts|
89
95
  action = opts[:action]
90
96
 
91
97
  if only.present? && !only.include?(action)
@@ -100,8 +106,11 @@ module EffectiveResourcesHelper
100
106
  end
101
107
 
102
108
  # Select Partial
103
- partial = ['effective/resource/actions', partial.to_s].join('_') if partial.kind_of?(Symbol)
104
- partial = (partial.presence || 'effective/resource/actions') + '.html'
109
+ partial = if partial.kind_of?(Symbol)
110
+ "effective/resource/actions_#{partial}.html"
111
+ else
112
+ "#{partial.presence || 'effective/resource/actions'}.html"
113
+ end
105
114
 
106
115
  # Assign Locals
107
116
  locals = {
@@ -2,21 +2,27 @@
2
2
 
3
3
  module EffectiveResourcesPrivateHelper
4
4
  REPLACE_PAGE_ACTIONS = {'update' => :edit, 'create' => :new}
5
+ BLACKLIST = [:default, :only, :except, :if, :unless, :redirect, :success, :danger, :klass]
6
+
7
+ DATA_CONFIRM = 'data-confirm'
5
8
 
6
9
  def permitted_resource_actions(resource, actions)
7
10
  page_action = REPLACE_PAGE_ACTIONS[params[:action]] || params[:action].try(:to_sym) || :save
8
11
  executor = Effective::ResourceExec.new(self, resource)
9
12
 
10
- actions.select do |commit, args|
13
+ actions.each_with_object({}) do |(commit, args), h|
11
14
  action = (args[:action] == :save ? (resource.new_record? ? :create : :update) : args[:action])
12
15
 
13
- (args.key?(:only) ? args[:only].include?(page_action) : true) &&
14
- (args.key?(:except) ? !args[:except].include?(page_action) : true) &&
15
- (args.key?(:if) ? executor.instance_exec(&args[:if]) : true) &&
16
- (args.key?(:unless) ? !executor.instance_exec(&args[:unless]) : true) &&
17
- EffectiveResources.authorized?(controller, action, resource)
18
- end.inject({}) do |h, (commit, args)|
16
+ permitted = (args.key?(:only) ? args[:only].include?(page_action) : true) &&
17
+ (args.key?(:except) ? !args[:except].include?(page_action) : true) &&
18
+ (args.key?(:if) ? executor.instance_exec(&args[:if]) : true) &&
19
+ (args.key?(:unless) ? !executor.instance_exec(&args[:unless]) : true) &&
20
+ EffectiveResources.authorized?(controller, action, resource)
21
+
22
+ next unless permitted
23
+
19
24
  opts = args.except(:default, :only, :except, :if, :unless, :redirect, :success, :danger, :klass)
25
+ resource_to_s = resource.to_s.presence || resource.class.name.underscore
20
26
 
21
27
  # Transform data: { ... } hash into 'data-' keys
22
28
  if opts.key?(:data)
@@ -32,34 +38,34 @@ module EffectiveResourcesPrivateHelper
32
38
  end
33
39
 
34
40
  # Replace resource name in any token strings
35
- if opts.key?('data-confirm')
36
- opts['data-confirm'] = opts['data-confirm'].gsub('@resource', (resource.to_s.presence || resource.class.name.gsub('::', ' ').underscore.gsub('_', ' ')).to_s)
41
+ if opts[DATA_CONFIRM].present? && opts[DATA_CONFIRM].include?('@resource'.freeze)
42
+ opts[DATA_CONFIRM] = opts[DATA_CONFIRM].gsub('@resource'.freeze, resource_to_s)
37
43
  end
38
44
 
39
45
  # Assign class
40
46
  opts[:class] ||= (
41
- if opts['data-method'].to_s == 'delete'
42
- 'btn btn-danger'
47
+ if opts['data-method'.freeze] == 'delete'.freeze
48
+ 'btn btn-danger'.freeze
43
49
  elsif h.length == 0
44
- 'btn btn-primary'
50
+ 'btn btn-primary'.freeze
45
51
  elsif defined?(EffectiveBootstrap)
46
- 'btn btn-secondary'
52
+ 'btn btn-secondary'.freeze
47
53
  else
48
- 'btn btn-default'
54
+ 'btn btn-default'.freeze
49
55
  end
50
56
  )
51
57
 
52
58
  # Assign title
53
59
  opts[:title] ||= case opts[:action]
54
60
  when :save then commit
55
- when :edit then "Edit #{resource}"
56
- when :show then "#{resource}"
57
- when :destroy then "Delete #{resource}"
61
+ when :edit then "Edit #{resource_to_s}"
62
+ when :show then "#{resource_to_s}"
63
+ when :destroy then "Delete #{resource_to_s}"
58
64
  when :index then "All #{resource.class.name.gsub('::', ' ').underscore.gsub('_', ' ').titleize.pluralize}"
59
- else "#{opts[:action].to_s.titleize} #{resource}"
65
+ else "#{opts[:action].to_s.titleize} #{resource_to_s}"
60
66
  end
61
67
 
62
- h[commit] = opts; h
68
+ h[commit] = opts
63
69
  end
64
70
  end
65
71
 
@@ -44,6 +44,7 @@ module Effective
44
44
  when :string ; :string
45
45
  when :text ; :string
46
46
  when :time ; :time
47
+ when :uuid ; :uuid
47
48
  when FalseClass ; :boolean
48
49
  when (defined?(Integer) ? Integer : Fixnum) ; :integer
49
50
  when Float ; :decimal
@@ -139,6 +140,8 @@ module Effective
139
140
  value.to_s
140
141
  end
141
142
  end
143
+ when :uuid
144
+ value.to_s
142
145
  when :active_storage
143
146
  value.to_s
144
147
  else
@@ -1,6 +1,9 @@
1
1
  module Effective
2
2
  class ModelReader
3
- DATATYPES = [:binary, :boolean, :date, :datetime, :decimal, :float, :hstore, :inet, :integer, :string, :text, :permitted_param]
3
+ DATATYPES = [
4
+ :binary, :boolean, :date, :datetime, :decimal, :float, :hstore, :inet, :integer,
5
+ :string, :text, :uuid, :permitted_param
6
+ ]
4
7
 
5
8
  attr_reader :attributes
6
9
 
@@ -1,41 +1,60 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Effective
2
4
  module Resources
3
5
  module Actions
6
+ EMPTY_HASH = {}
7
+ POST_VERBS = ['POST', 'PUT', 'PATCH']
8
+ CRUD_ACTIONS = %i(index new create show edit update destroy)
4
9
 
5
10
  # This was written for the Edit actions fallback templates and Datatables
6
11
  # Effective::Resource.new('admin/posts').routes[:index]
7
12
  def routes
8
13
  @routes ||= (
9
- matches = [[namespace, plural_name].compact.join('/'.freeze), [namespace, name].compact.join('/'.freeze)]
14
+ matches = [
15
+ [namespace, plural_name].compact.join('/'),
16
+ [namespace, 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
23
 
11
- routes_engine.routes.routes.select do |route|
12
- matches.any? { |match| match == route.defaults[:controller] } && !route.name.to_s.end_with?('root')
13
- end.inject({}) do |h, route|
14
- h[route.defaults[:action].to_sym] = route; h
24
+ # Check engine routes
25
+ if routes.blank?
26
+ matches = [
27
+ [namespace, plural_name].compact.join('/'),
28
+ [namespace, name].compact.join('/'),
29
+ ['effective', namespace, plural_name].compact.join('/'),
30
+ ['effective', namespace, name].compact.join('/')
31
+ ]
32
+
33
+ Rails::Engine.subclasses.each do |engine|
34
+ routes = engine.routes.routes.select do |route|
35
+ (matches & [route.defaults[:controller]]).present? && !route.name.to_s.end_with?('root')
36
+ end
37
+
38
+ break if routes.present?
39
+ end
15
40
  end
16
- )
17
- end
18
41
 
19
- # Effective::Resource.new('effective/order', namespace: :admin)
20
- def routes_engine
21
- case class_name
22
- when 'Effective::Order'.freeze
23
- EffectiveOrders::Engine
24
- else
25
- Rails.application
26
- end
42
+ Array(routes).inject({}) { |h, route| h[route.defaults[:action].to_sym] = route; h }
43
+ )
27
44
  end
28
45
 
29
46
  # Effective::Resource.new('admin/posts').action_path_helper(:edit) => 'edit_admin_posts_path'
30
47
  # This will return empty for create, update and destroy
31
48
  def action_path_helper(action)
32
49
  return unless routes[action]
33
- return (routes[action].name + '_path'.freeze) if routes[action].name.present?
50
+ return (routes[action].name + '_path') if routes[action].name.present?
34
51
  end
35
52
 
36
53
  # Effective::Resource.new('admin/posts').action_path(:edit, Post.last) => '/admin/posts/3/edit'
37
54
  # Will work for any action. Returns the real path
38
- def action_path(action, resource = nil, opts = {})
55
+ def action_path(action, resource = nil, opts = nil)
56
+ opts ||= EMPTY_HASH
57
+
39
58
  if klass.nil? && resource.present? && initialized_name.kind_of?(ActiveRecord::Reflection::BelongsToReflection)
40
59
  return Effective::Resource.new(resource, namespace: namespace).action_path(action, resource, opts)
41
60
  end
@@ -53,12 +72,26 @@ module Effective
53
72
  end
54
73
  end
55
74
 
56
- # This generates the correct route when an object is overriding to_param
57
- if (resource || instance).respond_to?(:attributes)
58
- formattable = (resource || instance).attributes.symbolize_keys.merge(id: (resource || instance).to_param)
75
+ target = (resource || instance)
76
+
77
+ formattable = if routes[action].parts.include?(:id)
78
+ if target.respond_to?(:to_param) && target.respond_to?(:id) && (target.to_param != target.id.to_s)
79
+ routes[action].parts.each_with_object({}) do |part, h|
80
+ if part == :id
81
+ h[part] = target.to_param
82
+ elsif part == :format
83
+ # Nothing
84
+ elsif target.respond_to?(part)
85
+ h[part] = target.public_send(part)
86
+ end
87
+ end
88
+ else
89
+ target
90
+ end
59
91
  end
60
92
 
61
- path = routes[action].format(formattable || {}).presence
93
+ # Generate the path
94
+ path = routes[action].format(formattable || EMPTY_HASH)
62
95
 
63
96
  if path.present? && opts.present?
64
97
  uri = URI.parse(path)
@@ -70,74 +103,85 @@ module Effective
70
103
  end
71
104
 
72
105
  def actions
73
- routes.keys
106
+ @route_actions ||= routes.keys
74
107
  end
75
108
 
76
109
  def crud_actions
77
- actions & %i(index new create show edit update destroy)
110
+ @crud_actions ||= (actions & CRUD_ACTIONS)
78
111
  end
79
112
 
80
113
  # GET actions
81
114
  def collection_actions
82
- routes.values.map { |route| route.defaults[:action].to_sym if is_collection_route?(route) }.compact
115
+ @collection_actions ||= (
116
+ routes.map { |_, route| route.defaults[:action].to_sym if is_collection_route?(route) }.tap(&:compact!)
117
+ )
83
118
  end
84
119
 
85
120
  def collection_get_actions
86
- routes.values.map { |route| route.defaults[:action].to_sym if is_collection_route?(route) && is_get_route?(route) }.compact
121
+ @collection_get_actions ||= (
122
+ routes.map { |_, route| route.defaults[:action].to_sym if is_collection_route?(route) && is_get_route?(route) }.tap(&:compact!)
123
+ )
87
124
  end
88
125
 
89
126
  def collection_post_actions
90
- routes.values.map { |route| route.defaults[:action].to_sym if is_collection_route?(route) && is_post_route?(route) }.compact
127
+ @collection_post_actions ||= (
128
+ routes.map { |_, route| route.defaults[:action].to_sym if is_collection_route?(route) && is_post_route?(route) }.tap(&:compact!)
129
+ )
91
130
  end
92
131
 
93
132
  # All actions
94
133
  def member_actions
95
- routes.values.map { |route| route.defaults[:action].to_sym if is_member_route?(route) }.compact
134
+ @member_actions ||= (
135
+ routes.map { |_, route| route.defaults[:action].to_sym if is_member_route?(route) }.tap(&:compact!)
136
+ )
96
137
  end
97
138
 
98
139
  # GET actions
99
140
  def member_get_actions
100
- routes.values.map { |route| route.defaults[:action].to_sym if is_member_route?(route) && is_get_route?(route) }.compact
141
+ @member_get_actions ||= (
142
+ routes.map { |_, route| route.defaults[:action].to_sym if is_member_route?(route) && is_get_route?(route) }.tap(&:compact!)
143
+ )
101
144
  end
102
145
 
103
146
  def member_delete_actions
104
- routes.values.map { |route| route.defaults[:action].to_sym if is_member_route?(route) && is_delete_route?(route) }.compact
147
+ @member_delete_actions ||= (
148
+ routes.map { |_, route| route.defaults[:action].to_sym if is_member_route?(route) && is_delete_route?(route) }.tap(&:compact!)
149
+ )
105
150
  end
106
151
 
107
152
  # POST/PUT/PATCH actions
108
153
  def member_post_actions
109
- routes.values.map { |route| route.defaults[:action].to_sym if is_member_route?(route) && is_post_route?(route) }.compact
154
+ @member_post_actions ||= (
155
+ routes.map { |_, route| route.defaults[:action].to_sym if is_member_route?(route) && is_post_route?(route) }.tap(&:compact!)
156
+ )
110
157
  end
111
158
 
112
159
  # Same as controller_path in the view
113
160
  def controller_path
114
- [namespace, plural_name].compact * '/'.freeze
161
+ [namespace, plural_name].compact * '/'
115
162
  end
116
163
 
117
164
  private
118
165
 
119
166
  def is_member_route?(route)
120
- (route.path.required_names || []).include?('id'.freeze)
167
+ (route.path.required_names || []).include?('id')
121
168
  end
122
169
 
123
170
  def is_collection_route?(route)
124
- (route.path.required_names || []).include?('id'.freeze) == false
171
+ (route.path.required_names || []).include?('id') == false
125
172
  end
126
173
 
127
174
  def is_get_route?(route)
128
- route.verb.to_s.include?('GET'.freeze)
175
+ route.verb == 'GET'
129
176
  end
130
177
 
131
178
  def is_delete_route?(route)
132
- route.verb.to_s.include?('DELETE'.freeze)
179
+ route.verb == 'DELETE'
133
180
  end
134
181
 
135
182
  def is_post_route?(route)
136
- ['POST', 'PUT', 'PATCH'].freeze.any? { |verb| route.verb == verb }
183
+ POST_VERBS.include?(route.verb)
137
184
  end
138
185
  end
139
186
  end
140
187
  end
141
-
142
-
143
-
@@ -1,3 +1,5 @@
1
+ # frozen_sting_literals: true
2
+
1
3
  module Effective
2
4
  module Resources
3
5
  module Controller
@@ -29,22 +31,26 @@ module Effective
29
31
  end
30
32
 
31
33
  (member_post_actions - crud_actions).each do |action| # default true means it will be overwritten by dsl methods
32
- buttons[action.to_s.titleize] = case action
34
+ action_name = action.to_s.titleize
35
+
36
+ buttons[action_name] = case action
33
37
  when :archive
34
- { action: action, default: true, if: -> { !resource.archived? }, class: 'btn btn-danger', 'data-method' => :post, 'data-confirm' => "Really #{action} @resource?"}
38
+ { action: action, default: true, if: -> { !resource.archived? }, class: 'btn btn-danger', 'data-method' => :post, 'data-confirm' => "Really #{action_name} @resource?"}
35
39
  when :unarchive
36
- { action: action, default: true, if: -> { resource.archived? }, 'data-method' => :post, 'data-confirm' => "Really #{action} @resource?" }
40
+ { action: action, default: true, if: -> { resource.archived? }, 'data-method' => :post, 'data-confirm' => "Really #{action_name} @resource?" }
37
41
  else
38
- { action: action, default: true, 'data-method' => :post, 'data-confirm' => "Really #{action} @resource?"}
42
+ { action: action, default: true, 'data-method' => :post, 'data-confirm' => "Really #{action_name} @resource?"}
39
43
  end
40
44
  end
41
45
 
42
46
  member_delete_actions.each do |action|
47
+ action_name = action.to_s.titleize
48
+
43
49
  if action == :destroy
44
50
  next if buttons.values.find { |v| v[:action] == :archive }.present?
45
51
  buttons['Delete'] = { action: action, default: true, 'data-method' => :delete, 'data-confirm' => "Really delete @resource?" }
46
52
  else
47
- buttons[action.to_s.titleize] = { action: action, default: true, 'data-method' => :delete, 'data-confirm' => "Really #{action} @resource?" }
53
+ buttons[action_name] = { action: action, default: true, 'data-method' => :delete, 'data-confirm' => "Really #{action_name} @resource?" }
48
54
  end
49
55
  end
50
56
 
@@ -66,33 +72,39 @@ module Effective
66
72
  # It is used by datatables
67
73
  def resource_actions
68
74
  {}.tap do |actions|
69
- (member_get_actions & crud_actions).reverse_each do |action|
75
+ member_get_actions.reverse_each do |action|
76
+ next unless crud_actions.include?(action)
70
77
  actions[action.to_s.titleize] = { action: action, default: true }
71
78
  end
72
79
 
73
- (member_get_actions - crud_actions).each do |action|
80
+ member_get_actions.each do |action|
81
+ next if crud_actions.include?(action)
74
82
  actions[action.to_s.titleize] = { action: action, default: true }
75
83
  end
76
84
 
77
- (member_post_actions - crud_actions).each do |action|
78
- actions[action.to_s.titleize] = { action: action, default: true, 'data-method' => :post, 'data-confirm' => "Really #{action} @resource?" }
85
+ member_post_actions.each do |action|
86
+ next if crud_actions.include?(action)
79
87
 
80
- actions[action.to_s.titleize] = case action
88
+ action_name = action.to_s.titleize
89
+
90
+ actions[action_name] = case action
81
91
  when :archive
82
- { action: action, default: true, if: -> { !resource.archived? }, class: 'btn btn-danger', 'data-method' => :post, 'data-confirm' => "Really #{action} @resource?"}
92
+ { action: action, default: true, if: -> { !resource.archived? }, class: 'btn btn-danger', 'data-method' => :post, 'data-confirm' => "Really #{action_name} @resource?"}
83
93
  when :unarchive
84
- { action: action, default: true, if: -> { resource.archived? }, 'data-method' => :post, 'data-confirm' => "Really #{action} @resource?" }
94
+ { action: action, default: true, if: -> { resource.archived? }, 'data-method' => :post, 'data-confirm' => "Really #{action_name} @resource?" }
85
95
  else
86
- { action: action, default: true, 'data-method' => :post, 'data-confirm' => "Really #{action} @resource?" }
96
+ { action: action, default: true, 'data-method' => :post, 'data-confirm' => "Really #{action_name} @resource?" }
87
97
  end
88
98
  end
89
99
 
90
100
  member_delete_actions.each do |action|
101
+ action_name = action.to_s.titleize
102
+
91
103
  if action == :destroy
92
- next if actions.values.find { |v| v[:action] == :archive }.present?
104
+ next if actions.find { |_, v| v[:action] == :archive }.present?
93
105
  actions['Delete'] = { action: action, default: true, 'data-method' => :delete, 'data-confirm' => "Really delete @resource?" }
94
106
  else
95
- actions[action.to_s.titleize] = { action: action, default: true, 'data-method' => :delete, 'data-confirm' => "Really #{action} @resource?" }
107
+ actions[action_name] = { action: action, default: true, 'data-method' => :delete, 'data-confirm' => "Really #{action_name} @resource?" }
96
108
  end
97
109
  end
98
110
  end
@@ -6,8 +6,40 @@ module Effective
6
6
 
7
7
  def _initialize_input(input, namespace: nil)
8
8
  @initialized_name = input
9
+ @model_klass = _klass_by_input(input)
9
10
 
10
- @model_klass = case input
11
+ # Consider namespaces
12
+ if namespace
13
+ @namespaces = (namespace.kind_of?(String) ? namespace.split('/') : Array(namespace))
14
+ end
15
+
16
+ if input.kind_of?(Array) && @namespaces.blank?
17
+ @namespaces = input[0...-1].map { |input| input.to_s.presence }.compact
18
+ end
19
+
20
+ # Consider relation
21
+ if input.kind_of?(ActiveRecord::Relation)
22
+ @relation ||= input
23
+ end
24
+
25
+ if input.kind_of?(ActiveRecord::Reflection::MacroReflection) && input.scope
26
+ @relation ||= klass.where(nil).merge(input.scope)
27
+ end
28
+
29
+ # Consider instance
30
+ if klass && input.instance_of?(klass)
31
+ @instance ||= input
32
+ end
33
+
34
+ if klass && input.kind_of?(Array) && input.last.instance_of?(klass)
35
+ @instance ||= input.last
36
+ end
37
+ end
38
+
39
+ def _klass_by_input(input)
40
+ case input
41
+ when Array
42
+ _klass_by_input(input.last)
11
43
  when String, Symbol
12
44
  _klass_by_name(input)
13
45
  when Class
@@ -24,22 +56,6 @@ module Effective
24
56
  when nil ; raise 'expected a string or class'
25
57
  else ; _klass_by_name(input.class.name)
26
58
  end
27
-
28
- if namespace
29
- @namespaces = (namespace.kind_of?(String) ? namespace.split('/') : Array(namespace))
30
- end
31
-
32
- if input.kind_of?(ActiveRecord::Relation)
33
- @relation = input
34
- end
35
-
36
- if input.kind_of?(ActiveRecord::Reflection::MacroReflection) && input.scope
37
- @relation = klass.where(nil).merge(input.scope)
38
- end
39
-
40
- if klass && input.instance_of?(klass)
41
- @instance = input
42
- end
43
59
  end
44
60
 
45
61
  def _klass_by_name(input)
@@ -48,6 +64,7 @@ module Effective
48
64
 
49
65
  names = input.split('/')
50
66
 
67
+ # Crazy classify
51
68
  0.upto(names.length-1) do |index|
52
69
  class_name = names[index..-1].map { |name| name.classify } * '::'
53
70
  klass = class_name.safe_constantize
@@ -64,35 +81,19 @@ module Effective
64
81
  end
65
82
  end
66
83
 
67
- nil
68
- end
69
-
70
- # Lazy initialized
71
- def _initialize_written
72
- @written_attributes = []
73
- @written_belong_tos = []
74
- @written_scopes = []
75
-
76
- return unless File.exists?(model_file)
77
-
78
- Effective::CodeReader.new(model_file) do |reader|
79
- first = reader.index { |line| line == '# Attributes' }
80
- last = reader.index(from: first) { |line| line.start_with?('#') == false && line.length > 0 } if first
81
-
82
- if first && last
83
- @written_attributes = reader.select(from: first+1, to: last-1).map do |line|
84
- Effective::Attribute.parse_written(line).presence
85
- end.compact
86
- end
87
-
88
- @written_belong_tos = reader.select { |line| line.start_with?('belongs_to ') }.map do |line|
89
- line.scan(/belongs_to\s+:(\w+)/).flatten.first
90
- end
84
+ # Crazy engine
85
+ if names[0] == 'admin'
86
+ class_name = (['effective'] + names[1..-1]).map { |name| name.classify } * '::'
87
+ klass = class_name.safe_constantize
91
88
 
92
- @written_scopes = reader.select { |line| line.start_with?('scope ') }.map do |line|
93
- line.scan(/scope\s+:(\w+)/).flatten.first
89
+ if klass.present?
90
+ @namespaces ||= names[0...-1]
91
+ @model_klass = klass
92
+ return klass
94
93
  end
95
94
  end
95
+
96
+ nil
96
97
  end
97
98
 
98
99
  end
@@ -11,6 +11,9 @@ module Effective
11
11
  "#{namespaced_class_name.pluralize}Datatable".safe_constantize ||
12
12
  "#{class_name.pluralize.camelize}Datatable".safe_constantize ||
13
13
  "#{name.pluralize.camelize}Datatable".safe_constantize ||
14
+ "#{namespaced_class_name.pluralize.gsub('::', '')}Datatable".safe_constantize ||
15
+ "#{class_name.pluralize.camelize.gsub('::', '')}Datatable".safe_constantize ||
16
+ "#{name.pluralize.camelize.gsub('::', '')}Datatable".safe_constantize ||
14
17
  "Effective::Datatables::#{namespaced_class_name.pluralize}".safe_constantize ||
15
18
  "Effective::Datatables::#{class_name.pluralize.camelize}".safe_constantize ||
16
19
  "Effective::Datatables::#{name.pluralize.camelize}".safe_constantize
@@ -11,6 +11,10 @@ module Effective
11
11
  name.pluralize
12
12
  end
13
13
 
14
+ def resource_name # 'effective_post' used by permitted params
15
+ @resource_name ||= ((klass.present? ? klass.name : initialized_name).to_s.split(SPLIT).join('_') || '').singularize.underscore
16
+ end
17
+
14
18
  def initialized_name
15
19
  @initialized_name
16
20
  end
@@ -158,6 +158,12 @@ module Effective
158
158
  else
159
159
  relation.where("#{sql_column} = ?", term)
160
160
  end
161
+ when :uuid
162
+ if fuzzy
163
+ relation.where("#{sql_column}::text #{ilike} ?", "%#{term}%")
164
+ else
165
+ relation.where("#{sql_column}::text = ?", term)
166
+ end
161
167
  else
162
168
  raise "unsupported sql type #{sql_type}"
163
169
  end
@@ -1,5 +1,6 @@
1
1
  - permitted_resource_actions(resource, actions).each do |label, opts|
2
- = link_to(label, (effective_resource.action_path(opts[:action], resource) || '#'), opts.except(:action))
2
+ - action = opts.delete(:action)
3
+ = link_to(label, (effective_resource.action_path(action, resource) || '#'), opts)
3
4
 
4
5
  = instance_exec(resource, &format_block) if local_assigns[:format_block]
5
6
  = yield if block_given?
@@ -1,6 +1,9 @@
1
1
  = dropdown(variation: :dropleft, btn_class: btn_class) do
2
2
  - permitted_resource_actions(resource, actions).each do |label, opts|
3
- = dropdown_link_to(label, (effective_resource.action_path(opts[:action], resource) || '#'), opts.except(:action, :class).merge(btn_class: btn_class))
3
+ - action = opts.delete(:action)
4
+ - opts.delete(:class)
5
+
6
+ = dropdown_link_to(label, (effective_resource.action_path(action, resource) || '#'), opts)
4
7
 
5
8
  = instance_exec(resource, &format_block) if local_assigns[:format_block]
6
9
  = yield(resource) if block_given?
@@ -1,5 +1,8 @@
1
1
  - permitted_resource_actions(resource, actions).each do |label, opts|
2
- = glyphicon_to(opts[:action], (effective_resource.action_path(opts[:action], resource) || '#'), opts.except(:action, :class))
2
+ - action = opts.delete(:action)
3
+ - opts.delete(:class)
4
+
5
+ = glyphicon_to(action, (effective_resource.action_path(action, resource) || '#'), opts)
3
6
 
4
7
  = instance_exec(resource, &format_block) if local_assigns[:format_block]
5
8
  = yield(resource) if block_given?
@@ -1,4 +1,5 @@
1
1
  require 'effective_resources/engine'
2
+ require 'effective_resources/version'
2
3
 
3
4
  module EffectiveResources
4
5
 
@@ -1,3 +1,3 @@
1
1
  module EffectiveResources
2
- VERSION = '1.5.1'.freeze
2
+ VERSION = '1.6.0'.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.5.1
4
+ version: 1.6.0
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: 2020-09-17 00:00:00.000000000 Z
11
+ date: 2020-11-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -24,6 +24,76 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: 4.0.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: sqlite3
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: effective_datatables
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: effective_developer
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: haml
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: pry-byebug
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
27
97
  description: Make any controller an effective resource controller.
28
98
  email:
29
99
  - info@codeandeffect.com
@@ -113,7 +183,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
113
183
  - !ruby/object:Gem::Version
114
184
  version: '0'
115
185
  requirements: []
116
- rubygems_version: 3.1.4
186
+ rubygems_version: 3.1.2
117
187
  signing_key:
118
188
  specification_version: 4
119
189
  summary: Make any controller an effective resource controller.