active_scaffold 3.4.43 → 3.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (216) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +39 -0
  3. data/{LICENSE → LICENSE.md} +1 -1
  4. data/README.md +27 -19
  5. data/app/assets/javascripts/active_scaffold.js.erb +1 -1
  6. data/app/assets/javascripts/jquery/active_scaffold.js +95 -43
  7. data/app/assets/javascripts/jquery/tiny_mce_bridge.js +30 -6
  8. data/app/assets/javascripts/prototype/tiny_mce_bridge.js +11 -1
  9. data/app/assets/stylesheets/active_scaffold_colors.scss +2 -2
  10. data/app/assets/stylesheets/active_scaffold_layout.css +36 -28
  11. data/app/views/active_scaffold_overrides/_base_form.html.erb +2 -3
  12. data/app/views/active_scaffold_overrides/_field_search.html.erb +8 -7
  13. data/app/views/active_scaffold_overrides/_form_association.html.erb +9 -9
  14. data/app/views/active_scaffold_overrides/_form_association_footer.html.erb +6 -6
  15. data/app/views/active_scaffold_overrides/_form_association_record.html.erb +52 -50
  16. data/app/views/active_scaffold_overrides/_horizontal_subform.html.erb +1 -1
  17. data/app/views/active_scaffold_overrides/_horizontal_subform_header.html.erb +1 -1
  18. data/app/views/active_scaffold_overrides/_human_conditions.html.erb +3 -1
  19. data/app/views/active_scaffold_overrides/_list_calculations.html.erb +1 -1
  20. data/app/views/active_scaffold_overrides/_list_column_headings.html.erb +2 -0
  21. data/app/views/active_scaffold_overrides/_list_messages.html.erb +5 -3
  22. data/app/views/active_scaffold_overrides/_list_record.html.erb +3 -1
  23. data/app/views/active_scaffold_overrides/_list_with_header.html.erb +9 -9
  24. data/app/views/active_scaffold_overrides/_messages.html.erb +1 -1
  25. data/app/views/active_scaffold_overrides/_refresh_list.js.erb +18 -10
  26. data/app/views/active_scaffold_overrides/_render_field.js.erb +3 -3
  27. data/app/views/active_scaffold_overrides/_search.html.erb +7 -6
  28. data/app/views/active_scaffold_overrides/_show_actions.html.erb +14 -0
  29. data/app/views/active_scaffold_overrides/_show_association.html.erb +1 -1
  30. data/app/views/active_scaffold_overrides/_update_actions.html.erb +6 -2
  31. data/app/views/active_scaffold_overrides/_update_column.js.erb +1 -1
  32. data/app/views/active_scaffold_overrides/_update_form.html.erb +1 -1
  33. data/app/views/active_scaffold_overrides/destroy.js.erb +2 -3
  34. data/app/views/active_scaffold_overrides/edit_associated.js.erb +4 -3
  35. data/app/views/active_scaffold_overrides/on_action_update.js.erb +5 -3
  36. data/app/views/active_scaffold_overrides/on_create.js.erb +4 -4
  37. data/app/views/active_scaffold_overrides/on_update.js.erb +6 -6
  38. data/app/views/active_scaffold_overrides/show.html.erb +6 -0
  39. data/app/views/active_scaffold_overrides/update.html.erb +1 -1
  40. data/app/views/active_scaffold_overrides/update_column.js.erb +1 -1
  41. data/config/brakeman.ignore +26 -0
  42. data/config/brakeman.yml +3 -0
  43. data/config/i18n-tasks.yml +121 -0
  44. data/config/locales/de.yml +81 -70
  45. data/config/locales/en.yml +83 -74
  46. data/config/locales/es.yml +82 -73
  47. data/config/locales/fr.yml +86 -75
  48. data/config/locales/hu.yml +81 -70
  49. data/config/locales/ja.yml +71 -60
  50. data/config/locales/ru.yml +85 -74
  51. data/lib/active_scaffold.rb +3 -0
  52. data/lib/active_scaffold/actions/common_search.rb +11 -7
  53. data/lib/active_scaffold/actions/core.rb +119 -47
  54. data/lib/active_scaffold/actions/create.rb +1 -1
  55. data/lib/active_scaffold/actions/delete.rb +11 -8
  56. data/lib/active_scaffold/actions/field_search.rb +104 -6
  57. data/lib/active_scaffold/actions/list.rb +25 -21
  58. data/lib/active_scaffold/actions/mark.rb +12 -4
  59. data/lib/active_scaffold/actions/nested.rb +26 -26
  60. data/lib/active_scaffold/actions/search.rb +2 -2
  61. data/lib/active_scaffold/actions/show.rb +4 -5
  62. data/lib/active_scaffold/actions/subform.rb +9 -7
  63. data/lib/active_scaffold/actions/update.rb +20 -13
  64. data/lib/active_scaffold/active_record_permissions.rb +24 -5
  65. data/lib/active_scaffold/attribute_params.rb +68 -49
  66. data/lib/active_scaffold/bridges.rb +1 -1
  67. data/lib/active_scaffold/bridges/ancestry/ancestry_bridge.rb +15 -19
  68. data/lib/active_scaffold/bridges/bitfields.rb +1 -1
  69. data/lib/active_scaffold/bridges/bitfields/bitfields_bridge.rb +10 -14
  70. data/lib/active_scaffold/bridges/calendar_date_select.rb +0 -7
  71. data/lib/active_scaffold/bridges/calendar_date_select/as_cds_bridge.rb +19 -22
  72. data/lib/active_scaffold/bridges/cancan.rb +4 -3
  73. data/lib/active_scaffold/bridges/cancan/cancan_bridge.rb +11 -21
  74. data/lib/active_scaffold/bridges/carrierwave.rb +2 -1
  75. data/lib/active_scaffold/bridges/carrierwave/carrierwave_bridge.rb +2 -6
  76. data/lib/active_scaffold/bridges/carrierwave/form_ui.rb +6 -39
  77. data/lib/active_scaffold/bridges/carrierwave/list_ui.rb +1 -1
  78. data/lib/active_scaffold/bridges/chosen.rb +4 -1
  79. data/lib/active_scaffold/bridges/chosen/helpers.rb +3 -2
  80. data/lib/active_scaffold/bridges/country_select/country_select_bridge_helper.rb +2 -2
  81. data/lib/active_scaffold/bridges/date_picker.rb +3 -0
  82. data/lib/active_scaffold/bridges/date_picker/ext.rb +43 -38
  83. data/lib/active_scaffold/bridges/date_picker/helper.rb +24 -23
  84. data/lib/active_scaffold/bridges/dragonfly.rb +1 -1
  85. data/lib/active_scaffold/bridges/dragonfly/dragonfly_bridge.rb +3 -7
  86. data/lib/active_scaffold/bridges/dragonfly/form_ui.rb +3 -25
  87. data/lib/active_scaffold/bridges/dragonfly/list_ui.rb +2 -2
  88. data/lib/active_scaffold/bridges/file_column/as_file_column_bridge.rb +6 -8
  89. data/lib/active_scaffold/bridges/file_column/file_column_helpers.rb +1 -1
  90. data/lib/active_scaffold/bridges/file_column/form_ui.rb +0 -2
  91. data/lib/active_scaffold/bridges/file_column/list_ui.rb +2 -1
  92. data/lib/active_scaffold/bridges/file_column/test/test_helper.rb +1 -1
  93. data/lib/active_scaffold/bridges/paper_trail/actions.rb +1 -1
  94. data/lib/active_scaffold/bridges/paper_trail/helper.rb +1 -2
  95. data/lib/active_scaffold/bridges/paper_trail/paper_trail_bridge.rb +3 -7
  96. data/lib/active_scaffold/bridges/paperclip.rb +1 -1
  97. data/lib/active_scaffold/bridges/paperclip/form_ui.rb +3 -28
  98. data/lib/active_scaffold/bridges/paperclip/list_ui.rb +1 -1
  99. data/lib/active_scaffold/bridges/paperclip/paperclip_bridge.rb +3 -7
  100. data/lib/active_scaffold/bridges/record_select.rb +2 -0
  101. data/lib/active_scaffold/bridges/record_select/helpers.rb +14 -18
  102. data/lib/active_scaffold/bridges/semantic_attributes/column.rb +4 -8
  103. data/lib/active_scaffold/bridges/shared/date_bridge.rb +20 -20
  104. data/lib/active_scaffold/bridges/tiny_mce/helpers.rb +7 -22
  105. data/lib/active_scaffold/bridges/usa_state_select/usa_state_select_helper.rb +14 -14
  106. data/lib/active_scaffold/config/base.rb +9 -6
  107. data/lib/active_scaffold/config/core.rb +30 -21
  108. data/lib/active_scaffold/config/create.rb +2 -1
  109. data/lib/active_scaffold/config/delete.rb +2 -2
  110. data/lib/active_scaffold/config/field_search.rb +9 -3
  111. data/lib/active_scaffold/config/form.rb +4 -4
  112. data/lib/active_scaffold/config/list.rb +27 -23
  113. data/lib/active_scaffold/config/nested.rb +4 -4
  114. data/lib/active_scaffold/config/search.rb +6 -6
  115. data/lib/active_scaffold/config/show.rb +11 -1
  116. data/lib/active_scaffold/config/subform.rb +1 -1
  117. data/lib/active_scaffold/config/update.rb +4 -2
  118. data/lib/active_scaffold/constraints.rb +39 -36
  119. data/lib/active_scaffold/core.rb +36 -15
  120. data/lib/active_scaffold/data_structures/action_columns.rb +14 -9
  121. data/lib/active_scaffold/data_structures/action_link.rb +4 -5
  122. data/lib/active_scaffold/data_structures/action_links.rb +5 -4
  123. data/lib/active_scaffold/data_structures/actions.rb +2 -2
  124. data/lib/active_scaffold/data_structures/association.rb +8 -0
  125. data/lib/active_scaffold/data_structures/association/abstract.rb +147 -0
  126. data/lib/active_scaffold/data_structures/association/active_mongoid.rb +42 -0
  127. data/lib/active_scaffold/data_structures/association/active_record.rb +94 -0
  128. data/lib/active_scaffold/data_structures/association/mongoid.rb +45 -0
  129. data/lib/active_scaffold/data_structures/bridge.rb +3 -6
  130. data/lib/active_scaffold/data_structures/column.rb +100 -82
  131. data/lib/active_scaffold/data_structures/columns.rb +21 -3
  132. data/lib/active_scaffold/data_structures/nested_info.rb +22 -37
  133. data/lib/active_scaffold/data_structures/set.rb +4 -4
  134. data/lib/active_scaffold/data_structures/sorting.rb +29 -15
  135. data/lib/active_scaffold/engine.rb +3 -1
  136. data/lib/active_scaffold/extensions/action_controller_rendering.rb +10 -5
  137. data/lib/active_scaffold/extensions/action_view_rendering.rb +65 -59
  138. data/lib/active_scaffold/extensions/left_outer_joins.rb +48 -53
  139. data/lib/active_scaffold/extensions/localize.rb +3 -4
  140. data/lib/active_scaffold/extensions/name_option_for_datetime.rb +7 -11
  141. data/lib/active_scaffold/extensions/paginator_extensions.rb +20 -18
  142. data/lib/active_scaffold/extensions/routing_mapper.rb +104 -40
  143. data/lib/active_scaffold/extensions/to_label.rb +1 -1
  144. data/lib/active_scaffold/extensions/unsaved_associated.rb +4 -13
  145. data/lib/active_scaffold/extensions/unsaved_record.rb +12 -1
  146. data/lib/active_scaffold/finder.rb +200 -134
  147. data/lib/active_scaffold/helpers/action_link_helpers.rb +398 -0
  148. data/lib/active_scaffold/helpers/association_helpers.rb +12 -30
  149. data/lib/active_scaffold/helpers/controller_helpers.rb +74 -24
  150. data/lib/active_scaffold/helpers/form_column_helpers.rb +205 -112
  151. data/lib/active_scaffold/helpers/human_condition_helpers.rb +21 -11
  152. data/lib/active_scaffold/helpers/id_helpers.rb +1 -1
  153. data/lib/active_scaffold/helpers/list_column_helpers.rb +117 -39
  154. data/lib/active_scaffold/helpers/pagination_helpers.rb +11 -14
  155. data/lib/active_scaffold/helpers/search_column_helpers.rb +69 -32
  156. data/lib/active_scaffold/helpers/show_column_helpers.rb +9 -3
  157. data/lib/active_scaffold/helpers/view_helpers.rb +41 -426
  158. data/lib/active_scaffold/orm_checks.rb +109 -0
  159. data/lib/active_scaffold/paginator.rb +1 -1
  160. data/lib/active_scaffold/responds_to_parent.rb +12 -10
  161. data/lib/active_scaffold/tableless.rb +81 -43
  162. data/lib/active_scaffold/version.rb +2 -2
  163. data/lib/generators/active_scaffold/controller_generator.rb +49 -0
  164. data/lib/generators/active_scaffold/install_generator.rb +45 -0
  165. data/lib/generators/active_scaffold/resource_generator.rb +56 -0
  166. data/lib/generators/{active_scaffold_controller/templates → templates}/controller.rb +0 -0
  167. data/lib/generators/{active_scaffold_controller/templates → templates}/helper.rb +0 -0
  168. data/shoulda_macros/macros.rb +3 -3
  169. data/test/active_scaffold_config_mock.rb +33 -0
  170. data/test/bridges/bridge_test.rb +9 -9
  171. data/test/bridges/date_picker_test.rb +3 -1
  172. data/test/bridges/paper_trail_test.rb +2 -3
  173. data/test/bridges/paperclip_test.rb +21 -10
  174. data/test/bridges/tiny_mce_test.rb +20 -21
  175. data/test/class_with_finder.rb +42 -0
  176. data/test/company.rb +6 -4
  177. data/test/config/core_test.rb +1 -1
  178. data/test/config/create_test.rb +1 -1
  179. data/test/config/list_test.rb +3 -3
  180. data/test/config/update_test.rb +3 -3
  181. data/test/data_structures/action_columns_test.rb +3 -3
  182. data/test/data_structures/association_column_test.rb +5 -5
  183. data/test/data_structures/column_test.rb +14 -14
  184. data/test/data_structures/columns_test.rb +2 -2
  185. data/test/data_structures/set_test.rb +2 -2
  186. data/test/data_structures/sorting_test.rb +6 -4
  187. data/test/extensions/active_record_test.rb +1 -1
  188. data/test/extensions/routing_mapper_test.rb +64 -13
  189. data/test/helpers/form_column_helpers_test.rb +6 -6
  190. data/test/helpers/list_column_helpers_test.rb +9 -5
  191. data/test/helpers/pagination_helpers_test.rb +1 -0
  192. data/test/misc/active_record_permissions_test.rb +18 -1
  193. data/test/misc/attribute_params_test.rb +26 -17
  194. data/test/misc/calculation_test.rb +8 -31
  195. data/test/misc/configurable_test.rb +3 -2
  196. data/test/misc/constraints_test.rb +33 -22
  197. data/test/misc/convert_numbers_format_test.rb +28 -10
  198. data/test/misc/finder_test.rb +6 -29
  199. data/test/misc/parse_datetime_test.rb +160 -0
  200. data/test/misc/render_test.rb +1 -1
  201. data/test/misc/tableless_test.rb +24 -0
  202. data/test/mock_app/app/models/building.rb +2 -1
  203. data/test/mock_app/config.ru +1 -1
  204. data/test/mock_app/config/environments/test.rb +1 -1
  205. data/test/mock_app/config/routes.rb +11 -3
  206. data/test/model_stub.rb +11 -6
  207. data/test/run_all.rb +1 -1
  208. data/test/test_helper.rb +19 -4
  209. metadata +42 -23
  210. data/lib/active_scaffold/data_structures/error_message.rb +0 -22
  211. data/lib/active_scaffold/extensions/reverse_associations.rb +0 -119
  212. data/lib/generators/active_scaffold/USAGE +0 -29
  213. data/lib/generators/active_scaffold/active_scaffold_generator.rb +0 -21
  214. data/lib/generators/active_scaffold_controller/USAGE +0 -19
  215. data/lib/generators/active_scaffold_controller/active_scaffold_controller_generator.rb +0 -29
  216. data/test/data_structures/error_message_test.rb +0 -25
@@ -5,31 +5,85 @@ module ActiveScaffold
5
5
  end
6
6
 
7
7
  module ClassMethods
8
+ def self.extended(klass)
9
+ return unless klass.active_scaffold_config
10
+ if klass.active_scaffold_config.active_record?
11
+ klass.extend ActiveRecord
12
+ elsif klass.active_scaffold_config.mongoid?
13
+ klass.extend Mongoid
14
+ end
15
+ end
16
+
8
17
  # Takes a collection of search terms (the tokens) and creates SQL that
9
18
  # searches all specified ActiveScaffold columns. A row will match if each
10
19
  # token is found in at least one of the columns.
11
- def create_conditions_for_columns(tokens, columns, text_search = :full)
20
+ def conditions_for_columns(tokens, columns, text_search = :full)
12
21
  # if there aren't any columns, then just return a nil condition
13
22
  return unless columns.any?
14
- like_pattern = like_pattern(text_search)
15
23
 
16
24
  tokens = [tokens] if tokens.is_a? String
25
+ tokens = type_casted_tokens(tokens, columns, like_pattern(text_search))
26
+ create_conditions_for_columns(tokens, columns)
27
+ end
17
28
 
18
- where_clauses = []
19
- columns.each do |column|
20
- Array(column.search_sql).each do |search_sql|
21
- where_clauses << "#{search_sql} #{column.text? ? ActiveScaffold::Finder.like_operator : '='} ?"
29
+ def type_casted_tokens(tokens, columns, like_pattern)
30
+ tokens.map do |value|
31
+ columns.each_with_object({}) do |column, column_tokens|
32
+ column_tokens[column.name] = column.text? ? like_pattern.sub('?', value) : ActiveScaffold::Core.column_type_cast(value, column.column)
22
33
  end
