base_editing_bootstrap 1.6.0 → 1.7.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: 0f292b335541e77a9d78ae97f8ef87cc436468094aaccd69750dae0f8dcf44af
4
- data.tar.gz: 7c3f97b3261d736258efc021f36cc131943763b13f7d933cfb0967cbf58e5f36
3
+ metadata.gz: 9c363b9569d715a8519f72345250ee7dd019de73ce87330e6a49d62acc0e0e10
4
+ data.tar.gz: 9d7499c1ba5fbf66c2d0f466237e7b69bce234d022d21cd9d74b920564b6960f
5
5
  SHA512:
6
- metadata.gz: 3ec5909dab19b68590b2e2beb5dbcf7b502f2bbeb9ae3c2c244117837c4eeb54d20848ca30381f0cae4c88f6047bf3d55c2dfeef37dd28623b7b59933970e33c
7
- data.tar.gz: dbaca2890a7a8a6ea1f9cde44fcd6d07495a6f289ba82f62698cc8b9cac6bfe20809133590ca05c91cecd1078ea7ce4a87087c0501e52570146eca17efd65486
6
+ metadata.gz: b2243dc34ddba312f01465774a8df4662ea78e832885774966d29c685164201284aae102b8188a59c54d8b3c44a1c8af81227e04ef28a780651e6cdcd1abe6c1
7
+ data.tar.gz: 7c43ad6f7059a50c7719633e432d941ebd2433518712b9b5d20b5fff573cc802b24de785e81a96eebaf31c6d96639b2e3a99445dca3df086956d73a0529cc6d3
data/CHANGELOG.md CHANGED
@@ -2,6 +2,22 @@
2
2
  All notable changes to this project will be documented in this file. See [conventional commits](https://www.conventionalcommits.org/) for commit guidelines.
3
3
 
4
4
  - - -
5
+ ## 1.7.0 - 2025-02-11
6
+ #### Bug Fixes
7
+ - Add permits of ransack scopes - (5fa26c6) - Marino Bonetti
8
+ - Correct set select classes - (a5ef685) - Marino Bonetti
9
+ - Boolean_icon with nil value - (211c91f) - Marino Bonetti
10
+ - Better expection for spec helper - (65b26ce) - Marino Bonetti
11
+ #### Documentation
12
+ - Add documentation for help html version - (dbc1c01) - Marino Bonetti
13
+ #### Features
14
+ - Add HTML helper version in form field help - (f06c438) - Marino Bonetti
15
+ - Add Configurable distinct query in controller - (184d772) - Marino Bonetti
16
+ #### Tests
17
+ - Correct validation for selects - (e4b8016) - Marino Bonetti
18
+
19
+ - - -
20
+
5
21
  ## 1.6.0 - 2025-01-23
6
22
  #### Features
7
23
  - Add new test helper for association relation - (15690c7) - Marino Bonetti
data/README.md CHANGED
@@ -126,7 +126,8 @@ Utilizzo per modello base, in questo esempio prendiamo come modello Post come es
126
126
  ```
127
127
  - è possibile customizzare
128
128
  - un text help per ogni campo andando ad aggiungere nelle traduzioni la relativa
129
- traduzione nella posizione: `it.activerecord.attributes.MODEL.FIELD/help_text`
129
+ traduzione nella posizione: `it.activerecord.attributes.MODEL.FIELD/help_text` oppure `help_text_html` in caso di
130
+ contenuto con html
130
131
  - un blocco per l'unità di misura accanto al campo aggiungendo alle traduzioni:
131
132
  `it.activerecord.attributes.MODEL.FIELD/unit`
132
133
 
@@ -14,13 +14,19 @@ class BaseEditingController < RestrictedAreaController
14
14
  # Works like documented in https://activerecord-hackery.github.io/ransack/getting-started/sorting/#sorting-in-the-controller
15
15
  class_attribute :default_sorts, default: ["id"]
16
16
 
17
+ ##
18
+ # Configure default distinct results in the index query.
19
+ # Works like documented in https://activerecord-hackery.github.io/ransack/going-further/other-notes/#problem-with-distinct-selects
20
+ class_attribute :default_distinct, default: true
21
+
17
22
  def index
18
23
  authorize base_class
19
24
 
20
25
  q = policy_scope(base_scope)
21
26
  @search_instance = search_class.new(q, current_user,
22
27
  params: params.permit(:page, :q => {}), # FIXME trovare modo per essere più "STRONG"
23
- sorts: default_sorts
28
+ sorts: default_sorts,
29
+ distinct: default_distinct
24
30
  )
25
31
  @search_instance = yield(@search_instance) if block_given?
26
32
  end
@@ -1,4 +1,5 @@
1
1
  module Utilities::PageHelper
2
+ include Utilities::IconHelper
2
3
  # @param [BaseModel] base_class
3
4
  def title_mod_g(base_class)
4
5
  "#{t("edit")} #{base_class.model_name.human}"
@@ -24,12 +25,15 @@ module Utilities::PageHelper
24
25
  # end
25
26
  # end
26
27
 
27
- # @param [TrueClass, FalseClass] valore
28
+ # @param [TrueClass, FalseClass, NilClass] valore
28
29
  def boolean_to_icon(valore)
29
- if valore
30
+ case valore
31
+ when true
30
32
  icon("check-lg", class: "text-success")
31
- else
33
+ when false
32
34
  icon("x-lg", class: "text-danger")
35
+ else
36
+ nil
33
37
  end
34
38
  end
35
39
 
@@ -24,6 +24,8 @@ class BaseModelPolicy < ApplicationPolicy
24
24
  []
25
25
  end
26
26
 
27
+ def permitted_scopes_for_ransack = []
28
+
27
29
  def search_fields = []
28
30
 
29
31
  def search_result_fields = []
@@ -6,7 +6,13 @@
6
6
  %>
7
7
  <%# locals: (object:,field:) -%>
8
8
  <%
9
- help_text = object.class.human_attribute_name("#{field}/help_text", default: "")
9
+ nf = "NOT_FOUND_HTML"
10
+ help_text = object.class.human_attribute_name("#{field}/help_text_html", default: nf)
11
+ if help_text != nf
12
+ help_text = help_text.html_safe
13
+ else
14
+ help_text = object.class.human_attribute_name("#{field}/help_text", default: "")
15
+ end
10
16
  unless help_text.blank?
11
17
  %>
12
18
  <div class="form-text"><%= help_text %></div>
@@ -1 +1 @@
1
- 1.6.0
1
+ 1.7.0
@@ -7,7 +7,9 @@ module BaseEditingBootstrap
7
7
  included do
8
8
  include IsValidated
9
9
  include ActionTranslation
10
- delegate :ransackable_attributes, :ransackable_associations, to: :@class
10
+ delegate :ransackable_attributes,
11
+ :ransackable_associations,
12
+ :ransackable_scopes, to: :@class
11
13
 
12
14
 
13
15
  ##
@@ -34,6 +36,14 @@ module BaseEditingBootstrap
34
36
  Pundit.policy(User.new, self.new).permitted_associations_for_ransack.map(&:to_s)
35
37
  end
36
38
  end
39
+
40
+ def ransackable_scopes(auth_object = nil)
41
+ if auth_object
42
+ Pundit.policy(auth_object, self.new).permitted_scopes_for_ransack.map(&:to_s)
43
+ else
44
+ Pundit.policy(User.new, self.new).permitted_scopes_for_ransack.map(&:to_s)
45
+ end
46
+ end
37
47
  end
38
48
  end
39
49
  end
@@ -24,8 +24,8 @@ module BaseEditingBootstrap::Forms
24
24
  ##
25
25
  # Costruisce l'array delle classi che devono essere presenti sul campo della form
26
26
  #
27
- def form_style_class_for(method, options = {})
28
- classes = ["form-control"]
27
+ def form_style_class_for(method, options = {}, base_classes: ["form-control"])
28
+ classes = base_classes
29
29
  classes << "is-invalid" if object.errors && object.errors.include?(method)
30
30
  classes << options[:class].split(" ") if options[:class]
31
31
  classes.flatten.compact.uniq.join(" ")
@@ -46,8 +46,8 @@ module BaseEditingBootstrap::Forms
46
46
  end
47
47
 
48
48
  def select(method, choices = nil, options = {}, html_options = {}, &block)
49
- html_options[:class] = "form-select #{html_options[:class]}"
50
- super(method, choices, options, html_options.merge(class: form_style_class_for(method, html_options)), &block)
49
+ html_options.merge!(class: form_style_class_for(method, html_options, base_classes: ["form-control", "form-select"]))
50
+ super(method, choices, options,html_options , &block)
51
51
  end
52
52
 
53
53
  def check_box(method, options = {}, checked_value = "1", unchecked_value = "0")
@@ -57,9 +57,10 @@ module BaseEditingBootstrap::Forms
57
57
  else
58
58
  label_tag = nil
59
59
  end
60
- classes = (["form-check"] + (options.extract!(:class)[:class] || "").split(" ")).join(" ")
61
- @template.content_tag(:div, class: classes) do
62
- super(method, options.reverse_merge(class: "form-check-input"), checked_value, unchecked_value) + label_tag
60
+ @template.content_tag(:div, class: form_style_class_for(method, {class: (options.extract!(:class)[:class] || "")}, base_classes: ["form-check"])) do
61
+ checkbox_class = ["form-check-input"]
62
+ checkbox_class << "is-invalid" if object.errors && object.errors.include?(method)
63
+ super(method, options.reverse_merge(class: checkbox_class.join(" ")), checked_value, unchecked_value) + label_tag
63
64
  end
64
65
  end
65
66
 
@@ -75,7 +76,7 @@ module BaseEditingBootstrap::Forms
75
76
  options = {},
76
77
  html_options = {},
77
78
  &block)
78
- form_check_classes = (["form-check"] + [(html_options.delete(:form_check_class){""}).split(" ").collect(&:strip)]).compact.join(" ")
79
+ form_check_classes = (["form-check"] + [(html_options.delete(:form_check_class) { "" }).split(" ").collect(&:strip)]).compact.join(" ")
79
80
  super do |builder|
80
81
  @template.content_tag(:div, class: form_check_classes) do
81
82
  builder.check_box(class: "form-check-input") + builder.label(class: "form-check-label")
@@ -7,17 +7,18 @@ module BaseEditingBootstrap::Searches
7
7
  include ActiveModel::Naming
8
8
  include ActiveModel::Conversion
9
9
 
10
- attr_reader :model_klass, :user, :params, :scope, :sorts
10
+ attr_reader :model_klass, :user, :params, :scope, :sorts, :distinct
11
11
 
12
12
  # @param [User] user
13
13
  # @param [ActiveRecord::Associations::CollectionProxy] scope
14
14
  # @param [Array<String (frozen)>] sort
15
- def initialize(scope, user, params: {page: nil}, sorts: ["id"])
15
+ def initialize(scope, user, params: {page: nil}, sorts: ["id"], distinct: true)
16
16
  @model_klass = scope.klass
17
17
  @user = user
18
18
  @scope = scope
19
19
  @params = params
20
20
  @sorts = sorts
21
+ @distinct = distinct
21
22
  end
22
23
 
23
24
  ##
@@ -26,7 +27,7 @@ module BaseEditingBootstrap::Searches
26
27
  def results
27
28
  ransack_query
28
29
  .tap { |r| r.sorts = @sorts if r.sorts.empty? }
29
- .result(distinct: true)
30
+ .result(distinct: @distinct)
30
31
  .tap { |q| Rails.logger.debug { "[Ransack] params:#{params} - sql: #{q.to_sql}" } }
31
32
  .page(params[:page])
32
33
  end
@@ -5,11 +5,14 @@ RSpec.describe <%= class_name %>, type: :model do
5
5
 
6
6
  # it_behaves_like "a base model",
7
7
  # ransack_permitted_attributes: %w[<%= attributes_names.join(" ") %>],
8
- # ransack_permitted_associations: [] do
8
+ # ransack_permitted_associations: [],
9
+ # option_label_method: :to_s,
10
+ # ransack_permitted_scopes: [] do
9
11
  # let(:auth_object) { :auth_object } <- default
10
12
  # let(:auth_object) { create(:user, :as_admin) } <- in caso di necessità di override
11
13
  # let(:new_user_ransack_permitted_attributes) { ransack_permitted_attributes }
12
14
  # let(:new_user_ransack_permitted_associations) { ransack_permitted_associations }
15
+ # let(:new_user_ransack_permitted_scopes) { ransack_permitted_scopes }
13
16
  # end
14
17
 
15
18
  end
@@ -49,7 +49,9 @@ RSpec.shared_examples "base editing controller" do |factory: nil, only: [], exce
49
49
 
50
50
  ##
51
51
  # Possibili override per la costruzione delle path
52
- #
52
+
53
+ let(:default_sorts) { BaseEditingController.default_sorts } # configurazione nel controller per ordine di default del modello
54
+ let(:default_distinct) { BaseEditingController.default_distinct } # configurazione nel controller per distinct nella ricerca di ransack
53
55
  let(:url_for_new) { url_for([model.new, action: :new]) }
54
56
  let(:url_for_index) { url_for(model) }
55
57
  let(:url_for_create) { url_for(model.new) }
@@ -89,10 +91,13 @@ RSpec.shared_examples "base editing controller" do |factory: nil, only: [], exce
89
91
  params = {q: {"foo_eq": "foo"}}
90
92
  get url_for_index, params: params
91
93
  expect(response).to have_http_status(200)
92
- expect(assigns[:search_instance]).to be_an_instance_of(BaseEditingBootstrap::Searches::Base).and(have_attributes(
93
- user: user,
94
- params: ActionController::Parameters.new(params).permit!
95
- ))
94
+ expect(assigns[:search_instance]).to be_an_instance_of(BaseEditingBootstrap::Searches::Base)
95
+ .and(have_attributes(
96
+ user: user,
97
+ params: ActionController::Parameters.new(params).permit!,
98
+ sorts: default_sorts,
99
+ distinct: default_distinct,
100
+ ))
96
101
  end
97
102
  end
98
103
  end
@@ -194,7 +199,7 @@ default_unathorized_failure = -> { raise "TODO - passare proc con richiesta che
194
199
  RSpec.shared_examples "fail with unauthorized" do |request: default_unathorized_failure|
195
200
  it "is expected to redirect to root" do
196
201
 
197
- if Gem::Version.create( Pundit::VERSION) < Gem::Version.create('2.3.2')
202
+ if Gem::Version.create(Pundit::VERSION) < Gem::Version.create('2.3.2')
198
203
  allow(Pundit).to receive(:authorize).with(user, any_args).and_raise(Pundit::NotAuthorizedError)
199
204
  else
200
205
  allow_any_instance_of(Pundit::Context).to receive(:authorize).and_raise(Pundit::NotAuthorizedError)
@@ -1,4 +1,7 @@
1
- RSpec.shared_examples "a base model" do |ransack_permitted_attributes: [], ransack_permitted_associations: [], option_label_method: :to_s|
1
+ RSpec.shared_examples "a base model" do |ransack_permitted_attributes: [],
2
+ ransack_permitted_associations: [],
3
+ option_label_method: :to_s,
4
+ ransack_permitted_scopes: []|
2
5
 
3
6
  it_behaves_like "a validated? object"
4
7
 
@@ -10,15 +13,24 @@ RSpec.shared_examples "a base model" do |ransack_permitted_attributes: [], ransa
10
13
  instance = described_class.new
11
14
  expect(instance).to respond_to(:option_label)
12
15
 
13
- expect(instance).to receive(option_label_method).and_call_original
16
+ expect(instance).to receive(option_label_method).and_call_original, "Expected `#{instance.class}#option_label` chiami il metodo `##{option_label_method}` per la traduzione del label nelle options"
14
17
  instance.option_label
15
18
  end
16
19
 
20
+ if ransack_permitted_scopes.any?
21
+ it "have scopes" do
22
+ ransack_permitted_scopes.each do |scope|
23
+ expect(described_class).to respond_to(scope)
24
+ end
25
+ end
26
+ end
27
+
17
28
  ##
18
29
  # Oggetto solitamente di classe User che identifichi l'utente a cui eseguire il check dei permessi
19
30
  let(:auth_object) { :auth_object }
20
31
  let(:new_user_ransack_permitted_attributes) { ransack_permitted_attributes }
21
32
  let(:new_user_ransack_permitted_associations) { ransack_permitted_associations }
33
+ let(:new_user_ransack_permitted_scopes) { ransack_permitted_scopes }
22
34
 
23
35
  describe "with ransackables" do
24
36
  where(:base_model_method, :result, :new_user_result) do
@@ -29,6 +41,9 @@ RSpec.shared_examples "a base model" do |ransack_permitted_attributes: [], ransa
29
41
  [
30
42
  :ransackable_associations, ransack_permitted_associations.map(&:to_s), lazy { new_user_ransack_permitted_associations.map(&:to_s) }
31
43
  ],
44
+ [
45
+ :ransackable_scopes, ransack_permitted_scopes.map(&:to_s), lazy { new_user_ransack_permitted_scopes.map(&:to_s) }
46
+ ],
32
47
  ]
33
48
  end
34
49
 
@@ -25,6 +25,7 @@ RSpec.shared_examples "a standard base model policy" do |factory, check_default_
25
25
  [:search_fields],
26
26
  [:permitted_associations_for_ransack],
27
27
  [:permitted_attributes_for_ransack],
28
+ [:permitted_scopes_for_ransack],
28
29
  [:editable_attributes],
29
30
  [:permitted_attributes],
30
31
  ]
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: base_editing_bootstrap
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.0
4
+ version: 1.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marino Bonetti
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-01-23 00:00:00.000000000 Z
11
+ date: 2025-02-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails