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.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +210 -1
  3. data/app/assets/vendor/chart.umd.min.js +14 -0
  4. data/app/assets/vendor/easymde.min.css +7 -0
  5. data/app/assets/vendor/easymde.min.js +7 -0
  6. data/app/controllers/admin_suite/application_controller.rb +26 -47
  7. data/app/controllers/admin_suite/portals_controller.rb +2 -2
  8. data/app/controllers/admin_suite/resources_controller.rb +138 -6
  9. data/app/helpers/admin_suite/base_helper.rb +232 -391
  10. data/app/javascript/admin_suite_application.js +3 -0
  11. data/app/javascript/controllers/admin_suite/chart_controller.js +173 -0
  12. data/app/javascript/controllers/admin_suite/markdown_editor_controller.js +21 -2
  13. data/app/views/admin_suite/panels/_chart.html.erb +158 -18
  14. data/app/views/admin_suite/panels/_stat.html.erb +10 -0
  15. data/app/views/admin_suite/resources/index.html.erb +31 -82
  16. data/app/views/admin_suite/shared/_pagination.html.erb +74 -0
  17. data/app/views/admin_suite/shared/_sidebar.html.erb +15 -9
  18. data/app/views/layouts/admin_suite/application.html.erb +11 -3
  19. data/config/routes.rb +3 -0
  20. data/lib/admin/base/action_executor.rb +19 -51
  21. data/lib/admin/base/filter_builder.rb +48 -5
  22. data/lib/admin/base/resource.rb +92 -17
  23. data/lib/admin_suite/configuration.rb +32 -3
  24. data/lib/admin_suite/definition_loader.rb +194 -0
  25. data/lib/admin_suite/deprecation.rb +48 -0
  26. data/lib/admin_suite/engine.rb +55 -76
  27. data/lib/admin_suite/host_autoload_policy.rb +132 -0
  28. data/lib/admin_suite/legacy_custom_renderer_procs.rb +29 -0
  29. data/lib/admin_suite/portal_definition.rb +11 -0
  30. data/lib/admin_suite/renderer.rb +133 -0
  31. data/lib/admin_suite/renderer_registry.rb +81 -0
  32. data/lib/admin_suite/renderers/code_renderer.rb +15 -0
  33. data/lib/admin_suite/renderers/json_renderer.rb +15 -0
  34. data/lib/admin_suite/renderers/key_value_renderer.rb +39 -0
  35. data/lib/admin_suite/renderers/legacy_gleania.rb +232 -0
  36. data/lib/admin_suite/renderers/table_from_renderer.rb +22 -0
  37. data/lib/admin_suite/section_definition.rb +42 -0
  38. data/lib/admin_suite/ui/dashboard_definition.rb +6 -0
  39. data/lib/admin_suite/ui/field_renderer_registry.rb +31 -4
  40. data/lib/admin_suite/ui/form_field_renderer.rb +1 -7
  41. data/lib/admin_suite/ui/show_formatter_registry.rb +9 -0
  42. data/lib/admin_suite/ui/show_value_formatter.rb +7 -3
  43. data/lib/admin_suite/version.rb +1 -1
  44. data/lib/admin_suite.rb +48 -0
  45. data/lib/generators/admin_suite/install/templates/admin_suite.rb +0 -4
  46. data/test/controllers/resources_controller_test.rb +76 -1
  47. data/test/integration/association_linking_test.rb +292 -0
  48. data/test/integration/chart_panel_test.rb +491 -0
  49. data/test/integration/dashboard_test.rb +9 -2
  50. data/test/integration/index_table_test.rb +289 -0
  51. data/test/integration/layout_assets_test.rb +112 -0
  52. data/test/integration/navigation_sections_test.rb +62 -0
  53. data/test/integration/pagination_and_stats_test.rb +152 -0
  54. data/test/integration/searchable_select_search_test.rb +368 -0
  55. data/test/integration/show_hide_blank_test.rb +195 -0
  56. data/test/integration/toggle_test.rb +106 -0
  57. data/test/lib/action_executor_redirect_test.rb +41 -0
  58. data/test/lib/builtin_renderers_test.rb +202 -0
  59. data/test/lib/definition_loader_test.rb +276 -0
  60. data/test/lib/engine_defaults_test.rb +39 -0
  61. data/test/lib/form_field_renderer_test.rb +136 -0
  62. data/test/lib/format_table_cell_test.rb +49 -0
  63. data/test/lib/index_includes_test.rb +223 -0
  64. data/test/lib/legacy_renderer_deprecation_test.rb +42 -0
  65. data/test/lib/renderer_test.rb +237 -0
  66. data/test/lib/resource_exportable_deprecation_test.rb +47 -0
  67. data/test/lib/show_value_formatter_test.rb +88 -0
  68. data/test/lib/zeitwerk_integration_test.rb +28 -64
  69. data/test/test_helper.rb +121 -5
  70. 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
