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
@@ -32,51 +32,51 @@ module ActiveScaffold::Actions
32
32
  render(:partial => 'create_form')
33
33
  end
34
34
 
35
+ def create_respond_on_iframe
36
+ do_refresh_list if successful? && active_scaffold_config.create.refresh_list && !render_parent?
37
+ responds_to_parent do
38
+ render :action => 'on_create', :formats => [:js], :layout => false
39
+ end
40
+ end
41
+
35
42
  def create_respond_to_html
36
- if params[:iframe] == 'true' # was this an iframe post ?
37
- do_refresh_list if successful? && active_scaffold_config.create.refresh_list && !render_parent?
38
- responds_to_parent do
39
- render :action => 'on_create', :formats => [:js], :layout => false
40
- end
41
- else
42
- if successful?
43
- flash[:info] = as_(:created_model, :model => ERB::Util.h(@record.to_label))
44
- if (action = active_scaffold_config.create.action_after_create)
45
- redirect_to params_for(:action => action, :id => @record.to_param)
46
- elsif params[:dont_close]
47
- redirect_to params_for(:action => 'new')
48
- else
49
- return_to_main
50
- end
43
+ if successful?
44
+ flash[:info] = as_(:created_model, :model => ERB::Util.h(@record.to_label))
45
+ if (action = active_scaffold_config.create.action_after_create)
46
+ redirect_to params_for(:action => action, :id => @record.to_param)
47
+ elsif params[:dont_close]
48
+ redirect_to params_for(:action => 'new')
51
49
  else
52
- if active_scaffold_config.actions.include?(:list) && active_scaffold_config.list.always_show_create
53
- list
54
- else
55
- render(:action => 'create')
56
- end
50
+ return_to_main
57
51
  end
52
+ elsif active_scaffold_config.actions.include?(:list) && active_scaffold_config.list.always_show_create
53
+ list
54
+ else
55
+ render(:action => 'create')
58
56
  end
59
57
  end
60
58
 
61
59
  def create_respond_to_js
62
- do_refresh_list if successful? && active_scaffold_config.create.refresh_list && !render_parent?
63
- if successful? && params[:dont_close] && !render_parent?
64
- @saved_record = @record
65
- do_new
60
+ if successful? && !render_parent?
61
+ do_refresh_list if active_scaffold_config.create.refresh_list
62
+ if params[:dont_close]
63
+ @saved_record = @record
64
+ do_new
65
+ end
66
66
  end
67
67
  render :action => 'on_create'
68
68
  end
69
69
 
70
70
  def create_respond_to_xml
71
- render :xml => response_object, :only => create_columns_names + [active_scaffold_config.model.primary_key], :include => association_columns(create_columns_names), :methods => virtual_columns(create_columns_names), :status => response_status, :location => response_location
71
+ response_to_api(:xml, create_columns_names, location: response_location)
72
72
  end
73
73
 
74
74
  def create_respond_to_json
75
- render :json => response_object, :only => create_columns_names + [active_scaffold_config.model.primary_key], :include => association_columns(create_columns_names), :methods => virtual_columns(create_columns_names), :status => response_status, :location => response_location
75
+ response_to_api(:json, create_columns_names, location: response_location)
76
76
  end
77
77
 
78
78
  def create_columns_names
79
- active_scaffold_config.create.columns.names
79
+ active_scaffold_config.create.columns.visible_columns_names
80
80
  end
81
81
 
82
82
  # A simple method to find and prepare an example new record for the form
@@ -136,7 +136,7 @@ module ActiveScaffold::Actions
136
136
  private
137
137
 
138
138
  def create_authorized_filter
139
- link = active_scaffold_config.create.link || active_scaffold_config.create.class.link
139
+ link = active_scaffold_config.create.link || self.class.active_scaffold_config.create.class.link
140
140
  raise ActiveScaffold::ActionNotAllowed unless send(link.security_method)
141
141
  end
142
142
 
@@ -24,11 +24,11 @@ module ActiveScaffold::Actions
24
24
  end
25
25
 
26
26
  def destroy_respond_to_xml
27
- render :xml => successful? ? '' : response_object, :only => active_scaffold_config.list.columns.names, :status => response_status
27
+ render :xml => successful? ? '' : response_object, :only => active_scaffold_config.list.columns.visible_columns_names, :status => response_status
28
28
  end
