diff --git a/Gemfile.lock b/Gemfile.lock
index ef386b26c10..973bc7d69d5 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
- activeadmin (4.0.0.beta10)
+ activeadmin (4.0.0.beta11)
arbre (~> 2.0)
csv
formtastic (>= 3.1)
diff --git a/UPGRADING.md b/UPGRADING.md
index b79e5d22191..f02530ac78d 100644
--- a/UPGRADING.md
+++ b/UPGRADING.md
@@ -8,14 +8,14 @@ ActiveAdmin v4 uses TailwindCSS. It has **mobile web, dark mode and RTL support*
These instructions assume the `cssbundling-rails` and `importmap-rails` gems are already installed and you have run their install commands in your app. If you haven't done so, please do before continuing.
-Update your `Gemfile` with `gem "activeadmin", "4.0.0.beta10"` and then run `gem install activeadmin --pre`.
+Update your `Gemfile` with `gem "activeadmin", "4.0.0.beta11"` and then run `gem install activeadmin --pre`.
Now, run `rails generate active_admin:assets` to replace the old assets with the new files.
Then add the npm package and update the `build:css` script.
```
-yarn add @activeadmin/activeadmin@4.0.0-beta10
+yarn add @activeadmin/activeadmin@4.0.0-beta11
npm pkg set scripts.build:css="tailwindcss -i ./app/assets/stylesheets/active_admin.css -o ./app/assets/builds/active_admin.css --minify -c tailwind-active_admin.config.js"
```
diff --git a/app/controllers/active_admin/resource_controller/decorators.rb b/app/controllers/active_admin/resource_controller/decorators.rb
index 59243364d66..a916cb24b12 100644
--- a/app/controllers/active_admin/resource_controller/decorators.rb
+++ b/app/controllers/active_admin/resource_controller/decorators.rb
@@ -61,7 +61,7 @@ def self.wrap(decorator)
def self.wrap!(parent, name)
::Class.new parent do
delegate :reorder, :page, :current_page, :total_pages, :limit_value,
- :total_count, :total_pages, :offset, :to_key, :group_values,
+ :total_count, :offset, :to_key, :group_values,
:except, :find_each, :ransack, to: :object
define_singleton_method(:name) { name }
diff --git a/app/helpers/active_admin/display_helper.rb b/app/helpers/active_admin/display_helper.rb
index 60eef0ff783..f86172af422 100644
--- a/app/helpers/active_admin/display_helper.rb
+++ b/app/helpers/active_admin/display_helper.rb
@@ -108,8 +108,8 @@ def boolean_attr?(resource, attr, value)
when TrueClass, FalseClass
true
else
- if resource.class.respond_to? :columns_hash
- column = resource.class.columns_hash[attr.to_s] and column.type == :boolean
+ if resource.class.respond_to? :attribute_types
+ resource.class.attribute_types[attr.to_s].is_a?(ActiveModel::Type::Boolean)
end
end
end
diff --git a/config/importmap.rb b/config/importmap.rb
index e580fc67d87..d6555b8729a 100644
--- a/config/importmap.rb
+++ b/config/importmap.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
pin "flowbite", preload: true # downloaded from https://cdnjs.cloudflare.com/ajax/libs/flowbite/2.2.1/flowbite.min.js
-pin "@rails/ujs", to: "rails_ujs_esm.js", preload: true # downloaded from https://cdn.jsdelivr.net/npm/@rails/ujs@7.1.2/+esm
+pin "@rails/ujs", to: "rails_ujs_esm.js", preload: true # downloaded from https://cdn.jsdelivr.net/npm/@rails/ujs@7.1.400/+esm
pin "active_admin", to: "active_admin.js", preload: true
pin_all_from File.expand_path("../app/javascript/active_admin", __dir__), under: "active_admin", preload: true
diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock
index 092f323a9e8..ce47679e72e 100644
--- a/docs/Gemfile.lock
+++ b/docs/Gemfile.lock
@@ -275,4 +275,4 @@ DEPENDENCIES
github-pages
BUNDLED WITH
- 2.5.15
+ 2.5.17
diff --git a/gemfiles/rails_61/Gemfile.lock b/gemfiles/rails_61/Gemfile.lock
index 5691de209a0..ff9d5c3a100 100644
--- a/gemfiles/rails_61/Gemfile.lock
+++ b/gemfiles/rails_61/Gemfile.lock
@@ -1,7 +1,7 @@
PATH
remote: ../..
specs:
- activeadmin (4.0.0.beta10)
+ activeadmin (4.0.0.beta11)
arbre (~> 2.0)
csv
formtastic (>= 3.1)
diff --git a/gemfiles/rails_70/Gemfile.lock b/gemfiles/rails_70/Gemfile.lock
index 652de41a1fd..75e79a70223 100644
--- a/gemfiles/rails_70/Gemfile.lock
+++ b/gemfiles/rails_70/Gemfile.lock
@@ -1,7 +1,7 @@
PATH
remote: ../..
specs:
- activeadmin (4.0.0.beta10)
+ activeadmin (4.0.0.beta11)
arbre (~> 2.0)
csv
formtastic (>= 3.1)
diff --git a/gemfiles/rails_71/Gemfile b/gemfiles/rails_71/Gemfile
index d7be2247f33..e6c5d5b5945 100644
--- a/gemfiles/rails_71/Gemfile
+++ b/gemfiles/rails_71/Gemfile
@@ -33,7 +33,7 @@ group :test do
gem "launchy"
gem "parallel_tests"
gem "rspec-rails"
- gem "sqlite3", "~> 1.7", platform: :mri # FIXME: relax this dependency when rails/rails#51636 will be released
+ gem "sqlite3", platform: :mri
# Translations
gem "i18n-tasks"
diff --git a/gemfiles/rails_71/Gemfile.lock b/gemfiles/rails_71/Gemfile.lock
index 45235abcedb..e2cfe873003 100644
--- a/gemfiles/rails_71/Gemfile.lock
+++ b/gemfiles/rails_71/Gemfile.lock
@@ -1,7 +1,7 @@
PATH
remote: ../..
specs:
- activeadmin (4.0.0.beta10)
+ activeadmin (4.0.0.beta11)
arbre (~> 2.0)
csv
formtastic (>= 3.1)
@@ -374,10 +374,10 @@ GEM
actionpack (>= 6.1)
activesupport (>= 6.1)
sprockets (>= 3.0.0)
- sqlite3 (1.7.3)
+ sqlite3 (2.0.4)
mini_portile2 (~> 2.8.0)
- sqlite3 (1.7.3-arm64-darwin)
- sqlite3 (1.7.3-x86_64-linux)
+ sqlite3 (2.0.4-arm64-darwin)
+ sqlite3 (2.0.4-x86_64-linux-gnu)
stringio (3.1.1)
strscan (3.1.0)
sys-uname (1.3.0)
@@ -430,7 +430,7 @@ DEPENDENCIES
simplecov
simplecov-cobertura
sprockets-rails
- sqlite3 (~> 1.7)
+ sqlite3
webrick
BUNDLED WITH
diff --git a/lib/active_admin/version.rb b/lib/active_admin/version.rb
index 8e9375b33d3..a67922545d2 100644
--- a/lib/active_admin/version.rb
+++ b/lib/active_admin/version.rb
@@ -1,4 +1,4 @@
# frozen_string_literal: true
module ActiveAdmin
- VERSION = "4.0.0.beta10"
+ VERSION = "4.0.0.beta11"
end
diff --git a/lib/active_admin/views/components/attributes_table.rb b/lib/active_admin/views/components/attributes_table.rb
index 080c63d3c0e..ea13471f050 100644
--- a/lib/active_admin/views/components/attributes_table.rb
+++ b/lib/active_admin/views/components/attributes_table.rb
@@ -23,6 +23,7 @@ def rows(*attrs)
def row(*args, &block)
title = args[0]
+ data = args[1] || args[0]
options = args.extract_options!
options["data-row"] = title.to_s.parameterize(separator: "_") if title.present?
@@ -32,7 +33,7 @@ def row(*args, &block)
end
@collection.each do |record|
td do
- content_for(record, block || title)
+ content_for(record, block || data)
end
end
end
diff --git a/lib/active_admin/views/components/paginated_collection.rb b/lib/active_admin/views/components/paginated_collection.rb
index ee1395c776f..3169d537628 100644
--- a/lib/active_admin/views/components/paginated_collection.rb
+++ b/lib/active_admin/views/components/paginated_collection.rb
@@ -102,7 +102,10 @@ def build_pagination
# you pass in the :total_pages option. We issue a query to determine
# if there is another page or not, but the limit/offset make this
# query fast.
- offset = @collection.offset(@collection.current_page * @collection.limit_value).limit(1).count
+ offset_scope = @collection.offset(@collection.current_page * @collection.limit_value)
+ # Support array collections. Kaminari::PaginatableArray does not respond to except
+ offset_scope = offset_scope.except(:select, :order) if offset_scope.respond_to?(:except)
+ offset = offset_scope.limit(1).count
options[:total_pages] = @collection.current_page + offset
options[:right] = 0
end
diff --git a/package.json b/package.json
index 1e5f16c6f2a..67930cdf52c 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@activeadmin/activeadmin",
- "version": "4.0.0-beta10",
+ "version": "4.0.0-beta11",
"description": "The administration framework for Ruby on Rails.",
"main": "dist/active_admin.js",
"type": "module",
@@ -39,7 +39,7 @@
"prepublishOnly": "npm run build"
},
"dependencies": {
- "@rails/ujs": "7.1.2",
+ "@rails/ujs": "7.1.400",
"flowbite": "2.3.0"
}
}
diff --git a/plugin.js b/plugin.js
index d0646d57db5..cab6daf64cb 100644
--- a/plugin.js
+++ b/plugin.js
@@ -312,7 +312,7 @@ module.exports = plugin(
'[type=radio]': {
'@apply w-4 h-4 border-gray-300 focus:ring-2 focus:ring-blue-300 dark:focus:ring-blue-600 dark:focus:bg-blue-600 dark:bg-gray-700 dark:border-gray-600': {}
},
- [['[type=date]', '[type=email]', '[type=number]', '[type=password]', '[type=tel]', '[type=text]', '[type=time]', '[type=url]', 'select', 'textarea']]: {
+ [['[type=datetime-local]', '[type=month]', '[type=week]', '[type=search]', '[type=date]', '[type=email]', '[type=number]', '[type=password]', '[type=tel]', '[type=text]', '[type=time]', '[type=url]', 'select', 'textarea']]: {
'@apply bg-gray-50 border border-gray-300 text-gray-900 rounded-md focus:ring-blue-500 focus:border-blue-500 w-full dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500': {}
},
'a': {
diff --git a/spec/helpers/display_helper_spec.rb b/spec/helpers/display_helper_spec.rb
index aff9cf6d995..9860435d109 100644
--- a/spec/helpers/display_helper_spec.rb
+++ b/spec/helpers/display_helper_spec.rb
@@ -223,6 +223,22 @@ class ThisModel
expect(value.to_s).to eq "Yes\n"
end
+ context "with non-database boolean attribute" do
+ let(:model_class) do
+ Class.new(Post) do
+ attribute :a_virtual_attribute, :boolean
+ end
+ end
+
+ it "calls status_tag even when attribute is nil" do
+ post = model_class.new a_virtual_attribute: nil
+
+ value = helper.format_attribute post, :a_virtual_attribute
+
+ expect(value.to_s).to eq "Unknown\n"
+ end
+ end
+
it "calls status_tag for boolean non-database values" do
post = Post.new
post.define_singleton_method(:true_method) do
diff --git a/spec/support/matchers/perform_database_query_matcher.rb b/spec/support/matchers/perform_database_query_matcher.rb
index 7fc0b79d16c..9f44e364103 100644
--- a/spec/support/matchers/perform_database_query_matcher.rb
+++ b/spec/support/matchers/perform_database_query_matcher.rb
@@ -2,10 +2,12 @@
RSpec::Matchers.define :perform_database_query do |query|
match do |block|
+ query_regexp = query.is_a?(Regexp) ? query : Regexp.new(Regexp.escape(query))
+
@match = nil
callback = lambda do |_name, _started, _finished, _unique_id, payload|
- @match = Regexp.new(Regexp.escape(query)).match?(payload[:sql])
+ @match = query_regexp.match?(payload[:sql])
end
ActiveSupport::Notifications.subscribed(callback, "sql.active_record", &block)
diff --git a/spec/support/rails_template.rb b/spec/support/rails_template.rb
index 012ed3cbcec..93047e52d91 100644
--- a/spec/support/rails_template.rb
+++ b/spec/support/rails_template.rb
@@ -100,10 +100,11 @@ def self.ransackable_associations(auth_object=nil)
# Add some translations
append_file "config/locales/en.yml", File.read(File.expand_path("templates/en.yml", __dir__))
-# Add predefined admin resources
+# Add predefined admin resources, override any file that was generated by rails new generator
directory File.expand_path("templates/admin", __dir__), "app/admin"
directory File.expand_path("templates/views", __dir__), "app/views"
directory File.expand_path("templates/policies", __dir__), "app/policies"
+directory File.expand_path("templates/public", __dir__), "public", force: true
route "root to: redirect('admin')" if ENV["RAILS_ENV"] != "test"
diff --git a/spec/support/templates/public/favicon.ico b/spec/support/templates/public/favicon.ico
new file mode 100644
index 00000000000..db016de0d55
Binary files /dev/null and b/spec/support/templates/public/favicon.ico differ
diff --git a/spec/unit/views/components/attributes_table_spec.rb b/spec/unit/views/components/attributes_table_spec.rb
index b6db8b06870..3b54cc19082 100644
--- a/spec/unit/views/components/attributes_table_spec.rb
+++ b/spec/unit/views/components/attributes_table_spec.rb
@@ -47,6 +47,15 @@
end
end
},
+ "when you create each row with a string and symbol" => proc {
+ render_arbre_component(assigns) do
+ attributes_table_for post do
+ row "Id", :id
+ row "Title", :title
+ row "Body", :body
+ end
+ end
+ },
"when you create each row with a custom block that returns nil" => proc {
render_arbre_component(assigns) do
attributes_table_for post do
diff --git a/spec/unit/views/components/paginated_collection_spec.rb b/spec/unit/views/components/paginated_collection_spec.rb
index 81ac65a8f91..b4e36669d85 100644
--- a/spec/unit/views/components/paginated_collection_spec.rb
+++ b/spec/unit/views/components/paginated_collection_spec.rb
@@ -229,16 +229,43 @@ def paginated_collection(*args)
end
end
- it "makes no expensive COUNT queries when pagination_total is false" do
- undecorated_collection = Post.all.page(1).per(30)
+ describe "when pagination_total is false" do
+ it "makes no expensive COUNT queries" do
+ undecorated_collection = Post.all.page(1).per(30)
+
+ expect { paginated_collection(undecorated_collection, pagination_total: false) }
+ .not_to perform_database_query("SELECT COUNT(*) FROM \"posts\"")
+
+ decorated_collection = controller_with_decorator("index", PostDecorator).apply_collection_decorator(undecorated_collection.reset)
+
+ expect { paginated_collection(decorated_collection, pagination_total: false) }
+ .not_to perform_database_query("SELECT COUNT(*) FROM \"posts\"")
+ end
+
+ it "makes a performant COUNT query to figure out if we are on the last page" do
+ # "SELECT COUNT(*) FROM (SELECT 1". Let's make sure the subquery has LIMIT and OFFSET. It shouldn't have ORDER BY
+ count_query = %r{SELECT COUNT\(\*\) FROM \(SELECT 1 .*FROM "posts" (?=.*OFFSET \?)(?=.*LIMIT \?)(?!.*ORDER BY)}
- expect { paginated_collection(undecorated_collection, pagination_total: false) }
- .not_to perform_database_query("SELECT COUNT(*) FROM \"posts\"")
+ undecorated_collection = Post.all.page(1).per(30)
- decorated_collection = controller_with_decorator("index", PostDecorator).apply_collection_decorator(undecorated_collection)
+ expect { paginated_collection(undecorated_collection, pagination_total: false) }
+ .to perform_database_query(count_query)
- expect { paginated_collection(decorated_collection, pagination_total: false) }
- .not_to perform_database_query("SELECT COUNT(*) FROM \"posts\"")
+ undecorated_sorted_collection = undecorated_collection.reset.order(id: :desc)
+
+ expect { paginated_collection(undecorated_sorted_collection, pagination_total: false) }
+ .to perform_database_query(count_query)
+
+ decorated_collection = controller_with_decorator("index", PostDecorator).apply_collection_decorator(undecorated_collection.reset)
+
+ expect { paginated_collection(decorated_collection, pagination_total: false) }
+ .to perform_database_query(count_query)
+
+ decorated_sorted_collection = controller_with_decorator("index", PostDecorator).apply_collection_decorator(undecorated_sorted_collection.reset)
+
+ expect { paginated_collection(decorated_sorted_collection, pagination_total: false) }
+ .to perform_database_query(count_query)
+ end
end
it "makes no COUNT queries to figure out the last element of each page" do
diff --git a/tasks/bug_report_template.rb b/tasks/bug_report_template.rb
index 2f398f0abc2..18211de92ff 100644
--- a/tasks/bug_report_template.rb
+++ b/tasks/bug_report_template.rb
@@ -13,11 +13,11 @@
end
# Change Rails version if necessary.
- gem "rails", "~> 7.1.0"
+ gem "rails", "~> 7.2.0"
gem "sprockets", "~> 4.0"
gem "importmap-rails", "~> 2.0"
- gem "sqlite3", "~> 1.x", force_ruby_platform: true, platform: :mri
+ gem "sqlite3", force_ruby_platform: true, platform: :mri
# Fixes an issue on CI with default gems when using inline bundle with default
# gems that are already activated
@@ -54,6 +54,7 @@ class TestApp < Rails::Application
config.hosts << ".example.com"
config.session_store :cookie_store, key: "cookie_store_key"
config.secret_key_base = "secret_key_base"
+ config.eager_load = false
config.logger = Logger.new($stdout)
Rails.logger = config.logger
diff --git a/yarn.lock b/yarn.lock
index 2f80abe4a1d..ab0610b595d 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -191,10 +191,10 @@
resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570"
integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==
-"@rails/ujs@7.1.2":
- version "7.1.2"
- resolved "https://registry.yarnpkg.com/@rails/ujs/-/ujs-7.1.2.tgz#ea903bcc0224e17156015d995b6f1b83e27d64b2"
- integrity sha512-c5x02djEKEVVE4qfN4XgElJS4biM0xxtIVpcJ0ZHLK116U19rowTtmD0AJ/RCb3Xaewa4GPIWLlwgeC0dCQqzw==
+"@rails/ujs@7.1.400":
+ version "7.1.400"
+ resolved "https://registry.yarnpkg.com/@rails/ujs/-/ujs-7.1.400.tgz#b2a76bdccb5197b9e866954536106386c87cfab5"
+ integrity sha512-YwvXm3BR5tn+VCAKYGycLejMRVZE3Ionj5gFjEeGXCZnI0Rpi+7dKpmyu90kdUY7dRUFpHTdu9zZceEzFLl38w==
"@rollup/plugin-alias@^5.1.0":
version "5.1.0"
@@ -395,7 +395,7 @@ brace-expansion@^2.0.1:
dependencies:
balanced-match "^1.0.0"
-braces@^3.0.2, braces@~3.0.2:
+braces@^3.0.3, braces@~3.0.2:
version "3.0.3"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789"
integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==
@@ -979,11 +979,11 @@ merge2@^1.3.0:
integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
micromatch@^4.0.4, micromatch@^4.0.5:
- version "4.0.5"
- resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
- integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==
+ version "4.0.8"
+ resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202"
+ integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==
dependencies:
- braces "^3.0.2"
+ braces "^3.0.3"
picomatch "^2.3.1"
mini-svg-data-uri@^1.4.3: