rails_admin 3.2.1 → 4.0.0.beta

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 (214) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +8 -6
  3. data/README.md +11 -11
  4. data/Rakefile +3 -11
  5. data/app/assets/builds/rails_admin.css +10 -0
  6. data/app/assets/builds/rails_admin.js +34 -0
  7. data/app/controllers/rails_admin/main_controller.rb +3 -3
  8. data/app/helpers/rails_admin/application_helper.rb +3 -15
  9. data/app/helpers/rails_admin/form_builder.rb +10 -9
  10. data/app/views/layouts/rails_admin/_head.html.erb +10 -27
  11. data/app/views/layouts/rails_admin/_head_custom.html.erb +3 -0
  12. data/app/views/layouts/rails_admin/_secondary_navigation.html.erb +1 -1
  13. data/app/views/rails_admin/main/_dashboard_history.html.erb +1 -1
  14. data/app/views/rails_admin/main/_delete_notice.html.erb +3 -3
  15. data/app/views/rails_admin/main/_form_boolean.html.erb +9 -13
  16. data/app/views/rails_admin/main/_submit_buttons.html.erb +4 -4
  17. data/app/views/rails_admin/main/bulk_delete.html.erb +2 -2
  18. data/app/views/rails_admin/main/dashboard.html.erb +3 -3
  19. data/app/views/rails_admin/main/delete.html.erb +2 -2
  20. data/app/views/rails_admin/main/export.html.erb +2 -2
  21. data/app/views/rails_admin/main/history.html.erb +2 -2
  22. data/app/views/rails_admin/main/index.html.erb +3 -3
  23. data/config/initializers/active_record_extensions.rb +1 -4
  24. data/lib/generators/rails_admin/install_generator.rb +37 -92
  25. data/lib/generators/rails_admin/templates/rails_admin.scss.erb +1 -1
  26. data/lib/rails_admin/abstract_model.rb +69 -191
  27. data/lib/rails_admin/adapters/active_record/association.rb +1 -1
  28. data/lib/rails_admin/adapters/active_record/property.rb +7 -0
  29. data/lib/rails_admin/adapters/active_record/reflection.rb +87 -0
  30. data/lib/rails_admin/adapters/active_record/repository.rb +216 -0
  31. data/lib/rails_admin/adapters/active_record/statement_builder.rb +148 -0
  32. data/lib/rails_admin/adapters/active_record/where_builder.rb +40 -0
  33. data/lib/rails_admin/adapters/active_record.rb +7 -356
  34. data/lib/rails_admin/adapters/mongoid/association.rb +1 -1
  35. data/lib/rails_admin/adapters/mongoid/extension.rb +16 -6
  36. data/lib/rails_admin/adapters/mongoid/property.rb +7 -0
  37. data/lib/rails_admin/adapters/mongoid/reflection.rb +58 -0
  38. data/lib/rails_admin/adapters/mongoid/repository.rb +237 -0
  39. data/lib/rails_admin/adapters/mongoid/statement_builder.rb +93 -0
  40. data/lib/rails_admin/adapters/mongoid.rb +7 -300
  41. data/lib/rails_admin/adapters/reflection.rb +105 -0
  42. data/lib/rails_admin/adapters/repository.rb +169 -0
  43. data/lib/rails_admin/adapters/statement_builder.rb +109 -0
  44. data/lib/rails_admin/adapters.rb +94 -0
  45. data/lib/rails_admin/config/actions/base.rb +8 -0
  46. data/lib/rails_admin/config/actions/bulk_delete.rb +4 -4
  47. data/lib/rails_admin/config/actions/dashboard.rb +1 -1
  48. data/lib/rails_admin/config/actions/edit.rb +3 -2
  49. data/lib/rails_admin/config/actions/index.rb +1 -1
  50. data/lib/rails_admin/config/actions/new.rb +3 -2
  51. data/lib/rails_admin/config/actions/show.rb +1 -1
  52. data/lib/rails_admin/config/configurable.rb +4 -3
  53. data/lib/rails_admin/config/fields/association.rb +9 -8
  54. data/lib/rails_admin/config/fields/base.rb +55 -37
  55. data/lib/rails_admin/config/fields/collection_association.rb +2 -2
  56. data/lib/rails_admin/config/fields/factories/carrierwave.rb +35 -20
  57. data/lib/rails_admin/config/fields/factories/dragonfly.rb +30 -18
  58. data/lib/rails_admin/config/fields/factories/enum.rb +14 -8
  59. data/lib/rails_admin/config/fields/factories/paperclip.rb +29 -19
  60. data/lib/rails_admin/config/fields/factories/shrine.rb +19 -8
  61. data/lib/rails_admin/config/fields/singular_association.rb +6 -1
  62. data/lib/rails_admin/config/fields/types/active_record_enum.rb +3 -7
  63. data/lib/rails_admin/config/fields/types/belongs_to_association.rb +8 -4
  64. data/lib/rails_admin/config/fields/types/bson_object_id.rb +1 -1
  65. data/lib/rails_admin/config/fields/types/datetime.rb +0 -4
  66. data/lib/rails_admin/config/fields/types/dragonfly.rb +1 -1
  67. data/lib/rails_admin/config/fields/types/enum.rb +11 -2
  68. data/lib/rails_admin/config/fields/types/multiple_file_upload.rb +0 -2
  69. data/lib/rails_admin/config/fields/types/polymorphic_association.rb +4 -4
  70. data/lib/rails_admin/config/fields/types/time.rb +1 -1
  71. data/lib/rails_admin/config/model.rb +27 -5
  72. data/lib/rails_admin/config/sections/list.rb +0 -8
  73. data/lib/rails_admin/config.rb +7 -21
  74. data/lib/rails_admin/criteria/path.rb +66 -0
  75. data/lib/rails_admin/criteria/period.rb +67 -0
  76. data/lib/rails_admin/engine.rb +11 -15
  77. data/lib/rails_admin/extensions/pundit/authorization_adapter.rb +2 -2
  78. data/lib/rails_admin/extensions/url_for_extension.rb +8 -0
  79. data/lib/rails_admin/support/asset_source.rb +42 -0
  80. data/lib/rails_admin/support/composite_keys_serializer.rb +28 -2
  81. data/lib/rails_admin/support/csv_converter.rb +3 -10
  82. data/lib/rails_admin/version.rb +8 -7
  83. data/lib/rails_admin.rb +7 -0
  84. data/lib/tasks/assets.rake +23 -0
  85. data/package.json +12 -7
  86. data/src/rails_admin/base.js +18 -15
  87. data/src/rails_admin/filter-box.js +7 -4
  88. data/src/rails_admin/filtering-multiselect.js +1 -1
  89. data/src/rails_admin/filtering-select.js +8 -8
  90. data/src/rails_admin/flatpickr.js +14 -0
  91. data/src/rails_admin/remote-form.js +74 -48
  92. data/src/rails_admin/styles/base/theming.scss +48 -25
  93. data/src/rails_admin/styles/base.scss +25 -1
  94. data/src/rails_admin/styles/filtering-select.scss +23 -62
  95. data/src/rails_admin/ui.js +1 -1
  96. data/src/rails_admin/vendor/jquery_nested_form.js +4 -4
  97. data/src/rails_admin/widgets.js +44 -11
  98. metadata +34 -130
  99. data/app/assets/javascripts/rails_admin/application.js.erb +0 -30
  100. data/app/assets/javascripts/rails_admin/custom/ui.js +0 -1
  101. data/app/assets/stylesheets/rails_admin/application.scss.erb +0 -35
  102. data/app/assets/stylesheets/rails_admin/custom/mixins.scss +0 -11
  103. data/app/assets/stylesheets/rails_admin/custom/theming.scss +0 -13
  104. data/app/assets/stylesheets/rails_admin/custom/variables.scss +0 -15
  105. data/lib/generators/rails_admin/importmap_formatter.rb +0 -28
  106. data/lib/generators/rails_admin/templates/rails_admin.vite.js +0 -2
  107. data/lib/generators/rails_admin/templates/rails_admin.webpacker.js +0 -2
  108. data/lib/rails_admin/adapters/active_record/object_extension.rb +0 -13
  109. data/lib/rails_admin/adapters/active_record.rb.bak +0 -348
  110. data/lib/rails_admin/adapters/mongoid/object_extension.rb +0 -29
  111. data/lib/rails_admin/support/es_module_processor.rb +0 -23
  112. data/vendor/assets/fonts/rails_admin/fa-solid-900.ttf +0 -0
  113. data/vendor/assets/javascripts/rails_admin/bootstrap.js +0 -5046
  114. data/vendor/assets/javascripts/rails_admin/flatpickr-with-locales.js +0 -11246
  115. data/vendor/assets/javascripts/rails_admin/jquery-ui/data.js +0 -41
  116. data/vendor/assets/javascripts/rails_admin/jquery-ui/effect.js +0 -1637
  117. data/vendor/assets/javascripts/rails_admin/jquery-ui/ie.js +0 -17
  118. data/vendor/assets/javascripts/rails_admin/jquery-ui/keycode.js +0 -47
  119. data/vendor/assets/javascripts/rails_admin/jquery-ui/position.js +0 -500
  120. data/vendor/assets/javascripts/rails_admin/jquery-ui/safe-active-element.js +0 -42
  121. data/vendor/assets/javascripts/rails_admin/jquery-ui/scroll-parent.js +0 -47
  122. data/vendor/assets/javascripts/rails_admin/jquery-ui/unique-id.js +0 -51
  123. data/vendor/assets/javascripts/rails_admin/jquery-ui/version.js +0 -17
  124. data/vendor/assets/javascripts/rails_admin/jquery-ui/widget.js +0 -735
  125. data/vendor/assets/javascripts/rails_admin/jquery-ui/widgets/autocomplete.js +0 -689
  126. data/vendor/assets/javascripts/rails_admin/jquery-ui/widgets/menu.js +0 -680
  127. data/vendor/assets/javascripts/rails_admin/jquery-ui/widgets/mouse.js +0 -230
  128. data/vendor/assets/javascripts/rails_admin/jquery-ui/widgets/sortable.js +0 -1561
  129. data/vendor/assets/javascripts/rails_admin/jquery3.js +0 -10881
  130. data/vendor/assets/javascripts/rails_admin/popper.js +0 -1974
  131. data/vendor/assets/stylesheets/rails_admin/bootstrap/_accordion.scss +0 -118
  132. data/vendor/assets/stylesheets/rails_admin/bootstrap/_alert.scss +0 -57
  133. data/vendor/assets/stylesheets/rails_admin/bootstrap/_badge.scss +0 -29
  134. data/vendor/assets/stylesheets/rails_admin/bootstrap/_breadcrumb.scss +0 -28
  135. data/vendor/assets/stylesheets/rails_admin/bootstrap/_button-group.scss +0 -139
  136. data/vendor/assets/stylesheets/rails_admin/bootstrap/_buttons.scss +0 -111
  137. data/vendor/assets/stylesheets/rails_admin/bootstrap/_card.scss +0 -216
  138. data/vendor/assets/stylesheets/rails_admin/bootstrap/_carousel.scss +0 -229
  139. data/vendor/assets/stylesheets/rails_admin/bootstrap/_close.scss +0 -40
  140. data/vendor/assets/stylesheets/rails_admin/bootstrap/_containers.scss +0 -41
  141. data/vendor/assets/stylesheets/rails_admin/bootstrap/_dropdown.scss +0 -240
  142. data/vendor/assets/stylesheets/rails_admin/bootstrap/_forms.scss +0 -9
  143. data/vendor/assets/stylesheets/rails_admin/bootstrap/_functions.scss +0 -302
  144. data/vendor/assets/stylesheets/rails_admin/bootstrap/_grid.scss +0 -33
  145. data/vendor/assets/stylesheets/rails_admin/bootstrap/_helpers.scss +0 -9
  146. data/vendor/assets/stylesheets/rails_admin/bootstrap/_images.scss +0 -42
  147. data/vendor/assets/stylesheets/rails_admin/bootstrap/_list-group.scss +0 -174
  148. data/vendor/assets/stylesheets/rails_admin/bootstrap/_mixins.scss +0 -43
  149. data/vendor/assets/stylesheets/rails_admin/bootstrap/_modal.scss +0 -209
  150. data/vendor/assets/stylesheets/rails_admin/bootstrap/_nav.scss +0 -139
  151. data/vendor/assets/stylesheets/rails_admin/bootstrap/_navbar.scss +0 -335
  152. data/vendor/assets/stylesheets/rails_admin/bootstrap/_offcanvas.scss +0 -83
  153. data/vendor/assets/stylesheets/rails_admin/bootstrap/_pagination.scss +0 -64
  154. data/vendor/assets/stylesheets/rails_admin/bootstrap/_placeholders.scss +0 -51
  155. data/vendor/assets/stylesheets/rails_admin/bootstrap/_popover.scss +0 -158
  156. data/vendor/assets/stylesheets/rails_admin/bootstrap/_progress.scss +0 -48
  157. data/vendor/assets/stylesheets/rails_admin/bootstrap/_reboot.scss +0 -625
  158. data/vendor/assets/stylesheets/rails_admin/bootstrap/_root.scss +0 -54
  159. data/vendor/assets/stylesheets/rails_admin/bootstrap/_spinners.scss +0 -69
  160. data/vendor/assets/stylesheets/rails_admin/bootstrap/_tables.scss +0 -155
  161. data/vendor/assets/stylesheets/rails_admin/bootstrap/_toasts.scss +0 -51
  162. data/vendor/assets/stylesheets/rails_admin/bootstrap/_tooltip.scss +0 -115
  163. data/vendor/assets/stylesheets/rails_admin/bootstrap/_transitions.scss +0 -27
  164. data/vendor/assets/stylesheets/rails_admin/bootstrap/_type.scss +0 -104
  165. data/vendor/assets/stylesheets/rails_admin/bootstrap/_utilities.scss +0 -630
  166. data/vendor/assets/stylesheets/rails_admin/bootstrap/_variables.scss +0 -1641
  167. data/vendor/assets/stylesheets/rails_admin/bootstrap/bootstrap.scss +0 -53
  168. data/vendor/assets/stylesheets/rails_admin/bootstrap/forms/_floating-labels.scss +0 -63
  169. data/vendor/assets/stylesheets/rails_admin/bootstrap/forms/_form-check.scss +0 -152
  170. data/vendor/assets/stylesheets/rails_admin/bootstrap/forms/_form-control.scss +0 -219
  171. data/vendor/assets/stylesheets/rails_admin/bootstrap/forms/_form-range.scss +0 -91
  172. data/vendor/assets/stylesheets/rails_admin/bootstrap/forms/_form-select.scss +0 -72
  173. data/vendor/assets/stylesheets/rails_admin/bootstrap/forms/_form-text.scss +0 -11
  174. data/vendor/assets/stylesheets/rails_admin/bootstrap/forms/_input-group.scss +0 -121
  175. data/vendor/assets/stylesheets/rails_admin/bootstrap/forms/_labels.scss +0 -36
  176. data/vendor/assets/stylesheets/rails_admin/bootstrap/forms/_validation.scss +0 -12
  177. data/vendor/assets/stylesheets/rails_admin/bootstrap/helpers/_clearfix.scss +0 -3
  178. data/vendor/assets/stylesheets/rails_admin/bootstrap/helpers/_colored-links.scss +0 -12
  179. data/vendor/assets/stylesheets/rails_admin/bootstrap/helpers/_position.scss +0 -30
  180. data/vendor/assets/stylesheets/rails_admin/bootstrap/helpers/_ratio.scss +0 -26
  181. data/vendor/assets/stylesheets/rails_admin/bootstrap/helpers/_stacks.scss +0 -15
  182. data/vendor/assets/stylesheets/rails_admin/bootstrap/helpers/_stretched-link.scss +0 -15
  183. data/vendor/assets/stylesheets/rails_admin/bootstrap/helpers/_text-truncation.scss +0 -7
  184. data/vendor/assets/stylesheets/rails_admin/bootstrap/helpers/_visually-hidden.scss +0 -8
  185. data/vendor/assets/stylesheets/rails_admin/bootstrap/helpers/_vr.scss +0 -8
  186. data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_alert.scss +0 -11
  187. data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_backdrop.scss +0 -14
  188. data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_border-radius.scss +0 -78
  189. data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_box-shadow.scss +0 -18
  190. data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_breakpoints.scss +0 -127
  191. data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_buttons.scss +0 -133
  192. data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_caret.scss +0 -64
  193. data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_clearfix.scss +0 -9
  194. data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_color-scheme.scss +0 -7
  195. data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_container.scss +0 -9
  196. data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_deprecate.scss +0 -10
  197. data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_forms.scss +0 -144
  198. data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_gradients.scss +0 -47
  199. data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_grid.scss +0 -151
  200. data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_image.scss +0 -16
  201. data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_list-group.scss +0 -24
  202. data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_lists.scss +0 -7
  203. data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_pagination.scss +0 -31
  204. data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_reset-text.scss +0 -17
  205. data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_resize.scss +0 -6
  206. data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_table-variants.scss +0 -21
  207. data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_text-truncate.scss +0 -8
  208. data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_transition.scss +0 -26
  209. data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_utilities.scss +0 -89
  210. data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_visually-hidden.scss +0 -29
  211. data/vendor/assets/stylesheets/rails_admin/bootstrap/utilities/_api.scss +0 -47
  212. data/vendor/assets/stylesheets/rails_admin/bootstrap/vendor/_rfs.scss +0 -354
  213. data/vendor/assets/stylesheets/rails_admin/flatpickr.css +0 -791
  214. data/vendor/assets/stylesheets/rails_admin/font-awesome.scss +0 -6351
