avo 1.19.1.pre.9 → 1.19.1.pre.10

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of avo might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8420efb9e9758a27d9a7790cfad7a2cfd5f6d111a938ed33548e9794fab03e8f
4
- data.tar.gz: fae11215fbde5a22865111552a31a80e5f290e9e634b96bb2304584cee939594
3
+ metadata.gz: 1618da266ed143055270d6efe40cbc1672b074977668186ba979dd062e28c296
4
+ data.tar.gz: 03600d5cf52bc8cba85970ce6f7c7dceeba01747c17fff374a22ed4137048fc1
5
5
  SHA512:
6
- metadata.gz: ff28de78a9e12c1f03dcfccd41fd1b557a3e9743448a1ac215f8f6bed0a132e5558be2e0cb54fa351d145bf9cd0644469783f09fe828475ef53d4a69c70cb134
7
- data.tar.gz: a043c44e6ac2039fcbd2772f9115dcae3229040fef46a0bb07f1dcf32b086b3863c9fc4663d98a96ff14842f6984172d52113a36049a17abb589c315889a56d0
6
+ metadata.gz: 80856cc4b3c4e164570d5209140e09bfb617c90db0500652b577500a3ed675a96ec14b4944b5e4339849820860fe9f100a971fe414205352e8a37a7983b51bae
7
+ data.tar.gz: 3bc19b1c7feb5e06e485648c8ea30f46ddce97ff591c71c69ca0bfef7ac73b384001d202022f5004437a1cffa2c17ec94167160c5136c8e99d977e02df5ef601
data/Gemfile CHANGED
@@ -80,6 +80,7 @@ group :development do
80
80
  end
81
81
 
82
82
  group :development, :test do
83
+ gem 'ap'
83
84
  gem "faker", require: false
84
85
  end
85
86
 
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- avo (1.19.1.pre.9)
4
+ avo (1.19.1.pre.10)
5
5
  active_link_to
6
6
  addressable
7
7
  breadcrumbs_on_rails
@@ -83,6 +83,8 @@ GEM
83
83
  zeitwerk (~> 2.3)
84
84
  addressable (2.8.0)
85
85
  public_suffix (>= 2.0.2, < 5.0)
86
+ ap (0.1.1)
87
+ httparty (>= 0.7.7)
86
88
  appraisal (2.4.1)
87
89
  bundler
88
90
  rake
@@ -397,6 +399,7 @@ PLATFORMS
397
399
  DEPENDENCIES
398
400
  active_link_to
399
401
  addressable
402
+ ap
400
403
  appraisal
401
404
  avo!
402
405
  aws-sdk-s3
@@ -4,6 +4,7 @@
4
4
  show_path,
5
5
  title: t('avo.view_item', item: singular_resource_name).capitalize,
6
6
  data: {
7
+ target: 'control:view',
7
8
  control: :show,
8
9
  'tippy': 'tooltip',
9
10
  }
@@ -15,6 +16,7 @@
15
16
  edit_path,
16
17
  title: t('avo.edit_item', item: singular_resource_name).capitalize,
