admin_suite 0.3.1 → 0.4.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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +82 -0
  3. data/app/assets/vendor/easymde.min.css +7 -0
  4. data/app/assets/vendor/easymde.min.js +7 -0
  5. data/app/controllers/admin_suite/application_controller.rb +26 -47
  6. data/app/controllers/admin_suite/portals_controller.rb +2 -2
  7. data/app/controllers/admin_suite/resources_controller.rb +8 -1
  8. data/app/helpers/admin_suite/base_helper.rb +49 -385
  9. data/app/javascript/controllers/admin_suite/markdown_editor_controller.js +21 -2
  10. data/app/views/admin_suite/panels/_stat.html.erb +10 -0
  11. data/app/views/admin_suite/resources/index.html.erb +7 -77
  12. data/app/views/admin_suite/shared/_pagination.html.erb +74 -0
  13. data/app/views/admin_suite/shared/_sidebar.html.erb +1 -1
  14. data/app/views/layouts/admin_suite/application.html.erb +5 -3
  15. data/lib/admin/base/action_executor.rb +19 -51
  16. data/lib/admin/base/resource.rb +53 -14
  17. data/lib/admin_suite/configuration.rb +28 -3
  18. data/lib/admin_suite/definition_loader.rb +194 -0
  19. data/lib/admin_suite/deprecation.rb +48 -0
  20. data/lib/admin_suite/engine.rb +55 -76
  21. data/lib/admin_suite/host_autoload_policy.rb +132 -0
  22. data/lib/admin_suite/legacy_custom_renderer_procs.rb +29 -0
  23. data/lib/admin_suite/portal_definition.rb +11 -0
  24. data/lib/admin_suite/renderer.rb +133 -0
  25. data/lib/admin_suite/renderer_registry.rb +70 -0
  26. data/lib/admin_suite/renderers/code_renderer.rb +15 -0
  27. data/lib/admin_suite/renderers/json_renderer.rb +15 -0
  28. data/lib/admin_suite/renderers/key_value_renderer.rb +39 -0
  29. data/lib/admin_suite/renderers/legacy_gleania.rb +230 -0
  30. data/lib/admin_suite/renderers/table_from_renderer.rb +22 -0
  31. data/lib/admin_suite/section_definition.rb +42 -0
  32. data/lib/admin_suite/ui/field_renderer_registry.rb +31 -4
  33. data/lib/admin_suite/ui/form_field_renderer.rb +1 -7
  34. data/lib/admin_suite/ui/show_formatter_registry.rb +9 -0
  35. data/lib/admin_suite/ui/show_value_formatter.rb +7 -3
  36. data/lib/admin_suite/version.rb +1 -1
  37. data/lib/admin_suite.rb +37 -0
  38. data/lib/generators/admin_suite/install/templates/admin_suite.rb +0 -4
  39. data/test/controllers/resources_controller_test.rb +76 -1
  40. data/test/integration/layout_assets_test.rb +112 -0
  41. data/test/integration/navigation_sections_test.rb +41 -0
  42. data/test/integration/pagination_and_stats_test.rb +152 -0
  43. data/test/lib/action_executor_redirect_test.rb +41 -0
  44. data/test/lib/builtin_renderers_test.rb +202 -0
  45. data/test/lib/definition_loader_test.rb +264 -0
  46. data/test/lib/engine_defaults_test.rb +39 -0
  47. data/test/lib/form_field_renderer_test.rb +64 -0
  48. data/test/lib/legacy_renderer_deprecation_test.rb +35 -0
  49. data/test/lib/renderer_test.rb +221 -0
  50. data/test/lib/resource_exportable_deprecation_test.rb +39 -0
  51. data/test/lib/show_value_formatter_test.rb +88 -0
  52. data/test/lib/zeitwerk_integration_test.rb +28 -64
  53. data/test/test_helper.rb +77 -0
  54. metadata +28 -1
@@ -81,37 +81,14 @@ module AdminSuite
81
81
  #
82
82
  # @return [void]
83
83
  def ensure_resources_loaded!
84
- require "admin/base/resource" unless defined?(Admin::Base::Resource)
85
- return if Admin::Base::Resource.registered_resources.any?
86
-
87
- Array(AdminSuite.config.resource_globs).flat_map { |g| Dir[g] }.uniq.each do |file|
88
- require file
89
- end
90
- rescue NameError
91
- # Ensure base DSL is loaded first.
92
- require "admin/base/resource"
93
- retry
84
+ AdminSuite::DefinitionLoader.load!(:resources)
94
85
  end
95
86
 
96
87
  # Loads portal definition files in development (safe to call per-request).
97
88
  #
98
89
  # @return [void]
99
90
  def ensure_portals_loaded!
100
- globs = Array(AdminSuite.config.portal_globs).flat_map { |g| Dir[g] }.uniq
101
- return if globs.empty?
102
-
103
- if Rails.env.development?
104
- # Re-evaluate definitions on each request in development.
105
- AdminSuite::PortalRegistry.reset!
106
- globs.each { |file| load file }
107
- else
108
- # In non-dev, load once (typically at boot / first request).
109
- return if AdminSuite::PortalRegistry.all.any?
110
- globs.each { |file| require file }
111
- end
112
- rescue NameError
113
- require "admin_suite"
114
- retry
91
+ AdminSuite::DefinitionLoader.load!(:portals)
115
92
  end
