rails_admin_next 1.0.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 (184) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +46 -0
  3. data/LICENSE.md +21 -0
  4. data/README.md +317 -0
  5. data/Rakefile +25 -0
  6. data/app/assets/javascripts/rails_admin/custom/ui.js +1 -0
  7. data/app/assets/stylesheets/rails_admin/framework.css +2375 -0
  8. data/app/assets/stylesheets/rails_admin/skin.css +472 -0
  9. data/app/assets/stylesheets/rails_admin/tokens.css +281 -0
  10. data/app/assets/stylesheets/rails_admin.css +47 -0
  11. data/app/controllers/rails_admin_next/application_controller.rb +98 -0
  12. data/app/controllers/rails_admin_next/main_controller.rb +185 -0
  13. data/app/helpers/rails_admin_next/application_helper.rb +254 -0
  14. data/app/helpers/rails_admin_next/form_builder.rb +173 -0
  15. data/app/helpers/rails_admin_next/main_helper.rb +63 -0
  16. data/app/views/layouts/rails_admin_next/_head.html.erb +29 -0
  17. data/app/views/layouts/rails_admin_next/_navigation.html.erb +18 -0
  18. data/app/views/layouts/rails_admin_next/_secondary_navigation.html.erb +27 -0
  19. data/app/views/layouts/rails_admin_next/_sidebar_navigation.html.erb +6 -0
  20. data/app/views/layouts/rails_admin_next/application.html.erb +42 -0
  21. data/app/views/layouts/rails_admin_next/content.html.erb +22 -0
  22. data/app/views/layouts/rails_admin_next/modal.js.erb +7 -0
  23. data/app/views/rails_admin_next/main/_dashboard_history.html.erb +46 -0
  24. data/app/views/rails_admin_next/main/_delete_notice.html.erb +36 -0
  25. data/app/views/rails_admin_next/main/_form_action_text.html.erb +2 -0
  26. data/app/views/rails_admin_next/main/_form_boolean.html.erb +15 -0
  27. data/app/views/rails_admin_next/main/_form_colorpicker.html.erb +6 -0
  28. data/app/views/rails_admin_next/main/_form_datetime.html.erb +14 -0
  29. data/app/views/rails_admin_next/main/_form_enumeration.html.erb +26 -0
  30. data/app/views/rails_admin_next/main/_form_field.html.erb +2 -0
  31. data/app/views/rails_admin_next/main/_form_file_upload.html.erb +18 -0
  32. data/app/views/rails_admin_next/main/_form_filtering_multiselect.html.erb +20 -0
  33. data/app/views/rails_admin_next/main/_form_filtering_select.html.erb +22 -0
  34. data/app/views/rails_admin_next/main/_form_multiple_file_upload.html.erb +21 -0
  35. data/app/views/rails_admin_next/main/_form_nested_many.html.erb +20 -0
  36. data/app/views/rails_admin_next/main/_form_nested_one.html.erb +19 -0
  37. data/app/views/rails_admin_next/main/_form_polymorphic_association.html.erb +24 -0
  38. data/app/views/rails_admin_next/main/_form_text.html.erb +2 -0
  39. data/app/views/rails_admin_next/main/_pagination.html.erb +54 -0
  40. data/app/views/rails_admin_next/main/_submit_buttons.html.erb +26 -0
  41. data/app/views/rails_admin_next/main/bulk_delete.html.erb +19 -0
  42. data/app/views/rails_admin_next/main/dashboard.html.erb +64 -0
  43. data/app/views/rails_admin_next/main/delete.html.erb +21 -0
  44. data/app/views/rails_admin_next/main/edit.html.erb +3 -0
  45. data/app/views/rails_admin_next/main/export.html.erb +149 -0
  46. data/app/views/rails_admin_next/main/history.html.erb +80 -0
  47. data/app/views/rails_admin_next/main/index.html.erb +186 -0
  48. data/app/views/rails_admin_next/main/new.html.erb +3 -0
  49. data/app/views/rails_admin_next/main/show.html.erb +30 -0
  50. data/config/brakeman.ignore +145 -0
  51. data/config/importmap.rails_admin.rb +29 -0
  52. data/config/initializers/active_record_extensions.rb +21 -0
  53. data/config/locales/rails_admin_next.en.yml +158 -0
  54. data/config/routes.rb +14 -0
  55. data/lib/generators/rails_admin_next/install_generator.rb +39 -0
  56. data/lib/generators/rails_admin_next/templates/initializer.erb +40 -0
  57. data/lib/generators/rails_admin_next/utils.rb +21 -0
  58. data/lib/rails_admin_next/abstract_model.rb +266 -0
  59. data/lib/rails_admin_next/adapters/active_record/association.rb +130 -0
  60. data/lib/rails_admin_next/adapters/active_record/object_extension.rb +13 -0
  61. data/lib/rails_admin_next/adapters/active_record/property.rb +58 -0
  62. data/lib/rails_admin_next/adapters/active_record.rb +371 -0
  63. data/lib/rails_admin_next/config/actions/base.rb +160 -0
  64. data/lib/rails_admin_next/config/actions/bulk_delete.rb +67 -0
  65. data/lib/rails_admin_next/config/actions/dashboard.rb +62 -0
  66. data/lib/rails_admin_next/config/actions/delete.rb +58 -0
  67. data/lib/rails_admin_next/config/actions/edit.rb +56 -0
  68. data/lib/rails_admin_next/config/actions/export.rb +47 -0
  69. data/lib/rails_admin_next/config/actions/history_index.rb +40 -0
  70. data/lib/rails_admin_next/config/actions/history_show.rb +40 -0
  71. data/lib/rails_admin_next/config/actions/index.rb +108 -0
  72. data/lib/rails_admin_next/config/actions/new.rb +69 -0
  73. data/lib/rails_admin_next/config/actions/show.rb +36 -0
  74. data/lib/rails_admin_next/config/actions/show_in_app.rb +37 -0
  75. data/lib/rails_admin_next/config/actions.rb +122 -0
  76. data/lib/rails_admin_next/config/configurable.rb +107 -0
  77. data/lib/rails_admin_next/config/const_load_suppressor.rb +78 -0
  78. data/lib/rails_admin_next/config/fields/association.rb +169 -0
  79. data/lib/rails_admin_next/config/fields/base.rb +391 -0
  80. data/lib/rails_admin_next/config/fields/collection_association.rb +90 -0
  81. data/lib/rails_admin_next/config/fields/factories/action_text.rb +14 -0
  82. data/lib/rails_admin_next/config/fields/factories/active_storage.rb +34 -0
  83. data/lib/rails_admin_next/config/fields/factories/association.rb +32 -0
  84. data/lib/rails_admin_next/config/fields/factories/encrypted_password.rb +28 -0
  85. data/lib/rails_admin_next/config/fields/factories/enum.rb +23 -0
  86. data/lib/rails_admin_next/config/fields/factories/password.rb +19 -0
  87. data/lib/rails_admin_next/config/fields/group.rb +77 -0
  88. data/lib/rails_admin_next/config/fields/singular_association.rb +62 -0
  89. data/lib/rails_admin_next/config/fields/types/action_text.rb +20 -0
  90. data/lib/rails_admin_next/config/fields/types/active_record_enum.rb +62 -0
  91. data/lib/rails_admin_next/config/fields/types/active_storage.rb +70 -0
  92. data/lib/rails_admin_next/config/fields/types/all.rb +32 -0
  93. data/lib/rails_admin_next/config/fields/types/belongs_to_association.rb +48 -0
  94. data/lib/rails_admin_next/config/fields/types/boolean.rb +70 -0
  95. data/lib/rails_admin_next/config/fields/types/citext.rb +15 -0
  96. data/lib/rails_admin_next/config/fields/types/color.rb +43 -0
  97. data/lib/rails_admin_next/config/fields/types/date.rb +38 -0
  98. data/lib/rails_admin_next/config/fields/types/datetime.rb +79 -0
  99. data/lib/rails_admin_next/config/fields/types/decimal.rb +23 -0
  100. data/lib/rails_admin_next/config/fields/types/enum.rb +52 -0
  101. data/lib/rails_admin_next/config/fields/types/file_upload.rb +87 -0
  102. data/lib/rails_admin_next/config/fields/types/float.rb +23 -0
  103. data/lib/rails_admin_next/config/fields/types/has_and_belongs_to_many_association.rb +16 -0
  104. data/lib/rails_admin_next/config/fields/types/has_many_association.rb +16 -0
  105. data/lib/rails_admin_next/config/fields/types/has_one_association.rb +35 -0
  106. data/lib/rails_admin_next/config/fields/types/hidden.rb +31 -0
  107. data/lib/rails_admin_next/config/fields/types/inet.rb +15 -0
  108. data/lib/rails_admin_next/config/fields/types/integer.rb +20 -0
  109. data/lib/rails_admin_next/config/fields/types/json.rb +37 -0
  110. data/lib/rails_admin_next/config/fields/types/multiple_active_storage.rb +82 -0
  111. data/lib/rails_admin_next/config/fields/types/multiple_file_upload.rb +134 -0
  112. data/lib/rails_admin_next/config/fields/types/numeric.rb +24 -0
  113. data/lib/rails_admin_next/config/fields/types/password.rb +45 -0
  114. data/lib/rails_admin_next/config/fields/types/polymorphic_association.rb +118 -0
  115. data/lib/rails_admin_next/config/fields/types/serialized.rb +28 -0
  116. data/lib/rails_admin_next/config/fields/types/string.rb +50 -0
  117. data/lib/rails_admin_next/config/fields/types/string_like.rb +25 -0
  118. data/lib/rails_admin_next/config/fields/types/text.rb +28 -0
  119. data/lib/rails_admin_next/config/fields/types/time.rb +39 -0
  120. data/lib/rails_admin_next/config/fields/types/timestamp.rb +16 -0
  121. data/lib/rails_admin_next/config/fields/types/uuid.rb +15 -0
  122. data/lib/rails_admin_next/config/fields/types.rb +29 -0
  123. data/lib/rails_admin_next/config/fields.rb +88 -0
  124. data/lib/rails_admin_next/config/groupable.rb +20 -0
  125. data/lib/rails_admin_next/config/has_description.rb +14 -0
  126. data/lib/rails_admin_next/config/has_fields.rb +144 -0
  127. data/lib/rails_admin_next/config/has_groups.rb +27 -0
  128. data/lib/rails_admin_next/config/hideable.rb +30 -0
  129. data/lib/rails_admin_next/config/inspectable.rb +39 -0
  130. data/lib/rails_admin_next/config/lazy_model.rb +74 -0
  131. data/lib/rails_admin_next/config/model.rb +126 -0
  132. data/lib/rails_admin_next/config/proxyable/proxy.rb +42 -0
  133. data/lib/rails_admin_next/config/proxyable.rb +26 -0
  134. data/lib/rails_admin_next/config/sections/base.rb +36 -0
  135. data/lib/rails_admin_next/config/sections/create.rb +13 -0
  136. data/lib/rails_admin_next/config/sections/edit.rb +13 -0
  137. data/lib/rails_admin_next/config/sections/export.rb +13 -0
  138. data/lib/rails_admin_next/config/sections/list.rb +63 -0
  139. data/lib/rails_admin_next/config/sections/modal.rb +12 -0
  140. data/lib/rails_admin_next/config/sections/nested.rb +12 -0
  141. data/lib/rails_admin_next/config/sections/show.rb +12 -0
  142. data/lib/rails_admin_next/config/sections/update.rb +12 -0
  143. data/lib/rails_admin_next/config/sections.rb +39 -0
  144. data/lib/rails_admin_next/config.rb +436 -0
  145. data/lib/rails_admin_next/engine.rb +91 -0
  146. data/lib/rails_admin_next/extension.rb +35 -0
  147. data/lib/rails_admin_next/extensions/cancancan/authorization_adapter.rb +90 -0
  148. data/lib/rails_admin_next/extensions/cancancan.rb +5 -0
  149. data/lib/rails_admin_next/extensions/controller_extension.rb +8 -0
  150. data/lib/rails_admin_next/extensions/paper_trail/auditing_adapter.rb +192 -0
  151. data/lib/rails_admin_next/extensions/paper_trail.rb +5 -0
  152. data/lib/rails_admin_next/extensions/pundit/authorization_adapter.rb +72 -0
  153. data/lib/rails_admin_next/extensions/pundit.rb +5 -0
  154. data/lib/rails_admin_next/extensions/url_for_extension.rb +15 -0
  155. data/lib/rails_admin_next/icons.rb +143 -0
  156. data/lib/rails_admin_next/support/composite_keys_serializer.rb +15 -0
  157. data/lib/rails_admin_next/support/csv_converter.rb +100 -0
  158. data/lib/rails_admin_next/support/hash_helper.rb +31 -0
  159. data/lib/rails_admin_next/support/paginated_collection.rb +79 -0
  160. data/lib/rails_admin_next/version.rb +17 -0
  161. data/lib/rails_admin_next.rb +72 -0
  162. data/lib/tasks/rails_admin.rake +40 -0
  163. data/src/rails_admin/application.js +15 -0
  164. data/src/rails_admin/base.js +12 -0
  165. data/src/rails_admin/controllers/alert_controller.js +28 -0
  166. data/src/rails_admin/controllers/collapse_controller.js +16 -0
  167. data/src/rails_admin/controllers/dropdown_controller.js +110 -0
  168. data/src/rails_admin/controllers/feedback_controller.js +73 -0
  169. data/src/rails_admin/controllers/filter_box_controller.js +346 -0
  170. data/src/rails_admin/controllers/filtering_multiselect_controller.js +386 -0
  171. data/src/rails_admin/controllers/filtering_select_controller.js +509 -0
  172. data/src/rails_admin/controllers/index.js +31 -0
  173. data/src/rails_admin/controllers/modal_controller.js +142 -0
  174. data/src/rails_admin/controllers/nested_form_controller.js +117 -0
  175. data/src/rails_admin/controllers/polymorphic_controller.js +26 -0
  176. data/src/rails_admin/controllers/remote_form_controller.js +173 -0
  177. data/src/rails_admin/controllers/sidescroll_controller.js +23 -0
  178. data/src/rails_admin/dom-ready.js +20 -0
  179. data/src/rails_admin/dom.js +19 -0
  180. data/src/rails_admin/i18n.js +20 -0
  181. data/src/rails_admin/icons.js +64 -0
  182. data/src/rails_admin/interactions.js +221 -0
  183. data/src/rails_admin/select_query.js +65 -0
  184. metadata +346 -0
