active_scaffold 3.5.4 → 3.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (192) hide show
  1. checksums.yaml +4 -4
  2. data/{CHANGELOG → CHANGELOG.rdoc} +72 -0
  3. data/README.md +20 -8
  4. data/app/assets/javascripts/active_scaffold.js.erb +0 -1
  5. data/app/assets/javascripts/jquery/active_scaffold.js +98 -7
  6. data/app/assets/stylesheets/active_scaffold_colors.scss +1 -1
  7. data/app/assets/stylesheets/active_scaffold_layout.css +52 -29
  8. data/app/views/active_scaffold_overrides/_base_form.html.erb +2 -2
  9. data/app/views/active_scaffold_overrides/_form.html.erb +1 -1
  10. data/app/views/active_scaffold_overrides/_form_association.html.erb +2 -1
  11. data/app/views/active_scaffold_overrides/_form_association_footer.html.erb +3 -2
  12. data/app/views/active_scaffold_overrides/_form_association_record.html.erb +9 -7
  13. data/app/views/active_scaffold_overrides/_horizontal_subform.html.erb +4 -4
  14. data/app/views/active_scaffold_overrides/_horizontal_subform_header.html.erb +2 -1
  15. data/app/views/active_scaffold_overrides/_list.html.erb +2 -1
  16. data/app/views/active_scaffold_overrides/_list_header.html.erb +5 -7
  17. data/app/views/active_scaffold_overrides/_list_messages.html.erb +1 -0
  18. data/app/views/active_scaffold_overrides/_list_record.html.erb +4 -5
  19. data/app/views/active_scaffold_overrides/_list_with_header.html.erb +1 -1
  20. data/app/views/active_scaffold_overrides/_messages.html.erb +1 -0
  21. data/app/views/active_scaffold_overrides/_refresh_list.js.erb +4 -0
  22. data/app/views/active_scaffold_overrides/_render_field.js.erb +2 -1
  23. data/app/views/active_scaffold_overrides/_show_association_horizontal.html.erb +2 -1
  24. data/app/views/active_scaffold_overrides/_show_columns.html.erb +2 -2
  25. data/app/views/active_scaffold_overrides/_show_horizontal_record.html.erb +4 -4
  26. data/app/views/active_scaffold_overrides/_update_calculations.js.erb +1 -1
  27. data/app/views/active_scaffold_overrides/_update_column.js.erb +2 -2
  28. data/app/views/active_scaffold_overrides/_vertical_subform.html.erb +2 -2
  29. data/app/views/active_scaffold_overrides/action_confirmation.html.erb +2 -2
  30. data/app/views/active_scaffold_overrides/delete.html.erb +2 -2
  31. data/app/views/active_scaffold_overrides/on_action_update.js.erb +16 -6
  32. data/app/views/active_scaffold_overrides/on_update.js.erb +1 -1
  33. data/app/views/active_scaffold_overrides/row.js.erb +1 -1
  34. data/app/views/active_scaffold_overrides/update_column.js.erb +2 -2
  35. data/config/locales/de.yml +2 -1
  36. data/config/locales/en.yml +1 -0
  37. data/config/locales/es.yml +1 -0
  38. data/config/locales/fr.yml +2 -1
  39. data/config/locales/hu.yml +1 -0
  40. data/config/locales/ja.yml +1 -0
  41. data/config/locales/ru.yml +1 -0
  42. data/lib/active_scaffold.rb +19 -16
  43. data/lib/active_scaffold/actions/common_search.rb +11 -8
  44. data/lib/active_scaffold/actions/core.rb +91 -70
  45. data/lib/active_scaffold/actions/create.rb +28 -28
  46. data/lib/active_scaffold/actions/delete.rb +3 -3
  47. data/lib/active_scaffold/actions/field_search.rb +53 -43
  48. data/lib/active_scaffold/actions/list.rb +111 -27
  49. data/lib/active_scaffold/actions/nested.rb +65 -48
  50. data/lib/active_scaffold/actions/search.rb +1 -1
  51. data/lib/active_scaffold/actions/show.rb +4 -4
  52. data/lib/active_scaffold/actions/subform.rb +23 -22
  53. data/lib/active_scaffold/actions/update.rb +96 -77
  54. data/lib/active_scaffold/active_record_permissions.rb +2 -11
  55. data/lib/active_scaffold/attribute_params.rb +102 -94
  56. data/lib/active_scaffold/bridges.rb +8 -8
  57. data/lib/active_scaffold/bridges/active_storage.rb +6 -0
  58. data/lib/active_scaffold/bridges/active_storage/active_storage_bridge.rb +34 -0
  59. data/lib/active_scaffold/bridges/active_storage/active_storage_helpers.rb +54 -0
  60. data/lib/active_scaffold/bridges/active_storage/form_ui.rb +22 -0
  61. data/lib/active_scaffold/bridges/active_storage/list_ui.rb +36 -0
  62. data/lib/active_scaffold/bridges/bitfields.rb +2 -1
  63. data/lib/active_scaffold/bridges/bitfields/bitfields_bridge.rb +12 -15
  64. data/lib/active_scaffold/bridges/bitfields/list_ui.rb +19 -0
  65. data/lib/active_scaffold/bridges/calendar_date_select/as_cds_bridge.rb +1 -1
  66. data/lib/active_scaffold/bridges/cancan/cancan_bridge.rb +9 -12
  67. data/lib/active_scaffold/bridges/carrierwave/carrierwave_bridge.rb +1 -1
  68. data/lib/active_scaffold/bridges/carrierwave/list_ui.rb +2 -2
  69. data/lib/active_scaffold/bridges/chosen/helpers.rb +7 -6
  70. data/lib/active_scaffold/bridges/date_picker/ext.rb +0 -13
  71. data/lib/active_scaffold/bridges/date_picker/helper.rb +49 -44
  72. data/lib/active_scaffold/bridges/dragonfly/list_ui.rb +1 -1
  73. data/lib/active_scaffold/bridges/file_column/as_file_column_bridge.rb +1 -1
  74. data/lib/active_scaffold/bridges/file_column/file_column_helpers.rb +3 -3
  75. data/lib/active_scaffold/bridges/file_column/form_ui.rb +3 -3
  76. data/lib/active_scaffold/bridges/file_column/test/functional/file_column_keep_test.rb +10 -7
  77. data/lib/active_scaffold/bridges/paper_trail.rb +1 -1
  78. data/lib/active_scaffold/bridges/paper_trail/actions.rb +3 -1
  79. data/lib/active_scaffold/bridges/paperclip/list_ui.rb +1 -1
  80. data/lib/active_scaffold/bridges/paperclip/paperclip_bridge.rb +1 -1
  81. data/lib/active_scaffold/bridges/paperclip/paperclip_bridge_helpers.rb +2 -2
  82. data/lib/active_scaffold/bridges/record_select/helpers.rb +15 -17
  83. data/lib/active_scaffold/bridges/shared/date_bridge.rb +20 -19
  84. data/lib/active_scaffold/bridges/tiny_mce/helpers.rb +3 -1
  85. data/lib/active_scaffold/bridges/usa_state_select/usa_state_select_helper.rb +21 -4
  86. data/lib/active_scaffold/config/base.rb +133 -41
  87. data/lib/active_scaffold/config/core.rb +146 -18
  88. data/lib/active_scaffold/config/delete.rb +14 -1
  89. data/lib/active_scaffold/config/field_search.rb +7 -1
  90. data/lib/active_scaffold/config/form.rb +10 -1
  91. data/lib/active_scaffold/config/list.rb +39 -13
  92. data/lib/active_scaffold/config/mark.rb +4 -2
  93. data/lib/active_scaffold/config/nested.rb +16 -17
  94. data/lib/active_scaffold/config/search.rb +9 -0
  95. data/lib/active_scaffold/config/show.rb +4 -0
  96. data/lib/active_scaffold/config/update.rb +4 -0
  97. data/lib/active_scaffold/configurable.rb +14 -7
  98. data/lib/active_scaffold/constraints.rb +22 -20
  99. data/lib/active_scaffold/core.rb +67 -28
  100. data/lib/active_scaffold/data_structures/action_columns.rb +50 -59
  101. data/lib/active_scaffold/data_structures/action_link.rb +50 -20
  102. data/lib/active_scaffold/data_structures/action_links.rb +15 -13
  103. data/lib/active_scaffold/data_structures/association/abstract.rb +38 -15
  104. data/lib/active_scaffold/data_structures/association/active_mongoid.rb +2 -6
  105. data/lib/active_scaffold/data_structures/association/active_record.rb +6 -2
  106. data/lib/active_scaffold/data_structures/association/mongoid.rb +0 -3
  107. data/lib/active_scaffold/data_structures/column.rb +75 -66
  108. data/lib/active_scaffold/data_structures/columns.rb +3 -2
  109. data/lib/active_scaffold/data_structures/nested_info.rb +33 -19
  110. data/lib/active_scaffold/data_structures/set.rb +8 -0
  111. data/lib/active_scaffold/data_structures/sorting.rb +10 -2
  112. data/lib/active_scaffold/delayed_setup.rb +16 -5
  113. data/lib/active_scaffold/extensions/action_controller_rendering.rb +3 -2
  114. data/lib/active_scaffold/extensions/action_view_rendering.rb +93 -32
  115. data/lib/active_scaffold/extensions/cow_proxy.rb +95 -0
  116. data/lib/active_scaffold/extensions/ice_nine.rb +36 -0
  117. data/lib/active_scaffold/extensions/left_outer_joins.rb +8 -33
  118. data/lib/active_scaffold/extensions/localize.rb +3 -1
  119. data/lib/active_scaffold/extensions/routing_mapper.rb +6 -45
  120. data/lib/active_scaffold/extensions/to_label.rb +3 -2
  121. data/lib/active_scaffold/extensions/unsaved_record.rb +2 -4
  122. data/lib/active_scaffold/finder.rb +110 -77
  123. data/lib/active_scaffold/helpers/action_link_helpers.rb +62 -36
  124. data/lib/active_scaffold/helpers/association_helpers.rb +18 -16
  125. data/lib/active_scaffold/helpers/controller_helpers.rb +34 -10
  126. data/lib/active_scaffold/helpers/form_column_helpers.rb +196 -124
  127. data/lib/active_scaffold/helpers/human_condition_helpers.rb +1 -1
  128. data/lib/active_scaffold/helpers/id_helpers.rb +6 -2
  129. data/lib/active_scaffold/helpers/list_column_helpers.rb +90 -57
  130. data/lib/active_scaffold/helpers/pagination_helpers.rb +2 -2
  131. data/lib/active_scaffold/helpers/search_column_helpers.rb +29 -34
  132. data/lib/active_scaffold/helpers/show_column_helpers.rb +3 -5
  133. data/lib/active_scaffold/helpers/view_helpers.rb +39 -36
  134. data/lib/active_scaffold/marked_model.rb +2 -2
  135. data/lib/active_scaffold/orm_checks.rb +3 -7
  136. data/lib/active_scaffold/paginator.rb +7 -7
  137. data/lib/active_scaffold/registry.rb +33 -0
  138. data/lib/active_scaffold/responds_to_parent.rb +8 -11
  139. data/lib/active_scaffold/tableless.rb +82 -66
  140. data/lib/active_scaffold/version.rb +2 -2
  141. data/lib/generators/active_scaffold/controller_generator.rb +2 -2
  142. data/lib/generators/active_scaffold/install_generator.rb +52 -4
  143. data/lib/generators/active_scaffold/resource_generator.rb +2 -2
  144. data/shoulda_macros/macros.rb +3 -1
  145. data/test/bridges/date_picker_test.rb +1 -2
  146. data/test/bridges/paperclip_test.rb +6 -6
  147. data/test/class_with_finder.rb +2 -2
  148. data/test/company.rb +4 -4
  149. data/test/config/create_test.rb +4 -2
  150. data/test/config/nested_test.rb +1 -1
  151. data/test/config/show_test.rb +1 -1
  152. data/test/config/update_test.rb +7 -6
  153. data/test/data_structures/action_columns_test.rb +2 -2
  154. data/test/data_structures/action_links_test.rb +1 -1
  155. data/test/data_structures/column_test.rb +3 -6
  156. data/test/data_structures/columns_test.rb +2 -2
  157. data/test/data_structures/sorting_test.rb +7 -0
  158. data/test/extensions/action_view_rendering_test.rb +20 -0
  159. data/test/extensions/active_record_test.rb +4 -4
  160. data/test/extensions/routing_mapper_test.rb +2 -2
  161. data/test/helpers/list_column_helpers_test.rb +3 -1
  162. data/test/misc/active_record_permissions_test.rb +3 -11
  163. data/test/misc/attribute_params_test.rb +12 -8
  164. data/test/misc/calculation_test.rb +1 -1
  165. data/test/misc/configurable_test.rb +10 -10
  166. data/test/misc/constraints_test.rb +2 -2
  167. data/test/misc/convert_numbers_format_test.rb +7 -3
  168. data/test/misc/lang_test.rb +1 -1
  169. data/test/misc/parse_datetime_test.rb +3 -4
  170. data/test/misc/tableless_test.rb +14 -0
  171. data/test/mock_app/Rakefile +1 -1
  172. data/test/mock_app/app/assets/config/manifest.js +0 -0
  173. data/test/mock_app/app/controllers/cars_controller.rb +1 -0
  174. data/test/mock_app/app/controllers/people_controller.rb +5 -1
  175. data/test/mock_app/app/controllers/roles_controller.rb +4 -0
  176. data/test/mock_app/app/views/active_scaffold_overrides/_form.html.erb +2 -0
  177. data/test/mock_app/app/views/active_scaffold_overrides/list.html.erb +2 -0
  178. data/test/mock_app/app/views/people/_first_name_form_column.html.erb +2 -0
  179. data/test/mock_app/app/views/people/_form.html.erb +2 -0
  180. data/test/mock_app/app/views/people/list.html.erb +2 -0
  181. data/test/mock_app/config/application.rb +2 -1
  182. data/test/mock_app/config/boot.rb +1 -1
  183. data/test/mock_app/config/environment.rb +2 -2
  184. data/test/mock_app/config/routes.rb +4 -1
  185. data/test/mock_app/db/schema.rb +2 -0
  186. data/test/performance/list_cars_performance_test.rb +34 -0
  187. data/test/performance/list_people_performance_test.rb +31 -0
  188. data/test/performance_test_help.rb +3 -0
  189. data/test/test_helper.rb +12 -4
  190. metadata +69 -18
  191. data/app/assets/javascripts/prototype/rico_corner.js +0 -370
  192. data/lib/active_scaffold/bridges/file_column/test/test_helper.rb +0 -7