@@ -53,9 +53,29 @@ module RailsAdmin
53
53
  @excluded = !RailsAdmin::AbstractModel.all.collect(&:model_name).include?(abstract_model.try(:model_name))
54
54
  end
55
55
 
56
- def object_label
57
- bindings[:object].send(object_label_method).presence ||
58
- bindings[:object].send(:rails_admin_default_object_label_method)
56
+ # The label for one record: what object_label_method has to say about it.
57
+ # When it has nothing to say, +fallback+ if one is given; failing that, and
58
+ # for a model with no label method at all, a last resort built from the
59
+ # class and the id.
60
+ #
61
+ # Everything that shows a record by name goes through here, so that a
62
+ # record is called the same thing wherever it turns up.
63
+ #
64
+ # +fallback+ is positional because model configs are reached through
65
+ # proxies that do not forward keyword arguments.
66
+ def object_label(object = bindings[:object], fallback = nil)
67
+ return default_object_label(object) unless object_label_method
68
+
69
+ object.send(object_label_method).presence || fallback || default_object_label(object)
70
+ end
71
+
72
+ # What to call a record that has nothing to be called by.
73
+ #
74
+ # Models used to have to carry this themselves, as
75
+ # rails_admin_default_object_label_method, because it was reached by
76
+ # sending object_label_method to the record like any other label method.
77
+ def default_object_label(object)
78
+ object.new_record? ? "new #{object.class}" : "#{object.class} ##{object.id}"
59
79
  end