29
29
 
30
30
  def destroy_respond_to_json
31
- render :json => successful? ? '' : response_object, :only => active_scaffold_config.list.columns.names, :status => response_status
31
+ render :json => successful? ? '' : response_object, :only => active_scaffold_config.list.columns.visible_columns_names, :status => response_status
32
32
  end
33
33
 
34
34
  def destroy_find_record
@@ -65,7 +65,7 @@ module ActiveScaffold::Actions
65
65
  private
66
66
 
67
67
  def delete_authorized_filter
68
- link = active_scaffold_config.delete.link || active_scaffold_config.delete.class.link
68
+ link = active_scaffold_config.delete.link || self.class.active_scaffold_config.delete.class.link
69
69
  raise ActiveScaffold::ActionNotAllowed unless Array(send(link.security_method))[0]
70
70
  end
71
71
 
@@ -23,11 +23,11 @@ module ActiveScaffold::Actions
23
23
  end
24
24
 
25
25
  def store_search_params_into_session
26
- set_field_search_default_params(active_scaffold_config.field_search.default_params) unless active_scaffold_config.field_search.default_params.nil?
26
+ init_field_search_params(active_scaffold_config.field_search.default_params) unless active_scaffold_config.field_search.default_params.nil?
27
27
  super
28
28
  end
29
29
 
30
- def set_field_search_default_params(default_params)
30
+ def init_field_search_params(default_params)
31
31
  return unless (params[:search].is_a?(String) || search_params.nil?) && params[:search].blank?
32
32
  params[:search] = default_params.is_a?(Proc) ? instance_eval(&default_params) : default_params
33
33
  end
@@ -56,24 +56,25 @@ module ActiveScaffold::Actions
56
56
 
57
57
  def custom_finder_options
58
58
  if grouped_search?
59
- group_sql = calculation_for_group_by(search_group_column.try(:field) || search_group_name)
60
- group_by = group_sql.respond_to?(:to_sql) ? group_sql.to_sql : group_sql
61
-
62
- select_query = quoted_select_columns(search_group_column.try(:select_columns))
59
+ group_sql = calculation_for_group_by(search_group_column&.field || search_group_name)
60
+ select_query = grouped_search_select
63
61
  select_query << group_sql.as(search_group_column.name.to_s) if search_group_column && group_sql.respond_to?(:to_sql)
64
- if active_scaffold_config.model.columns_hash.include?(active_scaffold_config.model.inheritance_column)
65
- select_query << active_scaffold_config.columns[active_scaffold_config.model.inheritance_column].field
66
- end
67
- grouped_columns_calculations.each do |name, part|
68
- select_query << (part.respond_to?(:as) ? part : Arel::Nodes::SqlLiteral.new(part)).as(name.to_s)
69
- end
70
-
71
- {group: group_by, select: select_query}
62
+ {group: group_sql, select: select_query}
72
63
  else
73
64
  super
74
65
  end
75
66
  end
76
67
 
68
+ def grouped_search_select
69
+ select_query = quoted_select_columns(search_group_column&.select_columns || [search_group_name])
70
+ if active_scaffold_config.model.columns_hash.include?(active_scaffold_config.model.inheritance_column)
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)
75
+ end
76
+ end
77
+
77
78
  def grouped_columns_calculations
78
79
  @grouped_columns_calculations ||= list_columns[1..-1].each_with_object({}) do |c, h|
79
80
  h[c.name] = calculation_for_group_search(c)
@@ -113,12 +114,13 @@ module ActiveScaffold::Actions
113
114
  end
114
115
 
115
116
  def list_columns
116
- @list_columns ||= if grouped_search?
117
- columns = grouped_columns || super.select(&:calculation?)
118
- [search_group_column || search_group_name].concat columns
119
- else
120
- super
121
- end
117
+ @list_columns ||=
118
+ if grouped_search?
119
+ columns = grouped_columns || super.select(&:calculation?)
120
+ columns.unshift(search_group_column || search_group_name)
121
+ else
122
+ super
123
+ end
122
124
  end
123
125
 
124
126
  def grouped_columns
@@ -142,34 +144,42 @@ module ActiveScaffold::Actions
142
144
 
143
145
  def do_search
144
146
  if field_search_params.present?