@@ -0,0 +1,118 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails_admin_next/config/fields/types/belongs_to_association"
4
+
5
+ module RailsAdminNext
6
+ module Config
7
+ module Fields
8
+ module Types
9
+ class PolymorphicAssociation < RailsAdminNext::Config::Fields::Types::BelongsToAssociation
10
+ # Register field type for the type loader
11
+ RailsAdminNext::Config::Fields::Types.register(self)
12
+
13
+ register_instance_option :partial do
14
+ :form_polymorphic_association
15
+ end
16
+
17
+ # Accessor whether association is visible or not. By default
18
+ # association checks that any of the child models are included in
19
+ # configuration.
20
+ register_instance_option :visible? do
21
+ associated_model_config.any?
22
+ end
23
+
24
+ register_instance_option :formatted_value do
25
+ (o = value) && o.send(RailsAdminNext.config(o).object_label_method)
26
+ end
27
+
28
+ register_instance_option :sortable do
29
+ false
30
+ end
31
+
32
+ register_instance_option :searchable do
33
+ false
34
+ end
35
+
36
+ # TODO: not supported yet
37
+ register_instance_option :associated_collection_cache_all do
38
+ false
39
+ end
40
+
41
+ # TODO: not supported yet
42
+ register_instance_option :associated_collection_scope do
43
+ nil
44
+ end
45
+
46
+ register_instance_option :allowed_methods do
47
+ [children_fields]
48
+ end
49
+
50
+ register_instance_option :eager_load do
51
+ false
52
+ end
53
+
54
+ def associated_model_config
55
+ @associated_model_config ||= association.klass.collect { |type| RailsAdminNext.config(type) }.reject(&:excluded?)
56
+ end
57
+
58
+ def collection(_scope = nil)
59
+ if value
60
+ [[formatted_value, selected_id]]
61
+ else
62
+ [[]]
63
+ end
64
+ end
65
+
66
+ def type_column
67
+ association.foreign_type.to_s
68
+ end
69
+
70
+ def type_collection
71
+ associated_model_config.collect do |config|
72
+ [config.label, config.abstract_model.model.name]
73
+ end
74
+ end
75
+
76
+ def type_urls
77
+ types = associated_model_config.collect do |config|
78
+ [config.abstract_model.model.name, config.abstract_model.to_param]
79
+ end
80
+ ::Hash[*types.collect { |v| [v[0], bindings[:view].index_path(v[1])] }.flatten]
81
+ end
82
+
83
+ # Reader for field's value
84
+ def value
85
+ bindings[:object].send(association.name)
86
+ end
87
+
88
+ def widget_options_for_types
89
+ type_collection.inject({}) do |options, model|
90
+ options.merge(
91
+ model.second.downcase.gsub("::", "-") => {
92
+ xhr: true,
93
+ remote_source: bindings[:view].index_path(model.second.underscore, source_object_id: bindings[:object].id, source_abstract_model: abstract_model.to_param, current_action: bindings[:view].current_action, compact: true),
94
+ float_left: false
95
+ }
96
+ )
97
+ end
98
+ end
99
+
100
+ def widget_options
101
+ widget_options_for_types[selected_type.try(:downcase)] || {float_left: false}
102
+ end
103
+
104
+ def selected_type
105
+ bindings[:object].send(type_column)
106
+ end
107
+
108
+ def parse_input(params)
109
+ if (type_value = params[association.foreign_type.to_sym]).present?
110
+ config = associated_model_config.find { |c| type_value == c.abstract_model.model.name }
111
+ params[association.foreign_type.to_sym] = config.abstract_model.base_class.name if config
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails_admin_next/config/fields/types/text"
4
+
5
+ module RailsAdminNext
6
+ module Config
7
+ module Fields
8
+ module Types
9
+ class Serialized < RailsAdminNext::Config::Fields::Types::Text
10
+ # Register field type for the type loader
11
+ RailsAdminNext::Config::Fields::Types.register(self)
12
+
13
+ register_instance_option :formatted_value do
14
+ RailsAdminNext.yaml_dump(value) unless value.nil?
15
+ end
16
+
17
+ def parse_value(value)
18
+ value.present? ? (RailsAdminNext.yaml_load(value) || nil) : nil
19
+ end
20
+
21
+ def parse_input(params)
22
+ params[name] = parse_value(params[name]) if params[name].is_a?(::String)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails_admin_next/config/fields/types/string_like"
4
+
5
+ module RailsAdminNext
6
+ module Config
7
+ module Fields
8
+ module Types
9
+ class String < StringLike
10
+ RailsAdminNext::Config::Fields::Types.register(self)
11
+
12
+ def input_size
13
+ [50, length.to_i].reject(&:zero?).min
14
+ end
15
+
16
+ register_instance_option :html_attributes do
17
+ {
18
+ required: required?,
19
+ maxlength: length,
20
+ size: input_size
21
+ }
22
+ end
23
+
24
+ def generic_help
25
+ text = "#{required? ? I18n.translate("admin.form.required") : I18n.translate("admin.form.optional")}. "
26
+ if valid_length.present? && valid_length[:is].present?
27
+ text += "#{I18n.translate("admin.form.char_length_of").capitalize} #{valid_length[:is]}."
28
+ else
29
+ max_length = [length, valid_length[:maximum] || nil].compact.min
30
+ min_length = [0, valid_length[:minimum] || nil].compact.max
31
+ if max_length
32
+ text +=
33
+ if min_length == 0
34
+ "#{I18n.translate("admin.form.char_length_up_to").capitalize} #{max_length}."
35
+ else
36
+ "#{I18n.translate("admin.form.char_length_of").capitalize} #{min_length}-#{max_length}."
37
+ end
38
+ end
39
+ end
40
+ text
41
+ end
42
+
43
+ register_instance_option :partial do
44
+ :form_field
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails_admin_next/config/fields/base"
4
+
5
+ module RailsAdminNext
6
+ module Config
7
+ module Fields
8
+ module Types
9
+ class StringLike < RailsAdminNext::Config::Fields::Base
10
+ register_instance_option :filter_operators do
11
+ %w[_discard like not_like is starts_with ends_with] + (required? ? [] : %w[_separator _present _blank])
12
+ end
13
+
14
+ register_instance_option :treat_empty_as_nil? do
15
+ properties.try(:nullable?)
16
+ end
17
+
18
+ def parse_input(params)
19
+ params[name] = params[name].presence if params.key?(name) && treat_empty_as_nil?
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails_admin_next/config/fields/types/string_like"
4
+
5
+ module RailsAdminNext
6
+ module Config
7
+ module Fields
8
+ module Types
9
+ class Text < StringLike
10
+ # Register field type for the type loader
11
+ RailsAdminNext::Config::Fields::Types.register(self)
12
+
13
+ register_instance_option :html_attributes do
14
+ {
15
+ required: required?,
16
+ cols: "48",
17
+ rows: "3"
18
+ }
19
+ end
20
+
21
+ register_instance_option :partial do
22
+ :form_text
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails_admin_next/config/fields/types/datetime"
4
+
5
+ module RailsAdminNext
6
+ module Config
7
+ module Fields
8
+ module Types
9
+ class Time < RailsAdminNext::Config::Fields::Types::Datetime
10
+ RailsAdminNext::Config::Fields::Types.register(self)
11
+
12
+ def parse_value(value)
13
+ parsed = super
14
+ return unless parsed
15
+
16
+ # Anchor to the dummy date in the zone *before* serializing so the wall-clock time is
17
+ # preserved at that date's UTC offset. A native <input type="time"> submits a bare
18
+ # %H:%M:%S, which Time.zone.parse resolves against today — whose DST offset can differ
19
+ # from the time column's 2000-01-01 storage date and would otherwise shift the hour.
20
+ abstract_model.model.type_for_attribute(name.to_s).serialize(parsed.change(year: 2000, month: 1, day: 1))
21
+ end
22
+
23
+ register_instance_option :filter_operators do
24
+ %w[default between] + (required? ? [] : %w[_separator _not_null _null])
25
+ end
26
+
27
+ register_instance_option :strftime_format do
28
+ "%H:%M"
29
+ end
30
+
31
+ # The native <input type="time" step="1"> control accepts and emits %H:%M:%S.
32
+ def form_value
33
+ value&.in_time_zone&.strftime("%H:%M:%S") || form_default_value
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails_admin_next/config/fields/types/datetime"
4
+
5
+ module RailsAdminNext
6
+ module Config
7
+ module Fields
8
+ module Types
9
+ class Timestamp < RailsAdminNext::Config::Fields::Types::Datetime
10
+ # Register field type for the type loader
11
+ RailsAdminNext::Config::Fields::Types.register(self)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails_admin_next/config/fields/types/string"
4
+
5
+ module RailsAdminNext
6
+ module Config
7
+ module Fields
8
+ module Types
9
+ class Uuid < RailsAdminNext::Config::Fields::Types::String
10
+ RailsAdminNext::Config::Fields::Types.register(self)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/string/inflections"
4
+ require "rails_admin_next/config/fields"
5
+ require "rails_admin_next/config/fields/association"
6
+
7
+ module RailsAdminNext
8
+ module Config
9
+ module Fields
10
+ module Types
11
+ @@registry = {}
12
+
13
+ def self.load(type)
14
+ @@registry.fetch(type.to_sym) { raise "Unsupported field datatype: #{type}" }
15
+ end
16
+
17
+ def self.register(type, klass = nil)
18
+ if klass.nil? && type.is_a?(Class)
19
+ klass = type
20
+ type = klass.name.to_s.demodulize.underscore
21
+ end
22
+ @@registry[type.to_sym] = klass
23
+ end
24
+
25
+ require "rails_admin_next/config/fields/types/all"
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsAdminNext
4
+ module Config
5
+ module Fields
6
+ # Default field factory loads fields based on their property type or
7
+ # association type.
8
+ #
9
+ # @see RailsAdminNext::Config::Fields.registry
10
+ mattr_reader :default_factory
11
+ @@default_factory = proc do |parent, properties, fields|
12
+ # If it's an association
13
+ if properties.association?
14
+ association = parent.abstract_model.associations.detect { |a| a.name.to_s == properties.name.to_s }
15
+ field = RailsAdminNext::Config::Fields::Types.load("#{association.polymorphic? ? :polymorphic : properties.type}_association").new(parent, properties.name, association)
16
+ else
17
+ field = RailsAdminNext::Config::Fields::Types.load(properties.type).new(parent, properties.name, properties)
18
+ end
19
+ fields << field
20
+ field
21
+ end
22
+
23
+ # Registry of field factories.
24
+ #
25
+ # Field factory is an anonymous function that receives the parent object,
26
+ # an array of field properties and an array of fields already instantiated.
27
+ #
28
+ # If the factory returns true then that property will not be run through
29
+ # the rest of the registered factories. If it returns false then the
30
+ # arguments will be passed to the next factory.
31
+ #
32
+ # By default a basic factory is registered which loads fields by their
33
+ # database column type. Also a password factory is registered which
34
+ # loads fields if their name is password. A third factory handles
35
+ # encrypted_password columns (used by Devise and similar libs).
36
+ #
37
+ # @see RailsAdminNext::Config::Fields.register_factory
38
+ # @see rails_admin_next/config/fields/factories/password.rb
39
+ # @see rails_admin_next/config/fields/factories/encrypted_password.rb
40
+ @@registry = [@@default_factory]
41
+
42
+ # Build an array of fields by the provided parent object's abstract_model's
43
+ # property and association information. Each property and association is
44
+ # passed to the registered field factories which will populate the fields
45
+ # array that will be returned.
46
+ #
47
+ # @see RailsAdminNext::Config::Fields.registry
48
+ def self.factory(parent)
49
+ fields = []
50
+ # Load fields for all properties (columns)
51
+
52
+ parent.abstract_model.properties.each do |properties|
53
+ # Unless a previous factory has already loaded current field as well
54
+ next if fields.detect { |f| f.name == properties.name }
55
+
56
+ # Loop through factories until one returns true
57
+ @@registry.detect { |factory| factory.call(parent, properties, fields) }
58
+ end
59
+ # Load fields for all associations (relations)
60
+ parent.abstract_model.associations.reject { |a| a.type == :belongs_to }.each do |association| # :belongs_to are created by factory for belongs_to fields
61
+ # Unless a previous factory has already loaded current field as well
62
+ next if fields.detect { |f| f.name == association.name }
63
+
64
+ # Loop through factories until one returns true
65
+ @@registry.detect { |factory| factory.call(parent, association, fields) }
66
+ end
67
+ fields
68
+ end
69
+
70
+ # Register a field factory to be included in the factory stack.
71
+ #
72
+ # Factories are invoked lifo (last in first out).
73
+ #
74
+ # @see RailsAdminNext::Config::Fields.registry
75
+ def self.register_factory(&block)
76
+ @@registry.unshift(block)
77
+ end
78
+ end
79
+ end
80
+ end
81
+
82
+ require "rails_admin_next/config/fields/types"
83
+ require "rails_admin_next/config/fields/factories/password"
84
+ require "rails_admin_next/config/fields/factories/enum"
85
+ require "rails_admin_next/config/fields/factories/encrypted_password"
86
+ require "rails_admin_next/config/fields/factories/active_storage"
87
+ require "rails_admin_next/config/fields/factories/action_text"
88
+ require "rails_admin_next/config/fields/factories/association"
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails_admin_next/config/fields/group"
4
+
5
+ module RailsAdminNext
6
+ module Config
7
+ module Groupable
8
+ # Register a group instance variable and accessor methods for objects
9
+ # extending the has groups mixin. The extended objects must implement
10
+ # reader for a parent object which includes this module.
11
+ #
12
+ # @see RailsAdminNext::Config::HasGroups.group
13
+ # @see RailsAdminNext::Config::Fields::Group
14
+ def group(name = nil)
15
+ @group = parent.group(name) unless name.nil? # setter
16
+ @group ||= parent.group(:default) # getter
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsAdminNext
4
+ module Config
5
+ # Provides accessor and autoregistering of model's description.
6
+ module HasDescription
7
+ attr_reader :description
8
+
9
+ def desc(description, &_block)
10
+ @description ||= description
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,144 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsAdminNext
4
+ module Config
5
+ # Provides accessors and autoregistering of model's fields.
6
+ module HasFields
7
+ # Defines a configuration for a field.
8
+ def field(name, type = nil, add_to_section = true, &block)
9
+ field = _fields.detect { |f| name == f.name }
10
+
11
+ # some fields are hidden by default (belongs_to keys, has_many associations in list views.)
12
+ # unhide them if config specifically defines them
13
+ field.show if field && !field.instance_variable_get("@#{field.name}_registered").is_a?(Proc)
14
+ # Specify field as virtual if type is not specifically set and field was not
15
+ # found in default stack
16
+ if field.nil? && type.nil?
17
+ field = (_fields << RailsAdminNext::Config::Fields::Types.load(:string).new(self, name, nil)).last
18
+
19
+ # Register a custom field type if one is provided and it is different from
20
+ # one found in default stack
21
+ elsif type && type != (field.nil? ? nil : field.type)
22
+ if field
23
+ properties = field.properties
24
+ field = _fields[_fields.index(field)] = RailsAdminNext::Config::Fields::Types.load(type).new(self, name, properties)
25
+ else
26
+ properties = abstract_model.properties.detect { |p| name == p.name }
27
+ field = (_fields << RailsAdminNext::Config::Fields::Types.load(type).new(self, name, properties)).last
28
+ end
29
+ end
30
+
31
+ # If field has not been yet defined add some default properties
32
+ if add_to_section && !field.defined
33
+ field.defined = true
34
+ field.order = _fields.count(&:defined)
35
+ end
36
+
37
+ # If a block has been given evaluate it and sort fields after that
38
+ field.instance_eval(&block) if block
39
+ field
40
+ end
41
+
42
+ # configure field(s) from the default group in a section without changing the original order.
43
+ def configure(name, type = nil, &)
44
+ [*name].each { |field_name| field(field_name, type, false, &) }
45
+ end
46
+
47
+ # include fields by name and apply an optional block to each (through a call to fields),
48
+ # or include fields by conditions if no field names
49
+ def include_fields(*field_names, &)
50
+ if field_names.empty?
51
+ _fields.select { |f| f.instance_eval(&) }.each do |f|
52
+ next if f.defined
53
+
54
+ f.defined = true
55
+ f.order = _fields.count(&:defined)
56
+ end
57
+ else
58
+ fields(*field_names, &)
59
+ end
60
+ end
61
+
62
+ # exclude fields by name or by condition (block)
63
+ def exclude_fields(*field_names, &block)
64
+ block ||= proc { |f| field_names.include?(f.name) }
65
+ _fields.each { |f| f.defined = true } if _fields.select(&:defined).empty?
66
+ _fields.select { |f| f.instance_eval(&block) }.each { |f| f.defined = false }
67
+ end
68
+
69
+ # API candy
70
+ alias_method :exclude_fields_if, :exclude_fields
71
+ alias_method :include_fields_if, :include_fields
72
+
73
+ def include_all_fields
74
+ include_fields_if { true }
75
+ end
76
+
77
+ # Returns all field configurations for the model configuration instance. If no fields
78
+ # have been defined returns all fields. Defined fields are sorted to match their
79
+ # order property. If order was not specified it will match the order in which fields
80
+ # were defined.
81
+ #
82
+ # If a block is passed it will be evaluated in the context of each field
83
+ def fields(*field_names, &block)
84
+ return all_fields if field_names.empty? && !block
85
+
86
+ if field_names.empty?
87
+ defined = _fields.select(&:defined)
88
+ defined = _fields if defined.empty?
89
+ else
90
+ defined = field_names.collect { |field_name| _fields.detect { |f| f.name == field_name } }
91
+ end
92
+ defined.collect do |f|
93
+ unless f.defined
94
+ f.defined = true
95
+ f.order = _fields.count(&:defined)
96
+ end
97
+ f.instance_eval(&block) if block
98
+ f
99
+ end
100
+ end
101
+
102
+ # Defines configuration for fields by their type.
103
+ def fields_of_type(type, &block)
104
+ _fields.select { |f| type == f.type }.map! { |f| f.instance_eval(&block) } if block
105
+ end
106
+
107
+ # Accessor for all fields
108
+ def all_fields
109
+ ((ro_fields = _fields(true)).select(&:defined).presence || ro_fields).collect do |f|
110
+ f.section = self
111
+ f
112
+ end
113
+ end
114
+
115
+ # Get all fields defined as visible, in the correct order.
116
+ def visible_fields
117
+ i = 0
118
+ all_fields.collect { |f| f.with(bindings) }.select(&:visible?).sort_by { |f| [f.order, i += 1] } # stable sort, damn
119
+ end
120
+
121
+ def possible_fields
122
+ _fields(true)
123
+ end
124
+
125
+ protected
126
+
127
+ # Raw fields.
128
+ # Recursively returns parent section's raw fields
129
+ # Duping it if accessed for modification.
130
+ def _fields(readonly = false)
131
+ return @_fields if @_fields
132
+ return @_ro_fields if readonly && @_ro_fields
133
+
134
+ if instance_of?(RailsAdminNext::Config::Sections::Base)
135
+ @_ro_fields = @_fields = RailsAdminNext::Config::Fields.factory(self)
136
+ else
137
+ # parent is RailsAdminNext::Config::Model, recursion is on Section's classes
138
+ @_ro_fields ||= parent.send(self.class.superclass.to_s.underscore.split("/").last)._fields(true).clone.freeze
139
+ end
140
+ readonly ? @_ro_fields : (@_fields ||= @_ro_fields.collect(&:clone))
141
+ end
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails_admin_next/config/fields/group"
4
+
5
+ module RailsAdminNext
6
+ module Config
7
+ module HasGroups
8
+ # Accessor for a group
9
+ #
10
+ # If group with given name does not yet exist it will be created. If a
11
+ # block is passed it will be evaluated in the context of the group
12
+ def group(name, &block)
13
+ group = parent.groups.detect { |g| name == g.name }
14
+ group ||= (parent.groups << RailsAdminNext::Config::Fields::Group.new(self, name)).last
15
+ group.tap { |g| g.section = self }.instance_eval(&block) if block
16
+ group
17
+ end
18
+
19
+ # Reader for groups that are marked as visible
20
+ def visible_groups
21
+ parent.groups.collect { |f| f.section = self; f.with(bindings) }.select(&:visible?).select do |g| # rubocop:disable Style/Semicolon
22
+ g.visible_fields.present?
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsAdminNext
4
+ module Config
5
+ # Defines a visibility configuration
6
+ module Hideable
7
+ # Visibility defaults to true.
8
+ def self.included(klass)
9
+ klass.register_instance_option :visible? do
10
+ !root.try :excluded?
11
+ end
12
+ end
13
+
14
+ # Reader whether object is hidden.
15
+ def hidden?
16
+ !visible
17
+ end
18
+
19
+ # Writer to hide object.
20
+ def hide(&block)
21
+ visible block ? proc { instance_eval(&block) == false } : false
22
+ end
23
+
24
+ # Writer to show field.
25
+ def show(&block)
26
+ visible block || true
27
+ end
28
+ end
29
+ end
30
+ end