60
80
 
61
81
  # The display for a model instance (i.e. a single database record).
@@ -63,7 +83,9 @@ module RailsAdmin
63
83
  # any methods that may have been added to the label_methods array via Configuration.
64
84
  # Failing all of these, it'll return the class name followed by the model's id.
65
85
  register_instance_option :object_label_method do
66
- @object_label_method ||= Config.label_methods.detect { |method| (@dummy_object ||= abstract_model.model.new).respond_to? method } || :rails_admin_default_object_label_method
86
+ # nil when the model answers to none of them, which #object_label reads
87
+ # as "nothing to call it by" and #default_object_label then handles.
88
+ @object_label_method ||= Config.label_methods.detect { |method| (@dummy_object ||= abstract_model.dummy_record).respond_to? method }
67
89
  end
68
90
 
69
91
  register_instance_option :label do
@@ -107,7 +129,7 @@ module RailsAdmin
107
129
  end
108
130
 
109
131
  register_instance_option :last_created_at do
110
- abstract_model.model.last.try(:created_at) if abstract_model.properties.detect { |c| c.name == :created_at }
132
+ abstract_model.first(sort: abstract_model.primary_key).try(:created_at) if abstract_model.properties.detect { |c| c.name == :created_at }
111
133
  end
112
134
 