23
34
  end
24
- phrase = where_clauses.join(' OR ')
35
+ end
25
36
 
26
- tokens.collect do |value|
27
- columns.each_with_object([phrase]) do |column, condition|
28
- Array(column.search_sql).size.times do
29
- condition.push(column.text? ? like_pattern.sub('?', value) : ActiveScaffold::Core.column_type_cast(value, column.column))
37
+ module ActiveRecord
38
+ def create_conditions_for_columns(tokens, columns)
39
+ where_clauses = []
40
+ columns.each do |column|
41
+ column.search_sql.each do |search_sql|
42
+ where_clauses << "#{search_sql} #{column.text? ? ActiveScaffold::Finder.like_operator : '='} ?"
43
+ end
44
+ end
45
+ phrase = where_clauses.join(' OR ')
46
+
47
+ tokens.map do |columns_token|
48
+ columns.each_with_object([phrase]) do |column, condition|
49
+ condition.concat([columns_token[column.name]] * column.search_sql.size)
30
50
  end
31
51
  end
32
52
  end
53
+
54
+ def like_pattern(text_search)
55
+ case text_search
56
+ when :full then '%?%'
57
+ when :start then '?%'
58
+ when :end then '%?'
59
+ else '?'
60
+ end
61
+ end
62
+ end
63
+
64
+ module Mongoid
65
+ def create_conditions_for_columns(tokens, columns)
66
+ conditions = tokens.map do |columns_token|
67
+ token_conditions = columns.map do |column|
68
+ value = columns_token[column.name]
69
+ value = /#{value}/ if column.text?
70
+ column.search_sql.map do |search_sql|
71
+ {search_sql => value}
72
+ end
73
+ end.flatten
74
+ active_scaffold_config.model.or(token_conditions).selector
75
+ end
76
+ [active_scaffold_config.model.and(conditions).selector]
77
+ end
78
+
79
+ def like_pattern(text_search)
80
+ case text_search
81
+ when :full then '?'
82
+ when :start then '^?'
83
+ when :end then '?$'
84
+ else '^?$'
85
+ end
86
+ end
33
87
  end
34
88
 
35
89
  # Generates an SQL condition for the given ActiveScaffold column based on
@@ -38,14 +92,14 @@ module ActiveScaffold
38
92
  def condition_for_column(column, value, text_search = :full)
39
93
  like_pattern = like_pattern(text_search)
40
94
  value = value.with_indifferent_access if value.is_a? Hash
41
- if self.respond_to?("condition_for_#{column.name}_column")
95
+ if respond_to?("condition_for_#{column.name}_column")
42
96
  return send("condition_for_#{column.name}_column", column, value, like_pattern)
43
97
  end
44
- return unless column && column.search_sql && !value.blank?
45
- search_ui = column.search_ui || column.column.try(:type)
98
+ return unless column && column.search_sql && value.present?
99
+ search_ui = column.search_ui || column.column_type
46
100
  begin
47
101
  sql, *values =
48
- if search_ui && self.respond_to?("condition_for_#{search_ui}_type")
102
+ if search_ui && respond_to?("condition_for_#{search_ui}_type")
49
103
  send("condition_for_#{search_ui}_type", column, value, like_pattern)
50
104
  else
51
105
  if column.search_sql.instance_of? Proc
@@ -60,7 +114,7 @@ module ActiveScaffold
60
114
  conditions += values * column.search_sql.size if values.present?
61
115
  conditions
62
116
  rescue StandardError => e
63
- logger.error "#{e.class.name}: #{e.message} -- on the ActiveScaffold column :#{column.name}, search_ui = #{search_ui} in #{name}"
117
+ Rails.logger.error "#{e.class.name}: #{e.message} -- on the ActiveScaffold column :#{column.name}, search_ui = #{search_ui} in #{name}"
64
118
  raise e
65
119
  end
66
120
  end
@@ -76,7 +130,8 @@ module ActiveScaffold
76
130
  when :date, :time, :datetime, :timestamp
77
131
  condition_for_datetime(column, value)
78
132
  when :select, :multi_select, :country, :usa_state, :chosen, :multi_chosen
79
- ['%{search_sql} in (?)', Array(value)]
133
+ values = Array(value).select(&:present?)
134
+ ['%{search_sql} in (?)', values] if values.present?
80
135
  else
81
136
  if column.text?
82
137
  ["%{search_sql} #{ActiveScaffold::Finder.like_operator} ?", like_pattern.sub('?', value)]
@@ -120,61 +175,89 @@ module ActiveScaffold
120
175
  end
121
176
  end
122
177
 
123
- def translate_days_and_months(value, format)
178
+ def tables_for_translating_days_and_months(format)
124
179
  keys = {
125
180
  '%A' => 'date.day_names',
126
181
  '%a' => 'date.abbr_day_names',
127
182
  '%B' => 'date.month_names',
128
183
  '%b' => 'date.abbr_month_names'
129
184
  }
130
- keys.each do |f, k|
131
- if format.include? f
132
- table = Hash[I18n.t(k).compact.zip(I18n.t(k, :locale => :en).compact)]
133
- value.gsub!(Regexp.union(table.keys)) { |s| table[s] }
134
- end
185
+ key_index = keys.keys.map { |key| [key, format.index(key)] }.to_h
186
+ keys.select! { |k, _| key_index[k] }
187
+ keys.sort_by { |k, _| key_index[k] }.map do |_, k|
188
+ I18n.t(k).compact.zip(I18n.t(k, :locale => :en).compact).to_h
135
189
  end
136
- value
137
190
  end
138
191
 
139
- def condition_value_for_datetime(column, value, conversion = :to_time)
140
- if value.is_a? Hash
141
- time = Time.zone.local(*[:year, :month, :day, :hour, :minute, :second].collect { |part| value[part].to_i }) rescue nil
142
- time.send(conversion) if time
143
- elsif value.respond_to?(:strftime)
144
- if conversion == :to_time
145
- # Explicitly get the current zone, because TimeWithZone#to_time in rails 3.2.3 returns UTC.
146
- # https://github.com/rails/rails/pull/2453
147
- value.to_time.in_time_zone
148
- else
149
- value.send(conversion)
192
+ def translate_days_and_months(value, format)
193
+ translated = ''
194
+ tables_for_translating_days_and_months(format).each do |table|
195
+ regexp = Regexp.union(table.keys)
196
+ index = value.index(regexp)
197
+ next unless index
198
+ translated << value.slice!(0...index)
199
+ value.sub!(regexp) do |str|
200
+ translated << table[str]
201
+ ''
150
202
  end
151
- elsif conversion == :to_date
152
- Date.strptime(value, I18n.t("date.formats.#{column.options[:format] || :default}")) rescue nil
203
+ end
204
+ translated << value
205
+ end
206
+
207
+ def format_for_datetime(column, value)
208
+ parts = Date._parse(value)
209
+ if ActiveScaffold.js_framework == :jquery
210
+ format = I18n.translate "time.formats.#{column.options[:format] || :picker}", :default => ''
211
+ end
212
+
213
+ if format.blank?
214
+ time_parts = [[:hour, '%H'], [:min, '%M'], [:sec, '%S']].map do |part, format_part|
215
+ format_part if parts[part].present?
216
+ end.compact
217
+ format = "#{I18n.t('date.formats.default')} #{time_parts.join(':')} #{'%z' if parts[:offset].present?}"
153
218
  else
154
- parts = Date._parse(value)
155
- format = I18n.translate "time.formats.#{column.options[:format] || :picker}", :default => '' if ActiveScaffold.js_framework == :jquery
156
- if format.blank?
157
- time_parts = [[:hour, '%H'], [:min, '%M'], [:sec, '%S']].collect { |part, format_part| format_part if parts[part].present? }.compact
158
- format = "#{I18n.t('date.formats.default')} #{time_parts.join(':')} #{'%z' if parts[:offset].present?}"
159
- else
160
- if parts[:hour]
161
- [[:min, '%M'], [:sec, '%S']].each { |part, f| format.gsub!(":#{f}", '') unless parts[part].present? }
219
+ [[:hour, '%H'], [:min, ':%M'], [:sec, ':%S']].each do |part, f|
220
+ format.gsub!(f, '') if parts[part].blank?
221
+ end
222
+ format += ' %z' if parts[:offset].present? && format !~ /%z/i
223
+ end
224
+
225
+ format.gsub!(/.*(?=%H)/, '') if !parts[:year] && !parts[:month] && !parts[:mday]
226
+ [format, parts[:offset]]
227
+ end
228
+
229
+ def condition_value_for_datetime(column, value, conversion = :to_time)
230
+ unless value.nil? || value.blank?
231
+ if value.is_a? Hash
232
+ time = Time.zone.local(*%i[year month day hour minute second].collect { |part| value[part].to_i }) rescue nil
233
+ time.send(conversion) if time
234
+ elsif value.respond_to?(:strftime)
235
+ if conversion == :to_time
236
+ # Explicitly get the current zone, because TimeWithZone#to_time in rails 3.2.3 returns UTC.
237
+ # https://github.com/rails/rails/pull/2453
238
+ value.to_time.in_time_zone
162
239
  else
163
- value += ' 00:00:00'
240
+ value.send(conversion)
164
241
  end
165
- format += ' %z' if parts[:offset].present? && format !~ /%z/i
166
- end
167
- if !parts[:year] && !parts[:month] && !parts[:mday]
168
- value = "#{Date.today.strftime(format.gsub(/%[HI].*/, ''))} #{value}"
169
- end
170
- value = translate_days_and_months(value, format) if I18n.locale != :en
171
- time = DateTime.strptime(value, format) rescue nil
172
- if time
173
- time = Time.zone.local_to_utc(time).in_time_zone unless parts[:offset]
174
- time = time.send(conversion) unless conversion == :to_time
242
+ elsif conversion == :to_date
243
+ format = I18n.t("date.formats.#{column.options[:format] || :default}")
244
+ format.gsub!(/%-d|%-m|%_m/) { |s| s.gsub(/[-_]/, '') } # strptime fails with %-d, %-m, %_m
245
+ value = translate_days_and_months(value, format) if I18n.locale != :en
246
+ Date.strptime(value, format) rescue nil
247
+ elsif value.include?('T')
248
+ Time.zone.parse(value)
249
+ else # datetime
250
+ format, offset = format_for_datetime(column, value)
251
+ format.gsub!(/%-d|%-m|%_m/) { |s| s.gsub(/[-_]/, '') } # strptime fails with %-d, %-m, %_m
252
+ value = translate_days_and_months(value, format) if I18n.locale != :en
253
+ time = DateTime.strptime(value, format) rescue nil
254
+ if time
255
+ time = Time.zone.local_to_utc(time).in_time_zone unless offset
256
+ time = time.send(conversion) unless conversion == :to_time
257
+ end
258
+ time
175
259
  end
176
- time
177
- end unless value.nil? || value.blank?
260
+ end
178
261
  end
179
262
 
180
263
  def condition_value_for_numeric(column, value)
@@ -185,9 +268,9 @@ module ActiveScaffold
185
268
  when :float then value.to_f
186
269
  when :decimal
187
270
  if Rails.version >= '4.2.0'
188
- ActiveRecord::Type::Decimal.new.type_cast_from_user(value)
271
+ ::ActiveRecord::Type::Decimal.new.type_cast_from_user(value)
189
272
  else
190
- ActiveRecord::ConnectionAdapters::Column.value_to_decimal(value)
273
+ ::ActiveRecord::ConnectionAdapters::Column.value_to_decimal(value)
191
274
  end
192
275
  else
193
276
  value
@@ -234,15 +317,6 @@ module ActiveScaffold
234
317
  ['%{search_sql} is not null', []]
235
318
  end
236
319
  end
237
-
238
- def like_pattern(text_search)
239
- case text_search
240
- when :full then '%?%'
241
- when :start then '?%'
242
- when :end then '%?'
243
- else '?'
244
- end
245
- end
246
320
  end
247
321
 
248
322
  NUMERIC_COMPARATORS = [
@@ -253,13 +327,13 @@ module ActiveScaffold
253
327
  '<',
254
328
  '!=',
255
329
  'BETWEEN'
256
- ]
330
+ ].freeze
257
331
  STRING_COMPARATORS = {
258
332
  :contains => '%?%',
259
333
  :begins_with => '?%',
260
334
  :ends_with => '%?'
261
- }
262
- NULL_COMPARATORS = %w(null not_null)
335
+ }.freeze
336
+ NULL_COMPARATORS = %w[null not_null].freeze
263
337
 
264
338
  def self.included(klass)
265
339
  klass.extend ClassMethods
@@ -277,16 +351,6 @@ module ActiveScaffold
277
351
  @active_scaffold_preload ||= []
278
352
  end
279
353
 
280
- def active_scaffold_includes=(value)
281
- ActiveSupport::Deprecation.warn "active_scaffold_includes doesn't exist anymore, use active_scaffold_preload, active_scaffold_outer_joins or active_scaffold_references"
282
- self.active_scaffold_preload = value
283
- end
284
-
285
- def active_scaffold_includes
286
- ActiveSupport::Deprecation.warn "active_scaffold_includes doesn't exist anymore, use active_scaffold_preload, active_scaffold_outer_joins or active_scaffold_references"
287
- active_scaffold_preload
288
- end
289
-
290
354
  attr_writer :active_scaffold_habtm_joins
291
355
  def active_scaffold_habtm_joins
292
356
  @active_scaffold_habtm_joins ||= []
@@ -303,18 +367,20 @@ module ActiveScaffold
303
367
  end
304
368
 
305
369
  # Override this method on your controller to define conditions to be used when querying a recordset (e.g. for List). The return of this method should be any format compatible with the :conditions clause of ActiveRecord::Base's find.
306
- def conditions_for_collection
307
- end
370
+ def conditions_for_collection; end
308
371
 
309
372
  # Override this method on your controller to define joins to be used when querying a recordset (e.g. for List). The return of this method should be any format compatible with the :joins clause of ActiveRecord::Base's find.
310
- def joins_for_collection
311
- end
373
+ def joins_for_collection; end
312
374
 
313
375
  # Override this method on your controller to provide custom finder options to the find() call. The return of this method should be a hash.
314
376
  def custom_finder_options
315
377
  {}
316
378
  end
317
379
 
380
+ def active_scaffold_embedded_conditions
381
+ params_hash active_scaffold_embedded_params[:conditions]
382
+ end
383
+
318
384
  def all_conditions
319
385
  [
320
386
  id_condition, # for list with id (e.g. /users/:id/index)
@@ -322,12 +388,12 @@ module ActiveScaffold
322
388
  conditions_for_collection, # from the dev
323
389
  conditions_from_params, # from the parameters (e.g. /users/list?first_name=Fred)
324
390
  conditions_from_constraints, # from any constraints (embedded scaffolds)
325
- active_scaffold_session_storage['conditions'] # embedding conditions (weaker constraints)
391
+ active_scaffold_embedded_conditions # embedding conditions (weaker constraints)
326
392
  ].reject(&:blank?)
327
393
  end
328
394
 
329
395
  def id_condition
330
- {active_scaffold_config.model.primary_key => params[:id]} if params[:id]
396
+ {active_scaffold_config.primary_key => params[:id]} if params[:id]
331
397
  end
332
398
 
333
399
  # returns a single record (the given id) but only if it's allowed for the specified security options.
@@ -339,26 +405,30 @@ module ActiveScaffold
339
405
  raise ActiveScaffold::RecordNotAllowed, "#{klass} with id = #{id}" unless record.authorized_for? security_options
340
406
  record
341
407
  end
408
+
342
409
  # valid options may include:
343
410
  # * :sorting - a Sorting DataStructure (basically an array of hashes of field => direction, e.g. [{:field1 => 'asc'}, {:field2 => 'desc'}]). please note that multi-column sorting has some limitations: if any column in a multi-field sort uses method-based sorting, it will be ignored. method sorting only works for single-column sorting.
344
411
  # * :per_page
345
412
  # * :page
346
413
  def finder_options(options = {})
347
414
  search_conditions = all_conditions
348
- full_includes = (active_scaffold_references.blank? ? nil : active_scaffold_references)
349
415
 
350
416
  # create a general-use options array that's compatible with Rails finders
351
417
  finder_options = {
352
- :reorder => options[:sorting].try(:clause),
353
- :conditions => search_conditions,
354
- :joins => joins_for_finder,
355
- :outer_joins => active_scaffold_outer_joins,
356
- :preload => active_scaffold_preload,
357
- :includes => full_includes,
358
- :select => options[:select]
418
+ :reorder => options[:sorting].try(:clause, (grouped_columns_calculations if grouped_search?)),
419
+ :conditions => search_conditions
359
420
  }
360
- if Rails::VERSION::MAJOR >= 4
361
- finder_options.merge!(:references => active_scaffold_references)
421
+ if active_scaffold_config.mongoid?
422
+ finder_options[:includes] = [active_scaffold_references, active_scaffold_preload].compact.flatten.uniq.presence
423
+ else
424
+ finder_options.merge!(
425
+ :joins => joins_for_finder,
426
+ :left_joins => active_scaffold_outer_joins,
427
+ :preload => active_scaffold_preload,
428
+ :includes => active_scaffold_references.presence,
429
+ :references => active_scaffold_references.presence,
430
+ :select => options[:select]
431
+ )
362
432
  end
363
433
 
364
434
  finder_options.merge! custom_finder_options
