active_scaffold 3.7.4 → 3.7.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.rdoc +19 -0
- data/app/views/active_scaffold_overrides/_list_with_header.html.erb +1 -1
- data/app/views/active_scaffold_overrides/_render_field.js.erb +10 -7
- data/lib/active_scaffold/actions/common_search.rb +3 -3
- data/lib/active_scaffold/actions/core.rb +2 -1
- data/lib/active_scaffold/actions/create.rb +3 -1
- data/lib/active_scaffold/actions/field_search.rb +29 -18
- data/lib/active_scaffold/constraints.rb +18 -11
- data/lib/active_scaffold/core.rb +1 -1
- data/lib/active_scaffold/data_structures/association/abstract.rb +4 -0
- data/lib/active_scaffold/data_structures/association/active_record.rb +4 -0
- data/lib/active_scaffold/data_structures/nested_info.rb +11 -4
- data/lib/active_scaffold/data_structures/sorting.rb +3 -3
- data/lib/active_scaffold/finder.rb +15 -7
- data/lib/active_scaffold/helpers/action_link_helpers.rb +1 -1
- data/lib/active_scaffold/helpers/association_helpers.rb +9 -3
- data/lib/active_scaffold/helpers/form_column_helpers.rb +4 -2
- data/lib/active_scaffold/helpers/human_condition_helpers.rb +2 -0
- data/lib/active_scaffold/helpers/list_column_helpers.rb +5 -2
- data/lib/active_scaffold/helpers/search_column_helpers.rb +17 -9
- data/lib/active_scaffold/helpers/view_helpers.rb +15 -20
- data/lib/active_scaffold/version.rb +1 -1
- data/test/helpers/form_column_helpers_test.rb +2 -1
- data/test/misc/finder_test.rb +5 -5
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cfe45d0a0b28f5cb324e7104add97e7a105de6986accf8d64db02cf5f65f19ea
|
4
|
+
data.tar.gz: f8461b6a58ce6b1c93df2eaf95540a759fdcd73d21f256ee1a38ea4436b73987
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a65de91f886605e57d0f183295d9d1f8ea358b4657826143265f8359f4d41761b6325644fcb6d5dc1d5e5d4b87bbc966bf8cd9d4ee66db0ae4048455eb0c6b62
|
7
|
+
data.tar.gz: 9fb4e3b6a38c5f340cdf430146ae9616a5e9a93e66c11e32dece9cb5704229499610da861916371e3bafb973d43705c9716228b5d04c50f896d5538320e0125c
|
data/CHANGELOG.rdoc
CHANGED
@@ -1,3 +1,22 @@
|
|
1
|
+
= 3.7.6
|
2
|
+
- Respect :label_method set in form_ui_options on list.
|
3
|
+
- Fix grouped search for PostgreSQL
|
4
|
+
- Fix loading current search params when search form is open and store_user_settings is disabled
|
5
|
+
|
6
|
+
= 3.7.5
|
7
|
+
- Fix always show search for nested list on multi-level through associations
|
8
|
+
- Support changing step, min and max options with column options or search_ui options for decimal and integer search_ui
|
9
|
+
- Support null and not null comparators in date and datetime search_ui
|
10
|
+
- Support defining options_for_association_conditions, association_klass_scoped and active_scaffold_enum_options helpers prefixed with model name. Useful when clear_helpers is not called in ApplicationController.
|
11
|
+
- Fix class for columns with form_ui or css_class when refreshing column
|
12
|
+
- Eval after_config_callback proc in the context of AS config
|
13
|
+
- Fix registering constrained fields on polymorphic association
|
14
|
+
- Improve applying constraints for polymorphic associations when constraining with multiple ids, set only the foreign type
|
15
|
+
- Support prefixing helpers with base class for models using STI besides model name prefix
|
16
|
+
- Support create when nested on through association when source association is singular, as rails creates the joint model
|
17
|
+
- Allow to set controller nil for association columns to STI models
|
18
|
+
- Add session argument to condition_for_column and condition_for_<column>_column, so custom conditions on columns may use the session, make it optional so it's backwards compatible
|
19
|
+
|
1
20
|
= 3.7.4
|
2
21
|
- Support loading both jquery and jquery-ui externally
|
3
22
|
- Fix subquery in search with STI
|
@@ -5,7 +5,7 @@
|
|
5
5
|
<table>
|
6
6
|
<tbody class="before-header" id="<%= before_header_id -%>">
|
7
7
|
<% if active_scaffold_config.list.always_show_search %>
|
8
|
-
<% old_record, @record = @record,
|
8
|
+
<% old_record, @record = @record, active_scaffold_config.model.new %>
|
9
9
|
<tr>
|
10
10
|
<td>
|
11
11
|
<div class="active-scaffold show_search-view <%= "#{id_from_controller params[:controller]}-view" %> view">
|
@@ -1,9 +1,10 @@
|
|
1
1
|
<%
|
2
|
-
column =
|
3
|
-
render_field
|
4
|
-
|
5
|
-
|
6
|
-
|
2
|
+
column =
|
3
|
+
if render_field.is_a? ActiveScaffold::DataStructures::Column
|
4
|
+
render_field
|
5
|
+
else
|
6
|
+
active_scaffold_config.columns[render_field]
|
7
|
+
end
|
7
8
|
return unless @main_columns.include? column.name
|
8
9
|
@rendered ||= Set.new
|
9
10
|
return if @rendered.include? column.name
|
@@ -14,13 +15,15 @@
|
|
14
15
|
renders_as = column_renders_as(column)
|
15
16
|
form_ui = column.form_ui
|
16
17
|
end
|
18
|
+
|
19
|
+
column_css_class = column.css_class unless column.css_class.nil? || column.css_class.is_a?(Proc)
|
17
20
|
options = {field_class: "#{column.name}-input", hidden: form_ui == :hidden}
|
18
21
|
options[:subform_class] = "#{column.name}-sub-form" if column.association
|
19
22
|
options[:attrs] =
|
20
23
|
if renders_as == :subform
|
21
|
-
active_scaffold_subform_attributes(column,
|
24
|
+
active_scaffold_subform_attributes(column, column_css_class)
|
22
25
|
else
|
23
|
-
{class:
|
26
|
+
{class: "form-element #{:required if column.required?(@form_action)} #{column.form_ui} #{column_css_class}", id: ''}
|
24
27
|
end
|
25
28
|
html =
|
26
29
|
if scope
|
@@ -3,9 +3,9 @@ module ActiveScaffold::Actions
|
|
3
3
|
def self.included(base)
|
4
4
|
return if base < InstanceMethods
|
5
5
|
base.send :include, InstanceMethods
|
6
|
-
base.before_action :search_authorized_filter, :
|
7
|
-
base.before_action :store_search_params_into_session, :only => [
|
8
|
-
base.before_action :do_search, :
|
6
|
+
base.before_action :search_authorized_filter, only: :show_search
|
7
|
+
base.before_action :store_search_params_into_session, :only => %i[index show_search]
|
8
|
+
base.before_action :do_search, only: [:index]
|
9
9
|
base.helper_method :search_params
|
10
10
|
end
|
11
11
|
|
@@ -17,6 +17,7 @@ module ActiveScaffold::Actions
|
|
17
17
|
base.helper_method :embedded?
|
18
18
|
base.helper_method :loading_embedded?
|
19
19
|
base.helper_method :calculate_query
|
20
|
+
base.helper_method :calculate_subquery
|
20
21
|
base.helper_method :new_model
|
21
22
|
base.helper_method :touch_device?
|
22
23
|
base.helper_method :hover_via_click?
|
@@ -134,8 +135,8 @@ module ActiveScaffold::Actions
|
|
134
135
|
parent = parent_model.new
|
135
136
|
copy_attributes(find_if_allowed(params[:parent_id], :read, parent_model), parent) if params[:parent_id]
|
136
137
|
parent.id = params[:parent_id]
|
137
|
-
parent = update_record_from_params(parent, cfg.send(params[:parent_id] ? :update : :create).columns, params[:record], true) if @column.send_form_on_update_column
|
138
138
|
apply_constraints_to_record(parent) unless params[:parent_id]
|
139
|
+
parent = update_record_from_params(parent, cfg.send(params[:parent_id] ? :update : :create).columns, params[:record], true) if @column.send_form_on_update_column
|
139
140
|
if association.collection?
|
140
141
|
record.send(association.name) << parent
|
141
142
|
else
|
@@ -93,8 +93,10 @@ module ActiveScaffold::Actions
|
|
93
93
|
def do_create(options = {})
|
94
94
|
attributes = options[:attributes] || params[:record]
|
95
95
|
active_scaffold_config.model.transaction do
|
96
|
-
@record =
|
96
|
+
@record = new_model
|
97
|
+
# before assign params, to set foreign_type of constraints in polymorphic association with multiple id
|
97
98
|
apply_constraints_to_record(@record, :allow_autosave => true)
|
99
|
+
@record = update_record_from_params(@record, active_scaffold_config.create.columns, attributes)
|
98
100
|
create_association_with_parent(@record) if nested?
|
99
101
|
before_create_save(@record)
|
100
102
|
# errors to @record can be added by update_record_from_params when association fails to set and ActiveRecord::RecordNotSaved is raised
|
@@ -56,25 +56,33 @@ module ActiveScaffold::Actions
|
|
56
56
|
|
57
57
|
def custom_finder_options
|
58
58
|
if grouped_search?
|
59
|
-
|
60
|
-
select_query = grouped_search_select
|
61
|
-
select_query << group_sql.as(search_group_column.name.to_s) if search_group_column && group_sql.respond_to?(:to_sql)
|
62
|
-
{group: group_sql, select: select_query}
|
59
|
+
grouped_search_finder_options
|
63
60
|
else
|
64
61
|
super
|
65
62
|
end
|
66
63
|
end
|
67
64
|
|
65
|
+
def grouped_search_finder_options
|
66
|
+
group_sql = calculation_for_group_by(search_group_column&.field || search_group_name, search_group_function) if search_group_function
|
67
|
+
group_by = group_sql&.to_sql || quoted_select_columns(search_group_column&.select_columns || [search_group_name])
|
68
|
+
select_query = grouped_search_select + (group_sql ? [group_sql.as(search_group_column.name.to_s)] : group_by)
|
69
|
+
{group: group_by, select: select_query, reorder: grouped_sorting(group_by)}
|
70
|
+
end
|
71
|
+
|
68
72
|
def grouped_search_select
|
69
|
-
|
70
|
-
|
71
|
-
select_query << active_scaffold_config.columns[active_scaffold_config.model.inheritance_column].field
|
72
|
-
end
|
73
|
-
grouped_columns_calculations.each do |name, part|
|
74
|
-
select_query << (part.respond_to?(:as) ? part : Arel::Nodes::SqlLiteral.new(part)).as(name.to_s)
|
73
|
+
grouped_columns_calculations.map do |name, part|
|
74
|
+
(part.respond_to?(:as) ? part : Arel::Nodes::SqlLiteral.new(part)).as(name.to_s)
|
75
75
|
end
|
76
76
|
end
|
77
77
|
|
78
|
+
def grouped_sorting(group_sql)
|
79
|
+
return unless search_group_column && active_scaffold_config.list.user.sorting
|
80
|
+
group_sort = search_group_function ? group_sql : search_group_column.sort[:sql] if search_group_column.sortable?
|
81
|
+
grouped_columns = grouped_columns_calculations.merge(search_group_column.name => group_sort)
|
82
|
+
sorting = active_scaffold_config.list.user.sorting.clause(grouped_columns)
|
83
|
+
active_scaffold_config.active_record? ? sorting&.map(&Arel.method(:sql)) : sorting
|
84
|
+
end
|
85
|
+
|
78
86
|
def grouped_columns_calculations
|
79
87
|
@grouped_columns_calculations ||= list_columns[1..-1].each_with_object({}) do |c, h|
|
80
88
|
h[c.name] = calculation_for_group_search(c)
|
@@ -85,21 +93,24 @@ module ActiveScaffold::Actions
|
|
85
93
|
sql_function column.calculate.to_s, column.active_record_class.arel_table[column.name]
|
86
94
|
end
|
87
95
|
|
88
|
-
def calculation_for_group_by(group_sql)
|
89
|
-
return group_sql unless search_group_function
|
96
|
+
def calculation_for_group_by(group_sql, group_function)
|
90
97
|
group_sql = Arel::Nodes::SqlLiteral.new(group_sql)
|
91
|
-
case
|
98
|
+
case group_function
|
92
99
|
when 'year', 'month', 'quarter'
|
93
|
-
|
100
|
+
extract_sql_fn(group_function, group_sql)
|
94
101
|
when 'year_month'
|
95
|
-
|
102
|
+
sql_operator(sql_operator(extract_sql_fn('year', group_sql), '*', 100), '+', extract_sql_fn('month', group_sql))
|
96
103
|
when 'year_quarter'
|
97
|
-
sql_operator(sql_operator(
|
104
|
+
sql_operator(sql_operator(extract_sql_fn('year', group_sql), '*', 10), '+', extract_sql_fn('quarter', group_sql))
|
98
105
|
else
|
99
|
-
raise "#{
|
106
|
+
raise "#{group_function} unsupported, override calculation_for_group_by in #{self.class.name}"
|
100
107
|
end
|
101
108
|
end
|
102
109
|
|
110
|
+
def extract_sql_fn(part, column)
|
111
|
+
sql_function('extract', sql_operator(Arel::Nodes::SqlLiteral.new(part), 'FROM', column))
|
112
|
+
end
|
113
|
+
|
103
114
|
def sql_function(function, *args)
|
104
115
|
args.map! { |arg| quoted_arel_value(arg) }
|
105
116
|
Arel::Nodes::NamedFunction.new(function, args)
|
@@ -157,7 +168,7 @@ module ActiveScaffold::Actions
|
|
157
168
|
search_params.each do |key, value|
|
158
169
|
next unless columns.include? key
|
159
170
|
column = active_scaffold_config.columns[key]
|
160
|
-
search_condition = self.class.condition_for_column(column, value, text_search)
|
171
|
+
search_condition = self.class.condition_for_column(column, value, text_search, session)
|
161
172
|
next if search_condition.blank?
|
162
173
|
|
163
174
|
active_scaffold_conditions << search_condition
|
@@ -8,17 +8,20 @@ module ActiveScaffold
|
|
8
8
|
|
9
9
|
# Returns the current constraints
|
10
10
|
def active_scaffold_constraints
|
11
|
-
@active_scaffold_constraints ||= active_scaffold_embedded_params[:constraints] || {}
|
11
|
+
@active_scaffold_constraints ||= active_scaffold_embedded_params[:constraints]&.to_unsafe_h || {}
|
12
12
|
end
|
13
13
|
|
14
|
-
def
|
15
|
-
if params_hash?(value)
|
16
|
-
|
17
|
-
elsif value.is_a?(Array)
|
14
|
+
def columns_from_constraint(column_name, value)
|
15
|
+
return if params_hash?(value)
|
16
|
+
if value.is_a?(Array)
|
18
17
|
column = active_scaffold_config.columns[column_name]
|
19
|
-
|
18
|
+
if column&.association&.polymorphic?
|
19
|
+
[column.association.foreign_type.to_sym, (column.name if value.size == 2)].compact
|
20
|
+
elsif column && value.size == 1
|
21
|
+
column.name
|
22
|
+
end
|
20
23
|
else
|
21
|
-
|
24
|
+
column_name.to_sym
|
22
25
|
end
|
23
26
|
end
|
24
27
|
|
@@ -31,7 +34,7 @@ module ActiveScaffold
|
|
31
34
|
# we do NOT register the constraint column, as records will have different values in the column.
|
32
35
|
def register_constraints_with_action_columns(constrained_fields = nil)
|
33
36
|
constrained_fields ||= []
|
34
|
-
constrained_fields |= active_scaffold_constraints.
|
37
|
+
constrained_fields |= active_scaffold_constraints.flat_map { |k, v| columns_from_constraint(k, v) }.compact
|
35
38
|
exclude_actions = []
|
36
39
|
%i[list update].each do |action_name|
|
37
40
|
if active_scaffold_config.actions.include? action_name
|
@@ -161,12 +164,16 @@ module ActiveScaffold
|
|
161
164
|
column = config.columns[k]
|
162
165
|
if column&.association
|
163
166
|
if column.association.collection?
|
164
|
-
record.send(k.to_s).send(:<<, column.association.klass.find(v))
|
167
|
+
record.send(k.to_s).send(:<<, column.association.klass.find(v)) unless column.association.nested?
|
165
168
|
elsif column.association.polymorphic?
|
166
169
|
unless v.is_a?(Array) && v.size >= 2
|
167
170
|
raise ActiveScaffold::MalformedConstraint, polymorphic_constraint_error(column.association), caller
|
168
171
|
end
|
169
|
-
|
172
|
+
if v.size == 2
|
173
|
+
record.send("#{k}=", v[0].constantize.find(v[1]))
|
174
|
+
else
|
175
|
+
record.send("#{column.association.foreign_type}=", v[0])
|
176
|
+
end
|
170
177
|
elsif !column.association.source_reflection&.options&.include?(:through) && # regular singular association, or one-level through association
|
171
178
|
!v.is_a?(Array)
|
172
179
|
record.send("#{k}=", column.association.klass.find(v))
|
@@ -177,7 +184,7 @@ module ActiveScaffold
|
|
177
184
|
# note that we can't take the extra step to correct this unless we're permitted to
|
178
185
|
# run operations where activerecord auto-saves the object.
|
179
186
|
reverse = column.association.reverse_association
|
180
|
-
if reverse
|
187
|
+
if reverse&.singular? && !reverse.belongs_to? && options[:allow_autosave]
|
181
188
|
record.send(k).send("#{reverse.name}=", record)
|
182
189
|
end
|
183
190
|
end
|
data/lib/active_scaffold/core.rb
CHANGED
@@ -63,7 +63,7 @@ module ActiveScaffold
|
|
63
63
|
active_scaffold_config.configure(&block) if block_given?
|
64
64
|
active_scaffold_config.class.after_config_callbacks.each do |callback|
|
65
65
|
if callback.is_a?(Proc)
|
66
|
-
callback
|
66
|
+
instance_eval(&callback)
|
67
67
|
elsif active_scaffold_config.respond_to?(callback)
|
68
68
|
active_scaffold_config.send(callback)
|
69
69
|
end
|
@@ -90,15 +90,22 @@ module ActiveScaffold::DataStructures
|
|
90
90
|
delegate :name, :belongs_to?, :has_one?, :has_many?, :habtm?, :readonly?, :to => :association
|
91
91
|
|
92
92
|
# A through association with has_one or has_many as source association
|
93
|
-
# create cannot be called in nested through associations, and not-nested through associations
|
94
|
-
#
|
95
|
-
#
|
96
|
-
#
|
93
|
+
# create cannot be called in nested through associations, and not-nested through associations, unless:
|
94
|
+
# 1. is through singular and source association has reverse, e.g.:
|
95
|
+
# Employee belongs to vendor, Vendor has many rates, Rate belongs to vendor, Employee has many rates through vendor
|
96
|
+
# Rates association through singular association vendor, source association in Vendor (rates) has reverse (vendor in Rate)
|
97
|
+
# AS will assign the vendor of the employee to the new Rate
|
98
|
+
# 2. source association is singular, e.g.:
|
99
|
+
# Customer has many networks, Network has one (or belongs to) firewall, Customer has many firewalls through networks
|
100
|
+
# 3. create columns include through association of reverse association, e.g.:
|
101
|
+
# Vendor has many employees, Employee has many rates, Vendor has many rates through employees, Rate has one vendor through employee
|
102
|
+
# RatesController has employee in create action columns (reverse is vendor, and through association employee is in create form).
|
97
103
|
def readonly_through_association?(columns)
|
98
104
|
return false unless through_association?
|
99
105
|
return true if association.through_reflection.options[:through] # create not possible, too many levels
|
100
106
|
return true if association.source_reflection.options[:through] # create not possible, too many levels
|
101
107
|
return false if create_through_singular? # create allowed, AS has code for this
|
108
|
+
return false unless association.source_reflection.collection? # create allowed if source is singular, rails creates joint model
|
102
109
|
|
103
110
|
# create allowed only if through reflection in record to be created is included in create columns
|
104
111
|
!child_association || !columns.include?(child_association.through_reflection.name)
|
@@ -121,14 +121,14 @@ module ActiveScaffold::DataStructures
|
|
121
121
|
end
|
122
122
|
|
123
123
|
# builds an order-by clause
|
124
|
-
def clause(
|
124
|
+
def clause(grouped_columns = nil)
|
125
125
|
return nil if sorts_by_method? || default_sorting?
|
126
126
|
|
127
127
|
# unless the sorting is by method, create the sql string
|
128
128
|
order = []
|
129
129
|
each do |sort_column, sort_direction|
|
130
130
|
next if constraint_columns.include? sort_column.name
|
131
|
-
sql =
|
131
|
+
sql = grouped_columns ? grouped_columns[sort_column.name] : sort_column.sort[:sql]
|
132
132
|
next if sql.blank?
|
133
133
|
sql = sql.to_sql if sql.respond_to?(:to_sql)
|
134
134
|
|
@@ -138,7 +138,7 @@ module ActiveScaffold::DataStructures
|
|
138
138
|
order << parts
|
139
139
|
end
|
140
140
|
|
141
|
-
order << @primary_key_order_clause if @sorting_by_primary_key
|
141
|
+
order << @primary_key_order_clause if @sorting_by_primary_key && grouped_columns.nil?
|
142
142
|
order.flatten!(1)
|
143
143
|
order unless order.empty?
|
144
144
|
end
|
@@ -96,11 +96,14 @@ module ActiveScaffold
|
|
96
96
|
# Generates an SQL condition for the given ActiveScaffold column based on
|
97
97
|
# that column's database type (or form_ui ... for virtual columns?).
|
98
98
|
# TODO: this should reside on the column, not the controller
|
99
|
-
def condition_for_column(column, value, text_search
|
99
|
+
def condition_for_column(column, value, text_search, session)
|
100
100
|
like_pattern = like_pattern(text_search)
|
101
101
|
value = value.with_indifferent_access if value.is_a? Hash
|
102
|
-
|
103
|
-
|
102
|
+
column_method = "condition_for_#{column.name}_column"
|
103
|
+
if respond_to?(column_method)
|
104
|
+
args = [column, value, like_pattern]
|
105
|
+
args << session if method(column_method).arity == 4
|
106
|
+
return send("condition_for_#{column.name}_column", *args)
|
104
107
|
end
|
105
108
|
return unless column&.search_sql && value.present?
|
106
109
|
search_ui = column.search_ui || column.column_type
|
@@ -363,6 +366,8 @@ module ActiveScaffold
|
|
363
366
|
|
364
367
|
if column.search_sql.is_a? Proc
|
365
368
|
column.search_sql.call(from_value, to_value, operator)
|
369
|
+
elsif ActiveScaffold::Finder::NULL_COMPARATORS.include?(value['opt'])
|
370
|
+
condition_for_null_type(column, value['opt'], like_pattern)
|
366
371
|
elsif operator.nil?
|
367
372
|
['%<search_sql>s BETWEEN ? AND ?', from_value, to_value] unless from_value.nil? || to_value.nil?
|
368
373
|
else
|
@@ -572,7 +577,7 @@ module ActiveScaffold
|
|
572
577
|
def finder_options(options = {})
|
573
578
|
search_conditions = all_conditions
|
574
579
|
|
575
|
-
sorting = options[:sorting]&.clause
|
580
|
+
sorting = options[:sorting]&.clause
|
576
581
|
sorting = sorting.map(&Arel.method(:sql)) if sorting && active_scaffold_config.active_record?
|
577
582
|
# create a general-use options array that's compatible with Rails finders
|
578
583
|
finder_options = {
|
@@ -650,7 +655,7 @@ module ActiveScaffold
|
|
650
655
|
@last_modified = query.maximum(:updated_at)
|
651
656
|
end
|
652
657
|
|
653
|
-
def
|
658
|
+
def calculate_subquery(id_condition = true)
|
654
659
|
conditions = all_conditions(id_condition)
|
655
660
|
includes = active_scaffold_config.list.count_includes
|
656
661
|
includes ||= active_scaffold_references if conditions.present?
|
@@ -658,8 +663,11 @@ module ActiveScaffold
|
|
658
663
|
left_joins += includes if includes
|
659
664
|
primary_key = active_scaffold_config.primary_key
|
660
665
|
subquery = append_to_query(beginning_of_chain, :conditions => conditions, :joins => joins_for_finder, :left_joins => left_joins, :select => active_scaffold_config.columns[primary_key].field)
|
661
|
-
subquery
|
662
|
-
|
666
|
+
subquery.unscope(:order)
|
667
|
+
end
|
668
|
+
|
669
|
+
def calculate_query(id_condition = true)
|
670
|
+
active_scaffold_config.model.where(active_scaffold_config.primary_key => calculate_subquery(id_condition))
|
663
671
|
end
|
664
672
|
|
665
673
|
def append_to_query(relation, options)
|
@@ -99,7 +99,7 @@ module ActiveScaffold
|
|
99
99
|
def action_link_to_inline_form(link, record)
|
100
100
|
link = link.dup
|
101
101
|
associated = record.send(link.column.association.name)
|
102
|
-
if link.column.association&.polymorphic?
|
102
|
+
if link.column.association&.polymorphic? || link.controller.nil?
|
103
103
|
link.controller = controller_path_for_activerecord(associated.class)
|
104
104
|
return link if link.controller.nil?
|
105
105
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
|
1
2
|
module ActiveScaffold
|
2
3
|
module Helpers
|
3
4
|
module AssociationHelpers
|
@@ -12,6 +13,11 @@ module ActiveScaffold
|
|
12
13
|
end
|
13
14
|
end
|
14
15
|
|
16
|
+
def association_helper_method(association, method)
|
17
|
+
model = association.inverse_klass
|
18
|
+
override_helper_per_model(method, model)
|
19
|
+
end
|
20
|
+
|
15
21
|
# Provides a way to honor the :conditions on an association while searching the association's klass
|
16
22
|
def association_options_find(association, conditions = nil, klass = nil, record = nil)
|
17
23
|
if klass.nil? && association.polymorphic?
|
@@ -24,9 +30,9 @@ module ActiveScaffold
|
|
24
30
|
klass ||= association.klass
|
25
31
|
end
|
26
32
|
|
27
|
-
conditions ||=
|
33
|
+
conditions ||= send(association_helper_method(association, :options_for_association_conditions), association, record)
|
28
34
|
cache_association_options(association, conditions, klass, cache) do
|
29
|
-
klass =
|
35
|
+
klass = send(association_helper_method(association, :association_klass_scoped), association, klass, record)
|
30
36
|
relation = klass.where(conditions)
|
31
37
|
column = column_for_association(association, record)
|
32
38
|
if column&.includes
|
@@ -80,7 +86,7 @@ module ActiveScaffold
|
|
80
86
|
end
|
81
87
|
|
82
88
|
def options_for_association_count(association, record)
|
83
|
-
conditions =
|
89
|
+
conditions = send(association_helper_method(association, :options_for_association_conditions), association, record)
|
84
90
|
association_options_count(association, conditions)
|
85
91
|
end
|
86
92
|
|
@@ -486,7 +486,8 @@ module ActiveScaffold
|
|
486
486
|
record = html_options.delete(:object)
|
487
487
|
options[:selected] = record.send(column.name)
|
488
488
|
options[:object] = record
|
489
|
-
|
489
|
+
enum_options_method = override_helper_per_model(:active_scaffold_enum_options, record.class)
|
490
|
+
options_for_select = send(enum_options_method, column, record, ui_options: ui_options).collect do |text, value|
|
490
491
|
active_scaffold_translated_option(column, text, value)
|
491
492
|
end
|
492
493
|
html_options.merge!(ui_options[:html_options] || {})
|
@@ -535,7 +536,8 @@ module ActiveScaffold
|
|
535
536
|
if column.association
|
536
537
|
sorted_association_options_find(column.association, nil, record)
|
537
538
|
else
|
538
|
-
active_scaffold_enum_options
|
539
|
+
enum_options_method = override_helper_per_model(:active_scaffold_enum_options, record.class)
|
540
|
+
send(enum_options_method, column, record, ui_options: ui_options)
|
539
541
|
end
|
540
542
|
|
541
543
|
selected = record.send(column.association.name) if column.association
|
@@ -55,6 +55,8 @@ module ActiveScaffold
|
|
55
55
|
when 'PAST', 'FUTURE'
|
56
56
|
from, to = controller.class.datetime_from_to(column, value)
|
57
57
|
"#{column.active_record_class.human_attribute_name(column.name)} #{as_('BETWEEN'.downcase).downcase} #{I18n.l(from)} - #{I18n.l(to)}"
|
58
|
+
when 'null', 'not_null'
|
59
|
+
"#{column.active_record_class.human_attribute_name(column.name)} #{as_(value['opt'].downcase).downcase}"
|
58
60
|
else
|
59
61
|
from, to = controller.class.datetime_from_to(column, value)
|
60
62
|
"#{column.active_record_class.human_attribute_name(column.name)} #{as_(value['opt'].downcase).downcase} #{I18n.l(from)} #{value['opt'] == 'BETWEEN' ? '- ' + I18n.l(to) : ''}"
|
@@ -226,7 +226,10 @@ module ActiveScaffold
|
|
226
226
|
I18n.t(options[:group_format] || search_group_function, scope: 'date.formats', num: value)
|
227
227
|
when 'month'
|
228
228
|
I18n.l(Date.new(Time.zone.today.year, value, 1), format: options[:group_format] || search_group_function.to_sym)
|
229
|
-
|
229
|
+
when 'year'
|
230
|
+
value.to_i
|
231
|
+
else
|
232
|
+
value
|
230
233
|
end
|
231
234
|
end
|
232
235
|
|
@@ -263,7 +266,7 @@ module ActiveScaffold
|
|
263
266
|
end
|
264
267
|
|
265
268
|
def format_association_value(value, column, size)
|
266
|
-
method = column.options[:label_method] || :to_label
|
269
|
+
method = (column.list_ui_options || column.options)[:label_method] || :to_label
|
267
270
|
value =
|
268
271
|
if column.association.collection?
|
269
272
|
format_collection_association_value(value, column, method, size)
|
@@ -85,7 +85,8 @@ module ActiveScaffold
|
|
85
85
|
[r.send(method), r.id]
|
86
86
|
end
|
87
87
|
else
|
88
|
-
|
88
|
+
enum_options_method = override_helper_per_model(:active_scaffold_enum_options, record.class)
|
89
|
+
select_options = send(enum_options_method, column, record, ui_options: ui_options).collect do |text, value|
|
89
90
|
active_scaffold_translated_option(column, text, value)
|
90
91
|
end
|
91
92
|
end
|
@@ -103,7 +104,8 @@ module ActiveScaffold
|
|
103
104
|
select_options = sorted_association_options_find(column.association, false, record)
|
104
105
|
else
|
105
106
|
method = column.name
|
106
|
-
|
107
|
+
enum_options_method = override_helper_per_model(:active_scaffold_enum_options, record.class)
|
108
|
+
select_options = send(enum_options_method, column, record, ui_options: ui_options).collect do |text, value|
|
107
109
|
active_scaffold_translated_option(column, text, value)
|
108
110
|
end
|
109
111
|
end
|
@@ -242,11 +244,13 @@ module ActiveScaffold
|
|
242
244
|
alias active_scaffold_search_string active_scaffold_search_range
|
243
245
|
|
244
246
|
def active_scaffold_search_integer(column, options, ui_options: column.options)
|
245
|
-
|
247
|
+
number_opts = ui_options.slice(:step, :min, :max).reverse_merge(step: '1')
|
248
|
+
active_scaffold_search_range(column, options, :number_field_tag, number_opts, ui_options: ui_options)
|
246
249
|
end
|
247
250
|
|
248
251
|
def active_scaffold_search_decimal(column, options, ui_options: column.options)
|
249
|
-
|
252
|
+
number_opts = ui_options.slice(:step, :min, :max).reverse_merge(step: :any)
|
253
|
+
active_scaffold_search_range(column, options, :number_field_tag, number_opts, ui_options: ui_options)
|
250
254
|
end
|
251
255
|
alias active_scaffold_search_float active_scaffold_search_decimal
|
252
256
|
|
@@ -255,7 +259,7 @@ module ActiveScaffold
|
|
255
259
|
'number' => 1, 'unit' => 'DAYS', 'range' => nil}
|
256
260
|
current_search.merge!(options[:value]) unless options[:value].nil?
|
257
261
|
tags = [
|
258
|
-
active_scaffold_search_datetime_comparator_tag(column, options, current_search),
|
262
|
+
active_scaffold_search_datetime_comparator_tag(column, options, current_search, ui_options: column.options),
|
259
263
|
active_scaffold_search_datetime_trend_tag(column, options, current_search),
|
260
264
|
active_scaffold_search_datetime_numeric_tag(column, options, current_search, ui_options: ui_options, field_ui: field_ui),
|
261
265
|
active_scaffold_search_datetime_range_tag(column, options, current_search)
|
@@ -275,13 +279,17 @@ module ActiveScaffold
|
|
275
279
|
active_scaffold_search_datetime(column, options, ui_options: ui_options, field_ui: :date)
|
276
280
|
end
|
277
281
|
|
278
|
-
def active_scaffold_search_datetime_comparator_options(column)
|
282
|
+
def active_scaffold_search_datetime_comparator_options(column, ui_options: column.options)
|
279
283
|
select_options = ActiveScaffold::Finder::DATE_COMPARATORS.collect { |comp| [as_(comp.downcase.to_sym), comp] }
|
280
|
-
select_options
|
284
|
+
select_options.concat(ActiveScaffold::Finder::NUMERIC_COMPARATORS.collect { |comp| [as_(comp.downcase.to_sym), comp] })
|
285
|
+
if include_null_comparators? column, ui_options: ui_options
|
286
|
+
select_options.concat(ActiveScaffold::Finder::NULL_COMPARATORS.collect { |comp| [as_(comp), comp] })
|
287
|
+
end
|
288
|
+
select_options
|
281
289
|
end
|
282
290
|
|
283
|
-
def active_scaffold_search_datetime_comparator_tag(column, options, current_search)
|
284
|
-
choices = options_for_select(active_scaffold_search_datetime_comparator_options(column), current_search['opt'])
|
291
|
+
def active_scaffold_search_datetime_comparator_tag(column, options, current_search, ui_options: column.options)
|
292
|
+
choices = options_for_select(active_scaffold_search_datetime_comparator_options(column, ui_options: ui_options), current_search['opt'])
|
285
293
|
select_tag("#{options[:name]}[opt]", choices, id: "#{options[:id]}_opt", class: 'as_search_range_option as_search_date_time_option')
|
286
294
|
end
|
287
295
|
|
@@ -76,15 +76,9 @@ module ActiveScaffold
|
|
76
76
|
link_to label, '#', :data => data, :style => 'display: none;', :class => 'as-js-button visibility-toggle'
|
77
77
|
end
|
78
78
|
|
79
|
-
def list_row_class_method(record)
|
80
|
-
return @_list_row_class_method if defined? @_list_row_class_method
|
81
|
-
class_override_helper = "#{clean_class_name(record.class.name)}_list_row_class"
|
82
|
-
@_list_row_class_method = (class_override_helper if respond_to?(class_override_helper))
|
83
|
-
end
|
84
|
-
|
85
79
|
def list_row_class(record)
|
86
|
-
class_override_helper =
|
87
|
-
class_override_helper ? send(class_override_helper, record) : ''
|
80
|
+
class_override_helper = override_helper_per_model(:list_row_class, record.class)
|
81
|
+
class_override_helper != :list_row_class ? send(class_override_helper, record) : ''
|
88
82
|
end
|
89
83
|
|
90
84
|
def list_row_attributes(tr_class, tr_id, data_refresh)
|
@@ -159,21 +153,22 @@ module ActiveScaffold
|
|
159
153
|
name.underscore.tr('/', '_')
|
160
154
|
end
|
161
155
|
|
162
|
-
|
163
|
-
|
164
|
-
|
156
|
+
def override_helper_per_model(method, model, cache_keys = nil)
|
157
|
+
cache_keys ||= [method, model.name]
|
158
|
+
ActiveScaffold::Registry.cache(*cache_keys) do
|
159
|
+
model_names = [model.name]
|
160
|
+
model_names << model.base_class.name if model.base_class != model
|
161
|
+
method_with_class = model_names.find do |model_name|
|
162
|
+
method_with_class = "#{clean_class_name(model_name)}_#{method}"
|
163
|
+
break method_with_class if respond_to?(method_with_class)
|
164
|
+
end
|
165
|
+
method_with_class || (method if respond_to?(method))
|
166
|
+
end
|
165
167
|
end
|
166
168
|
|
167
169
|
def override_helper(column, suffix)
|
168
|
-
|
169
|
-
|
170
|
-
if respond_to?(method_with_class)
|
171
|
-
method_with_class
|
172
|
-
else
|
173
|
-
method = override_helper_name(column, suffix)
|
174
|
-
method if respond_to?(method)
|
175
|
-
end
|
176
|
-
end
|
170
|
+
method = "#{clean_column_name(column.name)}_#{suffix}"
|
171
|
+
override_helper_per_model(method, column.active_record_class, [suffix, column.cache_key])
|
177
172
|
end
|
178
173
|
|
179
174
|
def history_state
|
@@ -2,10 +2,11 @@ require 'test_helper'
|
|
2
2
|
|
3
3
|
class FormColumnHelpersTest < ActionView::TestCase
|
4
4
|
include ActiveScaffold::Helpers::FormColumnHelpers
|
5
|
+
include ActiveScaffold::Helpers::ViewHelpers
|
5
6
|
|
6
7
|
def setup
|
7
8
|
@column = ActiveScaffold::DataStructures::Column.new(:a, ModelStub)
|
8
|
-
@record =
|
9
|
+
@record = ModelStub.new(a: nil)
|
9
10
|
end
|
10
11
|
|
11
12
|
def test_choices_for_select_form_ui_for_simple_column
|
data/test/misc/finder_test.rb
CHANGED
@@ -75,13 +75,13 @@ class FinderTest < Minitest::Test
|
|
75
75
|
|
76
76
|
def test_condition_for_column
|
77
77
|
column = ActiveScaffold::DataStructures::Column.new('adult', Person)
|
78
|
-
assert_equal ['"people"."adult" = ?', false], ClassWithFinder.condition_for_column(column, '0')
|
78
|
+
assert_equal ['"people"."adult" = ?', false], ClassWithFinder.condition_for_column(column, '0', :full, {})
|
79
79
|
end
|
80
80
|
|
81
81
|
def test_condition_for_polymorphic_column
|
82
82
|
column = ActiveScaffold::DataStructures::Column.new('addressable', Address)
|
83
83
|
column.search_sql = [{subquery: [Building, 'name']}]
|
84
|
-
condition = ClassWithFinder.condition_for_column(column, 'test search')
|
84
|
+
condition = ClassWithFinder.condition_for_column(column, 'test search', :full, {})
|
85
85
|
assert_equal Building.where(['name LIKE ?', '%test search%']).select(:id).to_sql, condition[1].to_sql
|
86
86
|
assert_equal '"addresses"."addressable_id" IN (?) AND "addresses"."addressable_type" = ?', condition[0]
|
87
87
|
assert_equal ['Building'], condition[2..-1]
|
@@ -90,7 +90,7 @@ class FinderTest < Minitest::Test
|
|
90
90
|
def test_condition_for_polymorphic_column_with_relation
|
91
91
|
column = ActiveScaffold::DataStructures::Column.new('contactable', Contact)
|
92
92
|
column.search_sql = [{subquery: [Person.joins(:buildings), 'first_name', 'last_name']}]
|
93
|
-
condition = ClassWithFinder.condition_for_column(column, 'test search')
|
93
|
+
condition = ClassWithFinder.condition_for_column(column, 'test search', :full, {})
|
94
94
|
assert_equal Person.joins(:buildings).where(['first_name LIKE ? OR last_name LIKE ?', '%test search%', '%test search%']).select(:id).to_sql, condition[1].to_sql
|
95
95
|
assert_equal '"contacts"."contactable_id" IN (?) AND "contacts"."contactable_type" = ?', condition[0]
|
96
96
|
assert_equal ['Person'], condition[2..-1]
|
@@ -100,7 +100,7 @@ class FinderTest < Minitest::Test
|
|
100
100
|
column = ActiveScaffold::DataStructures::Column.new('owner', Building)
|
101
101
|
column.search_sql = [{subquery: [Person, 'first_name', 'last_name'], conditions: ['floor_count > 0']}]
|
102
102
|
column.search_ui = :text
|
103
|
-
condition = ClassWithFinder.condition_for_column(column, 'test search')
|
103
|
+
condition = ClassWithFinder.condition_for_column(column, 'test search', :full, {})
|
104
104
|
assert_equal Person.where(['first_name LIKE ? OR last_name LIKE ?', '%test search%', '%test search%']).select(:id).to_sql, condition[1].to_sql
|
105
105
|
assert_equal '"buildings"."owner_id" IN (?) AND floor_count > 0', condition[0]
|
106
106
|
assert_equal [], condition[2..-1]
|
@@ -110,7 +110,7 @@ class FinderTest < Minitest::Test
|
|
110
110
|
column = ActiveScaffold::DataStructures::Column.new('owner', Building)
|
111
111
|
column.search_sql = [{subquery: [Person, 'first_name', 'last_name'], conditions: ['floor_count > 0 AND name != ?', '']}]
|
112
112
|
column.search_ui = :text
|
113
|
-
condition = ClassWithFinder.condition_for_column(column, 'test search')
|
113
|
+
condition = ClassWithFinder.condition_for_column(column, 'test search', :full, {})
|
114
114
|
assert_equal Person.where(['first_name LIKE ? OR last_name LIKE ?', '%test search%', '%test search%']).select(:id).to_sql, condition[1].to_sql
|
115
115
|
assert_equal '"buildings"."owner_id" IN (?) AND floor_count > 0 AND name != ?', condition[0]
|
116
116
|
assert_equal [''], condition[2..-1]
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_scaffold
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.7.
|
4
|
+
version: 3.7.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Many, see README
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-06-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|