@@ -57,7 +57,7 @@ module ActiveScaffold
57
57
 
58
58
  def active_scaffold_human_condition_boolean(column, value)
59
59
  attribute = column.active_record_class.human_attribute_name(column.name)
60
- label = as_(ActiveScaffold::Core.column_type_cast(value, column.column) ? :true : :false)
60
+ label = as_(ActiveScaffold::Core.column_type_cast(value, column.column) ? :true : :false) # rubocop:disable Lint/BooleanSymbol
61
61
  as_(:boolean, :scope => :human_conditions, :column => attribute, :value => label)
62
62
  end
63
63
  alias active_scaffold_human_condition_checkbox active_scaffold_human_condition_boolean
@@ -10,12 +10,16 @@ module ActiveScaffold
10
10
  'as_' + id_from_controller(controller)
11
11
  end
12
12
 
13
+ def nested?
14
+ false # will be overrided in AS controllers with helper_method
15
+ end
16
+
13
17
  def nested_parent_id
14
18
  nested_parent_record.id
15
19
  end
16
20
 
17
- def nested_id(controller = params[:controller])
18
- "#{nested.parent_scaffold.controller_path}-#{nested_parent_id}-#{controller}" if nested?
21
+ def nested_id(controller = nil)
22
+ "#{nested.parent_scaffold.controller_path}-#{nested_parent_id}-#{controller || params[:parent_controller] || params[:controller]}" if nested?
19
23
  end
