rails_admin 3.2.1 → 4.0.0.beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile +8 -6
- data/README.md +11 -11
- data/Rakefile +3 -11
- data/app/assets/builds/rails_admin.css +10 -0
- data/app/assets/builds/rails_admin.js +34 -0
- data/app/controllers/rails_admin/main_controller.rb +3 -3
- data/app/helpers/rails_admin/application_helper.rb +3 -15
- data/app/helpers/rails_admin/form_builder.rb +10 -9
- data/app/views/layouts/rails_admin/_head.html.erb +10 -27
- data/app/views/layouts/rails_admin/_head_custom.html.erb +3 -0
- data/app/views/layouts/rails_admin/_secondary_navigation.html.erb +1 -1
- data/app/views/rails_admin/main/_dashboard_history.html.erb +1 -1
- data/app/views/rails_admin/main/_delete_notice.html.erb +3 -3
- data/app/views/rails_admin/main/_form_boolean.html.erb +9 -13
- data/app/views/rails_admin/main/_submit_buttons.html.erb +4 -4
- data/app/views/rails_admin/main/bulk_delete.html.erb +2 -2
- data/app/views/rails_admin/main/dashboard.html.erb +3 -3
- data/app/views/rails_admin/main/delete.html.erb +2 -2
- data/app/views/rails_admin/main/export.html.erb +2 -2
- data/app/views/rails_admin/main/history.html.erb +2 -2
- data/app/views/rails_admin/main/index.html.erb +3 -3
- data/config/initializers/active_record_extensions.rb +1 -4
- data/lib/generators/rails_admin/install_generator.rb +37 -92
- data/lib/generators/rails_admin/templates/rails_admin.scss.erb +1 -1
- data/lib/rails_admin/abstract_model.rb +69 -191
- data/lib/rails_admin/adapters/active_record/association.rb +1 -1
- data/lib/rails_admin/adapters/active_record/property.rb +7 -0
- data/lib/rails_admin/adapters/active_record/reflection.rb +87 -0
- data/lib/rails_admin/adapters/active_record/repository.rb +216 -0
- data/lib/rails_admin/adapters/active_record/statement_builder.rb +148 -0
- data/lib/rails_admin/adapters/active_record/where_builder.rb +40 -0
- data/lib/rails_admin/adapters/active_record.rb +7 -356
- data/lib/rails_admin/adapters/mongoid/association.rb +1 -1
- data/lib/rails_admin/adapters/mongoid/extension.rb +16 -6
- data/lib/rails_admin/adapters/mongoid/property.rb +7 -0
- data/lib/rails_admin/adapters/mongoid/reflection.rb +58 -0
- data/lib/rails_admin/adapters/mongoid/repository.rb +237 -0
- data/lib/rails_admin/adapters/mongoid/statement_builder.rb +93 -0
- data/lib/rails_admin/adapters/mongoid.rb +7 -300
- data/lib/rails_admin/adapters/reflection.rb +105 -0
- data/lib/rails_admin/adapters/repository.rb +169 -0
- data/lib/rails_admin/adapters/statement_builder.rb +109 -0
- data/lib/rails_admin/adapters.rb +94 -0
- data/lib/rails_admin/config/actions/base.rb +8 -0
- data/lib/rails_admin/config/actions/bulk_delete.rb +4 -4
- data/lib/rails_admin/config/actions/dashboard.rb +1 -1
- data/lib/rails_admin/config/actions/edit.rb +3 -2
- data/lib/rails_admin/config/actions/index.rb +1 -1
- data/lib/rails_admin/config/actions/new.rb +3 -2
- data/lib/rails_admin/config/actions/show.rb +1 -1
- data/lib/rails_admin/config/configurable.rb +4 -3
- data/lib/rails_admin/config/fields/association.rb +9 -8
- data/lib/rails_admin/config/fields/base.rb +55 -37
- data/lib/rails_admin/config/fields/collection_association.rb +2 -2
- data/lib/rails_admin/config/fields/factories/carrierwave.rb +35 -20
- data/lib/rails_admin/config/fields/factories/dragonfly.rb +30 -18
- data/lib/rails_admin/config/fields/factories/enum.rb +14 -8
- data/lib/rails_admin/config/fields/factories/paperclip.rb +29 -19
- data/lib/rails_admin/config/fields/factories/shrine.rb +19 -8
- data/lib/rails_admin/config/fields/singular_association.rb +6 -1
- data/lib/rails_admin/config/fields/types/active_record_enum.rb +3 -7
- data/lib/rails_admin/config/fields/types/belongs_to_association.rb +8 -4
- data/lib/rails_admin/config/fields/types/bson_object_id.rb +1 -1
- data/lib/rails_admin/config/fields/types/datetime.rb +0 -4
- data/lib/rails_admin/config/fields/types/dragonfly.rb +1 -1
- data/lib/rails_admin/config/fields/types/enum.rb +11 -2
- data/lib/rails_admin/config/fields/types/multiple_file_upload.rb +0 -2
- data/lib/rails_admin/config/fields/types/polymorphic_association.rb +4 -4
- data/lib/rails_admin/config/fields/types/time.rb +1 -1
- data/lib/rails_admin/config/model.rb +27 -5
- data/lib/rails_admin/config/sections/list.rb +0 -8
- data/lib/rails_admin/config.rb +7 -21
- data/lib/rails_admin/criteria/path.rb +66 -0
- data/lib/rails_admin/criteria/period.rb +67 -0
- data/lib/rails_admin/engine.rb +11 -15
- data/lib/rails_admin/extensions/pundit/authorization_adapter.rb +2 -2
- data/lib/rails_admin/extensions/url_for_extension.rb +8 -0
- data/lib/rails_admin/support/asset_source.rb +42 -0
- data/lib/rails_admin/support/composite_keys_serializer.rb +28 -2
- data/lib/rails_admin/support/csv_converter.rb +3 -10
- data/lib/rails_admin/version.rb +8 -7
- data/lib/rails_admin.rb +7 -0
- data/lib/tasks/assets.rake +23 -0
- data/package.json +12 -7
- data/src/rails_admin/base.js +18 -15
- data/src/rails_admin/filter-box.js +7 -4
- data/src/rails_admin/filtering-multiselect.js +1 -1
- data/src/rails_admin/filtering-select.js +8 -8
- data/src/rails_admin/flatpickr.js +14 -0
- data/src/rails_admin/remote-form.js +74 -48
- data/src/rails_admin/styles/base/theming.scss +48 -25
- data/src/rails_admin/styles/base.scss +25 -1
- data/src/rails_admin/styles/filtering-select.scss +23 -62
- data/src/rails_admin/ui.js +1 -1
- data/src/rails_admin/vendor/jquery_nested_form.js +4 -4
- data/src/rails_admin/widgets.js +44 -11
- metadata +34 -130
- data/app/assets/javascripts/rails_admin/application.js.erb +0 -30
- data/app/assets/javascripts/rails_admin/custom/ui.js +0 -1
- data/app/assets/stylesheets/rails_admin/application.scss.erb +0 -35
- data/app/assets/stylesheets/rails_admin/custom/mixins.scss +0 -11
- data/app/assets/stylesheets/rails_admin/custom/theming.scss +0 -13
- data/app/assets/stylesheets/rails_admin/custom/variables.scss +0 -15
- data/lib/generators/rails_admin/importmap_formatter.rb +0 -28
- data/lib/generators/rails_admin/templates/rails_admin.vite.js +0 -2
- data/lib/generators/rails_admin/templates/rails_admin.webpacker.js +0 -2
- data/lib/rails_admin/adapters/active_record/object_extension.rb +0 -13
- data/lib/rails_admin/adapters/active_record.rb.bak +0 -348
- data/lib/rails_admin/adapters/mongoid/object_extension.rb +0 -29
- data/lib/rails_admin/support/es_module_processor.rb +0 -23
- data/vendor/assets/fonts/rails_admin/fa-solid-900.ttf +0 -0
- data/vendor/assets/javascripts/rails_admin/bootstrap.js +0 -5046
- data/vendor/assets/javascripts/rails_admin/flatpickr-with-locales.js +0 -11246
- data/vendor/assets/javascripts/rails_admin/jquery-ui/data.js +0 -41
- data/vendor/assets/javascripts/rails_admin/jquery-ui/effect.js +0 -1637
- data/vendor/assets/javascripts/rails_admin/jquery-ui/ie.js +0 -17
- data/vendor/assets/javascripts/rails_admin/jquery-ui/keycode.js +0 -47
- data/vendor/assets/javascripts/rails_admin/jquery-ui/position.js +0 -500
- data/vendor/assets/javascripts/rails_admin/jquery-ui/safe-active-element.js +0 -42
- data/vendor/assets/javascripts/rails_admin/jquery-ui/scroll-parent.js +0 -47
- data/vendor/assets/javascripts/rails_admin/jquery-ui/unique-id.js +0 -51
- data/vendor/assets/javascripts/rails_admin/jquery-ui/version.js +0 -17
- data/vendor/assets/javascripts/rails_admin/jquery-ui/widget.js +0 -735
- data/vendor/assets/javascripts/rails_admin/jquery-ui/widgets/autocomplete.js +0 -689
- data/vendor/assets/javascripts/rails_admin/jquery-ui/widgets/menu.js +0 -680
- data/vendor/assets/javascripts/rails_admin/jquery-ui/widgets/mouse.js +0 -230
- data/vendor/assets/javascripts/rails_admin/jquery-ui/widgets/sortable.js +0 -1561
- data/vendor/assets/javascripts/rails_admin/jquery3.js +0 -10881
- data/vendor/assets/javascripts/rails_admin/popper.js +0 -1974
- data/vendor/assets/stylesheets/rails_admin/bootstrap/_accordion.scss +0 -118
- data/vendor/assets/stylesheets/rails_admin/bootstrap/_alert.scss +0 -57
- data/vendor/assets/stylesheets/rails_admin/bootstrap/_badge.scss +0 -29
- data/vendor/assets/stylesheets/rails_admin/bootstrap/_breadcrumb.scss +0 -28
- data/vendor/assets/stylesheets/rails_admin/bootstrap/_button-group.scss +0 -139
- data/vendor/assets/stylesheets/rails_admin/bootstrap/_buttons.scss +0 -111
- data/vendor/assets/stylesheets/rails_admin/bootstrap/_card.scss +0 -216
- data/vendor/assets/stylesheets/rails_admin/bootstrap/_carousel.scss +0 -229
- data/vendor/assets/stylesheets/rails_admin/bootstrap/_close.scss +0 -40
- data/vendor/assets/stylesheets/rails_admin/bootstrap/_containers.scss +0 -41
- data/vendor/assets/stylesheets/rails_admin/bootstrap/_dropdown.scss +0 -240
- data/vendor/assets/stylesheets/rails_admin/bootstrap/_forms.scss +0 -9
- data/vendor/assets/stylesheets/rails_admin/bootstrap/_functions.scss +0 -302
- data/vendor/assets/stylesheets/rails_admin/bootstrap/_grid.scss +0 -33
- data/vendor/assets/stylesheets/rails_admin/bootstrap/_helpers.scss +0 -9
- data/vendor/assets/stylesheets/rails_admin/bootstrap/_images.scss +0 -42
- data/vendor/assets/stylesheets/rails_admin/bootstrap/_list-group.scss +0 -174
- data/vendor/assets/stylesheets/rails_admin/bootstrap/_mixins.scss +0 -43
- data/vendor/assets/stylesheets/rails_admin/bootstrap/_modal.scss +0 -209
- data/vendor/assets/stylesheets/rails_admin/bootstrap/_nav.scss +0 -139
- data/vendor/assets/stylesheets/rails_admin/bootstrap/_navbar.scss +0 -335
- data/vendor/assets/stylesheets/rails_admin/bootstrap/_offcanvas.scss +0 -83
- data/vendor/assets/stylesheets/rails_admin/bootstrap/_pagination.scss +0 -64
- data/vendor/assets/stylesheets/rails_admin/bootstrap/_placeholders.scss +0 -51
- data/vendor/assets/stylesheets/rails_admin/bootstrap/_popover.scss +0 -158
- data/vendor/assets/stylesheets/rails_admin/bootstrap/_progress.scss +0 -48
- data/vendor/assets/stylesheets/rails_admin/bootstrap/_reboot.scss +0 -625
- data/vendor/assets/stylesheets/rails_admin/bootstrap/_root.scss +0 -54
- data/vendor/assets/stylesheets/rails_admin/bootstrap/_spinners.scss +0 -69
- data/vendor/assets/stylesheets/rails_admin/bootstrap/_tables.scss +0 -155
- data/vendor/assets/stylesheets/rails_admin/bootstrap/_toasts.scss +0 -51
- data/vendor/assets/stylesheets/rails_admin/bootstrap/_tooltip.scss +0 -115
- data/vendor/assets/stylesheets/rails_admin/bootstrap/_transitions.scss +0 -27
- data/vendor/assets/stylesheets/rails_admin/bootstrap/_type.scss +0 -104
- data/vendor/assets/stylesheets/rails_admin/bootstrap/_utilities.scss +0 -630
- data/vendor/assets/stylesheets/rails_admin/bootstrap/_variables.scss +0 -1641
- data/vendor/assets/stylesheets/rails_admin/bootstrap/bootstrap.scss +0 -53
- data/vendor/assets/stylesheets/rails_admin/bootstrap/forms/_floating-labels.scss +0 -63
- data/vendor/assets/stylesheets/rails_admin/bootstrap/forms/_form-check.scss +0 -152
- data/vendor/assets/stylesheets/rails_admin/bootstrap/forms/_form-control.scss +0 -219
- data/vendor/assets/stylesheets/rails_admin/bootstrap/forms/_form-range.scss +0 -91
- data/vendor/assets/stylesheets/rails_admin/bootstrap/forms/_form-select.scss +0 -72
- data/vendor/assets/stylesheets/rails_admin/bootstrap/forms/_form-text.scss +0 -11
- data/vendor/assets/stylesheets/rails_admin/bootstrap/forms/_input-group.scss +0 -121
- data/vendor/assets/stylesheets/rails_admin/bootstrap/forms/_labels.scss +0 -36
- data/vendor/assets/stylesheets/rails_admin/bootstrap/forms/_validation.scss +0 -12
- data/vendor/assets/stylesheets/rails_admin/bootstrap/helpers/_clearfix.scss +0 -3
- data/vendor/assets/stylesheets/rails_admin/bootstrap/helpers/_colored-links.scss +0 -12
- data/vendor/assets/stylesheets/rails_admin/bootstrap/helpers/_position.scss +0 -30
- data/vendor/assets/stylesheets/rails_admin/bootstrap/helpers/_ratio.scss +0 -26
- data/vendor/assets/stylesheets/rails_admin/bootstrap/helpers/_stacks.scss +0 -15
- data/vendor/assets/stylesheets/rails_admin/bootstrap/helpers/_stretched-link.scss +0 -15
- data/vendor/assets/stylesheets/rails_admin/bootstrap/helpers/_text-truncation.scss +0 -7
- data/vendor/assets/stylesheets/rails_admin/bootstrap/helpers/_visually-hidden.scss +0 -8
- data/vendor/assets/stylesheets/rails_admin/bootstrap/helpers/_vr.scss +0 -8
- data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_alert.scss +0 -11
- data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_backdrop.scss +0 -14
- data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_border-radius.scss +0 -78
- data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_box-shadow.scss +0 -18
- data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_breakpoints.scss +0 -127
- data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_buttons.scss +0 -133
- data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_caret.scss +0 -64
- data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_clearfix.scss +0 -9
- data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_color-scheme.scss +0 -7
- data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_container.scss +0 -9
- data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_deprecate.scss +0 -10
- data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_forms.scss +0 -144
- data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_gradients.scss +0 -47
- data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_grid.scss +0 -151
- data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_image.scss +0 -16
- data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_list-group.scss +0 -24
- data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_lists.scss +0 -7
- data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_pagination.scss +0 -31
- data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_reset-text.scss +0 -17
- data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_resize.scss +0 -6
- data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_table-variants.scss +0 -21
- data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_text-truncate.scss +0 -8
- data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_transition.scss +0 -26
- data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_utilities.scss +0 -89
- data/vendor/assets/stylesheets/rails_admin/bootstrap/mixins/_visually-hidden.scss +0 -29
- data/vendor/assets/stylesheets/rails_admin/bootstrap/utilities/_api.scss +0 -47
- data/vendor/assets/stylesheets/rails_admin/bootstrap/vendor/_rfs.scss +0 -354
- data/vendor/assets/stylesheets/rails_admin/flatpickr.css +0 -791
- data/vendor/assets/stylesheets/rails_admin/font-awesome.scss +0 -6351
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RailsAdmin
|
|
4
|
+
module Adapters
|
|
5
|
+
# The half of an adapter that describes a model: what its attributes and
|
|
6
|
+
# associations are, what the store calls them, and what the model layer says
|
|
7
|
+
# about them.
|
|
8
|
+
#
|
|
9
|
+
# One instance per model, held by the AbstractModel that composes it. A
|
|
10
|
+
# reflection answers questions about the model and never reads or writes a
|
|
11
|
+
# record; the store it does reach for is the schema, not the data.
|
|
12
|
+
class Reflection
|
|
13
|
+
attr_reader :abstract_model
|
|
14
|
+
|
|
15
|
+
def initialize(abstract_model)
|
|
16
|
+
@abstract_model = abstract_model
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
delegate :model, to: :abstract_model
|
|
20
|
+
|
|
21
|
+
# The columns the primary key is made of, always as an array of symbols --
|
|
22
|
+
# a single-column key is an array of one.
|
|
23
|
+
#
|
|
24
|
+
# #primary_key answers in the store's own shape, a name or an array of
|
|
25
|
+
# them, and keeps doing so for callers outside the gem. Everything inside
|
|
26
|
+
# asks in the plural instead, so that a composite key is not a special case
|
|
27
|
+
# each caller has to remember to notice.
|
|
28
|
+
def primary_keys
|
|
29
|
+
Array(primary_key).collect(&:to_sym)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Stores that neither qualify nor quote identifiers use the names as they
|
|
33
|
+
# are; ActiveRecord asks its connection instead.
|
|
34
|
+
def quoted_table_name
|
|
35
|
+
table_name
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def quote_column_name(name)
|
|
39
|
+
name
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def pretty_name
|
|
43
|
+
model.model_name.human
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def human_attribute_name(name)
|
|
47
|
+
model.human_attribute_name(name)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Whether the model makes the given attribute mandatory, either through a
|
|
51
|
+
# validation or through the options of a belongs_to association.
|
|
52
|
+
#
|
|
53
|
+
# +context+ is +:create+, +:update+ or +nil+ and is matched against the
|
|
54
|
+
# +:on+ option of a validation. Adapters may override this when their ORM
|
|
55
|
+
# expresses requiredness differently.
|
|
56
|
+
def attribute_required?(name, context)
|
|
57
|
+
!!(required_by_validation?(name, context) || required_by_association?(name))
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Options of the length validation defined on the given attribute, if any.
|
|
61
|
+
def attribute_length_options(name)
|
|
62
|
+
model.validators_on(name).detect { |validator| validator.kind == :length }.try(&:options) || {}
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# The values of an enum defined on the given attribute, or nil.
|
|
66
|
+
#
|
|
67
|
+
# Only stores with a native enum concept answer this; the rest leave enums
|
|
68
|
+
# to the model, which is what Types::Enum asks about instead.
|
|
69
|
+
def attribute_enum_values(_name)
|
|
70
|
+
nil
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# A throwaway record, for asking what an instance of this model responds to
|
|
74
|
+
# when no real one is at hand.
|
|
75
|
+
#
|
|
76
|
+
# Instantiating one is wasteful and runs the model's initialization, but
|
|
77
|
+
# method_defined? would miss anything served through method_missing, so the
|
|
78
|
+
# original behavior is kept until there is a reason to narrow it.
|
|
79
|
+
def dummy_record
|
|
80
|
+
model.new
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
private
|
|
84
|
+
|
|
85
|
+
def required_by_validation?(name, context)
|
|
86
|
+
model.validators_on(name).detect do |validator|
|
|
87
|
+
!(validator.options[:allow_nil] || validator.options[:allow_blank]) &&
|
|
88
|
+
%i[presence numericality attachment_presence].include?(validator.kind) &&
|
|
89
|
+
(validator.options[:on] == context || validator.options[:on].blank?) &&
|
|
90
|
+
(validator.options[:if].blank? && validator.options[:unless].blank?)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def required_by_association?(name)
|
|
95
|
+
model.reflect_on_all_associations(:belongs_to).detect do |association|
|
|
96
|
+
next unless association.name == name
|
|
97
|
+
|
|
98
|
+
required = association.options[:required] if association.options.key?(:required)
|
|
99
|
+
required = !association.options[:optional] if association.options.key?(:optional) && required.nil?
|
|
100
|
+
required.nil? ? belongs_to_required_by_default : required
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RailsAdmin
|
|
4
|
+
module Adapters
|
|
5
|
+
# The half of an adapter that reaches the store: reading records, writing
|
|
6
|
+
# them, and compiling what the configuration asked for into a query.
|
|
7
|
+
#
|
|
8
|
+
# One instance per model, held by the AbstractModel that composes it. Where a
|
|
9
|
+
# repository needs to know something about the model rather than do something
|
|
10
|
+
# with it, it asks through that facade, so that the two halves of an adapter
|
|
11
|
+
# never have to reach for each other directly.
|
|
12
|
+
class Repository
|
|
13
|
+
attr_reader :abstract_model
|
|
14
|
+
|
|
15
|
+
def initialize(abstract_model)
|
|
16
|
+
@abstract_model = abstract_model
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
delegate :model, :config, :associations, :primary_key, :primary_keys, :table_name,
|
|
20
|
+
:quoted_table_name, :quote_column_name, to: :abstract_model
|
|
21
|
+
|
|
22
|
+
# Yield the records of a scope one at a time, in the order it is sorted in,
|
|
23
|
+
# without holding all of them at once. What an export walks.
|
|
24
|
+
#
|
|
25
|
+
# Anything that is not a scope, such as an array of records, is taken as it
|
|
26
|
+
# comes. A store whose query already streams in order needs nothing more
|
|
27
|
+
# than #each, which is all this does.
|
|
28
|
+
def each_record(scope, &block)
|
|
29
|
+
return to_enum(:each_record, scope) unless block
|
|
30
|
+
|
|
31
|
+
scope.each(&block)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Read one attribute from a record: the value the store holds, rather than
|
|
35
|
+
# what a reader of the same name would return.
|
|
36
|
+
#
|
|
37
|
+
# It began as a way around a column named +format+, which on Rails 3
|
|
38
|
+
# reached Kernel#format when it was sent, because attribute methods were
|
|
39
|
+
# not defined until method_missing had been hit (955cca09, 2011). Rails
|
|
40
|
+
# defines them earlier now and that particular collision is gone, but the
|
|
41
|
+
# rule it left behind is what a model redefining a reader still meets.
|
|
42
|
+
#
|
|
43
|
+
# Adapters say how their store tells an attribute from a method.
|
|
44
|
+
def read(record, name)
|
|
45
|
+
record.send(name)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Persist a record, and whatever else the store needs doing to call it
|
|
49
|
+
# saved. Answers the way the record's own #save does.
|
|
50
|
+
def save(record)
|
|
51
|
+
record.save
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def where(conditions)
|
|
55
|
+
model.where(conditions)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def each_associated_children(object)
|
|
59
|
+
associations.each do |association|
|
|
60
|
+
case association.type
|
|
61
|
+
when :has_one
|
|
62
|
+
child = object.send(association.name)
|
|
63
|
+
yield(association, [child]) if child
|
|
64
|
+
when :has_many
|
|
65
|
+
children = object.send(association.name)
|
|
66
|
+
yield(association, Array.new(children))
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# How a record's id travels through a URL, and back.
|
|
72
|
+
#
|
|
73
|
+
# Only composite keys need more than the id itself, so the default is to
|
|
74
|
+
# hand it over unchanged.
|
|
75
|
+
def format_id(id)
|
|
76
|
+
id
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def parse_id(id)
|
|
80
|
+
id
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Convert a Ruby value into the representation the store holds for the
|
|
84
|
+
# given attribute, and back.
|
|
85
|
+
#
|
|
86
|
+
# Both default to passing the value through. Adapters override them when
|
|
87
|
+
# their ORM has a type system able to do better; the Mongoid adapter does
|
|
88
|
+
# not, so values reach it unconverted.
|
|
89
|
+
def serialize_attribute(_name, value)
|
|
90
|
+
value
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def deserialize_attribute(_name, value)
|
|
94
|
+
value
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Render a sort target as an expression the store understands. Adapters
|
|
98
|
+
# that need qualification or quoting override this; anything already
|
|
99
|
+
# expressed in the store's own terms is handed through.
|
|
100
|
+
def sort_expression(order)
|
|
101
|
+
order.to_s
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Render a search target as the column reference the store understands.
|
|
105
|
+
# Overridden where reaching an associated attribute takes more than the
|
|
106
|
+
# dotted path itself.
|
|
107
|
+
def search_column(target)
|
|
108
|
+
target.to_s
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
private
|
|
112
|
+
|
|
113
|
+
def parse_field_value(field, value)
|
|
114
|
+
value.is_a?(Array) ? value.map { |v| parse_one_value(field, v) } : parse_one_value(field, value)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# What reaches here came from the query string, so a field is regularly
|
|
118
|
+
# handed something it cannot make sense of -- a date out of range, YAML
|
|
119
|
+
# which does not parse. A field which cannot read the term takes no part in
|
|
120
|
+
# the condition, rather than taking the whole page down with it.
|
|
121
|
+
def parse_one_value(field, value)
|
|
122
|
+
field.parse_value(value)
|
|
123
|
+
rescue StandardError
|
|
124
|
+
nil
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# The search box asks every queryable field about the same term. Yields
|
|
128
|
+
# [field, parsed value, operator] so that each adapter is left with turning
|
|
129
|
+
# a condition into a scope and combining them, rather than reimplementing
|
|
130
|
+
# which fields take part and how their values are parsed.
|
|
131
|
+
def each_query_condition(query, fields)
|
|
132
|
+
return to_enum(:each_query_condition, query, fields) unless block_given?
|
|
133
|
+
|
|
134
|
+
fields.each do |field|
|
|
135
|
+
yield field, parse_field_value(field, query), field.search_operator
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Each filter as [field, parsed value, operator].
|
|
140
|
+
#
|
|
141
|
+
# Filters naming a field that does not exist are skipped, and a filter
|
|
142
|
+
# arriving without an operator falls back to the configured default -- the
|
|
143
|
+
# field types that offer no operator in the filter UI submit none.
|
|
144
|
+
#
|
|
145
|
+
# filters looks like {"string_field" => {"0055" => {"o" => "like", "v" => "x"}}}
|
|
146
|
+
# where "0055" is the filter index and has no meaning here.
|
|
147
|
+
#
|
|
148
|
+
# Nothing guarantees that shape: the filters are read straight off the
|
|
149
|
+
# query string, which anyone can write by hand. Anything which is not the
|
|
150
|
+
# nesting the filter UI submits is ignored, so that a hand-written URL
|
|
151
|
+
# leaves the list unfiltered rather than raising.
|
|
152
|
+
def each_filter_condition(filters, fields)
|
|
153
|
+
return to_enum(:each_filter_condition, filters, fields) unless block_given?
|
|
154
|
+
return unless filters.respond_to?(:each_pair)
|
|
155
|
+
|
|
156
|
+
filters.each_pair do |field_name, filters_dump|
|
|
157
|
+
field = fields.detect { |f| f.name.to_s == field_name }
|
|
158
|
+
next unless field && filters_dump.respond_to?(:each_value)
|
|
159
|
+
|
|
160
|
+
filters_dump.each_value do |filter_dump|
|
|
161
|
+
next unless filter_dump.respond_to?(:each_pair)
|
|
162
|
+
|
|
163
|
+
yield field, parse_field_value(field, filter_dump[:v]), (filter_dump[:o] || RailsAdmin::Config.default_search_operator)
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rails_admin/criteria/period'
|
|
4
|
+
|
|
5
|
+
module RailsAdmin
|
|
6
|
+
module Adapters
|
|
7
|
+
# Turns one search or filter condition into whatever the store's query
|
|
8
|
+
# language calls a condition.
|
|
9
|
+
#
|
|
10
|
+
# The parts that are the same everywhere -- discarding a condition, unary
|
|
11
|
+
# operators, numeric parsing, resolving a relative date into a range -- live
|
|
12
|
+
# here; each adapter subclasses this and supplies the rest.
|
|
13
|
+
class StatementBuilder
|
|
14
|
+
def initialize(column, type, value, operator)
|
|
15
|
+
@column = column
|
|
16
|
+
@type = type
|
|
17
|
+
@value = value
|
|
18
|
+
@operator = operator
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def to_statement
|
|
22
|
+
if @value.is_a?(Array) && @value.include?('_discard')
|
|
23
|
+
@value = @value.reject { |v| v == '_discard' }
|
|
24
|
+
return if @value.empty?
|
|
25
|
+
end
|
|
26
|
+
return if [@operator, @value].any? { |v| v == '_discard' }
|
|
27
|
+
|
|
28
|
+
unary_operators[@operator] || unary_operators[@value] ||
|
|
29
|
+
build_statement_for_type_generic
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
protected
|
|
33
|
+
|
|
34
|
+
def filtering_duration
|
|
35
|
+
RailsAdmin::Criteria::Period.new(@operator, @value).bounds
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def build_statement_for_type_generic
|
|
39
|
+
build_statement_for_type || begin
|
|
40
|
+
case @type
|
|
41
|
+
when :date
|
|
42
|
+
build_statement_for_date
|
|
43
|
+
when :datetime, :timestamp, :time
|
|
44
|
+
build_statement_for_datetime_or_timestamp
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def build_statement_for_type
|
|
50
|
+
raise 'You must override build_statement_for_type in your StatementBuilder'
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def build_statement_for_integer_decimal_or_float
|
|
54
|
+
case @value
|
|
55
|
+
when Array
|
|
56
|
+
val, range_begin, range_end = *@value.collect do |v|
|
|
57
|
+
next unless v.to_i.to_s == v || v.to_f.to_s == v
|
|
58
|
+
|
|
59
|
+
@type == :integer ? v.to_i : v.to_f
|
|
60
|
+
end
|
|
61
|
+
case @operator
|
|
62
|
+
when 'between'
|
|
63
|
+
range_filter(range_begin, range_end)
|
|
64
|
+
else
|
|
65
|
+
column_for_value(val) if val
|
|
66
|
+
end
|
|
67
|
+
else
|
|
68
|
+
if @value.to_i.to_s == @value || @value.to_f.to_s == @value
|
|
69
|
+
@type == :integer ? column_for_value(@value.to_i) : column_for_value(@value.to_f)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def build_statement_for_date
|
|
75
|
+
start_date, end_date = filtering_duration
|
|
76
|
+
if start_date
|
|
77
|
+
start_date = begin
|
|
78
|
+
start_date.to_date
|
|
79
|
+
rescue StandardError
|
|
80
|
+
nil
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
if end_date
|
|
84
|
+
end_date = begin
|
|
85
|
+
end_date.to_date
|
|
86
|
+
rescue StandardError
|
|
87
|
+
nil
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
range_filter(start_date, end_date)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def build_statement_for_datetime_or_timestamp
|
|
94
|
+
start_date, end_date = filtering_duration
|
|
95
|
+
start_date = start_date.beginning_of_day if start_date.is_a?(Date)
|
|
96
|
+
end_date = end_date.end_of_day if end_date.is_a?(Date)
|
|
97
|
+
range_filter(start_date, end_date)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def unary_operators
|
|
101
|
+
raise 'You must override unary_operators in your StatementBuilder'
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def range_filter(_min, _max)
|
|
105
|
+
raise 'You must override range_filter in your StatementBuilder'
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RailsAdmin
|
|
4
|
+
# Which adapters exist, and how a model is matched to one of them.
|
|
5
|
+
#
|
|
6
|
+
# An adapter is a name, a way of telling whether a model belongs to it, and
|
|
7
|
+
# the two classes that implement it -- a Reflection and a Repository. Adding
|
|
8
|
+
# one from outside the gem is a call to +register+:
|
|
9
|
+
#
|
|
10
|
+
# RailsAdmin::Adapters.register(
|
|
11
|
+
# :sequel,
|
|
12
|
+
# reflection: RailsAdmin::Adapters::Sequel::Reflection,
|
|
13
|
+
# repository: RailsAdmin::Adapters::Sequel::Repository,
|
|
14
|
+
# ) { |model| model < ::Sequel::Model }
|
|
15
|
+
#
|
|
16
|
+
# The most recently registered adapter is asked first, so a registration can
|
|
17
|
+
# take over from a built-in one.
|
|
18
|
+
module Adapters
|
|
19
|
+
class Registration
|
|
20
|
+
attr_reader :name
|
|
21
|
+
|
|
22
|
+
# +reflection+ and +repository+ may be classes, or the names of classes as
|
|
23
|
+
# strings together with +require_path+. The built-in adapters name theirs,
|
|
24
|
+
# because loading an adapter loads its ORM and an application must not have
|
|
25
|
+
# the ORM it does not use required on its behalf.
|
|
26
|
+
def initialize(name, reflection:, repository:, require_path: nil, &detector)
|
|
27
|
+
@name = name
|
|
28
|
+
@reflection = reflection
|
|
29
|
+
@repository = repository
|
|
30
|
+
@require_path = require_path
|
|
31
|
+
@detector = detector
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Whether this adapter handles the given model. Called for every model in
|
|
35
|
+
# the pool, before anything is loaded, so it must not load the ORM either
|
|
36
|
+
# -- which is why the built-in detectors match on ancestor names rather
|
|
37
|
+
# than on the ancestors themselves.
|
|
38
|
+
def matches?(model)
|
|
39
|
+
@detector.call(model)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def reflection
|
|
43
|
+
@reflection = resolve(@reflection)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def repository
|
|
47
|
+
@repository = resolve(@repository)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def resolve(klass)
|
|
53
|
+
return klass unless klass.is_a?(::String)
|
|
54
|
+
|
|
55
|
+
require @require_path if @require_path
|
|
56
|
+
klass.constantize
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
class << self
|
|
61
|
+
def register(name, **options, &detector)
|
|
62
|
+
registrations.unshift(Registration.new(name, **options, &detector))
|
|
63
|
+
name
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def registrations
|
|
67
|
+
@registrations ||= []
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def detect(model)
|
|
71
|
+
registrations.detect { |registration| registration.matches?(model) }
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
register(
|
|
76
|
+
:active_record,
|
|
77
|
+
require_path: 'rails_admin/adapters/active_record',
|
|
78
|
+
reflection: 'RailsAdmin::Adapters::ActiveRecord::Reflection',
|
|
79
|
+
repository: 'RailsAdmin::Adapters::ActiveRecord::Repository',
|
|
80
|
+
) do |model|
|
|
81
|
+
model.ancestors.collect(&:to_s).include?('ActiveRecord::Base') &&
|
|
82
|
+
!model.abstract_class? && model.table_exists?
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
register(
|
|
86
|
+
:mongoid,
|
|
87
|
+
require_path: 'rails_admin/adapters/mongoid',
|
|
88
|
+
reflection: 'RailsAdmin::Adapters::Mongoid::Reflection',
|
|
89
|
+
repository: 'RailsAdmin::Adapters::Mongoid::Repository',
|
|
90
|
+
) do |model|
|
|
91
|
+
model.ancestors.collect(&:to_s).include?('Mongoid::Document')
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
@@ -149,6 +149,14 @@ module RailsAdmin
|
|
|
149
149
|
|
|
150
150
|
# Off API.
|
|
151
151
|
|
|
152
|
+
# Whether the action can be rendered as a plain <a> link, i.e. visiting
|
|
153
|
+
# its URL performs it. Actions that require a non-GET verb need a
|
|
154
|
+
# button/form instead, so callers render them as text or a disabled
|
|
155
|
+
# link. Tolerates verbs written as `:GET` or `"get"`.
|
|
156
|
+
def linkable?
|
|
157
|
+
http_methods.include?(:get) || http_methods.any? { |verb| verb.to_s.casecmp?('get') }
|
|
158
|
+
end
|
|
159
|
+
|
|
152
160
|
def key
|
|
153
161
|
self.class.key
|
|
154
162
|
end
|
|
@@ -20,7 +20,7 @@ module RailsAdmin
|
|
|
20
20
|
@objects = list_entries(@model_config, :destroy)
|
|
21
21
|
|
|
22
22
|
if @objects.blank?
|
|
23
|
-
flash[:error] = t('admin.flash.error', name: pluralize(0, @model_config.label), action: t('admin.actions.delete.done'))
|
|
23
|
+
flash[:error] = t('admin.flash.error', name: pluralize(0, @model_config.label, @model_config.label_plural), action: t('admin.actions.delete.done'))
|
|
24
24
|
redirect_to index_path
|
|
25
25
|
else
|
|
26
26
|
render @action.template_name
|
|
@@ -44,10 +44,10 @@ module RailsAdmin
|
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
if destroyed.nil?
|
|
47
|
-
flash[:error] = t('admin.flash.error', name: pluralize(0, @model_config.label), action: t('admin.actions.delete.done'))
|
|
47
|
+
flash[:error] = t('admin.flash.error', name: pluralize(0, @model_config.label, @model_config.label_plural), action: t('admin.actions.delete.done'))
|
|
48
48
|
else
|
|
49
|
-
flash[:success] = t('admin.flash.successful', name: pluralize(destroyed.count, @model_config.label), action: t('admin.actions.delete.done')) unless destroyed.empty?
|
|
50
|
-
flash[:error] = t('admin.flash.error', name: pluralize(not_destroyed.count, @model_config.label), action: t('admin.actions.delete.done')) unless not_destroyed.empty?
|
|
49
|
+
flash[:success] = t('admin.flash.successful', name: pluralize(destroyed.count, @model_config.label, @model_config.label_plural), action: t('admin.actions.delete.done')) unless destroyed.empty?
|
|
50
|
+
flash[:error] = t('admin.flash.error', name: pluralize(not_destroyed.count, @model_config.label, @model_config.label_plural), action: t('admin.actions.delete.done')) unless not_destroyed.empty?
|
|
51
51
|
end
|
|
52
52
|
redirect_to back_or_index
|
|
53
53
|
end
|
|
@@ -32,7 +32,7 @@ module RailsAdmin
|
|
|
32
32
|
scope = @authorization_adapter&.query(:index, config.abstract_model)
|
|
33
33
|
current_count = config.abstract_model.count({}, scope)
|
|
34
34
|
@max = current_count > @max ? current_count : @max
|
|
35
|
-
name = config.abstract_model.
|
|
35
|
+
name = config.abstract_model.model_name
|
|
36
36
|
@count[name] = current_count
|
|
37
37
|
@most_recent_created[name] = config.last_created_at
|
|
38
38
|
end
|
|
@@ -26,10 +26,11 @@ module RailsAdmin
|
|
|
26
26
|
elsif request.put? # UPDATE
|
|
27
27
|
sanitize_params_for!(request.xhr? ? :modal : :update)
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
# A form that posted nothing for the model leaves nothing to assign.
|
|
30
|
+
@object.assign_attributes(params[@abstract_model.param_key] || {})
|
|
30
31
|
@authorization_adapter&.authorize(:update, @abstract_model, @object)
|
|
31
32
|
changes = @object.changes
|
|
32
|
-
if @
|
|
33
|
+
if @abstract_model.save(@object)
|
|
33
34
|
@auditing_adapter&.update_object(@object, @abstract_model, _current_user, changes)
|
|
34
35
|
respond_to do |format|
|
|
35
36
|
format.html { redirect_to_on_success }
|
|
@@ -53,7 +53,7 @@ module RailsAdmin
|
|
|
53
53
|
if @association
|
|
54
54
|
@association.collection(@objects).collect { |(label, id)| {id: id, label: label} }
|
|
55
55
|
else
|
|
56
|
-
@objects.collect { |object| {id: object.id.to_s, label:
|
|
56
|
+
@objects.collect { |object| {id: object.id.to_s, label: @model_config.object_label(object).to_s} }
|
|
57
57
|
end
|
|
58
58
|
else
|
|
59
59
|
@objects.to_json(@schema)
|
|
@@ -39,10 +39,11 @@ module RailsAdmin
|
|
|
39
39
|
@object = @abstract_model.new
|
|
40
40
|
sanitize_params_for!(request.xhr? ? :modal : :create)
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
# A form that posted nothing for the model leaves nothing to assign.
|
|
43
|
+
@object.assign_attributes(params[@abstract_model.param_key] || {})
|
|
43
44
|
@authorization_adapter&.authorize(:create, @abstract_model, @object)
|
|
44
45
|
|
|
45
|
-
if @
|
|
46
|
+
if @abstract_model.save(@object)
|
|
46
47
|
@auditing_adapter&.create_object(@object, @abstract_model, _current_user)
|
|
47
48
|
respond_to do |format|
|
|
48
49
|
format.html { redirect_to_on_success }
|
|
@@ -9,7 +9,7 @@ module RailsAdmin
|
|
|
9
9
|
base.send :extend, ClassMethods
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
def has_option?(name) # rubocop:disable Naming/
|
|
12
|
+
def has_option?(name) # rubocop:disable Naming/PredicatePrefix
|
|
13
13
|
options = self.class.instance_variable_get('@config_options')
|
|
14
14
|
options&.key?(name)
|
|
15
15
|
end
|
|
@@ -57,7 +57,8 @@ module RailsAdmin
|
|
|
57
57
|
|
|
58
58
|
# If it's a boolean create an alias for it and remove question mark
|
|
59
59
|
if option_name.end_with?('?')
|
|
60
|
-
|
|
60
|
+
option_name = option_name.chop
|
|
61
|
+
scope.send(:define_method, "#{option_name}?") do
|
|
61
62
|
send(option_name)
|
|
62
63
|
end
|
|
63
64
|
end
|
|
@@ -84,7 +85,7 @@ module RailsAdmin
|
|
|
84
85
|
def register_deprecated_instance_option(option_name, replacement_option_name = nil, scope = self)
|
|
85
86
|
scope.send(:define_method, option_name) do |*args, &block|
|
|
86
87
|
if replacement_option_name
|
|
87
|
-
|
|
88
|
+
RailsAdmin.deprecator.warn("The #{option_name} configuration option is deprecated, please use #{replacement_option_name}.")
|
|
88
89
|
send(replacement_option_name, *args, &block)
|
|
89
90
|
elsif block_given?
|
|
90
91
|
yield
|
|
@@ -21,7 +21,7 @@ module RailsAdmin
|
|
|
21
21
|
[value].flatten.select(&:present?).collect do |associated|
|
|
22
22
|
amc = polymorphic? ? RailsAdmin.config(associated) : associated_model_config # perf optimization for non-polymorphic associations
|
|
23
23
|
am = amc.abstract_model
|
|
24
|
-
wording =
|
|
24
|
+
wording = amc.object_label(associated)
|
|
25
25
|
can_see = !am.embedded? && (show_action = v.action(:show, am, associated))
|
|
26
26
|
can_see ? v.link_to(wording, v.url_for(action: show_action.action_name, model_name: am.to_param, id: associated.id)) : ERB::Util.html_escape(wording)
|
|
27
27
|
end.to_sentence.html_safe.presence || '-'
|
|
@@ -36,7 +36,7 @@ module RailsAdmin
|
|
|
36
36
|
|
|
37
37
|
# use the association name as a key, not the association key anymore!
|
|
38
38
|
register_instance_option :label do
|
|
39
|
-
(@label ||= {})[::I18n.locale] ||= abstract_model.
|
|
39
|
+
(@label ||= {})[::I18n.locale] ||= abstract_model.human_attribute_name association.name
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
# scope for possible associable records
|
|
@@ -137,7 +137,7 @@ module RailsAdmin
|
|
|
137
137
|
# Returns collection of all selectable records
|
|
138
138
|
def collection(scope = nil)
|
|
139
139
|
(scope || bindings[:controller].list_entries(associated_model_config, :index, associated_collection_scope, false)).
|
|
140
|
-
map { |o| [
|
|
140
|
+
map { |o| [associated_model_config.object_label(o), format_key(o.send(associated_primary_key)).to_s] }
|
|
141
141
|
end
|
|
142
142
|
|
|
143
143
|
# has many?
|
|
@@ -155,12 +155,13 @@ module RailsAdmin
|
|
|
155
155
|
|
|
156
156
|
private
|
|
157
157
|
|
|
158
|
+
# A key travels through the DOM and comes back as a URL or form
|
|
159
|
+
# parameter, so it is the associated model's repository that decides how
|
|
160
|
+
# it is written down.
|
|
158
161
|
def format_key(key)
|
|
159
|
-
if key.
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
key
|
|
163
|
-
end
|
|
162
|
+
return if key.nil? # the association is not set
|
|
163
|
+
|
|
164
|
+
associated_model_config.abstract_model.format_id(key)
|
|
164
165
|
end
|
|
165
166
|
end
|
|
166
167
|
end
|