- # Helper method that simulates the Zeitwerk integration logic from engine.rb
34
- def simulate_zeitwerk_integration(app_root, loader)
35
- admin_suite_app_dir = app_root.join("app/admin_suite")
36
- admin_dir = app_root.join("app/admin")
37
- admin_portals_dir = app_root.join("app/admin/portals")
38
-
39
- # Map app/admin -> Admin namespace if files define Admin::* constants.
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
- # Create loader and simulate initializer logic
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
- simulate_zeitwerk_integration(app_root, loader)
53
+ AdminSuite::HostAutoloadPolicy.apply!(app, loaders: [ loader ])
85
54
 
86
55
  # Verify that app/admin/portals was ignored
87
- expected_path = app_root.join("app/admin/portals").to_s
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
- # Create loader and simulate initializer logic
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
- simulate_zeitwerk_integration(app_root, loader)
72
+ AdminSuite::HostAutoloadPolicy.apply!(app, loaders: [ loader ])
105
73
 
106
74
  # Verify that app/admin/portals was NOT ignored
107
- unexpected_path = app_root.join("app/admin/portals").to_s
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
- # Create loader and simulate initializer logic
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
- simulate_zeitwerk_integration(app_root, loader)
91
+ AdminSuite::HostAutoloadPolicy.apply!(app, loaders: [ loader ])
125
92
 
126
93
  # Verify that app/admin_suite was ignored
127
- expected_path = app_root.join("app/admin_suite").to_s
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
- # Create loader and simulate initializer logic
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
- simulate_zeitwerk_integration(app_root, loader)
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 = app_root.join("app/admin/portals").to_s
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
- # Create loader
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
- # Simulate the initializer logic
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 = app_root.join("app/admin/portals").to_s
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
- app_root = Pathname.new(@temp_dir)
168
+ app = create_fake_app(@temp_dir)
205
169
  loader, _ignored_dirs, pushed_dirs = create_tracked_loader
206
- simulate_zeitwerk_integration(app_root, loader)
170
+ AdminSuite::HostAutoloadPolicy.apply!(app, loaders: [ loader ])
207
171
 
208
- assert pushed_dirs.any? { |h| h[:path] == app_root.join("app/admin").to_s && h[:namespace] == Admin },
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
- app_root = Pathname.new(@temp_dir)
184
+ app = create_fake_app(@temp_dir)
221
185
  loader, _ignored_dirs, pushed_dirs = create_tracked_loader
222
- simulate_zeitwerk_integration(app_root, loader)
186
+ AdminSuite::HostAutoloadPolicy.apply!(app, loaders: [ loader ])
223
187
 
224
- assert pushed_dirs.none? { |h| h[:path] == app_root.join("app/admin").to_s },
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
- module TurboFrameTestHelper
36
- def turbo_frame_tag(name, **options, &block)
37
- content_tag(:turbo_frame, capture(&block), id: name, **options)
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.3.1
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: '6.0'
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: '11.0'
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: '6.0'
69
+ version: '2.0'
50
70
  - - "<"
51
71
  - !ruby/object:Gem::Version
52
- version: '11.0'
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