20
24
 
21
25
  def active_scaffold_id
@@ -10,8 +10,8 @@ module ActiveScaffold
10
10
  else
11
11
  value = nil
12
12
  end
13
- value = ' '.html_safe if value.nil? || value.blank? # fix for IE 6
14
- return value
13
+ value = ' '.html_safe if value.nil? || value.blank? # fix for IE 6 # rubocop:disable Rails/OutputSafety
14
+ value
15
15
  rescue StandardError => e
16
16
  logger.error "#{e.class.name}: #{e.message} -- on the ActiveScaffold column = :#{column.name} in #{controller.class}, record: #{record.inspect}"
17
17
  raise e
@@ -19,7 +19,7 @@ module ActiveScaffold
19
19
 
20
20
  def get_column_method(record, column)
21
21
  # check for an override helper
22
- column.list_method ||= begin
22
+ ActiveScaffold::Registry.cache :column_methods, column.cache_key do
23
23
  if (method = column_override(column))
24
24
  # we only pass the record as the argument. we previously also passed the formatted_value,
25
25
  # but mike perham pointed out that prohibited the usage of overrides to improve on the
@@ -47,8 +47,8 @@ module ActiveScaffold
47
47
  render_action_link(link, record, :link => text, :authorized => authorized, :not_authorized_reason => reason)