@@ -366,8 +436,8 @@ module ActiveScaffold
366
436
  end
367
437
 
368
438
  def count_items(query, find_options = {}, count_includes = nil)
369
- count_includes ||= find_options[:includes] unless find_options[:conditions].blank?
370
- options = find_options.reject { |k, _| [:select, :reorder].include? k }
439
+ count_includes ||= find_options[:includes] if find_options[:conditions].present?
440
+ options = find_options.reject { |k, _| %i[select reorder order].include? k }
371
441
  # NOTE: we must use includes in the count query, because some conditions may reference other tables
372
442
  options[:includes] = count_includes
373
443
 
@@ -396,20 +466,20 @@ module ActiveScaffold
396
466
 
397
467
  query = append_to_query(query, find_options)
398
468
  # we build the paginator differently for method- and sql-based sorting
399
- if options[:sorting] && options[:sorting].sorts_by_method?
400
- pager = ::Paginator.new(count, options[:per_page]) do |offset, per_page|
401
- calculate_last_modified(query)
402
- sorted_collection = sort_collection_by_column(query.to_a, *options[:sorting].first)
403
- sorted_collection = sorted_collection.slice(offset, per_page) if options[:pagination]
404
- sorted_collection
405
- end
406
- else
407
- pager = ::Paginator.new(count, options[:per_page]) do |offset, per_page|
408
- query = append_to_query(query, :offset => offset, :limit => per_page) if options[:pagination]
409
- calculate_last_modified(query)
410
- query
411
- end
412
- end
469
+ pager = if options[:sorting] && options[:sorting].sorts_by_method?
470
+ ::Paginator.new(count, options[:per_page]) do |offset, per_page|
471
+ calculate_last_modified(query)
472
+ sorted_collection = sort_collection_by_column(query.to_a, *options[:sorting].first)
473
+ sorted_collection = sorted_collection.slice(offset, per_page) if options[:pagination]
474
+ sorted_collection
475
+ end
476
+ else
477
+ ::Paginator.new(count, options[:per_page]) do |offset, per_page|
478
+ query = append_to_query(query, :offset => offset, :limit => per_page) if options[:pagination]
479
+ calculate_last_modified(query)
480
+ query
481
+ end
482
+ end
413
483
  pager.page(options[:page])
414
484
  end
415
485
 
@@ -421,38 +491,34 @@ module ActiveScaffold
421
491
  def calculate_query
422
492
  conditions = all_conditions
423
493
  includes = active_scaffold_config.list.count_includes
424
- includes ||= active_scaffold_references unless conditions.blank?
425
- outer_joins = active_scaffold_outer_joins
426
- outer_joins += includes if includes
427
- primary_key = active_scaffold_config.model.primary_key
428
- subquery = append_to_query(beginning_of_chain, :conditions => conditions, :joins => joins_for_finder, :outer_joins => outer_joins, :select => active_scaffold_config.columns[primary_key].field)
429
- subquery = subquery.unscope(:order) if Rails::VERSION::MAJOR >= 4
494
+ includes ||= active_scaffold_references if conditions.present?
495
+ left_joins = active_scaffold_outer_joins
496
+ left_joins += includes if includes
497
+ primary_key = active_scaffold_config.primary_key
498
+ 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)
499
+ subquery = subquery.unscope(:order)
430
500
  active_scaffold_config.model.where(primary_key => subquery)
431
501
  end
432
502
 
433
503
  def append_to_query(relation, options)
434
- options.assert_valid_keys :where, :select, :having, :group, :reorder, :limit, :offset, :joins, :outer_joins, :includes, :lock, :readonly, :from, :conditions, :preload, (:references if Rails::VERSION::MAJOR >= 4)
504
+ options.assert_valid_keys :where, :select, :having, :group, :reorder, :order, :limit, :offset, :joins, :left_joins, :left_outer_joins, :includes, :lock, :readonly, :from, :conditions, :preload, :references
435
505
  relation = options.reject { |_, v| v.blank? }.inject(relation) do |rel, (k, v)|
436
506
  k == :conditions ? apply_conditions(rel, *v) : rel.send(k, v)
437
507
  end
438
- if options[:outer_joins].present?
439
- if Rails::VERSION::MAJOR >= 4
440
- relation.distinct_value = true
441
- else
442
- relation = relation.uniq
443
- end
508
+ if options[:left_outer_joins].present? || options[:left_joins].present?
509
+ relation.distinct_value = true
444
510
  end
445
511
  relation
446
512
  end
447
513
 
448
514
  def joins_for_finder
449
515
  case joins_for_collection
450
- when String
451
- [joins_for_collection]
452
- when Array
453
- joins_for_collection
454
- else
455
- []
516
+ when String
517
+ [joins_for_collection]
518
+ when Array
519
+ joins_for_collection
520
+ else
521
+ []
456
522
  end + active_scaffold_habtm_joins
457
523
  end
458
524
 
@@ -474,7 +540,7 @@ module ActiveScaffold
474
540
  value = '' if value.nil?
475
541
  value
476
542
  end
477
- collection.reverse! if order.downcase == 'desc'
543
+ collection.reverse! if order.casecmp('DESC').zero?
478
544
  collection
479
545
  end
480
546
  end