rails_admin 3.3.0 → 3.3.1
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 +3 -2
- data/app/controllers/rails_admin/main_controller.rb +11 -0
- data/app/helpers/rails_admin/form_builder.rb +4 -3
- data/lib/generators/rails_admin/importmap_formatter.rb +1 -0
- data/lib/rails_admin/adapters/active_record/association.rb +1 -1
- data/lib/rails_admin/adapters/active_record.rb +4 -0
- data/lib/rails_admin/adapters/mongoid/association.rb +1 -1
- data/lib/rails_admin/adapters/mongoid.rb +5 -0
- data/lib/rails_admin/config/actions/export.rb +2 -1
- data/lib/rails_admin/config/actions/index.rb +2 -2
- data/lib/rails_admin/config/actions/show.rb +1 -1
- data/lib/rails_admin/config/configurable.rb +1 -1
- data/lib/rails_admin/extensions/pundit/authorization_adapter.rb +2 -1
- data/lib/rails_admin/support/serialization_schema.rb +72 -0
- data/lib/rails_admin/version.rb +1 -1
- data/lib/rails_admin.rb +1 -0
- data/package.json +1 -1
- metadata +4 -6
- data/lib/rails_admin/adapters/active_record.rb.bak +0 -348
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a18178de7305b2be5286d22268d911c84952d779d46210548887cf220f5c052d
|
|
4
|
+
data.tar.gz: d0f6a162ca88c76b4be204608f5d265843c8bb59a55d93fe0b5fd65575e7c36e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f93d7e8d4cab0ac54b62db959da642a0a83267234c92f7394cb2eb2ad44e821804fe7bd11a0a34eeb090eec10265dbe6abd06b0d3ce17c8992525976af513ba8
|
|
7
|
+
data.tar.gz: 2abe7c0eaca1c5ea064408bf389d7e108df43808e6551418c25945578e1d4d611a799a786b8de851b99ce86010f2615aa36a64b5d3b259c32f704f1b6ce627b9
|
data/Gemfile
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
source 'https://rubygems.org'
|
|
4
4
|
|
|
5
|
-
gem 'appraisal', '>= 2.0'
|
|
5
|
+
gem 'appraisal', '>= 2.0', require: false
|
|
6
6
|
gem 'devise', '~> 4.7'
|
|
7
7
|
gem 'net-smtp', require: false
|
|
8
8
|
gem 'rails'
|
|
@@ -18,12 +18,13 @@ end
|
|
|
18
18
|
|
|
19
19
|
group :test do
|
|
20
20
|
gem 'cancancan', '~> 3.0'
|
|
21
|
-
gem 'carrierwave', ['>= 2.
|
|
21
|
+
gem 'carrierwave', ['>= 2.2.1', '< 3']
|
|
22
22
|
gem 'cuprite', '!= 0.15.1'
|
|
23
23
|
gem 'database_cleaner-active_record', '>= 2.0', require: false
|
|
24
24
|
gem 'dragonfly', '~> 1.0'
|
|
25
25
|
gem 'factory_bot', '>= 4.2', '!= 6.4.5'
|
|
26
26
|
gem 'generator_spec', '>= 0.8'
|
|
27
|
+
gem 'json', '< 3' # Workaround for json 3.0 quirks_mode removal
|
|
27
28
|
gem 'kt-paperclip'
|
|
28
29
|
gem 'launchy', '>= 2.2'
|
|
29
30
|
gem 'mini_magick', '>= 3.4'
|
|
@@ -94,6 +94,17 @@ module RailsAdmin
|
|
|
94
94
|
model_config.send(action).with(controller: self, view: view_context, object: @object).visible_fields
|
|
95
95
|
end
|
|
96
96
|
|
|
97
|
+
# Memoized, as export renders through index, which switches @action
|
|
98
|
+
def serialization_schema
|
|
99
|
+
@serialization_schema ||= begin
|
|
100
|
+
section = %i[export show].include?(@action.key) ? @model_config.send(@action.key) : @model_config.list
|
|
101
|
+
RailsAdmin::SerializationSchema.new(
|
|
102
|
+
section.with(controller: self, view: view_context, object: @object || @abstract_model.model.new),
|
|
103
|
+
include_associations: @action.key == :export,
|
|
104
|
+
)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
97
108
|
def sanitize_params_for!(action, model_config = @model_config, target_params = params[@abstract_model.param_key])
|
|
98
109
|
return unless target_params.present?
|
|
99
110
|
|
|
@@ -149,9 +149,10 @@ module RailsAdmin
|
|
|
149
149
|
private
|
|
150
150
|
|
|
151
151
|
def nested_field_association?(field, nested_in)
|
|
152
|
-
|
|
153
|
-
(
|
|
154
|
-
field.
|
|
152
|
+
nested_in.presence &&
|
|
153
|
+
(field.name == nested_in.inverse_of ||
|
|
154
|
+
(field.inverse_of.presence && field.inverse_of == nested_in.name &&
|
|
155
|
+
@template.instance_variable_get(:@model_config).abstract_model == field.abstract_model))
|
|
155
156
|
end
|
|
156
157
|
end
|
|
157
158
|
end
|
|
@@ -12,6 +12,7 @@ module RailsAdmin
|
|
|
12
12
|
|
|
13
13
|
def format
|
|
14
14
|
imports = packager.import("rails_admin@#{RailsAdmin::Version.js}", from: 'jspm.io')
|
|
15
|
+
imports = imports[:imports] if imports.key?(:imports)
|
|
15
16
|
|
|
16
17
|
# Use ESM compatible version to work around https://github.com/cljsjs/packages/issues/1579
|
|
17
18
|
imports['@popperjs/core'].gsub!('lib/index.js', 'dist/esm/popper.js')
|
|
@@ -19,7 +19,8 @@ module RailsAdmin
|
|
|
19
19
|
format = params[:json] && :json || params[:csv] && :csv || params[:xml] && :xml
|
|
20
20
|
if format
|
|
21
21
|
request.format = format
|
|
22
|
-
|
|
22
|
+
serialization_schema # built while @action is still export
|
|
23
|
+
@schema = serialization_schema.sanitize(params[:schema].slice(:include, :methods, :only).permit!.to_h) if params[:schema]
|
|
23
24
|
@objects = list_entries(@model_config, :export)
|
|
24
25
|
index
|
|
25
26
|
else
|
|
@@ -56,7 +56,7 @@ module RailsAdmin
|
|
|
56
56
|
@objects.collect { |object| {id: object.id.to_s, label: object.send(@model_config.object_label_method).to_s} }
|
|
57
57
|
end
|
|
58
58
|
else
|
|
59
|
-
@objects.to_json(@schema)
|
|
59
|
+
@objects.to_json(@schema || serialization_schema.default)
|
|
60
60
|
end
|
|
61
61
|
|
|
62
62
|
if params[:send_data]
|
|
@@ -67,7 +67,7 @@ module RailsAdmin
|
|
|
67
67
|
end
|
|
68
68
|
|
|
69
69
|
format.xml do
|
|
70
|
-
output = @objects.to_xml(@schema)
|
|
70
|
+
output = @objects.to_xml(@schema || serialization_schema.default)
|
|
71
71
|
if params[:send_data]
|
|
72
72
|
send_data output, filename: "#{params[:model_name]}_#{DateTime.now.strftime('%Y-%m-%d_%Hh%Mm%S')}.xml"
|
|
73
73
|
else
|
|
@@ -21,7 +21,7 @@ module RailsAdmin
|
|
|
21
21
|
register_instance_option :controller do
|
|
22
22
|
proc do
|
|
23
23
|
respond_to do |format|
|
|
24
|
-
format.json { render json: @object }
|
|
24
|
+
format.json { render json: @object.to_json(serialization_schema.default) }
|
|
25
25
|
format.html { render @action.template_name }
|
|
26
26
|
end
|
|
27
27
|
end
|
|
@@ -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
|
|
@@ -44,7 +44,8 @@ module RailsAdmin
|
|
|
44
44
|
def query(_action, abstract_model)
|
|
45
45
|
@controller.send(:policy_scope, abstract_model.model.all)
|
|
46
46
|
rescue ::Pundit::NotDefinedError
|
|
47
|
-
|
|
47
|
+
# Fail closed: a missing policy scope must not be treated as "no restriction".
|
|
48
|
+
abstract_model.none
|
|
48
49
|
end
|
|
49
50
|
|
|
50
51
|
# This is called in the new/create actions to determine the initial attributes for new
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rails_admin/support/hash_helper'
|
|
4
|
+
|
|
5
|
+
module RailsAdmin
|
|
6
|
+
# The options passed to to_json/to_xml, limited to what a section exposes.
|
|
7
|
+
# It mirrors what the export form offers, as the form is what builds them.
|
|
8
|
+
class SerializationSchema
|
|
9
|
+
def initialize(section, include_associations:)
|
|
10
|
+
@section = section
|
|
11
|
+
@include_associations = include_associations
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def default
|
|
15
|
+
sanitize(allowed)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Anything not allowed is dropped, and `only` is always set, since leaving
|
|
19
|
+
# it out makes to_json/to_xml serialize every attribute.
|
|
20
|
+
def sanitize(requested)
|
|
21
|
+
requested = HashHelper.symbolize(requested)
|
|
22
|
+
schema = filter(requested, allowed)
|
|
23
|
+
if @include_associations && requested[:include].is_a?(Hash)
|
|
24
|
+
schema[:include] = requested[:include].each_with_object({}) do |(name, value), included|
|
|
25
|
+
next unless value.is_a?(Hash) && allowed[:include].key?(name)
|
|
26
|
+
|
|
27
|
+
included[name] = filter(value, allowed[:include][name])
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
schema
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def allowed
|
|
36
|
+
@allowed ||= begin
|
|
37
|
+
fields = @section.visible_fields
|
|
38
|
+
names = field_names(fields.select { |f| !f.association? || f.association.polymorphic? })
|
|
39
|
+
if @include_associations
|
|
40
|
+
names[:include] = fields.select { |f| f.association? && !f.association.polymorphic? }.to_h do |field|
|
|
41
|
+
associated = field.associated_model_config.export.with(@section.bindings.merge(object: field.associated_model_config.abstract_model.model.new))
|
|
42
|
+
[field.name, field_names(associated.visible_fields.reject(&:association?))]
|
|
43
|
+
end
|
|
44
|
+
else
|
|
45
|
+
# The list shows these associations already, so their keys expose nothing more
|
|
46
|
+
names[:only].concat(fields.select { |f| f.association? && f.association.type == :belongs_to && !f.association.polymorphic? }.
|
|
47
|
+
flat_map { |f| Array(f.association.foreign_key) })
|
|
48
|
+
end
|
|
49
|
+
names
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def field_names(fields)
|
|
54
|
+
fields.each_with_object({only: [], methods: []}) do |field, names|
|
|
55
|
+
list = field.virtual? ? names[:methods] : names[:only]
|
|
56
|
+
if field.association?
|
|
57
|
+
list << field.method_name.to_sym << field.association.foreign_type.to_sym
|
|
58
|
+
else
|
|
59
|
+
list << field.name
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def filter(requested, permitted)
|
|
65
|
+
only = Array(requested[:only]) & permitted[:only]
|
|
66
|
+
# Mongoid serializes every attribute for an empty `only`
|
|
67
|
+
schema = {only: only.presence || [:__none__]}
|
|
68
|
+
schema[:methods] = Array(requested[:methods]) & permitted[:methods] if requested.key?(:methods)
|
|
69
|
+
schema
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
data/lib/rails_admin/version.rb
CHANGED
data/lib/rails_admin.rb
CHANGED
|
@@ -10,6 +10,7 @@ require 'rails_admin/extensions/pundit'
|
|
|
10
10
|
require 'rails_admin/extensions/paper_trail'
|
|
11
11
|
require 'rails_admin/support/csv_converter'
|
|
12
12
|
require 'rails_admin/support/hash_helper'
|
|
13
|
+
require 'rails_admin/support/serialization_schema'
|
|
13
14
|
require 'yaml'
|
|
14
15
|
|
|
15
16
|
module RailsAdmin
|
data/package.json
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rails_admin
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.3.
|
|
4
|
+
version: 3.3.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Erik Michaels-Ober
|
|
@@ -9,10 +9,9 @@ authors:
|
|
|
9
9
|
- Petteri Kaapa
|
|
10
10
|
- Benoit Benezech
|
|
11
11
|
- Mitsuhiro Shibuya
|
|
12
|
-
autorequire:
|
|
13
12
|
bindir: bin
|
|
14
13
|
cert_chain: []
|
|
15
|
-
date:
|
|
14
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
16
15
|
dependencies:
|
|
17
16
|
- !ruby/object:Gem::Dependency
|
|
18
17
|
name: activemodel-serializers-xml
|
|
@@ -216,7 +215,6 @@ files:
|
|
|
216
215
|
- lib/rails_admin.rb
|
|
217
216
|
- lib/rails_admin/abstract_model.rb
|
|
218
217
|
- lib/rails_admin/adapters/active_record.rb
|
|
219
|
-
- lib/rails_admin/adapters/active_record.rb.bak
|
|
220
218
|
- lib/rails_admin/adapters/active_record/association.rb
|
|
221
219
|
- lib/rails_admin/adapters/active_record/object_extension.rb
|
|
222
220
|
- lib/rails_admin/adapters/active_record/property.rb
|
|
@@ -338,6 +336,7 @@ files:
|
|
|
338
336
|
- lib/rails_admin/support/datetime.rb
|
|
339
337
|
- lib/rails_admin/support/es_module_processor.rb
|
|
340
338
|
- lib/rails_admin/support/hash_helper.rb
|
|
339
|
+
- lib/rails_admin/support/serialization_schema.rb
|
|
341
340
|
- lib/rails_admin/version.rb
|
|
342
341
|
- lib/tasks/rails_admin.rake
|
|
343
342
|
- package.json
|
|
@@ -491,8 +490,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
491
490
|
- !ruby/object:Gem::Version
|
|
492
491
|
version: 1.8.11
|
|
493
492
|
requirements: []
|
|
494
|
-
rubygems_version:
|
|
495
|
-
signing_key:
|
|
493
|
+
rubygems_version: 4.0.19
|
|
496
494
|
specification_version: 4
|
|
497
495
|
summary: Admin for Rails
|
|
498
496
|
test_files: []
|
|
@@ -1,348 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'active_record'
|
|
4
|
-
require 'rails_admin/adapters/active_record/association'
|
|
5
|
-
require 'rails_admin/adapters/active_record/model_extension'
|
|
6
|
-
require 'rails_admin/adapters/active_record/property'
|
|
7
|
-
|
|
8
|
-
module RailsAdmin
|
|
9
|
-
module Adapters
|
|
10
|
-
module ActiveRecord
|
|
11
|
-
DISABLED_COLUMN_TYPES = %i[tsvector blob binary spatial hstore geometry].freeze
|
|
12
|
-
|
|
13
|
-
def model_with_extension
|
|
14
|
-
@model_with_extension ||=
|
|
15
|
-
begin
|
|
16
|
-
klass = Class.new(model) do
|
|
17
|
-
include ModelExtension
|
|
18
|
-
end
|
|
19
|
-
klass.instance_eval <<-RUBY, __FILE__, __LINE__+1
|
|
20
|
-
def name
|
|
21
|
-
"#{@model_name.to_s}"
|
|
22
|
-
end
|
|
23
|
-
RUBY
|
|
24
|
-
klass
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def new(params = {})
|
|
29
|
-
model_with_extension.new(params)
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def get(id, scope = nil)
|
|
33
|
-
scope = model_with_extension.merge(scope || scoped)
|
|
34
|
-
scope.where(primary_key => id).first
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def scoped
|
|
38
|
-
model_with_extension.all
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def first(options = {}, scope = nil)
|
|
42
|
-
all(options, scope).first
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
def all(options = {}, scope = nil)
|
|
46
|
-
scope = model_with_extension.merge(scope || scoped)
|
|
47
|
-
scope = scope.includes(options[:include]) if options[:include]
|
|
48
|
-
scope = scope.limit(options[:limit]) if options[:limit]
|
|
49
|
-
scope = bulk_scope(scope, options) if options[:bulk_ids]
|
|
50
|
-
scope = query_scope(scope, options[:query]) if options[:query]
|
|
51
|
-
scope = filter_scope(scope, options[:filters]) if options[:filters]
|
|
52
|
-
scope = scope.send(Kaminari.config.page_method_name, options[:page]).per(options[:per]) if options[:page] && options[:per]
|
|
53
|
-
scope = sort_scope(scope, options) if options[:sort]
|
|
54
|
-
scope
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
def count(options = {}, scope = nil)
|
|
58
|
-
all(options.merge(limit: false, page: false), scope).count(:all)
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
def destroy(objects)
|
|
62
|
-
Array.wrap(objects).each(&:destroy)
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def associations
|
|
66
|
-
model.reflect_on_all_associations.collect do |association|
|
|
67
|
-
Association.new(association, model)
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
def properties
|
|
72
|
-
columns = model.columns.reject do |c|
|
|
73
|
-
c.type.blank? ||
|
|
74
|
-
DISABLED_COLUMN_TYPES.include?(c.type.to_sym) ||
|
|
75
|
-
c.try(:array)
|
|
76
|
-
end
|
|
77
|
-
columns.collect do |property|
|
|
78
|
-
Property.new(property, model)
|
|
79
|
-
end
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
def base_class
|
|
83
|
-
model.base_class
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
delegate :primary_key, :table_name, to: :model, prefix: false
|
|
87
|
-
|
|
88
|
-
def quoted_table_name
|
|
89
|
-
model.quoted_table_name
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
def quote_column_name(name)
|
|
93
|
-
model.connection.quote_column_name(name)
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
def encoding
|
|
97
|
-
adapter =
|
|
98
|
-
if ::ActiveRecord::Base.respond_to?(:connection_db_config)
|
|
99
|
-
::ActiveRecord::Base.connection_db_config.configuration_hash[:adapter]
|
|
100
|
-
else
|
|
101
|
-
::ActiveRecord::Base.connection_config[:adapter]
|
|
102
|
-
end
|
|
103
|
-
case adapter
|
|
104
|
-
when 'postgresql'
|
|
105
|
-
::ActiveRecord::Base.connection.select_one("SELECT ''::text AS str;").values.first.encoding
|
|
106
|
-
when 'mysql2'
|
|
107
|
-
if RUBY_ENGINE == 'jruby'
|
|
108
|
-
::ActiveRecord::Base.connection.select_one("SELECT '' AS str;").values.first.encoding
|
|
109
|
-
else
|
|
110
|
-
::ActiveRecord::Base.connection.raw_connection.encoding
|
|
111
|
-
end
|
|
112
|
-
when 'oracle_enhanced'
|
|
113
|
-
::ActiveRecord::Base.connection.select_one('SELECT dummy FROM DUAL').values.first.encoding
|
|
114
|
-
else
|
|
115
|
-
::ActiveRecord::Base.connection.select_one("SELECT '' AS str;").values.first.encoding
|
|
116
|
-
end
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
def embedded?
|
|
120
|
-
false
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
def cyclic?
|
|
124
|
-
false
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
def adapter_supports_joins?
|
|
128
|
-
true
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
private
|
|
132
|
-
|
|
133
|
-
def bulk_scope(scope, options)
|
|
134
|
-
scope.where(primary_key => options[:bulk_ids])
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
def sort_scope(scope, options)
|
|
138
|
-
direction = options[:sort_reverse] ? :asc : :desc
|
|
139
|
-
case options[:sort]
|
|
140
|
-
when String, Symbol
|
|
141
|
-
scope.reorder("#{options[:sort]} #{direction}")
|
|
142
|
-
when Array
|
|
143
|
-
scope.reorder(options[:sort].zip(Array.new(options[:sort].size) { direction }).to_h)
|
|
144
|
-
when Hash
|
|
145
|
-
scope.reorder(options[:sort].map { |table_name, column| "#{table_name}.#{column}" }.
|
|
146
|
-
zip(Array.new(options[:sort].size) { direction }).to_h)
|
|
147
|
-
else
|
|
148
|
-
raise ArgumentError.new("Unsupported sort value: #{options[:sort]}")
|
|
149
|
-
end
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
class WhereBuilder
|
|
153
|
-
def initialize(scope)
|
|
154
|
-
@statements = []
|
|
155
|
-
@values = []
|
|
156
|
-
@tables = []
|
|
157
|
-
@scope = scope
|
|
158
|
-
end
|
|
159
|
-
|
|
160
|
-
def add(field, value, operator)
|
|
161
|
-
field.searchable_columns.flatten.each do |column_infos|
|
|
162
|
-
statement, value1, value2 = StatementBuilder.new(column_infos[:column], column_infos[:type], value, operator, @scope.connection.adapter_name).to_statement
|
|
163
|
-
@statements << statement if statement.present?
|
|
164
|
-
@values << value1 unless value1.nil?
|
|
165
|
-
@values << value2 unless value2.nil?
|
|
166
|
-
table, column = column_infos[:column].split('.')
|
|
167
|
-
@tables.push(table) if column
|
|
168
|
-
end
|
|
169
|
-
end
|
|
170
|
-
|
|
171
|
-
def build
|
|
172
|
-
scope = @scope.where(@statements.join(' OR '), *@values)
|
|
173
|
-
scope = scope.references(*@tables.uniq) if @tables.any?
|
|
174
|
-
scope
|
|
175
|
-
end
|
|
176
|
-
end
|
|
177
|
-
|
|
178
|
-
def query_scope(scope, query, fields = config.list.fields.select(&:queryable?))
|
|
179
|
-
if config.list.search_by
|
|
180
|
-
scope.send(config.list.search_by, query)
|
|
181
|
-
else
|
|
182
|
-
wb = WhereBuilder.new(scope)
|
|
183
|
-
fields.each do |field|
|
|
184
|
-
value = parse_field_value(field, query)
|
|
185
|
-
wb.add(field, value, field.search_operator)
|
|
186
|
-
end
|
|
187
|
-
# OR all query statements
|
|
188
|
-
wb.build
|
|
189
|
-
end
|
|
190
|
-
end
|
|
191
|
-
|
|
192
|
-
# filters example => {"string_field"=>{"0055"=>{"o"=>"like", "v"=>"test_value"}}, ...}
|
|
193
|
-
# "0055" is the filter index, no use here. o is the operator, v the value
|
|
194
|
-
def filter_scope(scope, filters, fields = config.list.fields.select(&:filterable?))
|
|
195
|
-
filters.each_pair do |field_name, filters_dump|
|
|
196
|
-
filters_dump.each_value do |filter_dump|
|
|
197
|
-
wb = WhereBuilder.new(scope)
|
|
198
|
-
field = fields.detect { |f| f.name.to_s == field_name }
|
|
199
|
-
value = parse_field_value(field, filter_dump[:v])
|
|
200
|
-
|
|
201
|
-
wb.add(field, value, (filter_dump[:o] || RailsAdmin::Config.default_search_operator))
|
|
202
|
-
# AND current filter statements to other filter statements
|
|
203
|
-
scope = wb.build
|
|
204
|
-
end
|
|
205
|
-
end
|
|
206
|
-
scope
|
|
207
|
-
end
|
|
208
|
-
|
|
209
|
-
def build_statement(column, type, value, operator)
|
|
210
|
-
StatementBuilder.new(column, type, value, operator, model.connection.adapter_name).to_statement
|
|
211
|
-
end
|
|
212
|
-
|
|
213
|
-
class StatementBuilder < RailsAdmin::AbstractModel::StatementBuilder
|
|
214
|
-
def initialize(column, type, value, operator, adapter_name)
|
|
215
|
-
super column, type, value, operator
|
|
216
|
-
@adapter_name = adapter_name
|
|
217
|
-
end
|
|
218
|
-
|
|
219
|
-
protected
|
|
220
|
-
|
|
221
|
-
def unary_operators
|
|
222
|
-
case @type
|
|
223
|
-
when :boolean
|
|
224
|
-
boolean_unary_operators
|
|
225
|
-
when :uuid
|
|
226
|
-
uuid_unary_operators
|
|
227
|
-
when :integer, :decimal, :float
|
|
228
|
-
numeric_unary_operators
|
|
229
|
-
else
|
|
230
|
-
generic_unary_operators
|
|
231
|
-
end
|
|
232
|
-
end
|
|
233
|
-
|
|
234
|
-
private
|
|
235
|
-
|
|
236
|
-
def generic_unary_operators
|
|
237
|
-
{
|
|
238
|
-
'_blank' => ["(#{@column} IS NULL OR #{@column} = '')"],
|
|
239
|
-
'_present' => ["(#{@column} IS NOT NULL AND #{@column} != '')"],
|
|
240
|
-
'_null' => ["(#{@column} IS NULL)"],
|
|
241
|
-
'_not_null' => ["(#{@column} IS NOT NULL)"],
|
|
242
|
-
'_empty' => ["(#{@column} = '')"],
|
|
243
|
-
'_not_empty' => ["(#{@column} != '')"],
|
|
244
|
-
}
|
|
245
|
-
end
|
|
246
|
-
|
|
247
|
-
def boolean_unary_operators
|
|
248
|
-
generic_unary_operators.merge(
|
|
249
|
-
'_blank' => ["(#{@column} IS NULL)"],
|
|
250
|
-
'_empty' => ["(#{@column} IS NULL)"],
|
|
251
|
-
'_present' => ["(#{@column} IS NOT NULL)"],
|
|
252
|
-
'_not_empty' => ["(#{@column} IS NOT NULL)"],
|
|
253
|
-
)
|
|
254
|
-
end
|
|
255
|
-
alias_method :numeric_unary_operators, :boolean_unary_operators
|
|
256
|
-
alias_method :uuid_unary_operators, :boolean_unary_operators
|
|
257
|
-
|
|
258
|
-
def range_filter(min, max)
|
|
259
|
-
if min && max && min == max
|
|
260
|
-
["(#{@column} = ?)", min]
|
|
261
|
-
elsif min && max
|
|
262
|
-
["(#{@column} BETWEEN ? AND ?)", min, max]
|
|
263
|
-
elsif min
|
|
264
|
-
["(#{@column} >= ?)", min]
|
|
265
|
-
elsif max
|
|
266
|
-
["(#{@column} <= ?)", max]
|
|
267
|
-
end
|
|
268
|
-
end
|
|
269
|
-
|
|
270
|
-
def build_statement_for_type
|
|
271
|
-
case @type
|
|
272
|
-
when :boolean then build_statement_for_boolean
|
|
273
|
-
when :integer, :decimal, :float then build_statement_for_integer_decimal_or_float
|
|
274
|
-
when :string, :text, :citext then build_statement_for_string_or_text
|
|
275
|
-
when :enum then build_statement_for_enum
|
|
276
|
-
when :belongs_to_association then build_statement_for_belongs_to_association
|
|
277
|
-
when :uuid then build_statement_for_uuid
|
|
278
|
-
end
|
|
279
|
-
end
|
|
280
|
-
|
|
281
|
-
def build_statement_for_boolean
|
|
282
|
-
case @value
|
|
283
|
-
when 'false', 'f', '0'
|
|
284
|
-
["(#{@column} IS NULL OR #{@column} = ?)", false]
|
|
285
|
-
when 'true', 't', '1'
|
|
286
|
-
["(#{@column} = ?)", true]
|
|
287
|
-
end
|
|
288
|
-
end
|
|
289
|
-
|
|
290
|
-
def column_for_value(value)
|
|
291
|
-
["(#{@column} = ?)", value]
|
|
292
|
-
end
|
|
293
|
-
|
|
294
|
-
def build_statement_for_belongs_to_association
|
|
295
|
-
return if @value.blank?
|
|
296
|
-
|
|
297
|
-
["(#{@column} = ?)", @value.to_i] if @value.to_i.to_s == @value
|
|
298
|
-
end
|
|
299
|
-
|
|
300
|
-
def build_statement_for_string_or_text
|
|
301
|
-
return if @value.blank?
|
|
302
|
-
|
|
303
|
-
return ["(#{@column} = ?)", @value] if ['is', '='].include?(@operator)
|
|
304
|
-
|
|
305
|
-
@value = @value.mb_chars.downcase unless %w[postgresql postgis].include? ar_adapter
|
|
306
|
-
|
|
307
|
-
@value =
|
|
308
|
-
case @operator
|
|
309
|
-
when 'default', 'like', 'not_like'
|
|
310
|
-
"%#{@value}%"
|
|
311
|
-
when 'starts_with'
|
|
312
|
-
"#{@value}%"
|
|
313
|
-
when 'ends_with'
|
|
314
|
-
"%#{@value}"
|
|
315
|
-
else
|
|
316
|
-
return
|
|
317
|
-
end
|
|
318
|
-
|
|
319
|
-
if %w[postgresql postgis].include? ar_adapter
|
|
320
|
-
if @operator == 'not_like'
|
|
321
|
-
["(#{@column} NOT ILIKE ?)", @value]
|
|
322
|
-
else
|
|
323
|
-
["(#{@column} ILIKE ?)", @value]
|
|
324
|
-
end
|
|
325
|
-
elsif @operator == 'not_like'
|
|
326
|
-
["(LOWER(#{@column}) NOT LIKE ?)", @value]
|
|
327
|
-
else
|
|
328
|
-
["(LOWER(#{@column}) LIKE ?)", @value]
|
|
329
|
-
end
|
|
330
|
-
end
|
|
331
|
-
|
|
332
|
-
def build_statement_for_enum
|
|
333
|
-
return if @value.blank?
|
|
334
|
-
|
|
335
|
-
["(#{@column} IN (?))", Array.wrap(@value)]
|
|
336
|
-
end
|
|
337
|
-
|
|
338
|
-
def build_statement_for_uuid
|
|
339
|
-
column_for_value(@value) if /\A[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}\z/.match?(@value.to_s)
|
|
340
|
-
end
|
|
341
|
-
|
|
342
|
-
def ar_adapter
|
|
343
|
-
@adapter_name.downcase
|
|
344
|
-
end
|
|
345
|
-
end
|
|
346
|
-
end
|
|
347
|
-
end
|
|
348
|
-
end
|