48
48
  elsif inplace_edit?(record, column)
49
49
  active_scaffold_inplace_edit(record, column, :formatted_column => text)
50
- elsif active_scaffold_config.actions.include?(:list) && active_scaffold_config.list.wrap_tag
51
- content_tag active_scaffold_config.list.wrap_tag, text
50
+ elsif column_wrap_tag
51
+ content_tag column_wrap_tag, text
52
52
  else
53
53
  text
54
54
  end
@@ -57,6 +57,11 @@ module ActiveScaffold
57
57
  raise e
58
58
  end
59
59
 
60
+ def column_wrap_tag
61
+ return @_column_wrap_tag if defined? @_column_wrap_tag
62
+ @_column_wrap_tag = (active_scaffold_config.list.wrap_tag if active_scaffold_config.actions.include?(:list))
63
+ end
64
+
60
65
  # There are two basic ways to clean a column's value: h() and sanitize(). The latter is useful
61
66
  # when the column contains *valid* html data, and you want to just disable any scripting. People
62
67
  # can always use field overrides to clean data one way or the other, but having this override
@@ -127,20 +132,21 @@ module ActiveScaffold
127
132
 
128
133
  # the naming convention for overriding column types with helpers
129
134
  def override_column_ui(list_ui)
130
- @_column_ui_overrides ||= {}
131
- return @_column_ui_overrides[list_ui] if @_column_ui_overrides.include? list_ui
132
- method = "active_scaffold_column_#{list_ui}"
133
- @_column_ui_overrides[list_ui] = (method if respond_to? method)
135
+ ActiveScaffold::Registry.cache :column_ui_overrides, list_ui do
136
+ method = "active_scaffold_column_#{list_ui}"
137
+ method if respond_to? method
138
+ end
134
139
  end
135
140
  alias override_column_ui? override_column_ui
136
141
 
137
142
  ##
138
143
  ## Formatting
139
144
  ##
145
+ FORM_UI_WITH_OPTIONS = %i[select radio].freeze
140
146
  def format_column_value(record, column, value = nil)
141
147
  value ||= record.send(column.name) unless record.nil?