145
- filtered_columns = []
146
- text_search = active_scaffold_config.field_search.text_search
147
- columns = active_scaffold_config.field_search.columns
148
- count_includes = active_scaffold_config.list.user.count_includes
149
- search_params.each do |key, value|
150
- next unless columns.include? key
151
- column = active_scaffold_config.columns[key]
152
- search_condition = self.class.condition_for_column(column, value, text_search)
153
- next if search_condition.blank?
154
-
155
- if count_includes.nil? && column.includes.present? && list_columns.include?(column) && !grouped_search?
156
- active_scaffold_references << column.includes
157
- elsif column.search_joins.present?
158
- active_scaffold_outer_joins << column.search_joins
159
- end
160
- active_scaffold_conditions << search_condition
161
- filtered_columns << column
162
- end
163
- if filtered_columns.present? || grouped_search?
164
- @filtered = active_scaffold_config.field_search.human_conditions ? filtered_columns : true
165
- end
166
-
167
- active_scaffold_config.list.user.page = nil
147
+ do_field_search
168
148
  else
169
149
  super
170
150
  end
171
151
  end
172
152
 
153
+ def do_field_search
154
+ filtered_columns = []
155
+ text_search = active_scaffold_config.field_search.text_search
156
+ columns = active_scaffold_config.field_search.columns
157
+ count_includes = active_scaffold_config.list.user.count_includes
158
+ search_params.each do |key, value|
159
+ next unless columns.include? key
160
+ column = active_scaffold_config.columns[key]
161
+ search_condition = self.class.condition_for_column(column, value, text_search)
162
+ next if search_condition.blank?
163
+
164
+ joins_for_search_on_column(column, count_includes)
165
+ active_scaffold_conditions << search_condition
166
+ filtered_columns << column
167
+ end
168
+ if filtered_columns.present? || grouped_search?
169
+ @filtered = active_scaffold_config.field_search.human_conditions ? filtered_columns : true
170
+ end
171
+
172
+ active_scaffold_config.list.user.page = nil
173
+ end
174
+
175
+ def joins_for_search_on_column(column, count_includes)
176
+ if count_includes.nil? && column.includes.present? && list_columns.include?(column) && !grouped_search?
177
+ active_scaffold_references << column.includes
178
+ elsif column.search_joins.present?
179
+ active_scaffold_outer_joins << column.search_joins
180
+ end
181
+ end
182
+
173
183
  def field_search_ignore?
174
184
  active_scaffold_config.list.always_show_search && active_scaffold_config.list.search_partial == 'field_search'
175
185
  end
@@ -2,7 +2,7 @@ module ActiveScaffold::Actions
2
2
  module List
3
3
  def self.included(base)
4
4
  base.before_action :list_authorized_filter, :only => :index
5
- base.helper_method :list_columns
5
+ base.helper_method :list_columns, :count_on_association_class?
6
6
  end
7
7
 
8
8
  def index
@@ -49,11 +49,11 @@ module ActiveScaffold::Actions
49
49
  end
50
50
 
51
51
  def list_respond_to_xml
52
- render :xml => response_object, :only => list_columns_names + [active_scaffold_config.model.primary_key], :include => association_columns(list_columns_names), :methods => virtual_columns(list_columns_names), :status => response_status
52
+ response_to_api(:xml, list_columns_names)
53
53
  end
54
54
 
55
55
  def list_respond_to_json
56
- render :json => response_object, :only => list_columns_names + [active_scaffold_config.model.primary_key], :include => association_columns(list_columns_names), :methods => virtual_columns(list_columns_names), :status => response_status
56
+ response_to_api(:json, list_columns_names)
57
57
  end
58
58
 
59
59
  def row_respond_to_html
@@ -71,7 +71,7 @@ module ActiveScaffold::Actions
71
71
  if respond_to?(:"#{action}_columns", true)
72
72
  send(:"#{action}_columns")
73
73
  else
74
- active_scaffold_config.send(action).columns.collect_visible(:flatten => true)
74
+ active_scaffold_config.send(action).columns.visible_columns(flatten: true)
75
75
  end
76
76
  joins_cols, preload_cols = columns.select { |c| c.includes.present? }.partition do |col|
77
77
  includes_need_join?(col, sorting) && !grouped_search?