113
135
  # Act as a proxy for the base section configuration that actually
@@ -46,17 +46,9 @@ module RailsAdmin
46
46
  ''
47
47
  end
48
48
 
49
- register_deprecated_instance_option :sidescroll do
50
- ActiveSupport::Deprecation.warn('The sidescroll configuration option was removed, it is always enabled now.')
51
- end
52
-
53
49
  def fields_for_table
54
50
  visible_fields.partition(&:sticky?).flatten
55
51
  end
56
-
57
- register_deprecated_instance_option :sort_reverse do
58
- ActiveSupport::Deprecation.warn('The sort_reverse configuration option is deprecated and has no effect.')
59
- end
60
52
  end
61
53
  end
62
54
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'rails_admin/config/lazy_model'
4
4
  require 'rails_admin/config/sections/list'
5
+ require 'rails_admin/support/asset_source'
5
6
  require 'rails_admin/support/composite_keys_serializer'
6
7
  require 'active_support/core_ext/module/attribute_accessors'
7
8
 
@@ -82,7 +83,9 @@ module RailsAdmin
82
83
  attr_accessor :navigation_static_links
83
84
  attr_accessor :navigation_static_label
84
85
 
85
- # Set where RailsAdmin fetches JS/CSS from, defaults to :sprockets
86
+ # How RailsAdmin serves its JS/CSS: :propshaft / :sprockets (bundle shipped in the
87
+ # gem, no build step), :external (your app builds it), or a callable rendering the
88
+ # <head> tags itself. nil (default) auto-detects the pipeline.
86
89
  attr_writer :asset_source
87
90
 
88
91
  # For customization of composite keys representation
@@ -236,7 +239,8 @@ module RailsAdmin
236
239
  when Class, ConstLoadSuppressor::ConstProxy
237
240
  entity.name.to_sym
238
241
  when String, Symbol