142
148
  if column.association.nil?
143
- if %i[select radio].include?(column.form_ui) && column.options[:options]
149
+ if FORM_UI_WITH_OPTIONS.include?(column.form_ui) && column.options[:options]
144
150
  text, val = column.options[:options].find { |t, v| (v.nil? ? t : v).to_s == value.to_s }
145
151
  value = active_scaffold_translated_option(column, text, val).first if text
146
152
  end
@@ -153,7 +159,7 @@ module ActiveScaffold
153
159
  end
154
160
  else
155
161
  if column.association.collection?
156
- associated_size = value.size if column.associated_number? # get count before cache association
162
+ associated_size = column_association_size(record, column, value) if column.associated_number? # get count before cache association
157
163
  if column.association.respond_to_target? && !value.loaded?
158
164
  cache_association(record.association(column.name), column, associated_size)
159
165
  end
@@ -162,20 +168,31 @@ module ActiveScaffold
162
168
  end
163
169
  end
164
170
 
171
+ def column_association_size(record, column, value)
172
+ cached_counts = @counts&.dig(column.name)
173
+ if cached_counts
174
+ key = column.association.primary_key if count_on_association_class?(column)
175
+ cached_counts[record.send(key || :id)] || 0
176
+ else
177
+ value.size
178
+ end
179
+ end
180
+
165
181
  def format_number_value(value, options = {})
166
182
  if value
167
- value = case options[:format]
168
- when :size
169
- number_to_human_size(value, options[:i18n_options] || {})
170
- when :percentage
171
- number_to_percentage(value, options[:i18n_options] || {})
172
- when :currency
173
- number_to_currency(value, options[:i18n_options] || {})
174
- when :i18n_number
175
- send("number_with_#{value.is_a?(Integer) ? 'delimiter' : 'precision'}", value, options[:i18n_options] || {})
176
- else
177
- value
178
- end
183
+ value =
184
+ case options[:format]
185
+ when :size
186
+ number_to_human_size(value, options[:i18n_options] || {})
187
+ when :percentage
188
+ number_to_percentage(value, options[:i18n_options] || {})
189
+ when :currency
190
+ number_to_currency(value, options[:i18n_options] || {})
191
+ when :i18n_number
192
+ send("number_with_#{value.is_a?(Integer) ? 'delimiter' : 'precision'}", value, options[:i18n_options] || {})
193
+ else
194
+ value
195
+ end
179
196
  end
180
197
  clean_column_value(value)
181
198
  end
@@ -196,18 +213,26 @@ module ActiveScaffold
196
213
  end
197
214
  end
198
215
 
216
+ def association_join_text(column = nil)
217
+ column_value = column&.association_join_text
218
+ return column_value if column_value
219
+ return @_association_join_text if defined? @_association_join_text
220
+ @_association_join_text = active_scaffold_config.list.association_join_text
221
+ end
222
+
199
223
  def format_collection_association_value(value, column, label_method, size)
200
- if column.associated_limit.nil?
224
+ associated_limit = column.associated_limit
225
+ if associated_limit.nil?
201
226
  firsts = value.collect(&label_method)
202
- safe_join firsts, active_scaffold_config.list.association_join_text
203
- elsif column.associated_limit.zero?
227
+ safe_join firsts, association_join_text(column)
228
+ elsif associated_limit.zero?
204
229
  size if column.associated_number?
205
230
  else
206
- firsts = value.loaded? ? value[0, column.associated_limit] : value.limit(column.associated_limit)
231
+ firsts = value.loaded? ? value[0, associated_limit] : value.limit(associated_limit)
207
232
  firsts = firsts.map(&label_method)
208
- firsts << '…' if value.size > column.associated_limit
209
- text = safe_join firsts, active_scaffold_config.list.association_join_text
210
- text << " (#{size})" if column.associated_number? && column.associated_limit && value.size > column.associated_limit
233
+ firsts << '…' if value.size > associated_limit
234
+ text = safe_join firsts, association_join_text(column)
235
+ text << " (#{size})" if column.associated_number? && associated_limit && value.size > associated_limit
211
236
  text
212
237
  end
213
238
  end
@@ -237,7 +262,7 @@ module ActiveScaffold
237
262
  empty_field_text
238
263
  elsif column_value.is_a?(Time) || column_value.is_a?(Date)
239
264
  l(column_value, :format => options[:format] || :default)
240
- elsif [FalseClass, TrueClass].include?(column_value.class)
265
+ elsif !!column_value == column_value # rubocop:disable Style/DoubleNegation fast check for boolean
241
266
  as_(column_value.to_s.to_sym)
242
267
  else
243
268
  column_value.to_s
@@ -246,15 +271,21 @@ module ActiveScaffold
246
271
  end
247
272
 
248
273
  def cache_association(association, column, size)
274
+ associated_limit = column.associated_limit
249
275
  # we are not using eager loading, cache firsts records in order not to query the database for whole association in a future
