rails_admin 3.3.1 → 4.0.0.beta2

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 (212) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +4 -3
  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 +4 -4
  8. data/app/helpers/rails_admin/application_helper.rb +3 -15
  9. data/app/helpers/rails_admin/form_builder.rb +6 -6
  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/property.rb +7 -0
  28. data/lib/rails_admin/adapters/active_record/reflection.rb +87 -0
  29. data/lib/rails_admin/adapters/active_record/repository.rb +216 -0
  30. data/lib/rails_admin/adapters/active_record/statement_builder.rb +148 -0
  31. data/lib/rails_admin/adapters/active_record/where_builder.rb +40 -0
  32. data/lib/rails_admin/adapters/active_record.rb +7 -360
  33. data/lib/rails_admin/adapters/mongoid/extension.rb +16 -6
  34. data/lib/rails_admin/adapters/mongoid/property.rb +7 -0
  35. data/lib/rails_admin/adapters/mongoid/reflection.rb +58 -0
  36. data/lib/rails_admin/adapters/mongoid/repository.rb +242 -0
  37. data/lib/rails_admin/adapters/mongoid/statement_builder.rb +93 -0
  38. data/lib/rails_admin/adapters/mongoid.rb +7 -305
  39. data/lib/rails_admin/adapters/reflection.rb +105 -0
  40. data/lib/rails_admin/adapters/repository.rb +176 -0
  41. data/lib/rails_admin/adapters/statement_builder.rb +109 -0
  42. data/lib/rails_admin/adapters.rb +94 -0
  43. data/lib/rails_admin/config/actions/base.rb +8 -0
  44. data/lib/rails_admin/config/actions/bulk_delete.rb +4 -4
  45. data/lib/rails_admin/config/actions/dashboard.rb +1 -1
  46. data/lib/rails_admin/config/actions/edit.rb +3 -2
  47. data/lib/rails_admin/config/actions/index.rb +1 -1
  48. data/lib/rails_admin/config/actions/new.rb +3 -2
  49. data/lib/rails_admin/config/actions/show.rb +1 -1
  50. data/lib/rails_admin/config/configurable.rb +3 -2
  51. data/lib/rails_admin/config/fields/association.rb +9 -8
  52. data/lib/rails_admin/config/fields/base.rb +55 -37
  53. data/lib/rails_admin/config/fields/collection_association.rb +2 -2
  54. data/lib/rails_admin/config/fields/factories/carrierwave.rb +35 -20
  55. data/lib/rails_admin/config/fields/factories/dragonfly.rb +30 -18
  56. data/lib/rails_admin/config/fields/factories/enum.rb +14 -8
  57. data/lib/rails_admin/config/fields/factories/paperclip.rb +29 -19
  58. data/lib/rails_admin/config/fields/factories/shrine.rb +19 -8
  59. data/lib/rails_admin/config/fields/singular_association.rb +6 -1
  60. data/lib/rails_admin/config/fields/types/active_record_enum.rb +3 -7
  61. data/lib/rails_admin/config/fields/types/belongs_to_association.rb +8 -4
  62. data/lib/rails_admin/config/fields/types/bson_object_id.rb +1 -1
  63. data/lib/rails_admin/config/fields/types/datetime.rb +0 -4
  64. data/lib/rails_admin/config/fields/types/dragonfly.rb +1 -1
  65. data/lib/rails_admin/config/fields/types/enum.rb +11 -2
  66. data/lib/rails_admin/config/fields/types/multiple_file_upload.rb +0 -2
  67. data/lib/rails_admin/config/fields/types/polymorphic_association.rb +4 -4
  68. data/lib/rails_admin/config/fields/types/time.rb +1 -1
  69. data/lib/rails_admin/config/model.rb +27 -5
  70. data/lib/rails_admin/config/sections/list.rb +0 -8
  71. data/lib/rails_admin/config.rb +7 -21
  72. data/lib/rails_admin/criteria/path.rb +66 -0
  73. data/lib/rails_admin/criteria/period.rb +67 -0
  74. data/lib/rails_admin/engine.rb +11 -15
  75. data/lib/rails_admin/extensions/pundit/authorization_adapter.rb +1 -1
  76. data/lib/rails_admin/extensions/url_for_extension.rb +8 -0
  77. data/lib/rails_admin/support/asset_source.rb +42 -0
  78. data/lib/rails_admin/support/composite_keys_serializer.rb +28 -2
  79. data/lib/rails_admin/support/csv_converter.rb +3 -10
  80. data/lib/rails_admin/support/serialization_schema.rb +1 -1
  81. data/lib/rails_admin/version.rb +8 -7
  82. data/lib/rails_admin.rb +7 -0
  83. data/lib/tasks/assets.rake +23 -0
  84. data/package.json +12 -7
  85. data/src/rails_admin/base.js +18 -15
  86. data/src/rails_admin/filter-box.js +7 -4
  87. data/src/rails_admin/filtering-multiselect.js +1 -1
  88. data/src/rails_admin/filtering-select.js +8 -8
  89. data/src/rails_admin/flatpickr.js +14 -0
  90. data/src/rails_admin/remote-form.js +74 -48
  91. data/src/rails_admin/styles/base/theming.scss +48 -25
  92. data/src/rails_admin/styles/base.scss +25 -1
  93. data/src/rails_admin/styles/filtering-select.scss +23 -62
  94. data/src/rails_admin/ui.js +1 -1
  95. data/src/rails_admin/vendor/jquery_nested_form.js +4 -4
  96. data/src/rails_admin/widgets.js +44 -11
  97. metadata +30 -123
  98. data/app/assets/javascripts/rails_admin/application.js.erb +0 -30
  99. data/app/assets/javascripts/rails_admin/custom/ui.js +0 -1
  100. data/app/assets/stylesheets/rails_admin/application.scss.erb +0 -35
  101. data/app/assets/stylesheets/rails_admin/custom/mixins.scss +0 -11
  102. data/app/assets/stylesheets/rails_admin/custom/theming.scss +0 -13
  103. data/app/assets/stylesheets/rails_admin/custom/variables.scss +0 -15
  104. data/lib/generators/rails_admin/importmap_formatter.rb +0 -29
  105. data/lib/generators/rails_admin/templates/rails_admin.vite.js +0 -2
  106. data/lib/generators/rails_admin/templates/rails_admin.webpacker.js +0 -2
  107. data/lib/rails_admin/adapters/active_record/object_extension.rb +0 -13
  108. data/lib/rails_admin/adapters/mongoid/object_extension.rb +0 -29
  109. data/lib/rails_admin/support/es_module_processor.rb +0 -23
  110. data/vendor/assets/fonts/rails_admin/fa-solid-900.ttf +0 -0
  111. data/vendor/assets/javascripts/rails_admin/bootstrap.js +0 -5046
  112. data/vendor/assets/javascripts/rails_admin/flatpickr-with-locales.js +0 -11246
  113. data/vendor/assets/javascripts/rails_admin/jquery-ui/data.js +0 -41
  114. data/vendor/assets/javascripts/rails_admin/jquery-ui/effect.js +0 -1637
  115. data/vendor/assets/javascripts/rails_admin/jquery-ui/ie.js +0 -17
  116. data/vendor/assets/javascripts/rails_admin/jquery-ui/keycode.js +0 -47
  117. data/vendor/assets/javascripts/rails_admin/jquery-ui/position.js +0 -500
  118. data/vendor/assets/javascripts/rails_admin/jquery-ui/safe-active-element.js +0 -42
  119. data/vendor/assets/javascripts/rails_admin/jquery-ui/scroll-parent.js +0 -47
  120. data/vendor/assets/javascripts/rails_admin/jquery-ui/unique-id.js +0 -51
  121. data/vendor/assets/javascripts/rails_admin/jquery-ui/version.js +0 -17
  122. data/vendor/assets/javascripts/rails_admin/jquery-ui/widget.js +0 -735
  123. data/vendor/assets/javascripts/rails_admin/jquery-ui/widgets/autocomplete.js +0 -689
  124. data/vendor/assets/javascripts/rails_admin/jquery-ui/widgets/menu.js +0 -680
  125. data/vendor/assets/javascripts/rails_admin/jquery-ui/widgets/mouse.js +0 -230
  126. data/vendor/assets/javascripts/rails_admin/jquery-ui/widgets/sortable.js +0 -1561
  127. data/vendor/assets/javascripts/rails_admin/jquery3.js +0 -10881
  128. data/vendor/assets/javascripts/rails_admin/popper.js +0 -1974
  129. data/vendor/assets/stylesheets/rails_admin/bootstrap/_accordion.scss +0 -118
  130. data/vendor/assets/stylesheets/rails_admin/bootstrap/_alert.scss +0 -57
  131. data/vendor/assets/stylesheets/rails_admin/bootstrap/_badge.scss +0 -29
  132. data/vendor/assets/stylesheets/rails_admin/bootstrap/_breadcrumb.scss +0 -28
  133. data/vendor/assets/stylesheets/rails_admin/bootstrap/_button-group.scss +0 -139
  134. data/vendor/assets/stylesheets/rails_admin/bootstrap/_buttons.scss +0 -111
  135. data/vendor/assets/stylesheets/rails_admin/bootstrap/_card.scss +0 -216
  136. data/vendor/assets/stylesheets/rails_admin/bootstrap/_carousel.scss +0 -229
  137. data/vendor/assets/stylesheets/rails_admin/bootstrap/_close.scss +0 -40
  138. data/vendor/assets/stylesheets/rails_admin/bootstrap/_containers.scss +0 -41
  139. data/vendor/assets/stylesheets/rails_admin/bootstrap/_dropdown.scss +0 -240
  140. data/vendor/assets/stylesheets/rails_admin/bootstrap/_forms.scss +0 -9
  141. data/vendor/assets/stylesheets/rails_admin/bootstrap/_functions.scss +0 -302
  142. data/vendor/assets/stylesheets/rails_admin/bootstrap/_grid.scss +0 -33
  143. data/vendor/assets/stylesheets/rails_admin/bootstrap/_helpers.scss +0 -9
  144. data/vendor/assets/stylesheets/rails_admin/bootstrap/_images.scss +0 -42
  145. data/vendor/assets/stylesheets/rails_admin/bootstrap/_list-group.scss +0 -174
  146. data/vendor/assets/stylesheets/rails_admin/bootstrap/_mixins.scss +0 -43
  147. data/vendor/assets/stylesheets/rails_admin/bootstrap/_modal.scss +0 -209
  148. data/vendor/assets/stylesheets/rails_admin/bootstrap/_nav.scss +0 -139
  149. data/vendor/assets/stylesheets/rails_admin/bootstrap/_navbar.scss +0 -335
  150. data/vendor/assets/stylesheets/rails_admin/bootstrap/_offcanvas.scss +0 -83
  151. data/vendor/assets/stylesheets/rails_admin/bootstrap/_pagination.scss +0 -64
  152. data/vendor/assets/stylesheets/rails_admin/bootstrap/_placeholders.scss +0 -51
  153. data/vendor/assets/stylesheets/rails_admin/bootstrap/_popover.scss +0 -158
  154. data/vendor/assets/stylesheets/rails_admin/bootstrap/_progress.scss +0 -48
  155. data/vendor/assets/stylesheets/rails_admin/bootstrap/_reboot.scss +0 -625
  156. data/vendor/assets/stylesheets/rails_admin/bootstrap/_root.scss +0 -54
  157. data/vendor/assets/stylesheets/rails_admin/bootstrap/_spinners.scss +0 -69
  158. data/vendor/assets/stylesheets/rails_admin/bootstrap/_tables.scss +0 -155
  159. data/vendor/assets/stylesheets/rails_admin/bootstrap/_toasts.scss +0 -51
  160. data/vendor/assets/stylesheets/rails_admin/bootstrap/_tooltip.scss +0 -115
  161. data/vendor/assets/stylesheets/rails_admin/bootstrap/_transitions.scss +0 -27
  162. data/vendor/assets/stylesheets/rails_admin/bootstrap/_type.scss +0 -104
  163. data/vendor/assets/stylesheets/rails_admin/bootstrap/_utilities.scss +0 -630
  164. data/vendor/assets/stylesheets/rails_admin/bootstrap/_variables.scss +0 -1641
  165. data/vendor/assets/stylesheets/rails_admin/bootstrap/bootstrap.scss +0 -53
  166. data/vendor/assets/stylesheets/rails_admin/bootstrap/forms/_floating-labels.scss +0 -63
  167. data/vendor/assets/stylesheets/rails_admin/bootstrap/forms/_form-check.scss +0 -152
  168. data/vendor/assets/stylesheets/rails_admin/bootstrap/forms/_form-control.scss +0 -219
  169. data/vendor/assets/stylesheets/rails_admin/bootstrap/forms/_form-range.scss +0 -91
  170. data/vendor/assets/stylesheets/rails_admin/bootstrap/forms/_form-select.scss +0 -72
  171. data/vendor/assets/stylesheets/rails_admin/bootstrap/forms/_form-text.scss +0 -11
  172. data/vendor/assets/stylesheets/rails_admin/bootstrap/forms/_input-group.scss +0 -121
  173. data/vendor/assets/stylesheets/rails_admin/bootstrap/forms/_labels.scss +0 -36
  174. data/vendor/assets/stylesheets/rails_admin/bootstrap/forms/_validation.scss +0 -12
  175. data/vendor/assets/stylesheets/rails_admin/bootstrap/helpers/_clearfix.scss +0 -3
  176. data/vendor/assets/stylesheets/rails_admin/bootstrap/helpers/_colored-links.scss +0 -12
  177. data/vendor/assets/stylesheets/rails_admin/bootstrap/helpers/_position.scss +0 -30
  178. data/vendor/assets/stylesheets/rails_admin/bootstrap/helpers/_ratio.scss +0 -26
  179. data/vendor/assets/stylesheets/rails_admin/bootstrap/helpers/_stacks.scss +0 -15
  180. data/vendor/assets/stylesheets/rails_admin/bootstrap/helpers/_stretched-link.scss +0 -15
  181. data/vendor/assets/stylesheets/rails_admin/bootstrap/helpers/_text-truncation.scss +0 -7
  182. data/vendor/assets/stylesheets/rails_admin/bootstrap/helpers/_visually-hidden.scss +0 -8
  183. data/vendor/assets/stylesheets/rails_admin/bootstrap/helpers/_vr.scss +0 -8
  184. data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_alert.scss +0 -11
  185. data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_backdrop.scss +0 -14
  186. data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_border-radius.scss +0 -78
  187. data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_box-shadow.scss +0 -18
  188. data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_breakpoints.scss +0 -127
  189. data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_buttons.scss +0 -133
  190. data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_caret.scss +0 -64
  191. data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_clearfix.scss +0 -9
  192. data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_color-scheme.scss +0 -7
  193. data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_container.scss +0 -9
  194. data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_deprecate.scss +0 -10
  195. data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_forms.scss +0 -144
  196. data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_gradients.scss +0 -47
  197. data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_grid.scss +0 -151
  198. data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_image.scss +0 -16
  199. data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_list-group.scss +0 -24
  200. data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_lists.scss +0 -7
  201. data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_pagination.scss +0 -31
  202. data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_reset-text.scss +0 -17
  203. data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_resize.scss +0 -6
  204. data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_table-variants.scss +0 -21
  205. data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_text-truncate.scss +0 -8
  206. data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_transition.scss +0 -26
  207. data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_utilities.scss +0 -89
  208. data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_visually-hidden.scss +0 -29
  209. data/vendor/assets/stylesheets/rails_admin/bootstrap/utilities/_api.scss +0 -47
  210. data/vendor/assets/stylesheets/rails_admin/bootstrap/vendor/_rfs.scss +0 -354
  211. data/vendor/assets/stylesheets/rails_admin/flatpickr.css +0 -791
  212. 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,371 +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 none
