rails_admin 3.3.0 → 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 +7 -5
  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 +7 -6
  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 +32 -128
  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
@@ -0,0 +1,148 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_admin/adapters/statement_builder'
4
+
5
+ module RailsAdmin
6
+ module Adapters
7
+ module ActiveRecord
8
+ class StatementBuilder < RailsAdmin::Adapters::StatementBuilder
9
+ def initialize(column, type, value, operator, adapter_name)
10
+ super column, type, value, operator
11
+ @adapter_name = adapter_name
12
+ end
13
+
14
+ protected
15
+
16
+ def unary_operators
17
+ case @type
18
+ when :boolean
19
+ boolean_unary_operators
20
+ when :uuid
21
+ uuid_unary_operators
22
+ when :integer, :decimal, :float
23
+ numeric_unary_operators
24
+ when :enum
25
+ enum_unary_operators
26
+ else
27
+ generic_unary_operators
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def generic_unary_operators
34
+ {
35
+ '_blank' => ["(#{@column} IS NULL OR #{@column} = '')"],
36
+ '_present' => ["(#{@column} IS NOT NULL AND #{@column} != '')"],
37
+ '_null' => ["(#{@column} IS NULL)"],
38
+ '_not_null' => ["(#{@column} IS NOT NULL)"],
39
+ '_empty' => ["(#{@column} = '')"],
40
+ '_not_empty' => ["(#{@column} != '')"],
41
+ }
42
+ end
43
+
44
+ def boolean_unary_operators
45
+ generic_unary_operators.merge(
46
+ '_blank' => ["(#{@column} IS NULL)"],
47
+ '_empty' => ["(#{@column} IS NULL)"],
48
+ '_present' => ["(#{@column} IS NOT NULL)"],
49
+ '_not_empty' => ["(#{@column} IS NOT NULL)"],
50
+ )
51
+ end
52
+ alias_method :numeric_unary_operators, :boolean_unary_operators
53
+ alias_method :uuid_unary_operators, :boolean_unary_operators
54
+ # An enum has no empty member, and comparing one against '' is a type
55
+ # error on stores which check.
56
+ alias_method :enum_unary_operators, :boolean_unary_operators
57
+
58
+ def range_filter(min, max)
59
+ if min && max && min == max
60
+ ["(#{@column} = ?)", min]
61
+ elsif min && max
62
+ ["(#{@column} BETWEEN ? AND ?)", min, max]
63
+ elsif min
64
+ ["(#{@column} >= ?)", min]
65
+ elsif max
66
+ ["(#{@column} <= ?)", max]
67
+ end
68
+ end
69
+
70
+ def build_statement_for_type
71
+ case @type
72
+ when :boolean then build_statement_for_boolean
73
+ when :integer, :decimal, :float then build_statement_for_integer_decimal_or_float
74
+ when :string, :text, :citext then build_statement_for_string_or_text
75
+ when :enum then build_statement_for_enum
76
+ when :belongs_to_association then build_statement_for_belongs_to_association
77
+ when :uuid then build_statement_for_uuid
78
+ end
79
+ end
80
+
81
+ def build_statement_for_boolean
82
+ case @value
83
+ when 'false', 'f', '0'
84
+ ["(#{@column} IS NULL OR #{@column} = ?)", false]
85
+ when 'true', 't', '1'
86
+ ["(#{@column} = ?)", true]
87
+ end
88
+ end
89
+
90
+ def column_for_value(value)
91
+ ["(#{@column} = ?)", value]
92
+ end
93
+
94
+ def build_statement_for_belongs_to_association
95
+ return if @value.blank?
96
+
97
+ ["(#{@column} = ?)", @value.to_i] if @value.to_i.to_s == @value
98
+ end
99
+
100
+ def build_statement_for_string_or_text
101
+ return if @value.blank?
102
+
103
+ return ["(#{@column} = ?)", @value] if ['is', '='].include?(@operator)
104
+
105
+ @value = @value.downcase unless %w[postgresql postgis].include? ar_adapter
106
+
107
+ @value =
108
+ case @operator
109
+ when 'default', 'like', 'not_like'
110
+ "%#{@value}%"
111
+ when 'starts_with'
112
+ "#{@value}%"
113
+ when 'ends_with'
114
+ "%#{@value}"
115
+ else
116
+ return
117
+ end
118
+
119
+ if %w[postgresql postgis].include? ar_adapter
120
+ if @operator == 'not_like'
121
+ ["(#{@column} NOT ILIKE ?)", @value]
122
+ else
123
+ ["(#{@column} ILIKE ?)", @value]
124
+ end
125
+ elsif @operator == 'not_like'
126
+ ["(LOWER(#{@column}) NOT LIKE ?)", @value]
127
+ else
128
+ ["(LOWER(#{@column}) LIKE ?)", @value]
129
+ end
130
+ end
131
+
132
+ def build_statement_for_enum
133
+ return if @value.blank?
134
+
135
+ ["(#{@column} IN (?))", Array.wrap(@value)]
136
+ end
137
+
138
+ def build_statement_for_uuid
139
+ column_for_value(@value) if /\A[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}\z/.match?(@value.to_s)
140
+ end
141
+
142
+ def ar_adapter
143
+ @adapter_name.downcase
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsAdmin
4
+ module Adapters
5
+ module ActiveRecord
6
+ # Collects the conditions a search or a filter produced into one relation.
7
+ #
8
+ # Statements are OR-ed together and the tables their columns belong to are
9
+ # collected, so that the relation can be told to reference them and the
10
+ # joins actually happen.
11
+ class WhereBuilder
12
+ def initialize(scope, repository)
13
+ @statements = []
14
+ @values = []
15
+ @tables = []
16
+ @scope = scope
17
+ @repository = repository
18
+ end
19
+
20
+ def add(field, value, operator)
21
+ field.searchable_columns.flatten.each do |column_infos|
22
+ column = @repository.search_column(column_infos[:column])
23
+ statement, value1, value2 = StatementBuilder.new(column, column_infos[:type], value, operator, @scope.connection.adapter_name).to_statement
24
+ @statements << statement if statement.present?
25
+ @values << value1 unless value1.nil?
26
+ @values << value2 unless value2.nil?
27
+ table, qualified = column.split('.')
28
+ @tables.push(table) if qualified
29
+ end
30
+ end
31
+
32
+ def build
33
+ scope = @scope.where(@statements.join(' OR '), *@values)
34
+ scope = scope.references(*@tables.uniq) if @tables.any?
35
+ scope
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -1,367 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'active_record'
4
- require 'rails_admin/adapters/active_record/association'
5
- require 'rails_admin/adapters/active_record/object_extension'
6
- require 'rails_admin/adapters/active_record/property'
7
4
 