250
- if column.associated_limit.nil?
276
+ if associated_limit.nil?
251
277
  logger.warn "ActiveScaffold: Enable eager loading for #{column.name} association to reduce SQL queries"
252
- elsif column.associated_limit > 0
278
+ elsif associated_limit.positive?
253
279
  # load at least one record more, is needed to display '...'
254
- association.target = association.reader.limit(column.associated_limit + 1).select(column.select_associated_columns || "#{association.klass.quoted_table_name}.*").to_a
280
+ association.target = association.reader.limit(associated_limit + 1).select(column.select_associated_columns || "#{association.klass.quoted_table_name}.*").to_a
255
281
  elsif @cache_associations
256
282
  # set array with at least one element if size > 0, so blank? or present? works, saving [nil] may cause exceptions
257
- association.target = size.to_i.zero? ? [] : [association.klass.new]
283
+ association.target =
284
+ if size.to_i.zero?
285
+ []
286
+ else
287
+ ActiveScaffold::Registry.cache(:cached_empty_association, association.klass) { [association.klass.new] }
288
+ end
258
289
  end
259
290
  end
260
291
 
@@ -265,14 +296,9 @@ module ActiveScaffold
265
296
  def inplace_edit?(record, column)
266
297
  return unless column.inplace_edit
267
298
  if controller.respond_to?(:update_authorized?, true)
268
- if controller.method(:update_authorized?).parameters.size == 2
269
- return Array(controller.send(:update_authorized?, record, column.name))[0]
270
- else
271
- ActiveSupport::Deprecation.warn 'add column = nil parameter to update_authorized? on your controller'
272
- editable = Array(controller.send(:update_authorized?, record))[0]
273
- end
299
+ return Array(controller.send(:update_authorized?, record, column.name))[0]
274
300
  end
275
- editable || record.authorized_for?(:crud_type => :update, :column => column.name)
301
+ record.authorized_for?(:crud_type => :update, :column => column.name)
276
302
  end
277
303
 
278
304
  def inplace_edit_cloning?(column)
@@ -280,23 +306,29 @@ module ActiveScaffold
280
306
  end
281
307
 
282
308
  def active_scaffold_inplace_edit_tag_options(record, column)
283
- id_options = {:id => record.id.to_s, :action => 'update_column', :name => column.name.to_s}
284
- tag_options = {:id => element_cell_id(id_options), :class => 'in_place_editor_field',
285
- :title => as_(:click_to_edit), :data => {:ie_id => record.to_param}}
309
+ @_inplace_edit_title ||= as_(:click_to_edit)
310
+ cell_id = ActiveScaffold::Registry.cache :inplace_edit_id, column.cache_key do
311
+ element_cell_id(id: '--ID--', action: 'update_column', name: column.name.to_s)
312
+ end
313
+ tag_options = {id: cell_id.sub('--ID--', record.id.to_s), class: 'in_place_editor_field',
314
+ title: @_inplace_edit_title, data: {:ie_id => record.to_param}}
286
315
  tag_options[:data][:ie_update] = column.inplace_edit if column.inplace_edit != true
287
316
  tag_options
288
317
  end
289
318
 
290
319
  def active_scaffold_inplace_edit(record, column, options = {})
291
320
  formatted_column = options[:formatted_column] || format_column_value(record, column)
292
- content_tag(:span, as_(:inplace_edit_handle), :class => 'handle') <<
293
- content_tag(:span, formatted_column, active_scaffold_inplace_edit_tag_options(record, column))
321
+ @_inplace_edit_handle ||= content_tag(:span, as_(:inplace_edit_handle), :class => 'handle')
322
+ span = content_tag(:span, formatted_column, active_scaffold_inplace_edit_tag_options(record, column))
323
+ @_inplace_edit_handle + span
294
324
  end
295
325
 
296
326
  def inplace_edit_control(column)
297
327
  return unless inplace_edit?(active_scaffold_config.model, column) && inplace_edit_cloning?(column)
298
- column = column.clone
299
- column.options = column.options.clone
328
+ unless ActiveScaffold.threadsafe
329
+ column = column.dup
330
+ column.options = column.options.dup
331
+ end
300
332
  column.form_ui = :select if column.association && column.form_ui.nil?
301
333
  options = active_scaffold_input_options(column).merge(:object => column.active_record_class.new)
302
334
  options[:class] = "#{options[:class]} inplace_field"
@@ -309,6 +341,7 @@ module ActiveScaffold
309
341
  'as_inplace_pattern'
310
342
  end
311
343
 
344
+ INPLACE_EDIT_PLURAL_FORM_UI = %i[select record_select].freeze
312
345
  def inplace_edit_data(column)
313
346
  data = {}
314
347
  data[:ie_url] = url_for(params_for(:action => 'update_column', :column => column.name, :id => '__id__'))
@@ -316,7 +349,7 @@ module ActiveScaffold
316
349
  data[:ie_loading_text] = column.options[:loading_text] || as_(:loading)