@@ -82,7 +82,7 @@ module ActiveScaffold::Actions
82
82
  end
83
83
 
84
84
  def set_includes_for_sorting(columns, sorting)
85
- sorting.each do |col, _|
85
+ sorting.each_column do |col|
86
86
  next unless col.includes.present? && !columns.include?(col)
87
87
  if active_scaffold_config.model.connection.needs_order_expressions_in_select?
88
88
  active_scaffold_references << col.includes
@@ -93,12 +93,12 @@ module ActiveScaffold::Actions
93
93
  end
94
94
 
95
95
  def includes_need_join?(column, sorting = active_scaffold_config.list.user.sorting)
96
- sorting.sorts_on?(column) || scoped_habtm?(column)
96
+ (sorting.sorts_by_sql? && sorting.sorts_on?(column)) || scoped_habtm?(column)
97
97
  end
98
98
 
99
99
  def scoped_habtm?(column)
100
- assoc = column.association if column.association.try :collection?
101
- assoc && assoc.habtm? && assoc.scope
100
+ assoc = column.association if column.association&.collection?
101
+ assoc&.habtm? && assoc.scope
102
102
  end
103
103
 
104
104
  def get_row(crud_type_or_security_options = :read)
@@ -106,37 +106,115 @@ module ActiveScaffold::Actions
106
106
  super
107
107
  end
108
108
 
109
+ def current_page
110
+ set_includes_for_columns
111
+
112
+ page = find_page(find_page_options)
113
+ total_pages = page.pager.number_of_pages
114
+ if !page.pager.infinite? && !total_pages.zero? && page.number > total_pages
115
+ page = page.pager.last
116
+ active_scaffold_config.list.user.page = page.number
117
+ end
118
+ page
119
+ end
120
+
109
121
  # The actual algorithm to prepare for the list view
110
122
  def do_list
111
123
  # id: nil needed in params_for because rails reuse it even
112
124
  # if it was deleted from params (like do_refresh_list does)
113
125
  @remove_id_from_list_links = params[:id].blank?
114
- set_includes_for_columns
126
+ @page = current_page
127
+ @records = @page.items
128
+ cache_column_counts
129
+ end
130
+
131
+ def columns_to_cache_counts
132
+ list_columns.select do |col|
133
+ col.association&.collection? && col.includes.blank? && col.associated_number? &&
134
+ !ActiveScaffold::OrmChecks.tableless?(col.association.klass) &&
135
+ !col.association.reverse_association&.counter_cache
136
+ end
137
+ end
138
+
139
+ def cache_column_counts
140
+ @counts = columns_to_cache_counts.each_with_object({}) do |column, counts|
141
+ if ActiveScaffold::OrmChecks.active_record?(column.association.klass)
142
+ counts[column.name] = count_query_for_column(column).count
143
+ elsif ActiveScaffold::OrmChecks.mongoid?(column.association.klass)
144
+ counts[column.name] = mongoid_count_for_column(column)
145
+ end
146
+ end
147
+ end
148
+
149
+ def count_on_association_class?(column)
150
+ column.association.has_many? && !column.association.through? &&
151
+ (!column.association.as || column.association.reverse_association)
152
+ end
153
+
154
+ def count_query_for_column(column)
155
+ if count_on_association_class?(column)
156
+ count_query_on_association_class(column)
157
+ else
158
+ count_query_with_join(column)
159
+ end
160
+ end
161
+
162
+ def count_query_on_association_class(column)
163
+ key = column.association.primary_key || :id
164
+ query = column.association.klass.where(column.association.foreign_key => @records.map(&key))
165
+ if column.association.as
166
+ query.where!(column.association.reverse_association.foreign_type => active_scaffold_config.model.name)
167
+ end
168
+ if column.association.scope
169
+ query = query.instance_exec(&column.association.scope)
170
+ end
171
+ query.group(column.association.foreign_key)
172
+ end
173
+
174
+ def count_query_with_join(column)
175
+ klass = column.association.klass
176
+ query = active_scaffold_config.model.where(active_scaffold_config.primary_key => @records.map(&:id))
177
+ .joins(column.name).group(active_scaffold_config.primary_key)
178
+ .select("#{klass.quoted_table_name}.#{klass.quoted_primary_key}")
179
+ query = query.uniq if column.association.scope && klass.instance_exec(&column.association.scope).values[:distinct]
180
+ query
181
+ end
182
+
183
+ def mongoid_count_for_column(column)
184
+ matches = {column.association.foreign_key => {'$in': @records.map(&:id)}}
185
+ if column.association.as
186
+ matches[column.association.reverse_association.foreign_type] = {'$eq': active_scaffold_config.model.name}
187
+ end
188
+ group = {_id: "$#{column.association.foreign_key}", count: {'$sum' => 1}}
189
+ query = column.association.klass.collection.aggregate([{'$match' => matches}, {'$group' => group}])
190
+ query.each_with_object({}) do |row, hash|
191
+ hash[row['_id']] = row['count']
192
+ end
193
+ end
194
+
195
+ def find_page_options
196
+ options = {
197
+ :sorting => active_scaffold_config.list.user.sorting,
198
+ :count_includes => active_scaffold_config.list.user.count_includes
199
+ }
115
200
 