29
- model.none
30
- end
31
-
32
- def first(options = {}, scope = nil)
33
- all(options, scope).first
34
- end
35
-
36
- def all(options = {}, scope = nil)
37
- scope ||= scoped
38
- scope = scope.includes(options[:include]) if options[:include]
39
- scope = scope.limit(options[:limit]) if options[:limit]
40
- scope = bulk_scope(scope, options) if options[:bulk_ids]
41
- scope = query_scope(scope, options[:query]) if options[:query]
42
- scope = filter_scope(scope, options[:filters]) if options[:filters]
43
- scope = scope.send(Kaminari.config.page_method_name, options[:page]).per(options[:per]) if options[:page] && options[:per]
44
- scope = sort_scope(scope, options) if options[:sort]
45
- scope
46
- end
47
-
48
- def count(options = {}, scope = nil)
49
- all(options.merge(limit: false, page: false), scope).count(:all)
50
- end
51
-
52
- def destroy(objects)
53
- Array.wrap(objects).each(&:destroy)
54
- end
55
-
56
- def associations
57
- model.reflect_on_all_associations.collect do |association|
58
- Association.new(association, model)
59
- end
60
- end
61
-
62
- def properties
63
- columns = model.columns.reject do |c|
64
- c.type.blank? ||
65
- DISABLED_COLUMN_TYPES.include?(c.type.to_sym) ||
66
- c.try(:array)
67
- end
68
- columns.collect do |property|
69
- Property.new(property, model)
70
- end
71
- end
72
-
73
- def base_class
74
- model.base_class
75
- end
76
-
77
- delegate :primary_key, :table_name, to: :model, prefix: false
78
-
79
- def quoted_table_name
80
- model.quoted_table_name
81
- end
82
-
83
- def quote_column_name(name)
84
- model.connection.quote_column_name(name)
85
- end
86
-
87
- def encoding
88
- adapter =
89
- if ::ActiveRecord::Base.respond_to?(:connection_db_config)
90
- ::ActiveRecord::Base.connection_db_config.configuration_hash[:adapter]
91
- else
92
- ::ActiveRecord::Base.connection_config[:adapter]
93
- end
94
- case adapter
95
- when 'postgresql'
96
- ::ActiveRecord::Base.connection.select_one("SELECT ''::text AS str;").values.first.encoding
97
- when 'mysql2'
98
- if RUBY_ENGINE == 'jruby'
99
- ::ActiveRecord::Base.connection.select_one("SELECT '' AS str;").values.first.encoding
100
- else
101
- ::ActiveRecord::Base.connection.raw_connection.encoding
102
- end
103
- when 'oracle_enhanced'
104
- ::ActiveRecord::Base.connection.select_one('SELECT dummy FROM DUAL').values.first.encoding
105
- else
106
- ::ActiveRecord::Base.connection.select_one("SELECT '' AS str;").values.first.encoding
107
- end
108
- end
109
-
110
- def embedded?
111
- false
112
- end
113
-
114
- def cyclic?
115
- false
116
- end
117
-
118
- def adapter_supports_joins?
119
- true
120
- end
121
-
122
- def format_id(id)
123
- if primary_key.is_a? Array
124
- RailsAdmin.config.composite_keys_serializer.serialize(id)
125
- else
126
- id
127
- end
128
- end
129
-
130
- def parse_id(id)
131
- if primary_key.is_a?(Array)
132
- ids = RailsAdmin.config.composite_keys_serializer.deserialize(id)
133
- primary_key.each_with_index do |key, i|
134
- ids[i] = model.type_for_attribute(key).cast(ids[i])
135
- end
136
- ids
137
- else
138
- id
139
- end
140
- end
141
-
142
- private
143
-
144
- def primary_key_scope(scope, id)
145
- if primary_key.is_a? Array
146
- scope.where(primary_key.zip(parse_id(id)).to_h)
147
- else
148
- scope.where(primary_key => id)
149
- end
150
- end
151
-
152
- def bulk_scope(scope, options)
153
- if primary_key.is_a? Array
154
- options[:bulk_ids].map { |id| primary_key_scope(scope, id) }.reduce(&:or)
155
- else
156
- scope.where(primary_key => options[:bulk_ids])
157
- end
158
- end
159
-
160
- def sort_scope(scope, options)
161
- direction = options[:sort_reverse] ? :asc : :desc
162
- case options[:sort]
163
- when String, Symbol
164
- scope.reorder("#{options[:sort]} #{direction}")
165
- when Array
166
- scope.reorder(options[:sort].zip(Array.new(options[:sort].size) { direction }).to_h)
167
- when Hash
168
- scope.reorder(options[:sort].map { |table_name, column| "#{table_name}.#{column}" }.
169
- zip(Array.new(options[:sort].size) { direction }).to_h)
170
- else
171
- raise ArgumentError.new("Unsupported sort value: #{options[:sort]}")
172
- end
173
- end
174
-
175
- class WhereBuilder
176
- def initialize(scope)
177
- @statements = []
178
- @values = []
179
- @tables = []
180
- @scope = scope
181
- end
182
-
183
- def add(field, value, operator)
184
- field.searchable_columns.flatten.each do |column_infos|
185
- statement, value1, value2 = StatementBuilder.new(column_infos[:column], column_infos[:type], value, operator, @scope.connection.adapter_name).to_statement
186
- @statements << statement if statement.present?
187
- @values << value1 unless value1.nil?
188
- @values << value2 unless value2.nil?
189
- table, column = column_infos[:column].split('.')
190
- @tables.push(table) if column
191
- end
192
- end
193
-
194
- def build
195
- scope = @scope.where(@statements.join(' OR '), *@values)
196
- scope = scope.references(*@tables.uniq) if @tables.any?
197
- scope
198
- end
199
- end
200
-
201
- def query_scope(scope, query, fields = config.list.fields.select(&:queryable?))
202
- if config.list.search_by
203
- scope.send(config.list.search_by, query)
204
- else
205
- wb = WhereBuilder.new(scope)
206
- fields.each do |field|
207
- value = parse_field_value(field, query)
208
- wb.add(field, value, field.search_operator)
209
- end
210
- # OR all query statements
211
- wb.build
212
- end
213
- end
214
-
215
- # filters example => {"string_field"=>{"0055"=>{"o"=>"like", "v"=>"test_value"}}, ...}
216
- # "0055" is the filter index, no use here. o is the operator, v the value
217
- def filter_scope(scope, filters, fields = config.list.fields.select(&:filterable?))
218
- filters.each_pair do |field_name, filters_dump|
219
- filters_dump.each_value do |filter_dump|
220
- wb = WhereBuilder.new(scope)
221
- field = fields.detect { |f| f.name.to_s == field_name }
222
- value = parse_field_value(field, filter_dump[:v])
223
-
224
- wb.add(field, value, (filter_dump[:o] || RailsAdmin::Config.default_search_operator))
225
- # AND current filter statements to other filter statements
226
- scope = wb.build
227
- end
228
- end
229
- scope
230
- end
231
-
232
- def build_statement(column, type, value, operator)
233
- StatementBuilder.new(column, type, value, operator, model.connection.adapter_name).to_statement
234
- end
235
-
236
- class StatementBuilder < RailsAdmin::AbstractModel::StatementBuilder
237
- def initialize(column, type, value, operator, adapter_name)
238
- super column, type, value, operator
239
- @adapter_name = adapter_name
240
- end
241
-
242
- protected
243
-
244
- def unary_operators
245
- case @type
246
- when :boolean
247
- boolean_unary_operators
248
- when :uuid
249
- uuid_unary_operators
250
- when :integer, :decimal, :float
251
- numeric_unary_operators
252
- else
253
- generic_unary_operators
254
- end
255
- end
256
-
257
- private
258
-
259
- def generic_unary_operators
260
- {
261
- '_blank' => ["(#{@column} IS NULL OR #{@column} = '')"],
262
- '_present' => ["(#{@column} IS NOT NULL AND #{@column} != '')"],
263
- '_null' => ["(#{@column} IS NULL)"],
264
- '_not_null' => ["(#{@column} IS NOT NULL)"],
265
- '_empty' => ["(#{@column} = '')"],
266
- '_not_empty' => ["(#{@column} != '')"],
267
- }
268
- end
269
-
270
- def boolean_unary_operators
271
- generic_unary_operators.merge(
272
- '_blank' => ["(#{@column} IS NULL)"],
273
- '_empty' => ["(#{@column} IS NULL)"],
274
- '_present' => ["(#{@column} IS NOT NULL)"],
275
- '_not_empty' => ["(#{@column} IS NOT NULL)"],
276
- )
277
- end
278
- alias_method :numeric_unary_operators, :boolean_unary_operators
279
- alias_method :uuid_unary_operators, :boolean_unary_operators
280
-
281
- def range_filter(min, max)
282
- if min && max && min == max
283
- ["(#{@column} = ?)", min]
284
- elsif min && max
285
- ["(#{@column} BETWEEN ? AND ?)", min, max]
286
- elsif min
287
- ["(#{@column} >= ?)", min]
288
- elsif max
289
- ["(#{@column} <= ?)", max]
290
- end
291
- end
292
-
293
- def build_statement_for_type
294
- case @type
295
- when :boolean then build_statement_for_boolean
296
- when :integer, :decimal, :float then build_statement_for_integer_decimal_or_float
297
- when :string, :text, :citext then build_statement_for_string_or_text
298
- when :enum then build_statement_for_enum
299
- when :belongs_to_association then build_statement_for_belongs_to_association
300
- when :uuid then build_statement_for_uuid
301
- end
302
- end
303
-
304
- def build_statement_for_boolean
305
- case @value
306
- when 'false', 'f', '0'
307
- ["(#{@column} IS NULL OR #{@column} = ?)", false]
308
- when 'true', 't', '1'
309
- ["(#{@column} = ?)", true]
310
- end
311
- end
312
-
313
- def column_for_value(value)
314
- ["(#{@column} = ?)", value]
315
- end
316
-
317
- def build_statement_for_belongs_to_association
318
- return if @value.blank?
319
-
320
- ["(#{@column} = ?)", @value.to_i] if @value.to_i.to_s == @value
321
- end
322
-
323
- def build_statement_for_string_or_text
324
- return if @value.blank?
325
-
326
- return ["(#{@column} = ?)", @value] if ['is', '='].include?(@operator)
327
-
328
- @value = @value.mb_chars.downcase unless %w[postgresql postgis].include? ar_adapter
329
-
330
- @value =
331
- case @operator
332
- when 'default', 'like', 'not_like'
333
- "%#{@value}%"
334
- when 'starts_with'
335
- "#{@value}%"
336
- when 'ends_with'
337
- "%#{@value}"
338
- else
339
- return
340
- end
341
-
342
- if %w[postgresql postgis].include? ar_adapter
343
- if @operator == 'not_like'
344
- ["(#{@column} NOT ILIKE ?)", @value]
345
- else
346
- ["(#{@column} ILIKE ?)", @value]
347
- end
348
- elsif @operator == 'not_like'
349
- ["(LOWER(#{@column}) NOT LIKE ?)", @value]
350
- else
351
- ["(LOWER(#{@column}) LIKE ?)", @value]
352
- end
353
- end
354
-
355
- def build_statement_for_enum
356
- return if @value.blank?
357
-
358
- ["(#{@column} IN (?))", Array.wrap(@value)]
359
- end
360
-
361
- def build_statement_for_uuid
362
- column_for_value(@value) if /\A[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}\z/.match?(@value.to_s)
363
- end
364
-
365
- def ar_adapter
366
- @adapter_name.downcase
367
- end
368
- end
369
9
  end
370
10
  end
371
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'
@@ -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