239
- entity.to_sym
242
+ # Class#name never has a leading '::', so strip it here too.
243
+ entity.to_s.delete_prefix('::').to_sym
240
244
  else
241
245
  entity.class.name.to_sym
242
246
  end
@@ -247,17 +251,7 @@ module RailsAdmin
247
251
  end
248
252
 
249
253
  def asset_source
250
- @asset_source ||=
251
- begin
252
- detected = defined?(Sprockets) ? :sprockets : :invalid
253
- unless ARGV.join(' ').include? 'rails_admin:install'
254
- warn <<~MSG
255
- [Warning] After upgrading RailsAdmin to 3.x you haven't set asset_source yet, using :#{detected} as the default.
256
- To suppress this message, run 'rails rails_admin:install' to setup the asset delivery method suitable to you.
257
- MSG
258
- end
259
- detected
260
- end
254
+ @asset_source = RailsAdmin::Support::AssetSource.resolve(@asset_source)
261
255
  end
262
256
 
263
257
  def default_hidden_fields=(fields)
@@ -283,14 +277,6 @@ module RailsAdmin
283
277
  end
284
278
  end
285
279
 
286
- def total_columns_width=(_)
287
- ActiveSupport::Deprecation.warn('The total_columns_width configuration option is deprecated and has no effect.')
288
- end
289
-
290
- def sidescroll=(_)
291
- ActiveSupport::Deprecation.warn('The sidescroll configuration option was removed, it is always enabled now.')
292
- end
293
-
294
280
  # Setup actions to be used.
295
281
  def actions(&block)
296
282
  return unless block
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsAdmin
4
+ module Criteria
5
+ # An attribute addressed by name, optionally reached through associations:
6
+ # Path[:name], or Path[:team, :name] for the associated team's name.
7
+ #
8
+ # Fields say what to sort or search by in these terms and stop there. Each
9
+ # adapter decides what that means for its store -- a qualified column and a
10
+ # join, a two-step lookup by id, or a refusal. Before this, fields built
11
+ # "table.column" strings themselves, which forced the Mongoid adapter to
12
+ # take them apart again and guess what the prefix had meant.
13
+ #
14
+ # A path is not the same thing as one of those strings: its segments are
15
+ # association and attribute names, not table and column names. Legacy
16
+ # "table.column" values therefore stay as they are and are handed to the
17
+ # store untouched.
18
+ class Path
19
+ attr_reader :segments
20
+
21
+ def self.[](*segments)
22
+ new(segments)
23
+ end
24
+
25
+ def initialize(segments)
26
+ @segments = Array(segments).map(&:to_sym).freeze
27
+ end
28
+
29
+ # The attribute itself, the last segment of the path.
30
+ def attribute
31
+ segments.last
32
+ end
33
+
34
+ # The associations to travel through to reach the attribute, if any.
35
+ def associations
36
+ segments[0..-2]
37
+ end
38
+
39
+ # Whether the attribute belongs to the model the path starts from.
40
+ def root?
41
+ segments.size == 1
42
+ end
43
+
44
+ def to_a
45
+ segments
46
+ end
47
+
48
+ def to_s
49
+ segments.join('.')
50
+ end
51
+
52
+ def ==(other)
53
+ other.is_a?(Path) && segments == other.segments
54
+ end
55
+ alias eql? ==
56
+
57
+ def hash
58
+ segments.hash
59
+ end
60
+
61
+ def inspect
62
+ "#<RailsAdmin::Criteria::Path #{self}>"
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsAdmin
4
+ module Criteria
5
+ # Resolves the period a date filter asks for into the two dates bounding it,
6
+ # so that what reaches a store is always absolute. Relative operators such as
7
+ # 'today' or 'this_week' are the config layer's vocabulary; no adapter needs
8
+ # to know them.
9
+ #
10
+ # Everything is read in the application time zone. Reading part of it from
11
+ # the system clock instead would let 'today' and 'yesterday' land on dates
12
+ # that are not a day apart whenever the two disagree.
13
+ class Period
14
+ def initialize(operator, value)
15
+ @operator = operator
16
+ @value = value
17
+ end
18
+
19
+ # [from, to], either of which may be nil for an open-ended period.
20
+ def bounds
21
+ case @operator
22
+ when 'between' then between
23
+ when 'today' then today
24
+ when 'yesterday' then yesterday
25
+ when 'this_week' then this_week
26
+ when 'last_week' then last_week
27
+ else default
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def today
34
+ [Date.current, Date.current]
35
+ end
36
+
37
+ def yesterday
38
+ [Date.current.yesterday, Date.current.yesterday]
39
+ end
40
+
41
+ def this_week
42
+ [Date.current.beginning_of_week, Date.current.end_of_week]
43
+ end
44
+
45
+ def last_week
46
+ last_week_day = Date.current - 1.week
47
+ [last_week_day.beginning_of_week, last_week_day.end_of_week]
48
+ end
49
+
50
+ # The filter UI submits three values, of which the last two are the bounds.
51
+ # A hand-written URL may carry fewer, or none at all, leaving the period
52
+ # open at both ends -- which every caller already handles.
53
+ def between
54
+ bounds = Array.wrap(@value)
55
+ [bounds[1], bounds[2]]
56
+ end
57
+
58
+ def default
59
+ [default_date, default_date]
60
+ end
61
+
62
+ def default_date
63
+ Array.wrap(@value).first
64
+ end
65
+ end
66
+ end
67
+ end
@@ -12,14 +12,16 @@ module RailsAdmin
12
12
  class Engine < Rails::Engine
13
13
  isolate_namespace RailsAdmin
14
14
 