8
5
  module RailsAdmin
9
6
  module Adapters
10
7
  module ActiveRecord
11
8
  DISABLED_COLUMN_TYPES = %i[tsvector blob binary spatial hstore geometry].freeze
12
-
13
- def new(params = {})
14
- model.new(params).extend(ObjectExtension)
15
- end
16
-
17
- def get(id, scope = scoped)
18
- object = primary_key_scope(scope, id).first
19
- return unless object
20
-
21
- object.extend(ObjectExtension)
22
- end
23
-
24
- def scoped
25
- model.all
26
- end
27
-
28
- def first(options = {}, scope = nil)
29
- all(options, scope).first
30
- end
31
-
32
- def all(options = {}, scope = nil)
33
- scope ||= scoped
34
- scope = scope.includes(options[:include]) if options[:include]
35
- scope = scope.limit(options[:limit]) if options[:limit]
36
- scope = bulk_scope(scope, options) if options[:bulk_ids]
37
- scope = query_scope(scope, options[:query]) if options[:query]
38
- scope = filter_scope(scope, options[:filters]) if options[:filters]
39
- scope = scope.send(Kaminari.config.page_method_name, options[:page]).per(options[:per]) if options[:page] && options[:per]
40
- scope = sort_scope(scope, options) if options[:sort]
41
- scope
42
- end
43
-
44
- def count(options = {}, scope = nil)
45
- all(options.merge(limit: false, page: false), scope).count(:all)
46
- end
47
-
48
- def destroy(objects)
49
- Array.wrap(objects).each(&:destroy)
50
- end
51
-
52
- def associations
53
- model.reflect_on_all_associations.collect do |association|
54
- Association.new(association, model)
55
- end
56
- end
57
-
58
- def properties
59
- columns = model.columns.reject do |c|
60
- c.type.blank? ||
61
- DISABLED_COLUMN_TYPES.include?(c.type.to_sym) ||
62
- c.try(:array)
63
- end
64
- columns.collect do |property|
65
- Property.new(property, model)
66
- end
67
- end
68
-
69
- def base_class
70
- model.base_class
71
- end
72
-
73
- delegate :primary_key, :table_name, to: :model, prefix: false
74
-
75
- def quoted_table_name
76
- model.quoted_table_name
77
- end
78
-
79
- def quote_column_name(name)
80
- model.connection.quote_column_name(name)
81
- end
82
-
83
- def encoding
84
- adapter =
85
- if ::ActiveRecord::Base.respond_to?(:connection_db_config)
86
- ::ActiveRecord::Base.connection_db_config.configuration_hash[:adapter]
87
- else
88
- ::ActiveRecord::Base.connection_config[:adapter]
89
- end
90
- case adapter
91
- when 'postgresql'
92
- ::ActiveRecord::Base.connection.select_one("SELECT ''::text AS str;").values.first.encoding
93
- when 'mysql2'
94
- if RUBY_ENGINE == 'jruby'
95
- ::ActiveRecord::Base.connection.select_one("SELECT '' AS str;").values.first.encoding
96
- else
97
- ::ActiveRecord::Base.connection.raw_connection.encoding
98
- end
99
- when 'oracle_enhanced'
100
- ::ActiveRecord::Base.connection.select_one('SELECT dummy FROM DUAL').values.first.encoding
101
- else
102
- ::ActiveRecord::Base.connection.select_one("SELECT '' AS str;").values.first.encoding
103
- end
104
- end
105
-
106
- def embedded?
107
- false
108
- end
109
-
110
- def cyclic?
111
- false
112
- end
113
-
114
- def adapter_supports_joins?
115
- true
116
- end
117
-
118
- def format_id(id)
119
- if primary_key.is_a? Array
120
- RailsAdmin.config.composite_keys_serializer.serialize(id)
121
- else
122
- id
123
- end
124
- end
125
-
126
- def parse_id(id)
127
- if primary_key.is_a?(Array)
128
- ids = RailsAdmin.config.composite_keys_serializer.deserialize(id)
129
- primary_key.each_with_index do |key, i|
130
- ids[i] = model.type_for_attribute(key).cast(ids[i])
131
- end
132
- ids
133
- else
134
- id
135
- end
136
- end
137
-
138
- private
139
-
140
- def primary_key_scope(scope, id)
141
- if primary_key.is_a? Array
142
- scope.where(primary_key.zip(parse_id(id)).to_h)
143
- else
144
- scope.where(primary_key => id)
145
- end
146
- end
147
-
148
- def bulk_scope(scope, options)
149
- if primary_key.is_a? Array
150
- options[:bulk_ids].map { |id| primary_key_scope(scope, id) }.reduce(&:or)
151
- else
152
- scope.where(primary_key => options[:bulk_ids])
153
- end
154
- end
155
-
156
- def sort_scope(scope, options)
157
- direction = options[:sort_reverse] ? :asc : :desc
158
- case options[:sort]
159
- when String, Symbol
160
- scope.reorder("#{options[:sort]} #{direction}")
161
- when Array
162
- scope.reorder(options[:sort].zip(Array.new(options[:sort].size) { direction }).to_h)
163
- when Hash
164
- scope.reorder(options[:sort].map { |table_name, column| "#{table_name}.#{column}" }.
165
- zip(Array.new(options[:sort].size) { direction }).to_h)
166
- else
167
- raise ArgumentError.new("Unsupported sort value: #{options[:sort]}")
168
- end
169
- end
170
-
171
- class WhereBuilder
172
- def initialize(scope)
173
- @statements = []
174
- @values = []
175
- @tables = []
176
- @scope = scope
177
- end
178
-
179
- def add(field, value, operator)
180
- field.searchable_columns.flatten.each do |column_infos|
181
- statement, value1, value2 = StatementBuilder.new(column_infos[:column], column_infos[:type], value, operator, @scope.connection.adapter_name).to_statement
182
- @statements << statement if statement.present?
183
- @values << value1 unless value1.nil?
184
- @values << value2 unless value2.nil?
185
- table, column = column_infos[:column].split('.')
186
- @tables.push(table) if column
187
- end
188
- end
189
-
190
- def build
191
- scope = @scope.where(@statements.join(' OR '), *@values)
192
- scope = scope.references(*@tables.uniq) if @tables.any?
193
- scope
194
- end
195
- end
196
-
197
- def query_scope(scope, query, fields = config.list.fields.select(&:queryable?))
198
- if config.list.search_by
199
- scope.send(config.list.search_by, query)
200
- else
201
- wb = WhereBuilder.new(scope)
202
- fields.each do |field|
203
- value = parse_field_value(field, query)
204
- wb.add(field, value, field.search_operator)
205
- end
206
- # OR all query statements
207
- wb.build
208
- end
209
- end
210
-
211
- # filters example => {"string_field"=>{"0055"=>{"o"=>"like", "v"=>"test_value"}}, ...}
212
- # "0055" is the filter index, no use here. o is the operator, v the value
213
- def filter_scope(scope, filters, fields = config.list.fields.select(&:filterable?))
214
- filters.each_pair do |field_name, filters_dump|
215
- filters_dump.each_value do |filter_dump|
216
- wb = WhereBuilder.new(scope)
217
- field = fields.detect { |f| f.name.to_s == field_name }
218
- value = parse_field_value(field, filter_dump[:v])
219
-
220
- wb.add(field, value, (filter_dump[:o] || RailsAdmin::Config.default_search_operator))
221
- # AND current filter statements to other filter statements
222
- scope = wb.build
223
- end
224
- end
225
- scope
226
- end
227
-
228
- def build_statement(column, type, value, operator)
229
- StatementBuilder.new(column, type, value, operator, model.connection.adapter_name).to_statement
230
- end
231
-
232
- class StatementBuilder < RailsAdmin::AbstractModel::StatementBuilder
233
- def initialize(column, type, value, operator, adapter_name)
234
- super column, type, value, operator
235
- @adapter_name = adapter_name
236
- end
237
-
238
- protected
239
-
240
- def unary_operators
241
- case @type
242
- when :boolean
243
- boolean_unary_operators
244
- when :uuid
245
- uuid_unary_operators
246
- when :integer, :decimal, :float
247
- numeric_unary_operators
248
- else
249
- generic_unary_operators
250
- end
251
- end
252
-
253
- private
254
-
255
- def generic_unary_operators
256
- {
257
- '_blank' => ["(#{@column} IS NULL OR #{@column} = '')"],
258
- '_present' => ["(#{@column} IS NOT NULL AND #{@column} != '')"],
259
- '_null' => ["(#{@column} IS NULL)"],
260
- '_not_null' => ["(#{@column} IS NOT NULL)"],
261
- '_empty' => ["(#{@column} = '')"],
262
- '_not_empty' => ["(#{@column} != '')"],
263
- }
264
- end
265
-
266
- def boolean_unary_operators
267
- generic_unary_operators.merge(
268
- '_blank' => ["(#{@column} IS NULL)"],
269
- '_empty' => ["(#{@column} IS NULL)"],
270
- '_present' => ["(#{@column} IS NOT NULL)"],
271
- '_not_empty' => ["(#{@column} IS NOT NULL)"],
272
- )
273
- end
274
- alias_method :numeric_unary_operators, :boolean_unary_operators
275
- alias_method :uuid_unary_operators, :boolean_unary_operators
276
-
277
- def range_filter(min, max)
278
- if min && max && min == max
279
- ["(#{@column} = ?)", min]
280
- elsif min && max
281
- ["(#{@column} BETWEEN ? AND ?)", min, max]
282
- elsif min
283
- ["(#{@column} >= ?)", min]
284
- elsif max
285
- ["(#{@column} <= ?)", max]
286
- end
287
- end
288
-
289
- def build_statement_for_type
290
- case @type
291
- when :boolean then build_statement_for_boolean
292
- when :integer, :decimal, :float then build_statement_for_integer_decimal_or_float
293
- when :string, :text, :citext then build_statement_for_string_or_text
294
- when :enum then build_statement_for_enum
295
- when :belongs_to_association then build_statement_for_belongs_to_association
296
- when :uuid then build_statement_for_uuid
297
- end
298
- end
299
-
300
- def build_statement_for_boolean
301
- case @value
302
- when 'false', 'f', '0'
303
- ["(#{@column} IS NULL OR #{@column} = ?)", false]
304
- when 'true', 't', '1'
305
- ["(#{@column} = ?)", true]
306
- end
307
- end
308
-
309
- def column_for_value(value)
310
- ["(#{@column} = ?)", value]
311
- end
312
-
313
- def build_statement_for_belongs_to_association
314
- return if @value.blank?
315
-
316
- ["(#{@column} = ?)", @value.to_i] if @value.to_i.to_s == @value
317
- end
318
-
319
- def build_statement_for_string_or_text
320
- return if @value.blank?
321
-
322
- return ["(#{@column} = ?)", @value] if ['is', '='].include?(@operator)
323
-
324
- @value = @value.mb_chars.downcase unless %w[postgresql postgis].include? ar_adapter
325
-
326
- @value =
327
- case @operator
328
- when 'default', 'like', 'not_like'
329
- "%#{@value}%"
330
- when 'starts_with'
331
- "#{@value}%"
332
- when 'ends_with'
333
- "%#{@value}"
334
- else
335
- return
336
- end
337
-
338
- if %w[postgresql postgis].include? ar_adapter
339
- if @operator == 'not_like'
340
- ["(#{@column} NOT ILIKE ?)", @value]
341
- else
342
- ["(#{@column} ILIKE ?)", @value]
343
- end
344
- elsif @operator == 'not_like'
345
- ["(LOWER(#{@column}) NOT LIKE ?)", @value]
346
- else
347
- ["(LOWER(#{@column}) LIKE ?)", @value]
348
- end
349
- end
350
-
351
- def build_statement_for_enum
352
- return if @value.blank?
353
-
354
- ["(#{@column} IN (?))", Array.wrap(@value)]
355
- end
356
-
357
- def build_statement_for_uuid
358
- column_for_value(@value) if /\A[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}\z/.match?(@value.to_s)
359
- end
360
-
361
- def ar_adapter
362
- @adapter_name.downcase
363
- end
364
- end
365
9
  end