17
18
  data: {
19
+ target: 'control:edit',
18
20
  control: :edit,
19
21
  'resource-id': @resource.model.id,
20
22
  'tippy': 'tooltip',
@@ -30,6 +32,7 @@
30
32
  title: t('avo.detach_item', item: singular_resource_name).capitalize,
31
33
  type: :submit,
32
34
  data: {
35
+ target: 'control:detach',
33
36
  confirm: t('avo.are_you_sure_detach_item', item: singular_resource_name),
34
37
  control: :detach,
35
38
  'resource-id': @resource.model.id,
@@ -49,6 +52,7 @@
49
52
  title: t('avo.delete_item', item: singular_resource_name).capitalize,
50
53
  type: :submit,
51
54
  data: {
55
+ target: 'control:destroy',
52
56
  confirm: t('avo.are_you_sure', item: singular_resource_name),
53
57
  control: :destroy,
54
58
  'resource-id': @resource.model.id,
@@ -10,15 +10,19 @@ class Avo::Index::ResourceControlsComponent < Avo::ResourceComponent
10
10
  def can_detach?
11
11
  @reflection.present? &&
12
12
  @resource.model.present? &&
13
- (@reflection.is_a?(::ActiveRecord::Reflection::HasManyReflection) || @reflection.is_a?(::ActiveRecord::Reflection::ThroughReflection)) &&
13
+ is_has_many_association &&
14
14
  authorize_association_for("detach")
15
15
  end
16
16
 
17
17
  def can_edit?
18
+ return authorize_association_for(:edit) if @reflection.present?
19
+
18
20
  @resource.authorization.authorize_action(:edit, raise_exception: false)
19
21
  end
20
22
 
21
23
  def can_view?
24
+ return authorize_association_for(:view) if @reflection.present?
25
+
22
26
  @resource.authorization.authorize_action(:show, raise_exception: false)
23
27
  end
24
28
 
@@ -50,7 +54,7 @@ class Avo::Index::ResourceControlsComponent < Avo::ResourceComponent
50
54
 
51
55
  def singular_resource_name
52
56
  if @reflection.present?
53
- ::Avo::App.get_resource_by_model_name(@reflection.class_name).name
57
+ reflection_resource.name
54
58
  else
55
59
  @resource.singular_name.present? ? @resource.singular_name : @resource.model_class.model_name.name.downcase
56
60
  end
@@ -61,4 +65,8 @@ class Avo::Index::ResourceControlsComponent < Avo::ResourceComponent
61
65
 
62
66
  ::Avo::App.get_resource_by_model_name @parent_model.class
63
67
  end
68
+
69
+ def is_has_many_association
70
+ @reflection.is_a?(::ActiveRecord::Reflection::HasManyReflection) || @reflection.is_a?(::ActiveRecord::Reflection::ThroughReflection)
71
+ end
64
72
  end
@@ -6,9 +6,12 @@
6
6
  <%= helpers.render_breadcrumbs(separator: helpers.svg('chevron-right', class: 'inline-block h-3 stroke-current relative top-[-1px] ml-1' )) if Avo.configuration.display_breadcrumbs %>
7
7
  </div>
8
8
  <% end %>
9
- <div class="text-2xl tracking-normal font-bold text-gray-800 truncate">
9
+ <div class="text-2xl tracking-normal font-bold text-gray-800 truncate" data-target="title">
10
10
  <%= @title %>
11
11
  </div>
12
+ <div class="text-sm tracking-normal text-gray-600" data-target="description">
13
+ <%== description %>
14
+ </div>
12
15
  </div>
13
16
 
14
17
  <div>
@@ -6,8 +6,9 @@ class Avo::PanelComponent < ViewComponent::Base
6
6
  renders_one :bare_content
7
7
  renders_one :footer
8
8
 
9
- def initialize(title: nil, body_classes: nil, data: {}, display_breadcrumbs: false, index: nil)
9
+ def initialize(title: nil, description: nil, body_classes: nil, data: {}, display_breadcrumbs: false, index: nil)
10
10
  @title = title
11
+ @description = description
11
12
  @body_classes = body_classes
12
13
  @data = data
13
14
  @display_breadcrumbs = display_breadcrumbs
@@ -25,4 +26,10 @@ class Avo::PanelComponent < ViewComponent::Base
25
26
  def display_breadcrumbs?
26
27
  @display_breadcrumbs
27
28
  end
29
+
30
+ def description
31
+ return @description if @description.present?
32
+
33
+ '&nbsp;'
34
+ end
28
35
  end
@@ -1,20 +1,27 @@
1
1
  class Avo::ResourceComponent < ViewComponent::Base
2
2
  def can_create?
3
+ return authorize_association_for(:create) if @reflection.present?
4
+
3
5
  @resource.authorization.authorize_action(:create, raise_exception: false)
4
6
  end
5
7
 
6
8
  def can_delete?
9
+ return authorize_association_for(:destroy) if @reflection.present?
10
+
7
11
  @resource.authorization.authorize_action(:destroy, raise_exception: false)
8
12
  end
9
13
 
10
14
  def authorize_association_for(policy_method)
11
15
  association_policy = true
16
+
12
17
  if @reflection.present?
13
18
  reflection_resource = ::Avo::App.get_resource_by_model_name(@reflection.active_record.name)
14
- if reflection_resource.present?
15
- method_name = "#{policy_method}_#{@reflection.name.to_s.underscore}?".to_sym
16
- defined_policy_methods = reflection_resource.authorization.defined_methods(reflection_resource.model_class, raise_exception: false)
17
- if defined_policy_methods.present? && defined_policy_methods.include?(method_name)
19
+ association_name = params['related_name']
20
+
21
+ if association_name.present?
22
+ method_name = "#{policy_method}_#{association_name}?".to_sym
23
+
24
+ if reflection_resource.authorization.has_method?(method_name, raise_exception: false)
18
25
  association_policy = reflection_resource.authorization.authorize_action(method_name, raise_exception: false)
19
26
  end
20
27
  end
@@ -25,7 +32,22 @@ class Avo::ResourceComponent < ViewComponent::Base
25
32
 
26
33
  private
27
34
 
35
+ # Figure out what is the corresponding field for this @reflection
36
+ def field
37
+ fields = ::Avo::App.get_resource_by_model_name(@reflection.active_record.name).get_field_definitions
38
+ fields.find { |f| f.id == @reflection.name }
39
+ rescue
40
+ nil
41
+ end
42
+
28
43
  def relation_resource
29
44
  ::Avo::App.get_resource_by_model_name params[:via_resource_class].safe_constantize
30
45
  end
46
+
47
+ # Get the resource for the resource using the klass attribute so we get the namespace too
48
+ def reflection_resource
49
+ ::Avo::App.get_resource_by_model_name(@reflection.klass.to_s)
50
+ rescue
51
+ nil
52
+ end
31
53
  end
@@ -1,9 +1,10 @@
1
1
  <div data-model-id="<%= @resource.model.id %>">
2
+ <%= @resource.form_scope %>
2
3
  <% @resource.panels.each do |resource_panel| %>
3
4
  <%= form_with model: @resource.model, scope: @resource.form_scope, url: helpers.resource_path(model: @resource.model, resource: @resource), method: :put, multipart: true do |form| %>
4
5
  <%= hidden_field_tag :referrer, back_path if params[:via_resource_class] %>
5
6
 
6
- <%= render Avo::PanelComponent.new(title: resource_panel[:name], display_breadcrumbs: true) do |c| %>
7
+ <%= render Avo::PanelComponent.new(title: resource_panel[:name], description: @resource.resource_description, display_breadcrumbs: true) do |c| %>
7
8
  <% c.tools do %>
8
9
  <div class="flex justify-end space-x-2">
9
10
  <%= a_link back_path do %>
@@ -1,16 +1,18 @@
1
1
  <div>
2
- <%= render Avo::PanelComponent.new(title: title, body_classes: 'py-4', data: { component: 'resources-index' }, display_breadcrumbs: @reflection.blank?) do |c| %>
2
+ <%= render Avo::PanelComponent.new(title: title, description: @resource.resource_description, body_classes: 'py-4', data: { component: 'resources-index' }, display_breadcrumbs: @reflection.blank?) do |c| %>
3
3
  <% c.tools do %>
4
- <%= render 'actions' if @actions.present? %>
4
+ <% if can_see_the_actions_button? %>
5
+ <%= render 'actions' if @actions.present? %>
6
+ <% end %>
5
7
 
6
8
  <% if can_see_the_create_button? %>
7
- <%= a_link create_path do %>
9
+ <%= a_link create_path, 'data-target': 'create' do %>
8
10
  <%= svg 'plus' %> <%= t('avo.create_new_item', item: singular_resource_name.downcase ) %>
9
11
  <% end %>
10
12
  <% end %>
11
13
 
12
14
  <% if can_attach? %>
13
- <%= a_link attach_path, color: 'indigo', 'data-turbo-frame': 'attach_modal' do %>
15
+ <%= a_link attach_path, color: 'indigo', 'data-turbo-frame': 'attach_modal', 'data-target': 'attach' do %>
14
16
  <%= svg 'view-grid-add' %> <%= t('avo.attach_item', item: singular_resource_name).capitalize %>
15
17
  <% end %>
16
18
  <% end %>
@@ -30,7 +30,9 @@ class Avo::Views::ResourceIndexComponent < Avo::ResourceComponent
30
30
 
31
31
  def title
32
32
  if @reflection.present?
33
- ::Avo::App.get_resource_by_model_name(@reflection.class_name).plural_name
33
+ return field.plural_name if field.present?
34
+
35
+ reflection_resource.plural_name
34
36
  else
35
37
  @resource.plural_name
36
38
  end
@@ -47,9 +49,17 @@ class Avo::Views::ResourceIndexComponent < Avo::ResourceComponent
47
49
  # The Create button is dependent on the new? policy method.
48
50
  # The create? should be called only when the user clicks the Save button so the developers gets access to the params from the form.
49
51
  def can_see_the_create_button?
52
+ return authorize_association_for("create") if @reflection.present?
53
+
50
54
  @resource.authorization.authorize_action(:new, raise_exception: false) && !has_reflection_and_is_read_only
51
55
  end
52
56
 
57
+ def can_see_the_actions_button?
58
+ return authorize_association_for("act_on") if @reflection.present?
59
+
60
+ @resource.authorization.authorize_action(:act_on, raise_exception: false) && !has_reflection_and_is_read_only
61
+ end
62
+
53
63
  def can_attach?
54
64
  klass = @reflection
55
65
  klass = @reflection.through_reflection if klass.is_a? ::ActiveRecord::Reflection::ThroughReflection
@@ -107,7 +117,7 @@ class Avo::Views::ResourceIndexComponent < Avo::ResourceComponent
107
117
 
108
118
  def singular_resource_name
109
119
  if @reflection.present?
110
- ::Avo::App.get_resource_by_model_name(@reflection.class_name).name
120
+ reflection_resource.name
111
121
  else
112
122
  @resource.singular_name || @resource.model_class.model_name.name.downcase
113
123
  end
@@ -1,7 +1,7 @@
1
1
  <div>
2
2
  <% @resource.panels.each do |resource_panel| %>
3
- <%= form_with model: @resource.model, url: helpers.resources_path(resource: @resource, via_relation_class: params[:via_relation_class], via_relation: params[:via_relation], via_resource_id: params[:via_resource_id]), local: true, multipart: true do |form| %>
4
- <%= render Avo::PanelComponent.new(title: resource_panel[:name], display_breadcrumbs: true) do |c| %>
3
+ <%= form_with model: @resource.model, scope: @resource.form_scope, url: helpers.resources_path(resource: @resource, via_relation_class: params[:via_relation_class], via_relation: params[:via_relation], via_resource_id: params[:via_resource_id]), local: true, multipart: true do |form| %>
4
+ <%= render Avo::PanelComponent.new(title: resource_panel[:name], description: @resource.resource_description, display_breadcrumbs: true) do |c| %>
5
5
  <% c.tools do %>
6
6
  <div class="flex justify-end space-x-2">
7
7
  <%= a_link back_path do %>
@@ -3,7 +3,7 @@
3
3
  data-selected-resources='["<%= @resource.model.id %>"]'
4
4
  >
5
5
  <% @resource.panels.each_with_index do |resource_panel, index| %>
6
- <%= render Avo::PanelComponent.new(title: resource_panel[:name], display_breadcrumbs: @reflection.blank?, index: index) do |c| %>
6
+ <%= render Avo::PanelComponent.new(title: title, description: @resource.resource_description, display_breadcrumbs: @reflection.blank?, index: index) do |c| %>
7
7
  <% c.tools do %>
8
8
  <% if resource_panel[:name] == @resource.default_panel_name %>
9
9
  <%= render 'actions' %>
@@ -6,13 +6,24 @@ class Avo::Views::ResourceShowComponent < Avo::ResourceComponent
6
6
 
7
7
  attr_reader :fields_by_panel, :has_one_panels, :has_many_panels, :has_as_belongs_to_many_panels
8
8
 
9
- def initialize(resource: nil, reflection: nil, parent_model: nil)
9
+ def initialize(resource: nil, reflection: nil, parent_model: nil, resource_panel: nil)
10
10
  @resource = resource
11
11
  @reflection = reflection
12
+ @resource_panel = resource_panel
12
13
 
13
14
  split_panel_fields
14
15
  end
15
16
 
17
+ def title
18
+ if @reflection.present?
19
+ return field.name if field.present?
20
+
21
+ reflection_resource.name
22
+ else
23
+ @resource.panels.first[:name]
24
+ end
25
+ end
26
+
16
27
  def back_path
17
28
  if via_resource?
18
29
  helpers.resource_path(model: params[:via_resource_class].safe_constantize, resource: relation_resource, resource_id: params[:via_resource_id])
@@ -111,6 +111,7 @@ module Avo
111
111
  end
112
112
 
113
113
  def fill_model
114
+ puts ['@model_to_fill->', @model_to_fill].inspect
114
115
  @model = @resource.fill_model(@model_to_fill, cast_nullable(model_params))
115
116
  end
116
117
 
@@ -194,10 +194,11 @@ module Avo
194
194
  private
195
195
 
196
196
  def model_params
197
- model_param_key = @resource.singular_model_key
197
+ model_param_key = @resource.form_scope
198
198
 
199
199
  request_params = params.require(model_param_key).permit(permitted_params)
200
200
 
201
+ puts ['model_param_key->', model_param_key, params, request_params].inspect
201
202
  if @resource.devise_password_optional && request_params[:password].blank? && request_params[:password_confirmation].blank?
202
203
  request_params.delete(:password_confirmation)
203
204
  request_params.delete(:password)
@@ -18,6 +18,11 @@ module Avo
18
18
  @parent_model = @parent_resource.class.find_scope.find(params[:id])
19
19
  @parent_resource.hydrate(model: @parent_model)
20
20
  @query = @authorization.apply_policy @parent_model.public_send(params[:related_name])
21
+ @association_field = @parent_resource.get_field params[:related_name]
22
+
23
+ if @association_field.present? && @association_field.scope.present?
24
+ @query = @query.instance_exec(&@association_field.scope)
25
+ end
21
26
 
22
27
  super
23
28
  end
data/config/routes.rb CHANGED
@@ -23,7 +23,7 @@ Avo::Engine.routes.draw do
23
23
 
24
24
  # Generate resource routes as below:
25
25
  # resources :posts
26
- instance_eval(&Avo::App.draw_routes)
26
+ Avo::DynamicRouter::routes(self)
27
27
 
28
28
  # Relations
29
29
  get "/:resource_name/:id/:related_name/new", to: "relations#new", as: "associations_new"
data/lib/avo/app.rb CHANGED
@@ -155,24 +155,6 @@ module Avo
155
155
  filename
156
156
  end
157
157
  end
158
-
159
- def draw_routes
160
- # We should eager load all the classes so we find all descendants
161
- Rails.application.eager_load!
162
-
163
- proc do
164
- BaseResource.descendants
165
- .select do |resource|
166
- resource != :BaseResource
167
- end
168
- .select do |resource|
169
- resource.is_a? Class
170
- end
171
- .map do |resource|
172
- resources resource.new.model_key
173
- end
174
- end
175
- end
176
158
  end
177
159
  end
178
160
  end
@@ -19,6 +19,7 @@ module Avo
19
19
 
20
20
  class_attribute :id, default: :id
21
21
  class_attribute :title, default: :id
22
+ class_attribute :description, default: :id
22
23
  class_attribute :search_query, default: nil
23
24
  class_attribute :search_query_help, default: ''
24
25
  class_attribute :includes, default: []
@@ -154,6 +155,12 @@ module Avo
154
155
  fields
155
156
  end
156
157
 
158
+ def get_field(id)
159
+ get_field_definitions.find do |f|
160
+ f.id == id.to_sym
161
+ end
162
+ end
163
+
157
164
  def get_grid_fields
158
165
  return if self.class.grid_loader.blank?
159
166
 
@@ -224,6 +231,16 @@ module Avo
224
231
  name
225
232
  end
226
233
 
234
+ def resource_description
235
+ return instance_exec(&self.class.description) if self.class.description.respond_to? :call
236
+
237
+ # Show the description only on the resource index view.
238
+ # If the user wants to conditionally it on all pages, they should use a block.
239
+ if view == :index
240
+ return self.class.description if self.class.description.is_a? String
241
+ end
242
+ end
243
+
227
244
  def translation_key
228
245
  return "avo.resource_translations.#{class_name_without_resource.underscore}" if self.class.translation_enabled
229
246
 
@@ -442,7 +459,7 @@ module Avo
442
459
  end
443
460
 
444
461
  def form_scope
445
- model.class.base_class.to_s.underscore.downcase
462
+ model_class.base_class.to_s.underscore.downcase
446
463
  end
447
464
  end
448
465
  end
@@ -0,0 +1,18 @@
1
+ module Avo
2
+ module DynamicRouter
3
+ def self.routes(router)
4
+ Rails.application.eager_load!
5
+
6
+ BaseResource.descendants
7
+ .select do |resource|
8
+ resource != :BaseResource
9
+ end
10
+ .select do |resource|
11
+ resource.is_a? Class
12
+ end
13
+ .map do |resource|
14
+ router.resources resource.new.model_key
15
+ end
16
+ end
17
+ end
18
+ end
@@ -102,6 +102,12 @@ module Avo
102
102
  @id.to_s.humanize(keep_id_suffix: true)
103
103
  end
104
104
 
105
+ def plural_name
106
+ return I18n.t(translation_key, count: 2).capitalize if translation_key
107
+
108
+ name.pluralize
109
+ end
110
+
105
111
  def placeholder
106
112
  return @placeholder if @placeholder.present?
107
113
 
@@ -2,11 +2,13 @@ module Avo
2
2
  module Fields
3
3
  class HasBaseField < BaseField
4
4
  attr_accessor :display
5
+ attr_accessor :scope
5
6
 
6
7
  def initialize(id, **args, &block)
7
8
  super(id, **args, &block)
8
9
 
9
10
  @display = args[:display].present? ? args[:display] : :show
11
+ @scope = args[:scope].present? ? args[:scope] : nil
10
12
  end
11
13
 
12
14
  def resource
@@ -37,6 +37,10 @@ module Avo
37
37
  self.class.defined_methods(user, model, **args)
38
38
  end
39
39
 
40
+ def has_method?(method, **args)
41
+ self.class.defined_methods(user, record, **args).include? method.to_sym
42
+ end
43
+
40
44
  class << self
41
45
  def authorize(user, record, action, **args)
42
46
  return true if skip_authorization
@@ -101,7 +105,7 @@ module Avo
101
105
  Pundit.policy!(user, record).methods
102
106
  rescue => error
103
107
  if args[:raise_exception] == false
104
- false
108
+ []
105
109
  else
106
110
  raise error
107
111
  end
data/lib/avo/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Avo
2
- VERSION = "1.19.1.pre.9"
2
+ VERSION = "1.19.1.pre.10"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: avo
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.19.1.pre.9
4
+ version: 1.19.1.pre.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adrian Marin
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2022-02-03 00:00:00.000000000 Z
12
+ date: 2022-02-07 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
@@ -583,6 +583,7 @@ files:
583
583
  - lib/avo/base_action.rb
584
584
  - lib/avo/base_resource.rb
585
585
  - lib/avo/configuration.rb
586
+ - lib/avo/dynamic_router.rb
586
587
  - lib/avo/engine.rb
587
588
  - lib/avo/fields/badge_field.rb
588
589
  - lib/avo/fields/base_field.rb