15
- attr_accessor :importmap
16
-
17
15
  config.action_dispatch.rescue_responses['RailsAdmin::ActionNotAllowed'] = :forbidden
18
16
 
19
17
  initializer 'RailsAdmin load UrlForExtension' do
20
18
  RailsAdmin::Engine.routes.singleton_class.prepend(RailsAdmin::Extensions::UrlForExtension)
21
19
  end
22
20
 
21
+ initializer 'RailsAdmin deprecator' do |app|
22
+ app.deprecators[:rails_admin] = RailsAdmin.deprecator if app.respond_to?(:deprecators)
23
+ end
24
+
23
25
  initializer 'RailsAdmin reload config in development' do |app|
24
26
  config.initializer_path = app.root.join('config/initializers/rails_admin.rb')
25
27
 
@@ -40,19 +42,13 @@ module RailsAdmin
40
42
  end
41
43
  end
42
44
 
43
- initializer 'RailsAdmin precompile hook', group: :all do |app|
44
- case RailsAdmin.config.asset_source
45
- when :sprockets
46
- app.config.assets.precompile += %w[
47
- rails_admin/application.js
48
- rails_admin/application.css
49
- ]
50
- app.config.assets.paths << RailsAdmin::Engine.root.join('src')
51
- require 'rails_admin/support/es_module_processor'
52
- Sprockets.register_bundle_processor 'application/javascript', RailsAdmin::Support::ESModuleProcessor
53
- when :importmap
54
- self.importmap = Importmap::Map.new.draw(app.root.join('config/importmap.rails_admin.rb'))
55
- end
45
+ initializer 'RailsAdmin assets', group: :all do |app|
46
+ next unless app.config.respond_to?(:assets)
47
+
48
+ builds_path = RailsAdmin::Engine.root.join('app/assets/builds').to_s
49
+ app.config.assets.paths << builds_path unless app.config.assets.paths.include?(builds_path)
50
+
51
+ app.config.assets.precompile += %w[rails_admin.js rails_admin.css] if RailsAdmin.config.asset_source == :sprockets
56
52
  end
57
53
 
58
54
  # Check for required middlewares, users may forget to use them in Rails API mode
@@ -42,9 +42,9 @@ module RailsAdmin
42
42
  # and bulk_delete/destroy actions and should return a scope which limits the records
43
43
  # to those which the user can perform the given action on.
44
44
  def query(_action, abstract_model)
45
- @controller.send(:policy_scope, abstract_model.model.all)
45
+ @controller.send(:policy_scope, abstract_model.scoped)
46
46
  rescue ::Pundit::NotDefinedError
47
- abstract_model.model.all
47
+ abstract_model.scoped
48
48
  end
49
49
 
50
50
  # This is called in the new/create actions to determine the initial attributes for new
@@ -2,6 +2,14 @@
2
2
 
3
3
  module RailsAdmin
4
4
  module Extensions
5
+ # Writes a composite id into a URL for callers that hand url_for the id
6
+ # straight from a record.
7
+ #
8
+ # Everywhere RailsAdmin knows which model an id belongs to it asks that
9
+ # model's repository instead, which is the only place that decides how an id
10
+ # is written down. This is the net underneath: by the time url_for sees the
11
+ # id there is no model left to ask, so it can only fall back to the globally
12
+ # configured serializer.
5
13
  module UrlForExtension
6
14
  def url_for(options, *args)
7
15
  case options[:id]
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsAdmin
4
+ module Support
5
+ # Resolves the value of RailsAdmin.config.asset_source: passes through the
6
+ # supported values, auto-detects when unset, maps deprecated values and
7
+ # rejects the removed ones.
8
+ module AssetSource
9
+ class << self
10
+ def resolve(value)
11
+ return value if value.respond_to?(:call)
12
+
13
+ case value
14
+ when :propshaft, :sprockets, :external
15
+ value
16
+ when nil
17
+ detect
18
+ when :webpack
19
+ warn '[RailsAdmin] config.asset_source = :webpack is deprecated, use :external instead.'
20
+ :external
21
+ when :importmap
22
+ warn '[RailsAdmin] config.asset_source = :importmap is deprecated; RailsAdmin ships a prebuilt bundle now. ' \
23
+ 'Use :propshaft / :sprockets or remove the setting.'
24
+ detect
25
+ when :webpacker, :vite
26
+ raise "config.asset_source = :#{value} is no longer supported. Serve the bundled assets with " \
27
+ ':propshaft / :sprockets, build them yourself with :external, or pass a callable.'
28
+ else
29
+ raise "Unknown config.asset_source: #{value.inspect}"
30
+ end
31
+ end
32
+
33
+ def detect
34
+ return :propshaft if defined?(Propshaft)
35
+ return :sprockets if defined?(Sprockets)
36
+
37
+ :propshaft
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -2,14 +2,40 @@
2
2
 
3
3
  module RailsAdmin
4
4
  module Support
5
+ # Writes the values of a composite primary key as one string, and reads it
6
+ # back.
7
+ #
8
+ # The delimiter is the one ActiveRecord joins a composite id with, so an
9
+ # ordinary key comes out looking exactly like the record's own to_param. A
10
+ # key value is free to contain the delimiter, though, so it is escaped to a
11
+ # form that cannot: the escape character followed by the byte in hex.
12
+ #
13
+ # Doubling the delimiter, which is what this used to do, cannot be undone --
14
+ # ["a_", "b"] and ["a", "_b"] both come out as "a___b". Values holding a
15
+ # delimiter have therefore never round-tripped; nothing else changes shape.
5
16
  module CompositeKeysSerializer