366
10
  end
367
11
  end
12
+
13
+ require 'rails_admin/adapters/active_record/association'
14
+ require 'rails_admin/adapters/active_record/property'
15
+ require 'rails_admin/adapters/active_record/reflection'
16
+ require 'rails_admin/adapters/active_record/repository'
17
+ require 'rails_admin/adapters/active_record/statement_builder'
18
+ require 'rails_admin/adapters/active_record/where_builder'
@@ -111,7 +111,7 @@ module RailsAdmin
111
111
  end
112
112
 
113
113
  def inverse_of
114
- association.inverse_of.try :to_sym
114
+ association.inverse_of.try(:to_sym) || as
115
115
  end
116
116
 
117
117
  def read_only?
@@ -19,11 +19,8 @@ module RailsAdmin
19
19
  end
20
20
  end
21
21
 
22
- def rails_admin_default_object_label_method
23
- new_record? ? "new #{self.class}" : "#{self.class} ##{id}"
24
- end
25
-
26
22
  def safe_send(value)
23
+ RailsAdmin.deprecator.warn('#safe_send is deprecated, please use RailsAdmin::AbstractModel#read.')
27
24
  if attributes.detect { |k, _v| k.to_s == value.to_s }
28
25
  read_attribute(value)
29
26
  else
@@ -32,8 +29,21 @@ module RailsAdmin
32
29
  end