317
350
  data[:ie_save_text] = column.options[:save_text] || as_(:update)
318
351
  data[:ie_saving_text] = column.options[:saving_text] || as_(:saving)
319
- data[:ie_rows] = column.options[:rows] || 5 if column.column.try(:type) == :text
352
+ data[:ie_rows] = column.options[:rows] || 5 if column.column&.type == :text
320
353
  data[:ie_cols] = column.options[:cols] if column.options[:cols]
321
354
  data[:ie_size] = column.options[:size] if column.options[:size]
322
355
  data[:ie_use_html] = column.options[:use_html] if column.options[:use_html]
@@ -327,7 +360,7 @@ module ActiveScaffold
327
360
  data[:ie_mode] = :clone
328
361
  elsif column.inplace_edit == :ajax
329
362
  url = url_for(params_for(:controller => params_for[:controller], :action => 'render_field', :id => '__id__', :update_column => column.name))
330
- plural = column.association.try(:collection?) && !override_form_field?(column) && %i[select record_select].include?(column.form_ui)
363
+ plural = column.association&.collection? && !override_form_field?(column) && INPLACE_EDIT_PLURAL_FORM_UI.include?(column.form_ui)
331
364
  data[:ie_render_url] = url
332
365
  data[:ie_mode] = :ajax
333
366
  data[:ie_plural] = plural
@@ -366,8 +399,8 @@ module ActiveScaffold
366
399
  :ie_mode => :inline_checkbox,
367
400
  :ie_url => url_for(params_for(:action => 'mark', :id => '__id__'))
368
401
  }
369
- else
370
- tag_options[:data] = inplace_edit_data(column) if column.inplace_edit
402
+ elsif column.inplace_edit
403
+ tag_options[:data] = inplace_edit_data(column)
371
404
  end
372
405
  content_tag(:th, column_heading_value(column, sorting, sort_direction) + inplace_edit_control(column), tag_options)
373
406
  end
@@ -398,16 +431,16 @@ module ActiveScaffold
398
431
 
399
432
  # CALCULATIONS
400
433
 
401
- def column_calculation(column)
434
+ def column_calculation(column, id_condition: true)
402
435
  if column.calculate.instance_of? Proc
403
436
  column.calculate.call(@records)
404
437
  else
405
- calculate_query.calculate(column.calculate, column.name)
438
+ calculate_query(id_condition).calculate(column.calculate, column.name)
406
439
  end
407
440
  end
408
441
 
409
- def render_column_calculation(column)
410
- calculation = column_calculation(column)
442
+ def render_column_calculation(column, id_condition: true)
443
+ calculation = column_calculation(column, id_condition: id_condition)
411
444
  override_formatter = "render_#{column.name}_#{column.calculate.is_a?(Proc) ? :calculate : column.calculate}"
412
445
  calculation = send(override_formatter, calculation) if respond_to? override_formatter
413
446
  format_column_calculation(column, calculation)
@@ -21,8 +21,8 @@ module ActiveScaffold
21
21
  start_number = 1 if start_number <= 0
22
22
  if current_page.pager.infinite?
23
23
  offsets = [20, 100]
24
- else
25
- end_number = current_page.pager.last.number if end_number > current_page.pager.last.number
24
+ elsif end_number > current_page.pager.last.number
25
+ end_number = current_page.pager.last.number
26
26
  end
27
27
 
28
28
  html = []
@@ -29,25 +29,22 @@ module ActiveScaffold
29
29
  send(method, record, options)
30
30
 
31
31
  # fallback: we get to make the decision
32
- else
33
- if column.association || column.virtual?
34
- active_scaffold_search_text(column, options)
35
-
36
- else # regular model attribute column
37
- # if we (or someone else) have created a custom render option for the column type, use that
38
- if (method = override_search(column.column.type))
39
- send(method, column, options)
40
- # if we (or someone else) have created a custom render option for the column type, use that
41
- elsif (method = override_input(column.column.type))
42
- send(method, column, options)
43
- # final ultimate fallback: use rails' generic input method
44
- else
45
- # for textual fields we pass different options
46
- text_types = %i[text string integer float decimal]
47
- options = active_scaffold_input_text_options(options) if text_types.include?(column.column.type)
48
- text_field(:record, column.name, options.merge(column.options))
49
- end
50
- end
32
+ elsif column.association || column.virtual?
33
+ active_scaffold_search_text(column, options)
34
+
35
+ elsif (method = override_search(column.column.type))
36
+ # if we (or someone else) have created a custom render option for the column type, use that
37
+ send(method, column, options)
38
+
39
+ elsif (method = override_input(column.column.type))
40
+ # if we (or someone else) have created a custom render option for the column type, use that
41
+ send(method, column, options)
42
+
43
+ else # final ultimate fallback: use rails' generic input method
44
+ # for textual fields we pass different options
45
+ text_types = %i[text string integer float decimal]
46
+ options = active_scaffold_input_text_options(options) if text_types.include?(column.column.type)
47
+ text_field(:record, column.name, options.merge(column.options))
51
48
  end
