rails_admin_next 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile +22 -23
- data/app/controllers/rails_admin_next/main_controller.rb +10 -3
- data/app/helpers/rails_admin_next/form_builder.rb +10 -3
- data/app/views/layouts/rails_admin_next/_head.html.erb +8 -6
- data/config/locales/rails_admin_next.en.yml +1 -0
- data/lib/rails_admin_next/adapters/active_record/association.rb +24 -1
- data/lib/rails_admin_next/config/actions/bulk_delete.rb +8 -3
- data/lib/rails_admin_next/config/actions/delete.rb +14 -6
- data/lib/rails_admin_next/config/configurable.rb +6 -2
- data/lib/rails_admin_next/config/fields/base.rb +3 -1
- data/lib/rails_admin_next/version.rb +4 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a47ededbcad2a173ee6e0d77e581ef0e4e7649458a7bf0e659592726299c6ae2
|
|
4
|
+
data.tar.gz: bded3ecf250c69185107635e6877b4d9467f678fb12919b08146a3e67fdb7842
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 88379320dc12d64a6311ed451b82a685862b7c7c3ea4449c44fc391a6b1cdad8beed31623a7d4709cc8bef210779cdfe41593e24b7a6688fdf7dcbdd101a6bbb
|
|
7
|
+
data.tar.gz: 6baf5beb0080b57a8f1fe08ccd6407872b1bec576fb43a493f58fefc76d5883c04f74c3d66e674756684c224038f6235c6f7e54b97483aa9d073a45c7dde8294
|
data/Gemfile
CHANGED
|
@@ -7,40 +7,39 @@ gem "rails"
|
|
|
7
7
|
gem "turbo-rails"
|
|
8
8
|
|
|
9
9
|
group :development, :test do
|
|
10
|
-
gem "pry"
|
|
10
|
+
gem "pry"
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
group :test do
|
|
14
14
|
gem "brakeman", require: false
|
|
15
|
-
gem "bundler-audit", require: false
|
|
16
15
|
gem "cancancan", "~> 3.0"
|
|
17
|
-
gem "cuprite"
|
|
18
|
-
gem "database_cleaner-active_record",
|
|
19
|
-
gem "factory_bot"
|
|
20
|
-
gem "generator_spec"
|
|
21
|
-
gem "image_processing"
|
|
16
|
+
gem "cuprite"
|
|
17
|
+
gem "database_cleaner-active_record", require: false
|
|
18
|
+
gem "factory_bot"
|
|
19
|
+
gem "generator_spec"
|
|
20
|
+
gem "image_processing"
|
|
22
21
|
gem "pundit"
|
|
23
|
-
gem "rspec-
|
|
24
|
-
gem "rspec-rails", ">= 4.0.0.beta2"
|
|
22
|
+
gem "rspec-rails"
|
|
25
23
|
gem "rspec-retry"
|
|
26
|
-
gem "ruby-vips"
|
|
27
|
-
gem "
|
|
28
|
-
gem "simplecov-lcov", require: false
|
|
29
|
-
gem "standard", ">= 1.35.1", require: false
|
|
30
|
-
|
|
31
|
-
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
|
|
32
|
-
gem "tzinfo-data", platforms: %i[mingw mswin x64_mingw jruby]
|
|
24
|
+
gem "ruby-vips"
|
|
25
|
+
gem "standard", require: false
|
|
33
26
|
gem "warden"
|
|
27
|
+
# Capybara's own default server is puma, which is not in this bundle; spec_helper selects
|
|
28
|
+
# webrick instead. It arrives as a transitive of ferrum, so declare what the suite runs on
|
|
29
|
+
# rather than borrowing someone else's dependency.
|
|
30
|
+
gem "webrick"
|
|
31
|
+
|
|
32
|
+
# Windows does not ship zoneinfo files.
|
|
33
|
+
gem "tzinfo-data", platforms: %i[windows]
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
+
# The dummy app boots with `Bundler.require(*Rails.groups, :active_record)`, so this group is
|
|
37
|
+
# loaded in every environment — that is what puts the adapter on the load path outside :test.
|
|
36
38
|
group :active_record do
|
|
37
|
-
gem "
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
gem "pg", ">= 1.0.0"
|
|
42
|
-
gem "sqlite3", ">= 1.3.0"
|
|
43
|
-
end
|
|
39
|
+
gem "mysql2"
|
|
40
|
+
gem "paper_trail"
|
|
41
|
+
gem "pg"
|
|
42
|
+
gem "sqlite3"
|
|
44
43
|
end
|
|
45
44
|
|
|
46
45
|
gemspec
|
|
@@ -108,10 +108,17 @@ module RailsAdminNext
|
|
|
108
108
|
model_config.send(action).with(controller: self, view: view_context, object: @object).visible_fields
|
|
109
109
|
end
|
|
110
110
|
|
|
111
|
-
def sanitize_params_for!(action, model_config = @model_config, target_params = params[@abstract_model.param_key])
|
|
111
|
+
def sanitize_params_for!(action, model_config = @model_config, target_params = params[@abstract_model.param_key], nested_in = nil)
|
|
112
112
|
return unless target_params.present?
|
|
113
113
|
|
|
114
|
-
|
|
114
|
+
# A subform never draws the child's link back to the record being edited — the parent
|
|
115
|
+
# association names it, and FormBuilder#nested_field_association? skips it on that name.
|
|
116
|
+
# Deriving the allowlist from visible_fields alone would keep permitting keys no form of
|
|
117
|
+
# ours submits, so drop that one field here too. Only at nested depth: editing a Comment
|
|
118
|
+
# on its own is a legitimate way to set its commentable.
|
|
119
|
+
fields = visible_fields(action, model_config).reject do |field|
|
|
120
|
+
nested_in && field.is_a?(Config::Fields::Association) && field.name == nested_in.inverse_of
|
|
121
|
+
end
|
|
115
122
|
allowed_methods = fields.collect(&:allowed_methods).flatten.uniq.collect(&:to_s) << "id" << "_destroy"
|
|
116
123
|
fields.each { |field| field.parse_input(target_params) }
|
|
117
124
|
target_params.slice!(*allowed_methods)
|
|
@@ -132,7 +139,7 @@ module RailsAdminNext
|
|
|
132
139
|
[]
|
|
133
140
|
end
|
|
134
141
|
children_params.each do |children_param|
|
|
135
|
-
sanitize_params_for!(:nested, association.associated_model_config, children_param) if children_param.respond_to?(:values)
|
|
142
|
+
sanitize_params_for!(:nested, association.associated_model_config, children_param, association) if children_param.respond_to?(:values)
|
|
136
143
|
end
|
|
137
144
|
end
|
|
138
145
|
end
|
|
@@ -164,10 +164,17 @@ module RailsAdminNext
|
|
|
164
164
|
|
|
165
165
|
private
|
|
166
166
|
|
|
167
|
+
# A field the parent association names as its inverse is the child's link back to
|
|
168
|
+
# the record being edited, so the user is never asked to fill it. That name match
|
|
169
|
+
# stands on its own: a polymorphic child such as Comment#commentable declares no
|
|
170
|
+
# inverse_of, so gating it on the child's own inverse_of would never let it fire.
|
|
171
|
+
# It stays limited to association fields because a polymorphic `as:` is never checked
|
|
172
|
+
# against the child class, and a plain column could otherwise match it by name.
|
|
167
173
|
def nested_field_association?(field, nested_in)
|
|
168
|
-
|
|
169
|
-
(
|
|
170
|
-
field.
|
|
174
|
+
nested_in.presence &&
|
|
175
|
+
((field.is_a?(Config::Fields::Association) && field.name == nested_in.inverse_of) ||
|
|
176
|
+
(field.inverse_of.presence && field.inverse_of == nested_in.name &&
|
|
177
|
+
@template.instance_variable_get(:@model_config).abstract_model == field.abstract_model))
|
|
171
178
|
end
|
|
172
179
|
end
|
|
173
180
|
end
|
|
@@ -10,17 +10,19 @@
|
|
|
10
10
|
CSP out of the picture and lets :light/:dark pins override the OS preference. %>
|
|
11
11
|
<meta content="<%= {auto: 'light dark', light: 'light', dark: 'dark'}.fetch(RailsAdminNext.config.color_scheme) %>" name="color-scheme">
|
|
12
12
|
<%# The entry stylesheet pulls tokens/framework/skin via @import, which the browser only discovers
|
|
13
|
-
after fetching and parsing the entry file. Preload the three targets so all four fetch in parallel.
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
after fetching and parsing the entry file. Preload the three targets so all four fetch in parallel.
|
|
14
|
+
A nonce-based style-src blocks a preload the same as a stylesheet, so these carry the nonce too;
|
|
15
|
+
`tag` drops the attribute entirely when no policy is configured and the nonce is nil. %>
|
|
16
|
+
<% %w[tokens framework skin].each do |sheet| %>
|
|
17
|
+
<%= tag.link rel: "preload", as: "style", href: asset_path("rails_admin/#{sheet}.css"), nonce: content_security_policy_nonce %>
|
|
18
|
+
<% end %>
|
|
17
19
|
<%# Date/time fields use native HTML5 inputs; the picker UI is the browser's. Icons are inline SVG (no icon font). %>
|
|
18
|
-
<%= stylesheet_link_tag "rails_admin", media: :all, data: {'turbo-track': 'reload'} %>
|
|
20
|
+
<%= stylesheet_link_tag "rails_admin", media: :all, nonce: true, data: {'turbo-track': 'reload'} %>
|
|
19
21
|
<%# Rich-text (ActionText) fields render a Trix editor; load its stylesheet so the toolbar is styled.
|
|
20
22
|
trix.css ships with the action_text-trix gem and is on the Propshaft load path when ActionText is used.
|
|
21
23
|
NOTE: trix.css is light-only for now — dark styling for the Trix toolbar/editor is deferred. %>
|
|
22
24
|
<% if defined?(ActionText::Engine) %>
|
|
23
|
-
<%= stylesheet_link_tag "trix", media: :all, data: {'turbo-track': 'reload'} %>
|
|
25
|
+
<%= stylesheet_link_tag "trix", media: :all, nonce: true, data: {'turbo-track': 'reload'} %>
|
|
24
26
|
<% end %>
|
|
25
27
|
<%# Zero-build delivery: browser-native ESM pinned by the engine-owned importmap, served by Propshaft.
|
|
26
28
|
The importmap-rails helpers thread the request CSP nonce onto every inline <script> they emit. %>
|
|
@@ -53,6 +53,7 @@ en:
|
|
|
53
53
|
flash:
|
|
54
54
|
successful: "%{name} successfully %{action}"
|
|
55
55
|
error: "%{name} failed to be %{action}"
|
|
56
|
+
delete_restricted: "%{name} failed to be deleted: %{reason}"
|
|
56
57
|
noaction: "No actions were taken"
|
|
57
58
|
model_not_found: "Model '%{model}' could not be found"
|
|
58
59
|
object_not_found: "%{model} with id '%{id}' could not be found"
|
|
@@ -104,8 +104,31 @@ module RailsAdminNext
|
|
|
104
104
|
options[:polymorphic] || false
|
|
105
105
|
end
|
|
106
106
|
|
|
107
|
+
# A polymorphic `as:` names the belongs_to on the other side, and Rails resolves
|
|
108
|
+
# an inverse for conventional pairs that declare nothing — both are the
|
|
109
|
+
# association's inverse for form-nesting purposes. `inverse_of: false` opts out
|
|
110
|
+
# of all of it, the way it does in Rails.
|
|
111
|
+
#
|
|
112
|
+
# The declared option is read first because it has to be: asking Rails for the
|
|
113
|
+
# inverse of a polymorphic belongs_to that declares one raises, since there is no
|
|
114
|
+
# single class to look the name up on.
|
|
107
115
|
def inverse_of
|
|
108
|
-
options[:inverse_of]
|
|
116
|
+
return nil if options[:inverse_of] == false
|
|
117
|
+
|
|
118
|
+
options[:inverse_of].try(:to_sym) || association.inverse_of&.name || as
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Rails guards the presence validator it adds for a required belongs_to with
|
|
122
|
+
# an `if:` condition, and the validator scan in Fields::Base#required? skips
|
|
123
|
+
# conditional validators, so required-ness has to come from the reflection.
|
|
124
|
+
def required?
|
|
125
|
+
return false unless type == :belongs_to
|
|
126
|
+
# A `default:` association is filled by a before_validation callback, so the
|
|
127
|
+
# record validates without the user choosing anything.
|
|
128
|
+
return false if options[:default]
|
|
129
|
+
|
|
130
|
+
optional = options[:optional]
|
|
131
|
+
optional.nil? ? model.belongs_to_required_by_default : !optional
|
|
109
132
|
end
|
|
110
133
|
|
|
111
134
|
def read_only?
|
|
@@ -34,9 +34,14 @@ module RailsAdminNext
|
|
|
34
34
|
unless params[:bulk_ids].blank?
|
|
35
35
|
@objects = list_entries(@model_config, :destroy)
|
|
36
36
|
unless @objects.blank?
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
not_destroyed =
|
|
37
|
+
# Rescue per record: dependent: :restrict_with_exception raises, and an
|
|
38
|
+
# escaping raise would abandon every record behind it in the batch.
|
|
39
|
+
destroyed, not_destroyed = Array.wrap(@objects).partition do |object|
|
|
40
|
+
object.destroy
|
|
41
|
+
object.destroyed?
|
|
42
|
+
rescue ActiveRecord::DeleteRestrictionError
|
|
43
|
+
false
|
|
44
|
+
end
|
|
40
45
|
destroyed.each do |object|
|
|
41
46
|
@auditing_adapter&.delete_object(object, @abstract_model, _current_user)
|
|
42
47
|
end
|
|
@@ -33,12 +33,20 @@ module RailsAdminNext
|
|
|
33
33
|
|
|
34
34
|
elsif request.delete? # DESTROY
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
36
|
+
begin
|
|
37
|
+
if @object.destroy
|
|
38
|
+
@auditing_adapter&.delete_object(@object, @abstract_model, _current_user)
|
|
39
|
+
flash[:success] = t("admin.flash.successful", name: @model_config.label, action: t("admin.actions.delete.done"))
|
|
40
|
+
redirect_to index_path
|
|
41
|
+
else
|
|
42
|
+
handle_save_error :delete
|
|
43
|
+
end
|
|
44
|
+
rescue ActiveRecord::DeleteRestrictionError => e
|
|
45
|
+
# dependent: :restrict_with_exception raises instead of adding an error to the
|
|
46
|
+
# record, so there is nothing for handle_save_error to render — report the
|
|
47
|
+
# association the exception names and send the admin back where they came from.
|
|
48
|
+
flash[:error] = t("admin.flash.delete_restricted", name: @model_config.label, reason: e.message)
|
|
49
|
+
redirect_to back_or_index, status: :see_other
|
|
42
50
|
end
|
|
43
51
|
|
|
44
52
|
end
|
|
@@ -55,9 +55,13 @@ module RailsAdminNext
|
|
|
55
55
|
option_name = option_name.to_s
|
|
56
56
|
options[option_name] = nil
|
|
57
57
|
|
|
58
|
-
# If it's a boolean create an alias for it and remove question mark
|
|
58
|
+
# If it's a boolean create an alias for it and remove question mark. The
|
|
59
|
+
# rebinding is what the rest of the method reads: the getter, the setter, the
|
|
60
|
+
# @<name>_registered variable and the recursion key all want the chopped name,
|
|
61
|
+
# while @config_options keeps the un-chopped one it was registered under.
|
|
59
62
|
if option_name.end_with?("?")
|
|
60
|
-
|
|
63
|
+
option_name = option_name.chop
|
|
64
|
+
scope.send(:define_method, "#{option_name}?") do
|
|
61
65
|
send(option_name)
|
|
62
66
|
end
|
|
63
67
|
end
|
|
@@ -215,7 +215,9 @@ module RailsAdminNext
|
|
|
215
215
|
:nil
|
|
216
216
|
end
|
|
217
217
|
|
|
218
|
-
|
|
218
|
+
# An association Rails treats as optional can still be required by a validator on its
|
|
219
|
+
# foreign key, so the reflection answer widens the validator scan instead of replacing it.
|
|
220
|
+
(@required ||= {})[context] ||= (association? && properties.try(:required?)) || !!([name] + children_fields).uniq.detect do |column_name|
|
|
219
221
|
abstract_model.model.validators_on(column_name).detect do |v|
|
|
220
222
|
!(v.options[:allow_nil] || v.options[:allow_blank]) &&
|
|
221
223
|
%i[presence numericality attachment_presence].include?(v.kind) &&
|
|
@@ -2,9 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
module RailsAdminNext
|
|
4
4
|
class Version
|
|
5
|
+
# Releases are cut by publishing a Release-Drafter draft: the gem-push workflow stamps
|
|
6
|
+
# the tag's version over these constants at build time, so the in-repo values are only
|
|
7
|
+
# the floor a local `gem build` produces.
|
|
5
8
|
MAJOR = 1
|
|
6
9
|
MINOR = 0
|
|
7
|
-
PATCH =
|
|
10
|
+
PATCH = 2
|
|
8
11
|
PRE = nil
|
|
9
12
|
|
|
10
13
|
class << self
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rails_admin_next
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Daniel López Prat
|
|
@@ -340,7 +340,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
340
340
|
- !ruby/object:Gem::Version
|
|
341
341
|
version: 1.8.11
|
|
342
342
|
requirements: []
|
|
343
|
-
rubygems_version: 4.0.
|
|
343
|
+
rubygems_version: 4.0.16
|
|
344
344
|
specification_version: 4
|
|
345
345
|
summary: Admin for Rails
|
|
346
346
|
test_files: []
|