17
+ DELIMITER = '_'
18
+ ESCAPE = '~' # unreserved in a URI, so it survives a path segment as it is
19
+ UNSAFE = /[#{Regexp.escape(ESCAPE + DELIMITER)}]/.freeze
20
+ ESCAPED = /#{Regexp.escape(ESCAPE)}([0-9A-Fa-f]{2})/.freeze
21
+
6
22
  def self.serialize(keys)
7
- keys.map { |key| key&.to_s&.gsub('_', '__') }.join('_')
23
+ keys.map { |key| escape(key) }.join(DELIMITER)
8
24
  end
9
25
 
10
26
  def self.deserialize(string)
11
- string.split('_').map { |key| key&.gsub('__', '_') }
27
+ string.split(DELIMITER, -1).map { |key| unescape(key) }
28
+ end
29
+
30
+ def self.escape(key)
31
+ key&.to_s&.gsub(UNSAFE) { |char| format('%s%02X', ESCAPE, char.ord) }
12
32
  end
33
+
34
+ def self.unescape(key)
35
+ key&.gsub(ESCAPED) { Regexp.last_match(1).hex.chr }
36
+ end
37
+
38
+ private_class_method :escape, :unescape
13
39
  end
14
40
  end
15
41
  end
@@ -9,7 +9,8 @@ module RailsAdmin
9
9
  @associations = []
10
10
  schema ||= {}
11
11
 
12
- return self if (@objects = objects).blank?
12
+ # Not blank?, which loads a relation whole just to find out.
13
+ return self if (@objects = objects).nil? || objects.empty?
13
14
 
14
15
  @model = objects.dup.first.class
15
16
  @abstract_model = RailsAdmin::AbstractModel.new(@model)
@@ -39,13 +40,6 @@ module RailsAdmin
39
40
  end
40
41
 
41
42
  def to_csv(options = {})
42
- if CSV::VERSION == '3.0.2'
43
- raise <<~MSG
44
- CSV library bundled with Ruby 2.6.0 has encoding issue, please upgrade Ruby to 2.6.1 or later.
45
- https://github.com/ruby/csv/issues/62
46
- MSG
47
- end
48
-
49
43
  options = HashWithIndifferentAccess.new(options)
50
44
  encoding_to = Encoding.find(options[:encoding_to]) if options[:encoding_to].present?
51
45
 
@@ -70,12 +64,11 @@ module RailsAdmin
70
64
 
71
65
  def generate_csv_string(options)
72
66
  generator_options = (options[:generator] || {}).symbolize_keys.delete_if { |_, value| value.blank? }
73
- method = @objects.respond_to?(:find_each) ? :find_each : :each
74
67
 
75
68
  CSV.generate(**generator_options) do |csv|
76
69
  csv << generate_csv_header unless options[:skip_header]
77
70
 
78
- @objects.send(method) do |object|
71
+ @abstract_model&.each_record(@objects) do |object|
79
72
  csv << generate_csv_row(object)
80
73
  end
81
74
  end
@@ -2,10 +2,10 @@
2
2
 
3
3
  module RailsAdmin
4
4
  class Version
5
- MAJOR = 3
6
- MINOR = 2
7
- PATCH = 1
8
- PRE = nil
5
+ MAJOR = 4
6
+ MINOR = 0
7
+ PATCH = 0
8
+ PRE = 'beta'
9
9
 
10
10
  class << self
11
11
  # @return [String]
@@ -18,8 +18,9 @@ module RailsAdmin
18
18
  end
19
19
 
20
20
  def actual_js_version
21
- case RailsAdmin.config.asset_source
22
- when :webpacker, :webpack
21
+ # :propshaft / :sprockets serve the bundle shipped in the gem, which always matches.
22
+ # Only an app-built (:external) setup can drift from the gem's expected version.
23
+ if RailsAdmin.config.asset_source == :external
23
24
  js_version_from_node_modules
24
25
  else
25
26
  js
@@ -33,7 +34,7 @@ module RailsAdmin
33
34
  when js
34
35
  # Good
35
36
  when nil
36
- warn "[Warning] Failed to detect RailsAdmin npm package, did you run 'yarn install'?"
37
+ warn '[Warning] Failed to detect the RailsAdmin npm package, did you install it?'
37
38
  else
38
39
  warn <<~MSG
39
40
  [Warning] RailsAdmin npm package version inconsistency detected, expected #{js} but actually used is #{actual_js_version}.
data/lib/rails_admin.rb CHANGED
@@ -38,6 +38,13 @@ module RailsAdmin
38
38
  end
39
39
  end
40
40
 
41
+ # RailsAdmin's own deprecation warnings go through this, so an application can
42
+ # configure or silence them independently. Registered as
43
+ # +app.deprecators[:rails_admin]+ on Rails 7.1+.
44
+ def self.deprecator
45
+ @deprecator ||= ActiveSupport::Deprecation.new('5.0', 'RailsAdmin')
46
+ end
47
+
41
48
  # Backwards-compatible with safe_yaml/load when SafeYAML isn't available.
42
49
  # Evaluates available YAML loaders at boot and creates appropriate method,
43
50
  # so no conditionals are required at runtime.
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :rails_admin do
4
+ desc 'Compile the precompiled assets shipped with the gem (app/assets/builds)'
5
+ task :build_assets do
6
+ Dir.chdir(File.expand_path('../..', __dir__)) do
7
+ abort 'npm is required to build RailsAdmin assets' unless system('npm --version', out: File::NULL)
8
+ abort 'npm ci failed' unless system('npm ci --silent')
9
+ abort 'Asset build failed' unless system('npm run build')
10
+ end
11
+ end
12
+
13
+ desc 'Fail if the committed assets in app/assets/builds are out of date'
14
+ task verify_assets: :build_assets do
15
+ Dir.chdir(File.expand_path('../..', __dir__)) do
16
+ diff = `git diff --stat -- app/assets/builds`
17
+ unless diff.empty?
18
+ warn "app/assets/builds is out of date. Run `npm run build` and commit the result:\n#{diff}"
19
+ abort
20
+ end
21
+ end
22
+ end
23
+ end
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rails_admin",
3
- "version": "3.2.1",
3
+ "version": "4.0.0-beta",
4
4
  "description": "RailsAdmin is a Rails engine that provides an easy-to-use interface for managing your data.",
5
5
  "homepage": "https://github.com/railsadminteam/rails_admin",
6
6
  "license": "MIT",
@@ -8,23 +8,28 @@
8
8
  "files": [
9
9
  "src"
10
10
  ],
11
+ "type": "module",
11
12
  "main": "src/rails_admin/base.js",
13
+ "sass": "src/rails_admin/styles/base.scss",
12
14
  "scripts": {
13
- "link": "yarn link && cd spec/dummy_app && yarn link rails_admin",
14
- "format": "prettier -w ."
15
+ "build": "node bin/build.js",
16
+ "format": "prettier -w .",
17
+ "format:check": "prettier --check ."
15
18
  },
16
19
  "dependencies": {
17
- "@babel/runtime": "^7.16.7",
18
20
  "@fortawesome/fontawesome-free": ">=5.15.0 <7.0.0",
19
- "@hotwired/turbo-rails": "^7.1.0",
21
+ "@hotwired/turbo-rails": "^8.0.21",
20
22
  "@popperjs/core": "^2.11.0",
21
- "@rails/ujs": "^6.1.4-1",
23
+ "@rails/actiontext": "^7.1.0",
24
+ "@rails/activestorage": "^7.1.0",
22
25
  "bootstrap": "^5.1.3",
23
26
  "flatpickr": "^4.6.9",
24
27
  "jquery": "^3.6.0",
25
28
  "jquery-ui": "^1.12.1 <1.14.0"
26
29
  },
27
30
  "devDependencies": {
28
- "prettier": "^2.4.1"
31
+ "esbuild": "^0.25.0",
32
+ "prettier": "^2.4.1",
33
+ "sass": "^1.83.0"
29
34
  }
30
35
  }