52
49
  rescue StandardError => e
53
50
  logger.error "#{e.class.name}: #{e.message} -- on the ActiveScaffold column = :#{column.name} in #{controller.class}"
@@ -61,8 +58,10 @@ module ActiveScaffold
61
58
 
62
59
  def search_attribute(column, record)
63
60
  column_options = active_scaffold_search_options(column).merge(:object => record)
64
- field = active_scaffold_search_for column, column_options
65
- %(<dl><dt>#{label_tag search_label_for(column, column_options), search_column_label(column, record)}</dt><dd>#{field}</dd></dl>).html_safe
61
+ content_tag :dl do
62
+ content_tag(:dt, label_tag(search_label_for(column, column_options), search_column_label(column, record))) <<
63
+ content_tag(:dd, active_scaffold_search_for(column, column_options))
64
+ end
66
65
  end
67
66
 
68
67
  def search_label_for(column, options)
@@ -135,8 +134,8 @@ module ActiveScaffold
135
134
  def active_scaffold_search_boolean(column, options)
136
135
  select_options = []
137
136
  select_options << [as_(:_select_), nil]
138
- select_options << [as_(:true), true]
139
- select_options << [as_(:false), false]
137
+ select_options << [as_(:true), true] # rubocop:disable Lint/BooleanSymbol
138
+ select_options << [as_(:false), false] # rubocop:disable Lint/BooleanSymbol
140
139
 
141
140
  select_tag(options[:name], options_for_select(select_options, ActiveScaffold::Core.column_type_cast(options[:value], column.column)), :id => options[:id])
142
141
  end
@@ -166,7 +165,7 @@ module ActiveScaffold
166
165
  def active_scaffold_search_null(column, options)
167
166
  select_options = []
168
167
  select_options << [as_(:_select_), nil]
169
- select_options.concat ActiveScaffold::Finder::NULL_COMPARATORS.collect { |comp| [as_(comp), comp] }
168
+ select_options.concat(ActiveScaffold::Finder::NULL_COMPARATORS.collect { |comp| [as_(comp), comp] })
170
169
  select_tag(options[:name], options_for_select(select_options, options[:value]), :id => options[:id])
171
170
  end
172
171
 
@@ -187,7 +186,7 @@ module ActiveScaffold
187
186
  select_options.unshift(*comparators)
188
187
  end
189
188
  if include_null_comparators? column
190
- select_options.concat ActiveScaffold::Finder::NULL_COMPARATORS.collect { |comp| [as_(comp), comp] }
189
+ select_options.concat(ActiveScaffold::Finder::NULL_COMPARATORS.collect { |comp| [as_(comp), comp] })
191
190
  end
192
191
  select_options
193
192
  end
@@ -195,9 +194,9 @@ module ActiveScaffold
195
194
  def include_null_comparators?(column)
196
195
  return column.options[:null_comparators] if column.options.key? :null_comparators
197
196
  if column.association
198
- !column.association.belongs_to? || active_scaffold_config.columns[column.association.foreign_key].column.try(:null)
197
+ !column.association.belongs_to? || active_scaffold_config.columns[column.association.foreign_key].column&.null
199
198
  else
200
- column.column.try(:null)
199
+ column.column&.null
201
200
  end
202
201
  end
203
202
 
@@ -205,11 +204,7 @@ module ActiveScaffold
205
204
  opt_value, from_value, to_value = field_search_params_range_values(column)
206
205
 
207
206
  select_options = active_scaffold_search_range_comparator_options(column)
208
- if active_scaffold_search_range_string?(column)
209
- text_field_size = 15
210
- else
211
- text_field_size = 10
212
- end
207
+ text_field_size = active_scaffold_search_range_string?(column) ? 15 : 10
213
208
  opt_value ||= select_options[0][1]
214
209
 
215
210
  from_value = controller.class.condition_value_for_numeric(column, from_value)
@@ -225,7 +220,7 @@ module ActiveScaffold
225
220
  content_tag(
226
221
  :span,
227
222
  safe_join([' - ', send(input_method, "#{options[:name]}[to]", to_value, to_options)]),
228
- :id => "#{options[:id]}_between", :class => 'as_search_range_between', :style => (opt_value == 'BETWEEN') ? nil : 'display: none'
223
+ :id => "#{options[:id]}_between", :class => 'as_search_range_between', :style => ('display: none' unless opt_value == 'BETWEEN')
229
224
  )
230
225
  end
231
226
  content_tag :span, html, :class => 'search_range'
@@ -292,7 +287,7 @@ module ActiveScaffold
292
287
  def visibles_and_hiddens(search_config)
293
288
  visibles = []
294
289
  hiddens = []
295
- search_config.columns.each do |column|
290
+ search_config.columns.each_column do |column|
296
291
  next unless column.search_sql
297
292
  if search_config.optional_columns.include?(column.name) && !searched_by?(column)
298
293
  hiddens << column