116
- options = {:sorting => active_scaffold_config.list.user.sorting,
117
- :count_includes => active_scaffold_config.list.user.count_includes}
118
- paginate = params[:format].nil? ? (accepts? :html, :js) : %w[html js].include?(params[:format])
201
+ paginate = params[:format].nil? ? accepts?(:html, :js) : %w[html js].include?(params[:format])
119
202
  options[:pagination] = active_scaffold_config.list.pagination if paginate
120
203
  if options[:pagination]
121
204
  options[:per_page] = active_scaffold_config.list.user.per_page
122
205
  options[:page] = active_scaffold_config.list.user.page
123
206
  end
207
+
124
208
  if active_scaffold_config.list.auto_select_columns
125
209
  auto_select_columns = list_columns + [active_scaffold_config.columns[active_scaffold_config.model.primary_key]]
126
210
  options[:select] = auto_select_columns.map { |c| quoted_select_columns(c.select_columns) }.compact.flatten
127
211
  end
128
212
 
129
- page = find_page(options)
130
- total_pages = page.pager.number_of_pages
131
- if !page.pager.infinite? && !total_pages.zero? && page.number > total_pages
132
- page = page.pager.last
133
- active_scaffold_config.list.user.page = page.number
134
- end
135
- @page, @records = page, page.items
213
+ options
136
214
  end
137
215
 
138
216
  def quoted_select_columns(columns)
139
- columns.map { |c| active_scaffold_config.columns[c].try(:field) || c } if columns
217
+ columns&.map { |c| active_scaffold_config.columns[c]&.field || c }
140
218
  end
141
219
 
142
220
  def do_refresh_list
@@ -149,17 +227,23 @@ module ActiveScaffold::Actions
149
227
  end
150
228
 
151
229
  def each_record_in_page
152
- current_page = active_scaffold_config.list.user.page
153
- do_search if respond_to? :do_search, true
154
- active_scaffold_config.list.user.page = current_page
155
- do_list
156
- @page.items.each { |record| yield record }
230
+ page_items.each { |record| yield record }
157
231
  end
158
232
 
159
233
  def each_record_in_scope
160
234
  scoped_query.each { |record| yield record }
161
235
  end
162
236
 
237
+ def page_items
238
+ @page_items ||= begin
239
+ page_number = active_scaffold_config.list.user.page
240
+ do_search if respond_to? :do_search, true
241
+ active_scaffold_config.list.user.page = page_number
242
+ @page = current_page
243
+ @page.items
244
+ end
245
+ end
246
+
163
247
  def scoped_query
164
248
  @scoped_query ||= begin
165
249
  do_search if respond_to? :do_search, true
@@ -189,7 +273,7 @@ module ActiveScaffold::Actions
189
273
  {:etag => active_scaffold_config.list.user.sorting.clause}
190
274
  end
191
275
  end
192
- objects.present? ? objects : super
276
+ objects.presence || super
193
277
  end
194
278
 
195
279
  private
@@ -216,7 +300,7 @@ module ActiveScaffold::Actions
216
300
  end
217
301
 
218
302
  def list_columns
219
- @list_columns ||= active_scaffold_config.list.columns.collect_visible
303
+ @list_columns ||= active_scaffold_config.list.columns.visible_columns
220
304
  end
221
305
 
222
306
  def list_columns_names