@@ -1,7 +1,12 @@
1
- import Rails from "@rails/ujs";
2
1
  import "@hotwired/turbo-rails";
3
- import "./jquery";
4
- import "./vendor/jquery_nested_form";
2
+ import * as ActiveStorage from "@rails/activestorage";
3
+ // The ActionText glue (~5 KB) so rich-text attachment uploads work out of the box.
4
+ // Trix itself is not bundled - widgets.js loads it from a CDN when a <trix-editor>
5
+ // appears without window.Trix. Bundle Trix yourself (asset_source :external) to
6
+ // avoid the CDN entirely.
7
+ import "@rails/actiontext";
8
+ import "./jquery.js";
9
+ import "./vendor/jquery_nested_form.js";
5
10
  import "bootstrap";
6
11
 
7
12
  // These jQuery-UI indirect dependencies need to be preloaded to be used within Import maps
@@ -17,16 +22,14 @@ import "jquery-ui/ui/widget.js";
17
22
  import "jquery-ui/ui/widgets/menu.js";
18
23
  import "jquery-ui/ui/widgets/mouse.js";
19
24
 
20
- import "./abstract-select";
21
- import "./filter-box";
22
- import "./filtering-multiselect";
23
- import "./filtering-select";
24
- import "./nested-form-hooks";
25
- import "./remote-form";
26
- import "./sidescroll";
27
- import "./ui";
28
- import "./widgets";
25
+ import "./abstract-select.js";
26
+ import "./filter-box.js";
27
+ import "./filtering-multiselect.js";
28
+ import "./filtering-select.js";
29
+ import "./nested-form-hooks.js";
30
+ import "./remote-form.js";
31
+ import "./sidescroll.js";
32
+ import "./ui.js";
33
+ import "./widgets.js";
29
34
 
30
- if (!window._rails_loaded) {
31
- Rails.start();
32
- }
35
+ ActiveStorage.start();
@@ -1,6 +1,6 @@
1
1
  import jQuery from "jquery";
2
- import I18n from "./i18n";
3
- import flatpickr from "flatpickr";
2
+ import I18n from "./i18n.js";
3
+ import flatpickr from "./flatpickr.js";
4
4
 
5
5
  (function ($) {
6
6
  var filters;
@@ -102,7 +102,10 @@ import flatpickr from "flatpickr";
102
102
  $(this).attr("selected", true);
103
103
  });
104
104
  if (multiple)
105
- control.find("option[value^=_],option[disabled]").hide();
105
+ control
106
+ .find("option[value^=_],option[disabled]")
107
+ .hide()
108
+ .prop("selected", false);
106
109
  }
107
110
  break;
108
111
  case "citext":
@@ -311,7 +314,7 @@ import flatpickr from "flatpickr";
311
314
  e.preventDefault();
312
315
  $.filters.append(
313
316
  $.extend(
314
- { index: $.now().toString().slice(6, 11) },
317
+ { index: Date.now().toString().slice(6, 11) },
315
318
  $(this).data("options")
316
319
  )
317
320
  );
@@ -1,6 +1,6 @@
1
1
  import jQuery from "jquery";
2
2
  import "jquery-ui/ui/widget.js";
3
- import I18n from "./i18n";
3
+ import I18n from "./i18n.js";
4
4
 
5
5
  (function ($) {
6
6
  $.widget("ra.filteringMultiselect", $.ra.abstractSelect, {