33
30
 
34
31
  module ClassMethods
35
- # Mongoid accepts_nested_attributes_for does not store options in accessible scope,
36
- # so we intercept the call and store it in instance variable which can be accessed from outside
32
+ # Keeps the options of accepts_nested_attributes_for, because Mongoid
33
+ # does not (checked against 9.1). It hands them to the block that
34
+ # defines the setter and keeps nothing: Model.nested_attributes only
35
+ # maps "xs_attributes" to its setter, and the association carries the
36
+ # autosave flag but nothing else.
37
+ #
38
+ # RailsAdmin needs allow_destroy to decide whether a nested form
39
+ # offers to remove an existing record. Guessing it wrong is not
40
+ # something anyone would notice: Mongoid takes _destroy out of the
41
+ # attributes before finding out it is not allowed to destroy, so the
42
+ # record is updated like any other and the row the user removed is
43
+ # simply still there after saving, with nothing raised.
44
+ #
45
+ # The defaults are ActiveRecord's own, so that nested_options has the
46
+ # same shape whichever adapter answered it.
37
47
  def accepts_nested_attributes_for_with_rails_admin(*args)
38
48
  options = args.extract_options!
39
49
  args.each do |arg|
@@ -65,6 +65,13 @@ module RailsAdmin
65
65
  false
