admin_suite 0.3.1 → 0.5.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 +4 -4
- data/CHANGELOG.md +210 -1
- data/app/assets/vendor/chart.umd.min.js +14 -0
- data/app/assets/vendor/easymde.min.css +7 -0
- data/app/assets/vendor/easymde.min.js +7 -0
- data/app/controllers/admin_suite/application_controller.rb +26 -47
- data/app/controllers/admin_suite/portals_controller.rb +2 -2
- data/app/controllers/admin_suite/resources_controller.rb +138 -6
- data/app/helpers/admin_suite/base_helper.rb +232 -391
- data/app/javascript/admin_suite_application.js +3 -0
- data/app/javascript/controllers/admin_suite/chart_controller.js +173 -0
- data/app/javascript/controllers/admin_suite/markdown_editor_controller.js +21 -2
- data/app/views/admin_suite/panels/_chart.html.erb +158 -18
- data/app/views/admin_suite/panels/_stat.html.erb +10 -0
- data/app/views/admin_suite/resources/index.html.erb +31 -82
- data/app/views/admin_suite/shared/_pagination.html.erb +74 -0
- data/app/views/admin_suite/shared/_sidebar.html.erb +15 -9
- data/app/views/layouts/admin_suite/application.html.erb +11 -3
- data/config/routes.rb +3 -0
- data/lib/admin/base/action_executor.rb +19 -51
- data/lib/admin/base/filter_builder.rb +48 -5
- data/lib/admin/base/resource.rb +92 -17
- data/lib/admin_suite/configuration.rb +32 -3
- data/lib/admin_suite/definition_loader.rb +194 -0
- data/lib/admin_suite/deprecation.rb +48 -0
- data/lib/admin_suite/engine.rb +55 -76
- data/lib/admin_suite/host_autoload_policy.rb +132 -0
- data/lib/admin_suite/legacy_custom_renderer_procs.rb +29 -0
- data/lib/admin_suite/portal_definition.rb +11 -0
- data/lib/admin_suite/renderer.rb +133 -0
- data/lib/admin_suite/renderer_registry.rb +81 -0
- data/lib/admin_suite/renderers/code_renderer.rb +15 -0
- data/lib/admin_suite/renderers/json_renderer.rb +15 -0
- data/lib/admin_suite/renderers/key_value_renderer.rb +39 -0
- data/lib/admin_suite/renderers/legacy_gleania.rb +232 -0
- data/lib/admin_suite/renderers/table_from_renderer.rb +22 -0
- data/lib/admin_suite/section_definition.rb +42 -0
- data/lib/admin_suite/ui/dashboard_definition.rb +6 -0
- data/lib/admin_suite/ui/field_renderer_registry.rb +31 -4
- data/lib/admin_suite/ui/form_field_renderer.rb +1 -7
- data/lib/admin_suite/ui/show_formatter_registry.rb +9 -0
- data/lib/admin_suite/ui/show_value_formatter.rb +7 -3
- data/lib/admin_suite/version.rb +1 -1
- data/lib/admin_suite.rb +48 -0
- data/lib/generators/admin_suite/install/templates/admin_suite.rb +0 -4
- data/test/controllers/resources_controller_test.rb +76 -1
- data/test/integration/association_linking_test.rb +292 -0
- data/test/integration/chart_panel_test.rb +491 -0
- data/test/integration/dashboard_test.rb +9 -2
- data/test/integration/index_table_test.rb +289 -0
- data/test/integration/layout_assets_test.rb +112 -0
- data/test/integration/navigation_sections_test.rb +62 -0
- data/test/integration/pagination_and_stats_test.rb +152 -0
- data/test/integration/searchable_select_search_test.rb +368 -0
- data/test/integration/show_hide_blank_test.rb +195 -0
- data/test/integration/toggle_test.rb +106 -0
- data/test/lib/action_executor_redirect_test.rb +41 -0
- data/test/lib/builtin_renderers_test.rb +202 -0
- data/test/lib/definition_loader_test.rb +276 -0
- data/test/lib/engine_defaults_test.rb +39 -0
- data/test/lib/form_field_renderer_test.rb +136 -0
- data/test/lib/format_table_cell_test.rb +49 -0
- data/test/lib/index_includes_test.rb +223 -0
- data/test/lib/legacy_renderer_deprecation_test.rb +42 -0
- data/test/lib/renderer_test.rb +237 -0
- data/test/lib/resource_exportable_deprecation_test.rb +47 -0
- data/test/lib/show_value_formatter_test.rb +88 -0
- data/test/lib/zeitwerk_integration_test.rb +28 -64
- data/test/test_helper.rb +121 -5
- metadata +62 -5
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "test_helper"
|
|
4
|
+
|
|
5
|
+
module AdminSuite
|
|
6
|
+
class ResourceExportableDeprecationTest < ActiveSupport::TestCase
|
|
7
|
+
# Regression fixture for Finding 1 of the whole-branch review: Task 8
|
|
8
|
+
# removed `exportable` after grepping the *gem* for readers, but host
|
|
9
|
+
# apps are *callers* of this DSL (gleania: 30 resource files;
|
|
10
|
+
# trust_growth: 1). A real removal raises `NoMethodError` at
|
|
11
|
+
# definition-load time -- and in production `DefinitionLoader` logs and
|
|
12
|
+
# swallows that, so the resource silently vanishes from the admin.
|
|
13
|
+
class LegacyExportableResource < Admin::Base::Resource
|
|
14
|
+
exportable :json, :csv
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
setup { LegacyExportableResource.reset_deprecation_notices! }
|
|
18
|
+
|
|
19
|
+
test "calling exportable in a resource body is harmless" do
|
|
20
|
+
assert_nothing_raised { LegacyExportableResource.exportable(:json, :csv) }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
test "exportable does not restore any export-format reader" do
|
|
24
|
+
refute LegacyExportableResource.respond_to?(:export_formats)
|
|
25
|
+
refute LegacyExportableResource.instance_variable_defined?(:@export_formats)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
test "exportable warns once per resource class, naming the class and the 0.6.0 removal" do
|
|
29
|
+
logged = []
|
|
30
|
+
LegacyExportableResource.stub(:warn_once_sink, ->(msg) { logged << msg }) do
|
|
31
|
+
2.times { LegacyExportableResource.exportable(:json, :csv) }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
assert_equal 1, logged.size
|
|
35
|
+
assert_includes logged.first, "LegacyExportableResource"
|
|
36
|
+
# Deliberately a hardcoded literal, not read off
|
|
37
|
+
# EXPORTABLE_DEPRECATION_MESSAGE_FORMAT: reading the version out of
|
|
38
|
+
# the constant under test and asserting the message contains it is
|
|
39
|
+
# true by construction (format() only substitutes %<resource>s, never
|
|
40
|
+
# the version digits), so it can never fail on a wrong retarget --
|
|
41
|
+
# exactly the property this test exists to catch. If the removal
|
|
42
|
+
# target changes again, this literal must be updated by hand; that's
|
|
43
|
+
# the point.
|
|
44
|
+
assert_includes logged.first, "0.6.0"
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "test_helper"
|
|
4
|
+
|
|
5
|
+
module AdminSuite
|
|
6
|
+
class ShowValueFormatterTest < ActionView::TestCase
|
|
7
|
+
# ActionView::Base includes ::ERB::Util (giving views access to `h`), but
|
|
8
|
+
# ActionView::TestCase::Behavior only includes ActionView::Helpers, not
|
|
9
|
+
# ERB::Util. Include it here so this test's context matches what
|
|
10
|
+
# BaseHelper actually runs inside at runtime (`h` is used by
|
|
11
|
+
# render_text_block / highlight_json).
|
|
12
|
+
include ::ERB::Util
|
|
13
|
+
include AdminSuite::BaseHelper
|
|
14
|
+
|
|
15
|
+
# A record stand-in exposing the value under the field name the formatter
|
|
16
|
+
# reads via `record.public_send(field_name)`.
|
|
17
|
+
Record = Struct.new(:some_field)
|
|
18
|
+
|
|
19
|
+
def fmt(value)
|
|
20
|
+
format_show_value(Record.new(value), :some_field)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
test "nil renders the em dash placeholder" do
|
|
24
|
+
assert_includes fmt(nil), "—"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
test "booleans render Yes/No with an icon" do
|
|
28
|
+
assert_includes fmt(true), "Yes"
|
|
29
|
+
assert_includes fmt(false), "No"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
test "dates and times render humanized with relative age" do
|
|
33
|
+
assert_includes fmt(Date.new(2026, 1, 2)), "January"
|
|
34
|
+
assert_includes fmt(Time.utc(2026, 1, 2, 3, 4)), "January"
|
|
35
|
+
assert_includes fmt(DateTime.new(2026, 1, 2, 3, 4)), "January"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
test "integers and floats render delimited" do
|
|
39
|
+
assert_includes fmt(1_234_567), "1,234,567"
|
|
40
|
+
assert_includes fmt(1234.5), "1,234"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
test "BigDecimal renders delimited rather than as an object string" do
|
|
44
|
+
result = fmt(BigDecimal("9876.5"))
|
|
45
|
+
assert_includes result, "9,876"
|
|
46
|
+
refute_includes result, "0.98765e4"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
test "hashes render as a JSON block" do
|
|
50
|
+
assert_includes fmt({ "a" => 1 }), "JSON"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
test "arrays of hashes render as a JSON block" do
|
|
54
|
+
assert_includes fmt([ { "a" => 1 } ]), "JSON"
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
test "empty arrays render a placeholder, populated arrays render chips" do
|
|
58
|
+
assert_includes fmt([]), "Empty array"
|
|
59
|
+
assert_includes fmt(%w[alpha beta]), "alpha"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
test "long strings render as a text block, short strings render inline" do
|
|
63
|
+
long = "x" * 250
|
|
64
|
+
assert_includes fmt(long), "x"
|
|
65
|
+
assert_includes fmt("short"), "short"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
test "strings containing JSON render as a JSON block" do
|
|
69
|
+
assert_includes fmt('{"mode":"fast"}'), "mode"
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
test "malformed JSON-looking strings fall back to plain rendering" do
|
|
73
|
+
assert_includes fmt("{not actually json"), "not actually json"
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
test "short strings containing a newline render as a text block" do
|
|
77
|
+
result = fmt("first line\nsecond line")
|
|
78
|
+
assert_includes result, "bg-slate-900"
|
|
79
|
+
assert_includes result, "second line"
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# ActiveRecord::Base branch (show_formatter_registry.rb:72-77) is
|
|
83
|
+
# untestable in this harness: the dummy app is database-free and
|
|
84
|
+
# ActiveRecord::Base is never loaded/defined here, so the registry's
|
|
85
|
+
# `if defined?(ActiveRecord::Base)` guard never registers that handler.
|
|
86
|
+
# See task-1-report.md's fix report for the verification.
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -30,43 +30,13 @@ module AdminSuite
|
|
|
30
30
|
[ loader, ignored_dirs, pushed_dirs ]
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
-
#
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if admin_dir.exist?
|
|
41
|
-
rb_files = Dir[admin_dir.join("**/*.rb").to_s]
|
|
42
|
-
rb_files.reject! { |f| f.include?("/portals/") }
|
|
43
|
-
|
|
44
|
-
host_uses_admin_namespace =
|
|
45
|
-
rb_files.any? do |file|
|
|
46
|
-
content = File.binread(file).encode("UTF-8", invalid: :replace, undef: :replace, replace: "")
|
|
47
|
-
content.match?(/\b(module|class)\s+Admin\b/) || content.match?(/\b(module|class)\s+Admin::/)
|
|
48
|
-
rescue StandardError
|
|
49
|
-
false
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
loader.push_dir(admin_dir, namespace: Admin) if host_uses_admin_namespace
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
loader.ignore(admin_suite_app_dir) if admin_suite_app_dir.exist?
|
|
56
|
-
|
|
57
|
-
# Ignore portal DSL files (side-effect DSL, not constants).
|
|
58
|
-
if admin_portals_dir.exist?
|
|
59
|
-
portal_files = Dir[admin_portals_dir.join("**/*.rb").to_s]
|
|
60
|
-
contains_admin_suite_portals =
|
|
61
|
-
portal_files.any? do |file|
|
|
62
|
-
content = File.binread(file).encode("UTF-8", invalid: :replace, undef: :replace, replace: "")
|
|
63
|
-
content.match?(/(::)?AdminSuite\s*\.\s*portal\b/)
|
|
64
|
-
rescue StandardError
|
|
65
|
-
false
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
loader.ignore(admin_portals_dir) if contains_admin_suite_portals
|
|
69
|
-
end
|
|
33
|
+
# A minimal stand-in for the Rails::Application instance HostAutoloadPolicy
|
|
34
|
+
# is invoked with in production: it only needs `#root` and a
|
|
35
|
+
# `#config.autoload_paths` / `#config.eager_load_paths` that respond to
|
|
36
|
+
# `#delete` (exercised only by the "maps app/admin..." test below).
|
|
37
|
+
def create_fake_app(root)
|
|
38
|
+
config = Struct.new(:autoload_paths, :eager_load_paths).new([], [])
|
|
39
|
+
Struct.new(:root, :config).new(Pathname.new(root), config)
|
|
70
40
|
end
|
|
71
41
|
|
|
72
42
|
test "ignores app/admin/portals when it contains AdminSuite portal DSL" do
|
|
@@ -78,13 +48,12 @@ module AdminSuite
|
|
|
78
48
|
"AdminSuite.portal :ops do\n # portal config\nend"
|
|
79
49
|
)
|
|
80
50
|
|
|
81
|
-
|
|
82
|
-
app_root = Pathname.new(@temp_dir)
|
|
51
|
+
app = create_fake_app(@temp_dir)
|
|
83
52
|
loader, ignored_dirs, _pushed_dirs = create_tracked_loader
|
|
84
|
-
|
|
53
|
+
AdminSuite::HostAutoloadPolicy.apply!(app, loaders: [ loader ])
|
|
85
54
|
|
|
86
55
|
# Verify that app/admin/portals was ignored
|
|
87
|
-
expected_path =
|
|
56
|
+
expected_path = app.root.join("app/admin/portals").to_s
|
|
88
57
|
assert_includes ignored_dirs, expected_path,
|
|
89
58
|
"Expected app/admin/portals to be ignored when it contains AdminSuite portal DSL"
|
|
90
59
|
end
|
|
@@ -98,13 +67,12 @@ module AdminSuite
|
|
|
98
67
|
"module Admin\n module Portals\n class AdminUser\n end\n end\nend"
|
|
99
68
|
)
|
|
100
69
|
|
|
101
|
-
|
|
102
|
-
app_root = Pathname.new(@temp_dir)
|
|
70
|
+
app = create_fake_app(@temp_dir)
|
|
103
71
|
loader, ignored_dirs, _pushed_dirs = create_tracked_loader
|
|
104
|
-
|
|
72
|
+
AdminSuite::HostAutoloadPolicy.apply!(app, loaders: [ loader ])
|
|
105
73
|
|
|
106
74
|
# Verify that app/admin/portals was NOT ignored
|
|
107
|
-
unexpected_path =
|
|
75
|
+
unexpected_path = app.root.join("app/admin/portals").to_s
|
|
108
76
|
assert_not_includes ignored_dirs, unexpected_path,
|
|
109
77
|
"Expected app/admin/portals to NOT be ignored when it contains only real constants"
|
|
110
78
|
end
|
|
@@ -118,13 +86,12 @@ module AdminSuite
|
|
|
118
86
|
"# Some DSL configuration"
|
|
119
87
|
)
|
|
120
88
|
|
|
121
|
-
|
|
122
|
-
app_root = Pathname.new(@temp_dir)
|
|
89
|
+
app = create_fake_app(@temp_dir)
|
|
123
90
|
loader, ignored_dirs, _pushed_dirs = create_tracked_loader
|
|
124
|
-
|
|
91
|
+
AdminSuite::HostAutoloadPolicy.apply!(app, loaders: [ loader ])
|
|
125
92
|
|
|
126
93
|
# Verify that app/admin_suite was ignored
|
|
127
|
-
expected_path =
|
|
94
|
+
expected_path = app.root.join("app/admin_suite").to_s
|
|
128
95
|
assert_includes ignored_dirs, expected_path,
|
|
129
96
|
"Expected app/admin_suite to always be ignored"
|
|
130
97
|
end
|
|
@@ -146,13 +113,12 @@ module AdminSuite
|
|
|
146
113
|
"AdminSuite.portal :ops do\n # portal config\nend"
|
|
147
114
|
)
|
|
148
115
|
|
|
149
|
-
|
|
150
|
-
app_root = Pathname.new(@temp_dir)
|
|
116
|
+
app = create_fake_app(@temp_dir)
|
|
151
117
|
loader, ignored_dirs, _pushed_dirs = create_tracked_loader
|
|
152
|
-
|
|
118
|
+
AdminSuite::HostAutoloadPolicy.apply!(app, loaders: [ loader ])
|
|
153
119
|
|
|
154
120
|
# Verify that app/admin/portals was ignored due to presence of portal DSL
|
|
155
|
-
expected_path =
|
|
121
|
+
expected_path = app.root.join("app/admin/portals").to_s
|
|
156
122
|
assert_includes ignored_dirs, expected_path,
|
|
157
123
|
"Expected app/admin/portals to be ignored when any file contains portal DSL"
|
|
158
124
|
end
|
|
@@ -164,8 +130,7 @@ module AdminSuite
|
|
|
164
130
|
test_file = File.join(portals_dir, "test.rb")
|
|
165
131
|
File.write(test_file, "AdminSuite.portal :ops do; end")
|
|
166
132
|
|
|
167
|
-
|
|
168
|
-
app_root = Pathname.new(@temp_dir)
|
|
133
|
+
app = create_fake_app(@temp_dir)
|
|
169
134
|
loader, ignored_dirs, _pushed_dirs = create_tracked_loader
|
|
170
135
|
|
|
171
136
|
# Temporarily override File.binread to simulate read errors
|
|
@@ -180,11 +145,10 @@ module AdminSuite
|
|
|
180
145
|
end
|
|
181
146
|
|
|
182
147
|
begin
|
|
183
|
-
|
|
184
|
-
simulate_zeitwerk_integration(app_root, loader)
|
|
148
|
+
AdminSuite::HostAutoloadPolicy.apply!(app, loaders: [ loader ])
|
|
185
149
|
|
|
186
150
|
# Verify that app/admin/portals was NOT ignored due to read error
|
|
187
|
-
unexpected_path =
|
|
151
|
+
unexpected_path = app.root.join("app/admin/portals").to_s
|
|
188
152
|
assert_not_includes ignored_dirs, unexpected_path,
|
|
189
153
|
"Expected app/admin/portals to NOT be ignored when file read fails"
|
|
190
154
|
ensure
|
|
@@ -201,11 +165,11 @@ module AdminSuite
|
|
|
201
165
|
"module Admin\n module Resources\n class UserResource; end\n end\nend\n"
|
|
202
166
|
)
|
|
203
167
|
|
|
204
|
-
|
|
168
|
+
app = create_fake_app(@temp_dir)
|
|
205
169
|
loader, _ignored_dirs, pushed_dirs = create_tracked_loader
|
|
206
|
-
|
|
170
|
+
AdminSuite::HostAutoloadPolicy.apply!(app, loaders: [ loader ])
|
|
207
171
|
|
|
208
|
-
assert pushed_dirs.any? { |h| h[:path] ==
|
|
172
|
+
assert pushed_dirs.any? { |h| h[:path] == app.root.join("app/admin").to_s && h[:namespace] == Admin },
|
|
209
173
|
"Expected app/admin to be pushed with namespace Admin when files define Admin::* constants"
|
|
210
174
|
end
|
|
211
175
|
|
|
@@ -217,11 +181,11 @@ module AdminSuite
|
|
|
217
181
|
"module Resources\n class UserResource; end\nend\n"
|
|
218
182
|
)
|
|
219
183
|
|
|
220
|
-
|
|
184
|
+
app = create_fake_app(@temp_dir)
|
|
221
185
|
loader, _ignored_dirs, pushed_dirs = create_tracked_loader
|
|
222
|
-
|
|
186
|
+
AdminSuite::HostAutoloadPolicy.apply!(app, loaders: [ loader ])
|
|
223
187
|
|
|
224
|
-
assert pushed_dirs.none? { |h| h[:path] ==
|
|
188
|
+
assert pushed_dirs.none? { |h| h[:path] == app.root.join("app/admin").to_s },
|
|
225
189
|
"Expected app/admin to NOT be pushed when files contain only top-level constants"
|
|
226
190
|
end
|
|
227
191
|
end
|
data/test/test_helper.rb
CHANGED
|
@@ -32,14 +32,17 @@ unless defined?(ActiveRecord::RecordNotFound)
|
|
|
32
32
|
end
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
# Same rationale as above: base_helper.rb's show-page/association rendering
|
|
36
|
+
# checks `record.is_a?(ActiveRecord::Base)` to distinguish a real AR
|
|
37
|
+
# association from a plain in-memory relation stand-in. Supply just the bare
|
|
38
|
+
# constant so that check can run (and return false for our PORO fixtures)
|
|
39
|
+
# without requiring the full active_record/railtie in this DB-free dummy app.
|
|
40
|
+
unless defined?(ActiveRecord::Base)
|
|
41
|
+
module ActiveRecord
|
|
42
|
+
class Base; end
|
|
38
43
|
end
|
|
39
44
|
end
|
|
40
45
|
|
|
41
|
-
ActionView::Base.include(TurboFrameTestHelper)
|
|
42
|
-
|
|
43
46
|
module ReadOnlyResourceFixtures
|
|
44
47
|
class Relation
|
|
45
48
|
include Enumerable
|
|
@@ -65,3 +68,116 @@ module ReadOnlyResourceFixtures
|
|
|
65
68
|
end
|
|
66
69
|
end
|
|
67
70
|
end
|
|
71
|
+
|
|
72
|
+
module RedirectFixtures
|
|
73
|
+
class Widget
|
|
74
|
+
extend ActiveModel::Naming
|
|
75
|
+
|
|
76
|
+
def persisted? = true
|
|
77
|
+
def to_param = "42"
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
module Admin
|
|
82
|
+
module Resources
|
|
83
|
+
class RedirectWidgetResource < Admin::Base::Resource
|
|
84
|
+
model RedirectFixtures::Widget
|
|
85
|
+
portal :ops
|
|
86
|
+
section :observability
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
RedirectFixtures::WidgetResource = Admin::Resources::RedirectWidgetResource
|
|
91
|
+
|
|
92
|
+
# Fixtures for exercising execute_model_method's exception-handling chain
|
|
93
|
+
# end-to-end, in the dummy app's DB-free configuration (no ActiveRecord
|
|
94
|
+
# loaded), which is where a bare `rescue ActiveRecord::RecordInvalid` or
|
|
95
|
+
# `rescue AASM::InvalidTransition` would raise NameError while handling the
|
|
96
|
+
# original exception instead of reporting it.
|
|
97
|
+
module ExceptionHandlingFixtures
|
|
98
|
+
class Boomer
|
|
99
|
+
extend ActiveModel::Naming
|
|
100
|
+
|
|
101
|
+
def kaboom
|
|
102
|
+
raise "actual failure message"
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Named to mirror AASM::InvalidTransition (and similar state-machine gem
|
|
107
|
+
# exceptions) without depending on AASM: only the class name matters to
|
|
108
|
+
# the rescue chain's class-name match.
|
|
109
|
+
class InvalidTransitionError < StandardError; end
|
|
110
|
+
|
|
111
|
+
class StateMachineWidget
|
|
112
|
+
extend ActiveModel::Naming
|
|
113
|
+
|
|
114
|
+
def transition
|
|
115
|
+
raise InvalidTransitionError, "cannot transition from draft to published"
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
module Admin
|
|
121
|
+
module Resources
|
|
122
|
+
class ExceptionHandlingBoomerResource < Admin::Base::Resource
|
|
123
|
+
model ExceptionHandlingFixtures::Boomer
|
|
124
|
+
portal :ops
|
|
125
|
+
section :observability
|
|
126
|
+
actions { action :kaboom }
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
class ExceptionHandlingStateMachineResource < Admin::Base::Resource
|
|
130
|
+
model ExceptionHandlingFixtures::StateMachineWidget
|
|
131
|
+
portal :ops
|
|
132
|
+
section :observability
|
|
133
|
+
actions { action :transition }
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# The dummy app is database-free and `ActiveRecord::Base` above is a bare
|
|
139
|
+
# stub class, so every other fixture in this suite is a PORO and
|
|
140
|
+
# `active_record_base?` is false for all of them -- every AR-dependent path
|
|
141
|
+
# (`auto_admin_suite_path_for`, `format_table_cell`'s AR branch) is
|
|
142
|
+
# otherwise unreachable by tests. Subclassing the stub gives a fixture that
|
|
143
|
+
# genuinely satisfies `active_record_base?`, with no database and no
|
|
144
|
+
# railtie required. Established here (Task 3, phase 2b) because it's
|
|
145
|
+
# reused by later tasks (4, 7); those tasks' own test files each `require
|
|
146
|
+
# "test_helper"`, so this is visible to them even when a single test file
|
|
147
|
+
# is run in isolation via `rake test TEST=...` -- unlike a fixture defined
|
|
148
|
+
# only in association_linking_test.rb, which would not be.
|
|
149
|
+
module LinkingFixtures
|
|
150
|
+
class Company < ActiveRecord::Base
|
|
151
|
+
extend ActiveModel::Naming
|
|
152
|
+
|
|
153
|
+
attr_reader :id, :name
|
|
154
|
+
|
|
155
|
+
def initialize(id: 7, name: "Acme Corp")
|
|
156
|
+
@id = id
|
|
157
|
+
@name = name
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def self.all = ReadOnlyResourceFixtures::Relation.new([ new ])
|
|
161
|
+
def self.column_names = %w[id name]
|
|
162
|
+
def self.primary_key = "id"
|
|
163
|
+
def self.columns_hash = { "id" => Struct.new(:type).new(:integer) }
|
|
164
|
+
def self.find(_id) = new
|
|
165
|
+
def to_param = id.to_s
|
|
166
|
+
def attributes = { "id" => id, "name" => name }
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
module Admin
|
|
171
|
+
module Resources
|
|
172
|
+
# Registered so `auto_admin_suite_path_for` can resolve a real path for
|
|
173
|
+
# `LinkingFixtures::Company` instances (it looks up `registered_resources`
|
|
174
|
+
# by `model_class`). Never call `Admin::Base::Resource.reset_registry!` in
|
|
175
|
+
# a test: `inherited` fires only on class creation, so a reset-then-reload
|
|
176
|
+
# would permanently empty the registry for the rest of the run.
|
|
177
|
+
class LinkingCompanyResource < Admin::Base::Resource
|
|
178
|
+
model LinkingFixtures::Company
|
|
179
|
+
portal :ops
|
|
180
|
+
section :observability
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: admin_suite
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- TechWright Labs
|
|
@@ -36,20 +36,40 @@ dependencies:
|
|
|
36
36
|
requirements:
|
|
37
37
|
- - ">="
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
|
-
version: '
|
|
39
|
+
version: '9.0'
|
|
40
|
+
- - "<"
|
|
41
|
+
- !ruby/object:Gem::Version
|
|
42
|
+
version: '10'
|
|
43
|
+
type: :runtime
|
|
44
|
+
prerelease: false
|
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
46
|
+
requirements:
|
|
47
|
+
- - ">="
|
|
48
|
+
- !ruby/object:Gem::Version
|
|
49
|
+
version: '9.0'
|
|
50
|
+
- - "<"
|
|
51
|
+
- !ruby/object:Gem::Version
|
|
52
|
+
version: '10'
|
|
53
|
+
- !ruby/object:Gem::Dependency
|
|
54
|
+
name: turbo-rails
|
|
55
|
+
requirement: !ruby/object:Gem::Requirement
|
|
56
|
+
requirements:
|
|
57
|
+
- - ">="
|
|
58
|
+
- !ruby/object:Gem::Version
|
|
59
|
+
version: '2.0'
|
|
40
60
|
- - "<"
|
|
41
61
|
- !ruby/object:Gem::Version
|
|
42
|
-
version: '
|
|
62
|
+
version: '3.0'
|
|
43
63
|
type: :runtime
|
|
44
64
|
prerelease: false
|
|
45
65
|
version_requirements: !ruby/object:Gem::Requirement
|
|
46
66
|
requirements:
|
|
47
67
|
- - ">="
|
|
48
68
|
- !ruby/object:Gem::Version
|
|
49
|
-
version: '
|
|
69
|
+
version: '2.0'
|
|
50
70
|
- - "<"
|
|
51
71
|
- !ruby/object:Gem::Version
|
|
52
|
-
version: '
|
|
72
|
+
version: '3.0'
|
|
53
73
|
- !ruby/object:Gem::Dependency
|
|
54
74
|
name: lucide-rails
|
|
55
75
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -126,6 +146,9 @@ files:
|
|
|
126
146
|
- app/assets/builds/admin_suite_tailwind.css
|
|
127
147
|
- app/assets/rouge.css
|
|
128
148
|
- app/assets/tailwind/admin_suite.css
|
|
149
|
+
- app/assets/vendor/chart.umd.min.js
|
|
150
|
+
- app/assets/vendor/easymde.min.css
|
|
151
|
+
- app/assets/vendor/easymde.min.js
|
|
129
152
|
- app/controllers/admin_suite/application_controller.rb
|
|
130
153
|
- app/controllers/admin_suite/dashboard_controller.rb
|
|
131
154
|
- app/controllers/admin_suite/docs_controller.rb
|
|
@@ -137,6 +160,7 @@ files:
|
|
|
137
160
|
- app/helpers/admin_suite/resources_helper.rb
|
|
138
161
|
- app/helpers/admin_suite/theme_helper.rb
|
|
139
162
|
- app/javascript/admin_suite_application.js
|
|
163
|
+
- app/javascript/controllers/admin_suite/chart_controller.js
|
|
140
164
|
- app/javascript/controllers/admin_suite/click_actions_controller.js
|
|
141
165
|
- app/javascript/controllers/admin_suite/clipboard_controller.js
|
|
142
166
|
- app/javascript/controllers/admin_suite/code_editor_controller.js
|
|
@@ -167,6 +191,7 @@ files:
|
|
|
167
191
|
- app/views/admin_suite/shared/_flash.html.erb
|
|
168
192
|
- app/views/admin_suite/shared/_form.html.erb
|
|
169
193
|
- app/views/admin_suite/shared/_json_editor_field.html.erb
|
|
194
|
+
- app/views/admin_suite/shared/_pagination.html.erb
|
|
170
195
|
- app/views/admin_suite/shared/_sidebar.html.erb
|
|
171
196
|
- app/views/admin_suite/shared/_toggle_cell.html.erb
|
|
172
197
|
- app/views/admin_suite/shared/_topbar.html.erb
|
|
@@ -183,10 +208,22 @@ files:
|
|
|
183
208
|
- lib/admin_suite/auth/http_basic.rb
|
|
184
209
|
- lib/admin_suite/auth/strategy.rb
|
|
185
210
|
- lib/admin_suite/configuration.rb
|
|
211
|
+
- lib/admin_suite/definition_loader.rb
|
|
212
|
+
- lib/admin_suite/deprecation.rb
|
|
186
213
|
- lib/admin_suite/engine.rb
|
|
214
|
+
- lib/admin_suite/host_autoload_policy.rb
|
|
215
|
+
- lib/admin_suite/legacy_custom_renderer_procs.rb
|
|
187
216
|
- lib/admin_suite/markdown_renderer.rb
|
|
188
217
|
- lib/admin_suite/portal_definition.rb
|
|
189
218
|
- lib/admin_suite/portal_registry.rb
|
|
219
|
+
- lib/admin_suite/renderer.rb
|
|
220
|
+
- lib/admin_suite/renderer_registry.rb
|
|
221
|
+
- lib/admin_suite/renderers/code_renderer.rb
|
|
222
|
+
- lib/admin_suite/renderers/json_renderer.rb
|
|
223
|
+
- lib/admin_suite/renderers/key_value_renderer.rb
|
|
224
|
+
- lib/admin_suite/renderers/legacy_gleania.rb
|
|
225
|
+
- lib/admin_suite/renderers/table_from_renderer.rb
|
|
226
|
+
- lib/admin_suite/section_definition.rb
|
|
190
227
|
- lib/admin_suite/theme_palette.rb
|
|
191
228
|
- lib/admin_suite/ui/dashboard_definition.rb
|
|
192
229
|
- lib/admin_suite/ui/field_renderer_registry.rb
|
|
@@ -245,18 +282,38 @@ files:
|
|
|
245
282
|
- test/dummy/public/robots.txt
|
|
246
283
|
- test/dummy/test/test_helper.rb
|
|
247
284
|
- test/fixtures/docs/progress/PROGRESS_REPORT.md
|
|
285
|
+
- test/integration/association_linking_test.rb
|
|
248
286
|
- test/integration/authentication_test.rb
|
|
249
287
|
- test/integration/authorization_test.rb
|
|
288
|
+
- test/integration/chart_panel_test.rb
|
|
250
289
|
- test/integration/dashboard_test.rb
|
|
251
290
|
- test/integration/docs_test.rb
|
|
291
|
+
- test/integration/index_table_test.rb
|
|
292
|
+
- test/integration/layout_assets_test.rb
|
|
293
|
+
- test/integration/navigation_sections_test.rb
|
|
294
|
+
- test/integration/pagination_and_stats_test.rb
|
|
252
295
|
- test/integration/read_only_resource_test.rb
|
|
296
|
+
- test/integration/searchable_select_search_test.rb
|
|
297
|
+
- test/integration/show_hide_blank_test.rb
|
|
253
298
|
- test/integration/theme_test.rb
|
|
299
|
+
- test/integration/toggle_test.rb
|
|
300
|
+
- test/lib/action_executor_redirect_test.rb
|
|
254
301
|
- test/lib/action_executor_test.rb
|
|
255
302
|
- test/lib/auth_http_basic_test.rb
|
|
256
303
|
- test/lib/auth_resolution_test.rb
|
|
257
304
|
- test/lib/auth_test.rb
|
|
305
|
+
- test/lib/builtin_renderers_test.rb
|
|
306
|
+
- test/lib/definition_loader_test.rb
|
|
307
|
+
- test/lib/engine_defaults_test.rb
|
|
308
|
+
- test/lib/form_field_renderer_test.rb
|
|
309
|
+
- test/lib/format_table_cell_test.rb
|
|
310
|
+
- test/lib/index_includes_test.rb
|
|
311
|
+
- test/lib/legacy_renderer_deprecation_test.rb
|
|
258
312
|
- test/lib/markdown_renderer_test.rb
|
|
313
|
+
- test/lib/renderer_test.rb
|
|
314
|
+
- test/lib/resource_exportable_deprecation_test.rb
|
|
259
315
|
- test/lib/resource_observability_extensions_test.rb
|
|
316
|
+
- test/lib/show_value_formatter_test.rb
|
|
260
317
|
- test/lib/theme_palette_test.rb
|
|
261
318
|
- test/lib/zeitwerk_integration_test.rb
|
|
262
319
|
- test/test_helper.rb
|