active_scaffold 3.5.4 → 3.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/{CHANGELOG → CHANGELOG.rdoc} +72 -0
- data/README.md +20 -8
- data/app/assets/javascripts/active_scaffold.js.erb +0 -1
- data/app/assets/javascripts/jquery/active_scaffold.js +98 -7
- data/app/assets/stylesheets/active_scaffold_colors.scss +1 -1
- data/app/assets/stylesheets/active_scaffold_layout.css +52 -29
- data/app/views/active_scaffold_overrides/_base_form.html.erb +2 -2
- data/app/views/active_scaffold_overrides/_form.html.erb +1 -1
- data/app/views/active_scaffold_overrides/_form_association.html.erb +2 -1
- data/app/views/active_scaffold_overrides/_form_association_footer.html.erb +3 -2
- data/app/views/active_scaffold_overrides/_form_association_record.html.erb +9 -7
- data/app/views/active_scaffold_overrides/_horizontal_subform.html.erb +4 -4
- data/app/views/active_scaffold_overrides/_horizontal_subform_header.html.erb +2 -1
- data/app/views/active_scaffold_overrides/_list.html.erb +2 -1
- data/app/views/active_scaffold_overrides/_list_header.html.erb +5 -7
- data/app/views/active_scaffold_overrides/_list_messages.html.erb +1 -0
- data/app/views/active_scaffold_overrides/_list_record.html.erb +4 -5
- data/app/views/active_scaffold_overrides/_list_with_header.html.erb +1 -1
- data/app/views/active_scaffold_overrides/_messages.html.erb +1 -0
- data/app/views/active_scaffold_overrides/_refresh_list.js.erb +4 -0
- data/app/views/active_scaffold_overrides/_render_field.js.erb +2 -1
- data/app/views/active_scaffold_overrides/_show_association_horizontal.html.erb +2 -1
- data/app/views/active_scaffold_overrides/_show_columns.html.erb +2 -2
- data/app/views/active_scaffold_overrides/_show_horizontal_record.html.erb +4 -4
- data/app/views/active_scaffold_overrides/_update_calculations.js.erb +1 -1
- data/app/views/active_scaffold_overrides/_update_column.js.erb +2 -2
- data/app/views/active_scaffold_overrides/_vertical_subform.html.erb +2 -2
- data/app/views/active_scaffold_overrides/action_confirmation.html.erb +2 -2
- data/app/views/active_scaffold_overrides/delete.html.erb +2 -2
- data/app/views/active_scaffold_overrides/on_action_update.js.erb +16 -6
- data/app/views/active_scaffold_overrides/on_update.js.erb +1 -1
- data/app/views/active_scaffold_overrides/row.js.erb +1 -1
- data/app/views/active_scaffold_overrides/update_column.js.erb +2 -2
- data/config/locales/de.yml +2 -1
- data/config/locales/en.yml +1 -0
- data/config/locales/es.yml +1 -0
- data/config/locales/fr.yml +2 -1
- data/config/locales/hu.yml +1 -0
- data/config/locales/ja.yml +1 -0
- data/config/locales/ru.yml +1 -0
- data/lib/active_scaffold.rb +19 -16
- data/lib/active_scaffold/actions/common_search.rb +11 -8
- data/lib/active_scaffold/actions/core.rb +91 -70
- data/lib/active_scaffold/actions/create.rb +28 -28
- data/lib/active_scaffold/actions/delete.rb +3 -3
- data/lib/active_scaffold/actions/field_search.rb +53 -43
- data/lib/active_scaffold/actions/list.rb +111 -27
- data/lib/active_scaffold/actions/nested.rb +65 -48
- data/lib/active_scaffold/actions/search.rb +1 -1
- data/lib/active_scaffold/actions/show.rb +4 -4
- data/lib/active_scaffold/actions/subform.rb +23 -22
- data/lib/active_scaffold/actions/update.rb +96 -77
- data/lib/active_scaffold/active_record_permissions.rb +2 -11
- data/lib/active_scaffold/attribute_params.rb +102 -94
- data/lib/active_scaffold/bridges.rb +8 -8
- data/lib/active_scaffold/bridges/active_storage.rb +6 -0
- data/lib/active_scaffold/bridges/active_storage/active_storage_bridge.rb +34 -0
- data/lib/active_scaffold/bridges/active_storage/active_storage_helpers.rb +54 -0
- data/lib/active_scaffold/bridges/active_storage/form_ui.rb +22 -0
- data/lib/active_scaffold/bridges/active_storage/list_ui.rb +36 -0
- data/lib/active_scaffold/bridges/bitfields.rb +2 -1
- data/lib/active_scaffold/bridges/bitfields/bitfields_bridge.rb +12 -15
- data/lib/active_scaffold/bridges/bitfields/list_ui.rb +19 -0
- data/lib/active_scaffold/bridges/calendar_date_select/as_cds_bridge.rb +1 -1
- data/lib/active_scaffold/bridges/cancan/cancan_bridge.rb +9 -12
- data/lib/active_scaffold/bridges/carrierwave/carrierwave_bridge.rb +1 -1
- data/lib/active_scaffold/bridges/carrierwave/list_ui.rb +2 -2
- data/lib/active_scaffold/bridges/chosen/helpers.rb +7 -6
- data/lib/active_scaffold/bridges/date_picker/ext.rb +0 -13
- data/lib/active_scaffold/bridges/date_picker/helper.rb +49 -44
- data/lib/active_scaffold/bridges/dragonfly/list_ui.rb +1 -1
- data/lib/active_scaffold/bridges/file_column/as_file_column_bridge.rb +1 -1
- data/lib/active_scaffold/bridges/file_column/file_column_helpers.rb +3 -3
- data/lib/active_scaffold/bridges/file_column/form_ui.rb +3 -3
- data/lib/active_scaffold/bridges/file_column/test/functional/file_column_keep_test.rb +10 -7
- data/lib/active_scaffold/bridges/paper_trail.rb +1 -1
- data/lib/active_scaffold/bridges/paper_trail/actions.rb +3 -1
- data/lib/active_scaffold/bridges/paperclip/list_ui.rb +1 -1
- data/lib/active_scaffold/bridges/paperclip/paperclip_bridge.rb +1 -1
- data/lib/active_scaffold/bridges/paperclip/paperclip_bridge_helpers.rb +2 -2
- data/lib/active_scaffold/bridges/record_select/helpers.rb +15 -17
- data/lib/active_scaffold/bridges/shared/date_bridge.rb +20 -19
- data/lib/active_scaffold/bridges/tiny_mce/helpers.rb +3 -1
- data/lib/active_scaffold/bridges/usa_state_select/usa_state_select_helper.rb +21 -4
- data/lib/active_scaffold/config/base.rb +133 -41
- data/lib/active_scaffold/config/core.rb +146 -18
- data/lib/active_scaffold/config/delete.rb +14 -1
- data/lib/active_scaffold/config/field_search.rb +7 -1
- data/lib/active_scaffold/config/form.rb +10 -1
- data/lib/active_scaffold/config/list.rb +39 -13
- data/lib/active_scaffold/config/mark.rb +4 -2
- data/lib/active_scaffold/config/nested.rb +16 -17
- data/lib/active_scaffold/config/search.rb +9 -0
- data/lib/active_scaffold/config/show.rb +4 -0
- data/lib/active_scaffold/config/update.rb +4 -0
- data/lib/active_scaffold/configurable.rb +14 -7
- data/lib/active_scaffold/constraints.rb +22 -20
- data/lib/active_scaffold/core.rb +67 -28
- data/lib/active_scaffold/data_structures/action_columns.rb +50 -59
- data/lib/active_scaffold/data_structures/action_link.rb +50 -20
- data/lib/active_scaffold/data_structures/action_links.rb +15 -13
- data/lib/active_scaffold/data_structures/association/abstract.rb +38 -15
- data/lib/active_scaffold/data_structures/association/active_mongoid.rb +2 -6
- data/lib/active_scaffold/data_structures/association/active_record.rb +6 -2
- data/lib/active_scaffold/data_structures/association/mongoid.rb +0 -3
- data/lib/active_scaffold/data_structures/column.rb +75 -66
- data/lib/active_scaffold/data_structures/columns.rb +3 -2
- data/lib/active_scaffold/data_structures/nested_info.rb +33 -19
- data/lib/active_scaffold/data_structures/set.rb +8 -0
- data/lib/active_scaffold/data_structures/sorting.rb +10 -2
- data/lib/active_scaffold/delayed_setup.rb +16 -5
- data/lib/active_scaffold/extensions/action_controller_rendering.rb +3 -2
- data/lib/active_scaffold/extensions/action_view_rendering.rb +93 -32
- data/lib/active_scaffold/extensions/cow_proxy.rb +95 -0
- data/lib/active_scaffold/extensions/ice_nine.rb +36 -0
- data/lib/active_scaffold/extensions/left_outer_joins.rb +8 -33
- data/lib/active_scaffold/extensions/localize.rb +3 -1
- data/lib/active_scaffold/extensions/routing_mapper.rb +6 -45
- data/lib/active_scaffold/extensions/to_label.rb +3 -2
- data/lib/active_scaffold/extensions/unsaved_record.rb +2 -4
- data/lib/active_scaffold/finder.rb +110 -77
- data/lib/active_scaffold/helpers/action_link_helpers.rb +62 -36
- data/lib/active_scaffold/helpers/association_helpers.rb +18 -16
- data/lib/active_scaffold/helpers/controller_helpers.rb +34 -10
- data/lib/active_scaffold/helpers/form_column_helpers.rb +196 -124
- data/lib/active_scaffold/helpers/human_condition_helpers.rb +1 -1
- data/lib/active_scaffold/helpers/id_helpers.rb +6 -2
- data/lib/active_scaffold/helpers/list_column_helpers.rb +90 -57
- data/lib/active_scaffold/helpers/pagination_helpers.rb +2 -2
- data/lib/active_scaffold/helpers/search_column_helpers.rb +29 -34
- data/lib/active_scaffold/helpers/show_column_helpers.rb +3 -5
- data/lib/active_scaffold/helpers/view_helpers.rb +39 -36
- data/lib/active_scaffold/marked_model.rb +2 -2
- data/lib/active_scaffold/orm_checks.rb +3 -7
- data/lib/active_scaffold/paginator.rb +7 -7
- data/lib/active_scaffold/registry.rb +33 -0
- data/lib/active_scaffold/responds_to_parent.rb +8 -11
- data/lib/active_scaffold/tableless.rb +82 -66
- data/lib/active_scaffold/version.rb +2 -2
- data/lib/generators/active_scaffold/controller_generator.rb +2 -2
- data/lib/generators/active_scaffold/install_generator.rb +52 -4
- data/lib/generators/active_scaffold/resource_generator.rb +2 -2
- data/shoulda_macros/macros.rb +3 -1
- data/test/bridges/date_picker_test.rb +1 -2
- data/test/bridges/paperclip_test.rb +6 -6
- data/test/class_with_finder.rb +2 -2
- data/test/company.rb +4 -4
- data/test/config/create_test.rb +4 -2
- data/test/config/nested_test.rb +1 -1
- data/test/config/show_test.rb +1 -1
- data/test/config/update_test.rb +7 -6
- data/test/data_structures/action_columns_test.rb +2 -2
- data/test/data_structures/action_links_test.rb +1 -1
- data/test/data_structures/column_test.rb +3 -6
- data/test/data_structures/columns_test.rb +2 -2
- data/test/data_structures/sorting_test.rb +7 -0
- data/test/extensions/action_view_rendering_test.rb +20 -0
- data/test/extensions/active_record_test.rb +4 -4
- data/test/extensions/routing_mapper_test.rb +2 -2
- data/test/helpers/list_column_helpers_test.rb +3 -1
- data/test/misc/active_record_permissions_test.rb +3 -11
- data/test/misc/attribute_params_test.rb +12 -8
- data/test/misc/calculation_test.rb +1 -1
- data/test/misc/configurable_test.rb +10 -10
- data/test/misc/constraints_test.rb +2 -2
- data/test/misc/convert_numbers_format_test.rb +7 -3
- data/test/misc/lang_test.rb +1 -1
- data/test/misc/parse_datetime_test.rb +3 -4
- data/test/misc/tableless_test.rb +14 -0
- data/test/mock_app/Rakefile +1 -1
- data/test/mock_app/app/assets/config/manifest.js +0 -0
- data/test/mock_app/app/controllers/cars_controller.rb +1 -0
- data/test/mock_app/app/controllers/people_controller.rb +5 -1
- data/test/mock_app/app/controllers/roles_controller.rb +4 -0
- data/test/mock_app/app/views/active_scaffold_overrides/_form.html.erb +2 -0
- data/test/mock_app/app/views/active_scaffold_overrides/list.html.erb +2 -0
- data/test/mock_app/app/views/people/_first_name_form_column.html.erb +2 -0
- data/test/mock_app/app/views/people/_form.html.erb +2 -0
- data/test/mock_app/app/views/people/list.html.erb +2 -0
- data/test/mock_app/config/application.rb +2 -1
- data/test/mock_app/config/boot.rb +1 -1
- data/test/mock_app/config/environment.rb +2 -2
- data/test/mock_app/config/routes.rb +4 -1
- data/test/mock_app/db/schema.rb +2 -0
- data/test/performance/list_cars_performance_test.rb +34 -0
- data/test/performance/list_people_performance_test.rb +31 -0
- data/test/performance_test_help.rb +3 -0
- data/test/test_helper.rb +12 -4
- metadata +69 -18
- data/app/assets/javascripts/prototype/rico_corner.js +0 -370
- data/lib/active_scaffold/bridges/file_column/test/test_helper.rb +0 -7
@@ -0,0 +1,36 @@
|
|
1
|
+
module IceNine
|
2
|
+
class Freezer
|
3
|
+
def self.find(name)
|
4
|
+
freezer = name.split('::').reduce(self) do |mod, const|
|
5
|
+
mod.const_lookup(const) or break mod # rubocop:disable Style/AndOr
|
6
|
+
end
|
7
|
+
freezer if freezer < self # only return a descendant freezer
|
8
|
+
end
|
9
|
+
|
10
|
+
class ObjectWithExclussion < Object
|
11
|
+
class_attribute :excluded_vars
|
12
|
+
self.excluded_vars = []
|
13
|
+
def self.freeze_instance_variables(object, recursion_guard)
|
14
|
+
object.instance_variables.each do |ivar_name|
|
15
|
+
next if excluded_vars.include? ivar_name
|
16
|
+
Freezer.guarded_deep_freeze(
|
17
|
+
object.instance_variable_get(ivar_name),
|
18
|
+
recursion_guard
|
19
|
+
)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
private_class_method :freeze_instance_variables
|
23
|
+
end
|
24
|
+
|
25
|
+
class ActiveScaffold < ::IceNine::Freezer::Object
|
26
|
+
class DataStructures < ::IceNine::Freezer::Object
|
27
|
+
class Column < ::IceNine::Freezer::ObjectWithExclussion
|
28
|
+
self.excluded_vars = %i[@active_record_class @column]
|
29
|
+
end
|
30
|
+
|
31
|
+
class Association < ::IceNine::Freezer::NoFreeze
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -18,11 +18,6 @@ if Rails.version < '5.0.0'
|
|
18
18
|
end
|
19
19
|
alias left_joins left_outer_joins
|
20
20
|
|
21
|
-
def outer_joins(*args)
|
22
|
-
ActiveSupport::Deprecation.warn 'use left_outer_joins or left_joins which is added to Rails 5.0.0'
|
23
|
-
left_outer_joins(*args)
|
24
|
-
end
|
25
|
-
|
26
21
|
def left_outer_joins!(*args)
|
27
22
|
self.joins_values += [''] # HACK: for using left_outer_joins in update_all/delete_all
|
28
23
|
self.left_outer_joins_values += args
|
@@ -30,39 +25,19 @@ if Rails.version < '5.0.0'
|
|
30
25
|
end
|
31
26
|
alias left_joins! left_outer_joins!
|
32
27
|
|
33
|
-
def
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
if left_outer_joins_values.empty?
|
41
|
-
super
|
42
|
-
else
|
43
|
-
relation = except(:left_outer_joins)
|
44
|
-
join_dependency = ActiveRecord::Associations::JoinDependency.new(@klass, left_outer_joins_values, [])
|
45
|
-
join_dependency.join_associations.each do |association|
|
46
|
-
relation = association.join_relation(relation)
|
47
|
-
end
|
48
|
-
relation.build_arel
|
49
|
-
end
|
50
|
-
end
|
51
|
-
else
|
52
|
-
def build_arel
|
53
|
-
if left_outer_joins_values.empty?
|
54
|
-
super
|
55
|
-
else
|
56
|
-
relation = except(:left_outer_joins)
|
57
|
-
relation.joins! ActiveRecord::Associations::JoinDependency.new(@klass, left_outer_joins_values, [])
|
58
|
-
relation.build_arel
|
59
|
-
end
|
28
|
+
def build_arel
|
29
|
+
if left_outer_joins_values.empty?
|
30
|
+
super
|
31
|
+
else
|
32
|
+
relation = except(:left_outer_joins)
|
33
|
+
relation.joins! ActiveRecord::Associations::JoinDependency.new(@klass, left_outer_joins_values, [])
|
34
|
+
relation.build_arel
|
60
35
|
end
|
61
36
|
end
|
62
37
|
end
|
63
38
|
end
|
64
39
|
ActiveRecord::Relation.send :include, ActiveScaffold::OuterJoins
|
65
40
|
module ActiveRecord::Querying
|
66
|
-
delegate :left_outer_joins, :left_joins, :
|
41
|
+
delegate :left_outer_joins, :left_joins, :to => :all
|
67
42
|
end
|
68
43
|
end
|
@@ -1,7 +1,9 @@
|
|
1
1
|
class Object
|
2
2
|
def as_(key, options = {})
|
3
3
|
if key.present?
|
4
|
-
|
4
|
+
scope = [:active_scaffold, *options.delete(:scope)]
|
5
|
+
options = options.reverse_merge(:scope => scope, :default => key.is_a?(String) ? key : key.to_s.titleize)
|
6
|
+
text = I18n.translate(key.to_s, **options).html_safe # rubocop:disable Rails/OutputSafety
|
5
7
|
# text = nil if text.include?('translation missing:')
|
6
8
|
end
|
7
9
|
text || key
|
@@ -16,9 +16,9 @@ module ActiveScaffold
|
|
16
16
|
|
17
17
|
def get_actions(actions_hash, options)
|
18
18
|
default_actions = default_actions(actions_hash)
|
19
|
-
if only = options[:only]
|
19
|
+
if (only = options[:only])
|
20
20
|
Array(only).map(&:to_sym)
|
21
|
-
elsif except = options[:except]
|
21
|
+
elsif (except = options[:except])
|
22
22
|
default_actions - Array(except).map(&:to_sym)
|
23
23
|
else
|
24
24
|
default_actions
|
@@ -29,13 +29,13 @@ module ActiveScaffold
|
|
29
29
|
actions = get_actions(ACTIVE_SCAFFOLD_ASSOCIATION_ROUTING, options)
|
30
30
|
|
31
31
|
mapper.collection do
|
32
|
-
|
32
|
+
ActiveScaffold::Routing::ACTIVE_SCAFFOLD_ASSOCIATION_ROUTING[:collection].each do |name, type|
|
33
33
|
mapper.match(name, via: type) if actions.include? name
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
37
37
|
mapper.member do
|
38
|
-
|
38
|
+
ActiveScaffold::Routing::ACTIVE_SCAFFOLD_ASSOCIATION_ROUTING[:member].each do |name, type|
|
39
39
|
mapper.match(name, via: type) if actions.include? name
|
40
40
|
end
|
41
41
|
end
|
@@ -52,13 +52,13 @@ module ActiveScaffold
|
|
52
52
|
actions = get_actions(ACTIVE_SCAFFOLD_CORE_ROUTING, options)
|
53
53
|
|
54
54
|
mapper.collection do
|
55
|
-
|
55
|
+
ActiveScaffold::Routing::ACTIVE_SCAFFOLD_CORE_ROUTING[:collection].each do |name, type|
|
56
56
|
mapper.match(name, via: type) if actions.include? name
|
57
57
|
end
|
58
58
|
end
|
59
59
|
|
60
60
|
mapper.member do
|
61
|
-
|
61
|
+
ActiveScaffold::Routing::ACTIVE_SCAFFOLD_CORE_ROUTING[:member].each do |name, type|
|
62
62
|
mapper.match(name, via: type) if actions.include? name
|
63
63
|
end
|
64
64
|
mapper.get 'list', action: :index if mapper.send(:parent_resource).actions.include? :index
|
@@ -72,47 +72,8 @@ end
|
|
72
72
|
|
73
73
|
module ActionDispatch
|
74
74
|
module Routing
|
75
|
-
ACTIVE_SCAFFOLD_CORE_ROUTING = ActiveScaffold::Routing::ACTIVE_SCAFFOLD_CORE_ROUTING
|
76
|
-
ACTIVE_SCAFFOLD_ASSOCIATION_ROUTING = ActiveScaffold::Routing::ACTIVE_SCAFFOLD_ASSOCIATION_ROUTING
|
77
|
-
|
78
75
|
class Mapper
|
79
76
|
module Resources
|
80
|
-
def parent_options
|
81
|
-
opts = parent_resource.instance_variable_get(:@options)
|
82
|
-
if Rails.version >= '5.0.0'
|
83
|
-
opts.merge(
|
84
|
-
only: parent_resource.instance_variable_get(:@only),
|
85
|
-
except: parent_resource.instance_variable_get(:@except)
|
86
|
-
)
|
87
|
-
end
|
88
|
-
opts
|
89
|
-
end
|
90
|
-
|
91
|
-
def define_active_scaffold_concern
|
92
|
-
ActiveSupport::Deprecation.warn 'Add concern :active_scaffold, ActiveScaffold::Routing::Basic.new(association: true) to your routes file.'
|
93
|
-
concern :active_scaffold, ActiveScaffold::Routing::Basic.new(association: true)
|
94
|
-
end
|
95
|
-
|
96
|
-
def define_active_scaffold_association_concern
|
97
|
-
ActiveSupport::Deprecation.warn 'Add concern :active_scaffold_association, ActiveScaffold::Routing::Association.new to your routes file.'
|
98
|
-
concern :active_scaffold_association, ActiveScaffold::Routing::Association.new
|
99
|
-
end
|
100
|
-
|
101
|
-
def as_routes(opts = {association: true})
|
102
|
-
define_active_scaffold_concern unless @concerns[:active_scaffold]
|
103
|
-
if opts[:association] && !@concerns[:active_scaffold_association]
|
104
|
-
define_active_scaffold_association_concern
|
105
|
-
end
|
106
|
-
ActiveSupport::Deprecation.warn 'Use concerns: :active_scaffold in resources instead of as_routes, or concerns :active_scaffold in resources block if want to disable association routes or restrict routes with only or except options.'
|
107
|
-
concerns :active_scaffold, parent_options.merge(association: opts[:association])
|
108
|
-
end
|
109
|
-
|
110
|
-
def as_association_routes
|
111
|
-
define_active_scaffold_association_concern unless @concerns[:active_scaffold_association]
|
112
|
-
ActiveSupport::Deprecation.warn 'Use concerns: :active_scaffold_association in resources instead of as_association_routes, or concerns :active_scaffold_association in resources block if want to restrict routes with only or except options.'
|
113
|
-
concerns :active_scaffold_association, parent_options
|
114
|
-
end
|
115
|
-
|
116
77
|
def as_nested_resources(*resources)
|
117
78
|
options = resources.extract_options!
|
118
79
|
nested_options = options.merge(parent_scaffold: parent_scaffold)
|
@@ -1,8 +1,9 @@
|
|
1
1
|
# the ever-useful to_label method
|
2
2
|
class ActiveRecord::Base
|
3
3
|
def to_label
|
4
|
-
|
5
|
-
|
4
|
+
to_label_method = ActiveScaffold::Registry.cache :to_label, self.class.name do
|
5
|
+
%i[name label title to_s].find { |attribute| respond_to?(attribute) }
|
6
6
|
end
|
7
|
+
send(to_label_method).to_s if to_label_method
|
7
8
|
end
|
8
9
|
end
|
@@ -18,10 +18,8 @@ module ActiveScaffold::UnsavedRecord
|
|
18
18
|
def keeping_errors
|
19
19
|
old_errors = errors.dup if errors.present?
|
20
20
|
result = yield
|
21
|
-
|
22
|
-
old_errors.each
|
23
|
-
old_errors[attr].each { |msg| errors.add(attr, msg) unless errors.added?(attr, msg) }
|
24
|
-
end
|
21
|
+
old_errors&.each do |attr|
|
22
|
+
old_errors[attr].each { |msg| errors.add(attr, msg) unless errors.added?(attr, msg) }
|
25
23
|
end
|
26
24
|
result && old_errors.blank?
|
27
25
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module ActiveScaffold
|
2
2
|
module Finder
|
3
3
|
def self.like_operator
|
4
|
-
@@like_operator ||= ::ActiveRecord::Base.connection.adapter_name
|
4
|
+
@@like_operator ||= ::ActiveRecord::Base.connection.adapter_name.in?(%w[PostgreSQL PostGIS]) ? 'ILIKE' : 'LIKE'
|
5
5
|
end
|
6
6
|
|
7
7
|
module ClassMethods
|
@@ -68,7 +68,9 @@ module ActiveScaffold
|
|
68
68
|
value = columns_token[column.name]
|
69
69
|
value = /#{value}/ if column.text?
|
70
70
|
column.search_sql.map do |search_sql|
|
71
|
-
|
71
|
+
# call .to_s so String is returned from CowProxy::String in threadsafe mode
|
72
|
+
# in other case, or method from Mongoid would fail
|
73
|
+
{search_sql.to_s => value}
|
72
74
|
end
|
73
75
|
end.flatten
|
74
76
|
active_scaffold_config.model.or(token_conditions).selector
|
@@ -95,18 +97,16 @@ module ActiveScaffold
|
|
95
97
|
if respond_to?("condition_for_#{column.name}_column")
|
96
98
|
return send("condition_for_#{column.name}_column", column, value, like_pattern)
|
97
99
|
end
|
98
|
-
return unless column
|
100
|
+
return unless column&.search_sql && value.present?
|
99
101
|
search_ui = column.search_ui || column.column_type
|
100
102
|
begin
|
101
103
|
sql, *values =
|
102
104
|
if search_ui && respond_to?("condition_for_#{search_ui}_type")
|
103
105
|
send("condition_for_#{search_ui}_type", column, value, like_pattern)
|
106
|
+
elsif column.search_sql.instance_of? Proc
|
107
|
+
column.search_sql.call(value)
|
104
108
|
else
|
105
|
-
|
106
|
-
column.search_sql.call(value)
|
107
|
-
else
|
108
|
-
condition_for_search_ui(column, value, like_pattern, search_ui)
|
109
|
-
end
|
109
|
+
condition_for_search_ui(column, value, like_pattern, search_ui)
|
110
110
|
end
|
111
111
|
return nil unless sql
|
112
112
|
|
@@ -122,7 +122,7 @@ module ActiveScaffold
|
|
122
122
|
def condition_for_search_ui(column, value, like_pattern, search_ui)
|
123
123
|
case search_ui
|
124
124
|
when :boolean, :checkbox
|
125
|
-
['
|
125
|
+
['%<search_sql>s = ?', column.column ? ActiveScaffold::Core.column_type_cast(value, column.column) : value]
|
126
126
|
when :integer, :decimal, :float
|
127
127
|
condition_for_numeric(column, value)
|
128
128
|
when :string, :range
|
@@ -131,57 +131,59 @@ module ActiveScaffold
|
|
131
131
|
condition_for_datetime(column, value)
|
132
132
|
when :select, :multi_select, :country, :usa_state, :chosen, :multi_chosen
|
133
133
|
values = Array(value).select(&:present?)
|
134
|
-
['
|
134
|
+
['%<search_sql>s in (?)', values] if values.present?
|
135
135
|
else
|
136
136
|
if column.text?
|
137
|
-
["
|
137
|
+
["%<search_sql>s #{ActiveScaffold::Finder.like_operator} ?", like_pattern.sub('?', value)]
|
138
138
|
else
|
139
|
-
['
|
139
|
+
['%<search_sql>s = ?', ActiveScaffold::Core.column_type_cast(value, column.column)]
|
140
140
|
end
|
141
141
|
end
|
142
142
|
end
|
143
143
|
|
144
144
|
def condition_for_numeric(column, value)
|
145
145
|
if !value.is_a?(Hash)
|
146
|
-
['
|
146
|
+
['%<search_sql>s = ?', condition_value_for_numeric(column, value)]
|
147
147
|
elsif ActiveScaffold::Finder::NULL_COMPARATORS.include?(value[:opt])
|
148
148
|
condition_for_null_type(column, value[:opt])
|
149
149
|
elsif value[:from].blank? || !ActiveScaffold::Finder::NUMERIC_COMPARATORS.include?(value[:opt])
|
150
150
|
nil
|
151
151
|
elsif value[:opt] == 'BETWEEN'
|
152
|
-
['(
|
152
|
+
['(%<search_sql>s BETWEEN ? AND ?)', condition_value_for_numeric(column, value[:from]), condition_value_for_numeric(column, value[:to])]
|
153
153
|
else
|
154
|
-
["
|
154
|
+
["%<search_sql>s #{value[:opt]} ?", condition_value_for_numeric(column, value[:from])]
|
155
155
|
end
|
156
156
|
end
|
157
157
|
|
158
158
|
def condition_for_range(column, value, like_pattern = nil)
|
159
159
|
if !value.is_a?(Hash)
|
160
160
|
if column.text?
|
161
|
-
["
|
161
|
+
["%<search_sql>s #{ActiveScaffold::Finder.like_operator} ?", like_pattern.sub('?', value)]
|
162
162
|
else
|
163
|
-
['
|
163
|
+
['%<search_sql>s = ?', ActiveScaffold::Core.column_type_cast(value, column.column)]
|
164
164
|
end
|
165
165
|
elsif ActiveScaffold::Finder::NULL_COMPARATORS.include?(value[:opt])
|
166
166
|
condition_for_null_type(column, value[:opt], like_pattern)
|
167
167
|
elsif value[:from].blank?
|
168
168
|
nil
|
169
169
|
elsif ActiveScaffold::Finder::STRING_COMPARATORS.values.include?(value[:opt])
|
170
|
-
["
|
170
|
+
["%<search_sql>s #{ActiveScaffold::Finder.like_operator} ?", value[:opt].sub('?', value[:from])]
|
171
171
|
elsif value[:opt] == 'BETWEEN'
|
172
|
-
['(
|
172
|
+
['(%<search_sql>s BETWEEN ? AND ?)', value[:from], value[:to]]
|
173
173
|
elsif ActiveScaffold::Finder::NUMERIC_COMPARATORS.include?(value[:opt])
|
174
|
-
["
|
174
|
+
["%<search_sql>s #{value[:opt]} ?", value[:from]]
|
175
175
|
end
|
176
176
|
end
|
177
177
|
|
178
178
|
def tables_for_translating_days_and_months(format)
|
179
|
+
# rubocop:disable Style/FormatStringToken
|
179
180
|
keys = {
|
180
181
|
'%A' => 'date.day_names',
|
181
182
|
'%a' => 'date.abbr_day_names',
|
182
183
|
'%B' => 'date.month_names',
|
183
184
|
'%b' => 'date.abbr_month_names'
|
184
185
|
}
|
186
|
+
# rubocop:enable Style/FormatStringToken
|
185
187
|
key_index = keys.keys.map { |key| [key, format.index(key)] }.to_h
|
186
188
|
keys.select! { |k, _| key_index[k] }
|
187
189
|
keys.sort_by { |k, _| key_index[k] }.map do |_, k|
|
@@ -226,37 +228,60 @@ module ActiveScaffold
|
|
226
228
|
[format, parts[:offset]]
|
227
229
|
end
|
228
230
|
|
231
|
+
def local_time_from_hash(value, conversion = :to_time)
|
232
|
+
time = Time.zone.local(*%i[year month day hour minute second].collect { |part| value[part].to_i })
|
233
|
+
time.send(conversion)
|
234
|
+
rescue StandardError => e
|
235
|
+
message = "Error creating time from #{value.inspect}:"
|
236
|
+
Rails.logger.warn "#{message}\n#{e.message}\n#{e.backtrace.join("\n")}"
|
237
|
+
nil
|
238
|
+
end
|
239
|
+
|
240
|
+
def parse_date_with_format(value, format_name)
|
241
|
+
format = I18n.t("date.formats.#{format_name || :default}")
|
242
|
+
format.gsub!(/%-d|%-m|%_m/) { |s| s.gsub(/[-_]/, '') } # strptime fails with %-d, %-m, %_m
|
243
|
+
en_value = I18n.locale == :en ? value : translate_days_and_months(value, format)
|
244
|
+
Date.strptime(en_value, format)
|
245
|
+
rescue StandardError => e
|
246
|
+
message = "Error parsing date from #{en_value}"
|
247
|
+
message << " (#{value})" if en_value != value
|
248
|
+
message << ", with format #{format}" if format
|
249
|
+
Rails.logger.warn "#{message}:\n#{e.message}\n#{e.backtrace.join("\n")}"
|
250
|
+
nil
|
251
|
+
end
|
252
|
+
|
253
|
+
def parse_time_with_format(value, format, offset)
|
254
|
+
format.gsub!(/%-d|%-m|%_m/) { |s| s.gsub(/[-_]/, '') } # strptime fails with %-d, %-m, %_m
|
255
|
+
en_value = I18n.locale == :en ? value : translate_days_and_months(value, format)
|
256
|
+
time = Time.strptime(en_value, format)
|
257
|
+
offset ? time : Time.zone.local_to_utc(time).in_time_zone
|
258
|
+
rescue StandardError => e
|
259
|
+
message = "Error parsing time from #{en_value}"
|
260
|
+
message << " (#{value})" if en_value != value
|
261
|
+
message << ", with format #{format}" if format
|
262
|
+
Rails.logger.warn "#{message}:\n#{e.message}\n#{e.backtrace.join("\n")}"
|
263
|
+
nil
|
264
|
+
end
|
265
|
+
|
229
266
|
def condition_value_for_datetime(column, value, conversion = :to_time)
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
value.send(conversion)
|
241
|
-
end
|
242
|
-
elsif conversion == :to_date
|
243
|
-
format = I18n.t("date.formats.#{column.options[:format] || :default}")
|
244
|
-
format.gsub!(/%-d|%-m|%_m/) { |s| s.gsub(/[-_]/, '') } # strptime fails with %-d, %-m, %_m
|
245
|
-
value = translate_days_and_months(value, format) if I18n.locale != :en
|
246
|
-
Date.strptime(value, format) rescue nil
|
247
|
-
elsif value.include?('T')
|
248
|
-
Time.zone.parse(value)
|
249
|
-
else # datetime
|
250
|
-
format, offset = format_for_datetime(column, value)
|
251
|
-
format.gsub!(/%-d|%-m|%_m/) { |s| s.gsub(/[-_]/, '') } # strptime fails with %-d, %-m, %_m
|
252
|
-
value = translate_days_and_months(value, format) if I18n.locale != :en
|
253
|
-
time = DateTime.strptime(value, format) rescue nil
|
254
|
-
if time
|
255
|
-
time = Time.zone.local_to_utc(time).in_time_zone unless offset
|
256
|
-
time = time.send(conversion) unless conversion == :to_time
|
257
|
-
end
|
258
|
-
time
|
267
|
+
return if value.nil? || value.blank?
|
268
|
+
if value.is_a? Hash
|
269
|
+
local_time_from_hash(value, conversion)
|
270
|
+
elsif value.respond_to?(:strftime)
|
271
|
+
if conversion == :to_time
|
272
|
+
# Explicitly get the current zone, because TimeWithZone#to_time in rails 3.2.3 returns UTC.
|
273
|
+
# https://github.com/rails/rails/pull/2453
|
274
|
+
value.to_time.in_time_zone
|
275
|
+
else
|
276
|
+
value.send(conversion)
|
259
277
|
end
|
278
|
+
elsif conversion == :to_date
|
279
|
+
parse_date_with_format(value, column.options[:format])
|
280
|
+
elsif value.include?('T')
|
281
|
+
Time.zone.parse(value)
|
282
|
+
else # datetime
|
283
|
+
time = parse_time_with_format(value, *format_for_datetime(column, value))
|
284
|
+
conversion == :to_time ? time : time.send(conversion)
|
260
285
|
end
|
261
286
|
end
|
262
287
|
|
@@ -264,14 +289,15 @@ module ActiveScaffold
|
|
264
289
|
return value if value.nil?
|
265
290
|
value = column.number_to_native(value) if column.options[:format] && column.search_ui != :number
|
266
291
|
case (column.search_ui || column.column.type)
|
267
|
-
when :integer then
|
268
|
-
|
269
|
-
|
270
|
-
if Rails.version >= '4.2.0'
|
271
|
-
::ActiveRecord::Type::Decimal.new.type_cast_from_user(value)
|
292
|
+
when :integer then
|
293
|
+
if value.is_a?(TrueClass) || value.is_a?(FalseClass)
|
294
|
+
value ? 1 : 0
|
272
295
|
else
|
273
|
-
|
296
|
+
value.to_i
|
274
297
|
end
|
298
|
+
when :float then value.to_f
|
299
|
+
when :decimal
|
300
|
+
::ActiveRecord::Type::Decimal.new.type_cast_from_user(value)
|
275
301
|
else
|
276
302
|
value
|
277
303
|
end
|
@@ -293,28 +319,28 @@ module ActiveScaffold
|
|
293
319
|
if from_value.nil? && to_value.nil?
|
294
320
|
nil
|
295
321
|
elsif !from_value
|
296
|
-
['
|
322
|
+
['%<search_sql>s <= ?', to_value.to_s(:db)]
|
297
323
|
elsif !to_value
|
298
|
-
['
|
324
|
+
['%<search_sql>s >= ?', from_value.to_s(:db)]
|
299
325
|
else
|
300
|
-
['
|
326
|
+
['%<search_sql>s BETWEEN ? AND ?', from_value.to_s(:db), to_value.to_s(:db)]
|
301
327
|
end
|
302
328
|
end
|
303
329
|
|
304
330
|
def condition_for_record_select_type(column, value, like_pattern = nil)
|
305
331
|
if value.is_a?(Array)
|
306
|
-
['
|
332
|
+
['%<search_sql>s IN (?)', value]
|
307
333
|
else
|
308
|
-
['
|
334
|
+
['%<search_sql>s = ?', value]
|
309
335
|
end
|
310
336
|
end
|
311
337
|
|
312
338
|
def condition_for_null_type(column, value, like_pattern = nil)
|
313
339
|
case value.to_s
|
314
340
|
when 'null'
|
315
|
-
['
|
341
|
+
['%<search_sql>s is null', []]
|
316
342
|
when 'not_null'
|
317
|
-
['
|
343
|
+
['%<search_sql>s is not null', []]
|
318
344
|
end
|
319
345
|
end
|
320
346
|
end
|
@@ -366,10 +392,12 @@ module ActiveScaffold
|
|
366
392
|
@active_scaffold_references ||= []
|
367
393
|
end
|
368
394
|
|
369
|
-
# Override this method on your controller to define conditions to be used when querying a recordset (e.g. for List).
|
395
|
+
# Override this method on your controller to define conditions to be used when querying a recordset (e.g. for List).
|
396
|
+
# The return of this method should be any format compatible with the :conditions clause of ActiveRecord::Base's find.
|
370
397
|
def conditions_for_collection; end
|
371
398
|
|
372
|
-
# Override this method on your controller to define joins to be used when querying a recordset (e.g. for List).
|
399
|
+
# Override this method on your controller to define joins to be used when querying a recordset (e.g. for List).
|
400
|
+
# The return of this method should be any format compatible with the :joins clause of ActiveRecord::Base's find.
|
373
401
|
def joins_for_collection; end
|
374
402
|
|
375
403
|
# Override this method on your controller to provide custom finder options to the find() call. The return of this method should be a hash.
|
@@ -381,9 +409,9 @@ module ActiveScaffold
|
|
381
409
|
params_hash active_scaffold_embedded_params[:conditions]
|
382
410
|
end
|
383
411
|
|
384
|
-
def all_conditions
|
412
|
+
def all_conditions(include_id_condition = true)
|
385
413
|
[
|
386
|
-
id_condition,
|
414
|
+
(id_condition if include_id_condition), # for list with id (e.g. /users/:id/index)
|
387
415
|
active_scaffold_conditions, # from the search modules
|
388
416
|
conditions_for_collection, # from the dev
|
389
417
|
conditions_from_params, # from the parameters (e.g. /users/list?first_name=Fred)
|
@@ -407,15 +435,20 @@ module ActiveScaffold
|
|
407
435
|
end
|
408
436
|
|
409
437
|
# valid options may include:
|
410
|
-
# * :sorting - a Sorting DataStructure (basically an array of hashes of field => direction,
|
438
|
+
# * :sorting - a Sorting DataStructure (basically an array of hashes of field => direction,
|
439
|
+
# e.g. [{:field1 => 'asc'}, {:field2 => 'desc'}]).
|
440
|
+
# please note that multi-column sorting has some limitations: if any column in a multi-field
|
441
|
+
# sort uses method-based sorting, it will be ignored. method sorting only works for single-column sorting.
|
411
442
|
# * :per_page
|
412
443
|
# * :page
|
413
444
|
def finder_options(options = {})
|
414
445
|
search_conditions = all_conditions
|
415
446
|
|
447
|
+
sorting = options[:sorting]&.clause((grouped_columns_calculations if grouped_search?))
|
448
|
+
sorting = sorting.map(&Arel.method(:sql)) if sorting && active_scaffold_config.active_record?
|
416
449
|
# create a general-use options array that's compatible with Rails finders
|
417
450
|
finder_options = {
|
418
|
-
:reorder =>
|
451
|
+
:reorder => sorting,
|
419
452
|
:conditions => search_conditions
|
420
453
|
}
|
421
454
|
if active_scaffold_config.mongoid?
|
@@ -466,7 +499,7 @@ module ActiveScaffold
|
|
466
499
|
|
467
500
|
query = append_to_query(query, find_options)
|
468
501
|
# we build the paginator differently for method- and sql-based sorting
|
469
|
-
pager = if options[:sorting]
|
502
|
+
pager = if options[:sorting]&.sorts_by_method?
|
470
503
|
::Paginator.new(count, options[:per_page]) do |offset, per_page|
|
471
504
|
calculate_last_modified(query)
|
472
505
|
sorted_collection = sort_collection_by_column(query.to_a, *options[:sorting].first)
|
@@ -484,12 +517,12 @@ module ActiveScaffold
|
|
484
517
|
end
|
485
518
|
|
486
519
|
def calculate_last_modified(query)
|
487
|
-
return unless conditional_get_support? && query.klass
|
520
|
+
return unless conditional_get_support? && ActiveScaffold::OrmChecks.columns_hash(query.klass)['updated_at']
|
488
521
|
@last_modified = query.maximum(:updated_at)
|
489
522
|
end
|
490
523
|
|
491
|
-
def calculate_query
|
492
|
-
conditions = all_conditions
|
524
|
+
def calculate_query(id_condition = true)
|
525
|
+
conditions = all_conditions(id_condition)
|
493
526
|
includes = active_scaffold_config.list.count_includes
|
494
527
|
includes ||= active_scaffold_references if conditions.present?
|
495
528
|
left_joins = active_scaffold_outer_joins
|
@@ -501,13 +534,13 @@ module ActiveScaffold
|
|
501
534
|
end
|
502
535
|
|
503
536
|
def append_to_query(relation, options)
|
504
|
-
options.assert_valid_keys :where, :select, :having, :group, :reorder, :order, :limit, :offset,
|
537
|
+
options.assert_valid_keys :where, :select, :having, :group, :reorder, :order, :limit, :offset,
|
538
|
+
:joins, :left_joins, :left_outer_joins, :includes, :lock, :readonly,
|
539
|
+
:from, :conditions, :preload, :references
|
505
540
|
relation = options.reject { |_, v| v.blank? }.inject(relation) do |rel, (k, v)|
|
506
541
|
k == :conditions ? apply_conditions(rel, *v) : rel.send(k, v)
|
507
542
|
end
|
508
|
-
if options[:left_outer_joins].present? || options[:left_joins].present?
|
509
|
-
relation.distinct_value = true
|
510
|
-
end
|
543
|
+
relation.distinct_value = true if options[:left_outer_joins].present? || options[:left_joins].present?
|
511
544
|
relation
|
512
545
|
end
|
513
546
|
|
@@ -536,7 +569,7 @@ module ActiveScaffold
|
|
536
569
|
def sort_collection_by_column(collection, column, order)
|
537
570
|
sorter = column.sort[:method]
|
538
571
|
collection = collection.sort_by do |record|
|
539
|
-
value =
|
572
|
+
value = sorter.is_a?(Proc) ? record.instance_eval(&sorter) : record.instance_eval(sorter.to_s)
|
540
573
|
value = '' if value.nil?
|
541
574
|
value
|
542
575
|
end
|