66
66
  end
67
67
 
68
+ # @see RailsAdmin::AbstractModel.register_classifier
69
+ def role
70
+ return @role if defined?(@role)
71
+
72
+ @role = RailsAdmin::AbstractModel.classify(model, self)
73
+ end
74
+
68
75
  def read_only?
69
76
  model.readonly_attributes.include? property.name.to_s
70
77
  end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_admin/adapters/reflection'
4
+ require 'rails_admin/adapters/mongoid/association'
5
+ require 'rails_admin/adapters/mongoid/property'
6
+
7
+ module RailsAdmin
8
+ module Adapters
9
+ module Mongoid
10
+ class Reflection < RailsAdmin::Adapters::Reflection
11
+ def primary_key
12
+ '_id'
13
+ end
14
+
15
+ def associations
16
+ model.relations.values.collect do |association|
17
+ Association.new(association, model)
18
+ end
19
+ end
20
+
21
+ def properties
22
+ fields = model.fields.reject { |_name, field| DISABLED_COLUMN_TYPES.include?(field.type.to_s) }
23
+ fields.collect { |_name, field| Property.new(field, model) }
24
+ end
25
+
26
+ def base_class
27
+ klass = model
28
+ klass = klass.superclass while klass.hereditary?
29
+ klass
30
+ end
31
+
32
+ def table_name
33
+ model.collection_name.to_s
34
+ end
35
+
36
+ def encoding
37
+ Encoding::UTF_8
38
+ end
39
+
40
+ def embedded?
41
+ associations.detect { |a| a.macro == :embedded_in }
42
+ end
43
+
44
+ def cyclic?
45
+ model.cyclic?
46
+ end
47
+
48
+ def adapter_supports_joins?
49
+ false
50
+ end
51
+
52
+ def belongs_to_required_by_default
53
+ ::Mongoid.belongs_to_required_by_default
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end