116
93
 
117
94
  # Loads the root dashboard definition files (safe to call per-request).
@@ -122,33 +99,25 @@ module AdminSuite
122
99
  #
123
100
  # @return [void]
124
101
  def ensure_root_dashboard_loaded!
125
- if Rails.env.development?
126
- globs = Array(AdminSuite.config.dashboard_globs).flat_map { |g| Dir[g] }.uniq
127
- # Re-evaluate dashboard layout on each request in development.
128
- # Always reset, even when no files match, so removed dashboards are cleared.
129
- AdminSuite.reset_root_dashboard!
130
- globs.each { |file| load file }
131
- else
132
- # In non-dev, load once.
133
- return if AdminSuite.config.root_dashboard_loaded
134
- globs = Array(AdminSuite.config.dashboard_globs).flat_map { |g| Dir[g] }.uniq
135
- if globs.empty?
136
- # Avoid hitting the filesystem on every request when no dashboard files exist.
137
- AdminSuite.config.root_dashboard_loaded = true
138
- return
139
- end
140
- globs.each { |file| require file }
141
- AdminSuite.config.root_dashboard_loaded = true
142
- end
143
- rescue NameError
144
- require "admin_suite"
145
- retry
102
+ AdminSuite::DefinitionLoader.load!(:dashboards)
146
103
  end
147
104
 
148
105
  # Builds the navigation structure from registered resources.
149
106
  #
107
+ # Memoized per request/controller-instance: views re-enter this method
108
+ # repeatedly while rendering a single page (e.g. `portal_color` and
109
+ # `portal_icon` in base_helper.rb each call it, and the sidebar partial
110
+ # calls those once per portal), and it's rebuilt from scratch each time
111
+ # it isn't memoized -- on top of that, `ensure_resources_loaded!` /
112
+ # `ensure_portals_loaded!` would otherwise glob the filesystem on every
113
+ # one of those re-entries in development.
114
+ #
150
115
  # @return [Hash]
151
116
  def navigation_items
117
+ @navigation_items ||= build_navigation_items
118
+ end
119
+
120
+ def build_navigation_items
152
121
  ensure_resources_loaded!
153
122
  ensure_portals_loaded!
154
123
 
@@ -163,6 +132,13 @@ module AdminSuite
163
132
  navigation[key.to_sym] ||= { label: key.to_s.humanize, order: 100, sections: {} }
164
133
  navigation[key.to_sym].merge!(definition.to_nav_meta)
165
134
  navigation[key.to_sym][:sections] ||= {}
135
+
136
+ # Declared sections appear even before any resource is assigned to
137
+ # them, so their label/order take effect immediately.
138
+ definition.sections.each do |section_key, section_definition|
139
+ navigation[key.to_sym][:sections][section_key] ||=
140
+ { label: section_key.to_s.humanize, order: 100, items: [] }.merge(section_definition.to_nav_meta)
141
+ end
166
142
  end
167
143
 
168
144
  Admin::Base::Resource.registered_resources.each do |resource|
@@ -172,7 +148,10 @@ module AdminSuite
172
148
  section = resource.section_name.to_sym
173
149
 
174
150
  navigation[portal] ||= { label: portal.to_s.humanize, order: 100, sections: {} }
175
- navigation[portal][:sections][section] ||= { label: section.to_s.humanize, items: [] }
151
+ navigation[portal][:sections][section] ||= begin
152
+ declared = AdminSuite::PortalRegistry.all[portal]&.sections&.[](section)
153
+ { label: section.to_s.humanize, order: 100, items: [] }.merge(declared&.to_nav_meta || {})
154
+ end
176
155
 
177
156
  label = resource.nav_label.presence || resource.human_name_plural
178
157
  navigation[portal][:sections][section][:items] << {
@@ -11,8 +11,8 @@ module AdminSuite
11
11
  raise ActionController::RoutingError, "Portal not found" if @portal.blank?
12
12
 
13
13
  @sections =
14
- (@portal[:sections] || {}).sort_by { |(_k, s)| s[:label].to_s }.map do |section_key, section|
15
- items = Array(section[:items]).sort_by { |it| it[:label].to_s }
14
+ (@portal[:sections] || {}).sort_by { |(_k, s)| [ (s[:order] || 100).to_i, s[:label].to_s ] }.map do |section_key, section|
15
+ items = Array(section[:items]).sort_by { |it| [ (it[:order] || 100).to_i, it[:label].to_s ] }
16
16
  [ section_key, section.merge(items: items) ]
17
17
  end
18
18
 
@@ -200,7 +200,14 @@ module AdminSuite
200
200
  end
201
201
 
202
202
  @resource = klass.find(id)
203
- rescue ActiveRecord::RecordNotFound
203
+ rescue StandardError => e
204
+ # A bare `rescue ActiveRecord::RecordNotFound` raises `NameError`
205
+ # *while handling* whatever the real exception was, in a host without
206
+ # ActiveRecord loaded (e.g. `column_names` above failing on a PORO
207
+ # model) -- destroying the original diagnostic. Only recover the one
208
+ # case this method actually knows how to recover from.
209
+ raise unless defined?(ActiveRecord::RecordNotFound) && e.is_a?(ActiveRecord::RecordNotFound)
210
+
204
211
  @resource = find_friendly_resource!(klass, id, columns)
205
212
  end
206
213