active_scaffold 3.4.43 → 3.5.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -24,7 +24,7 @@ module ActiveScaffold::DataStructures
24
24
  @_inheritable = []
25
25
  @set = []
26
26
 
27
- add *args
27
+ add(*args)
28
28
  end
29
29
 
30
30
  # the way to add columns to the set. this is primarily useful for virtual columns.
@@ -38,7 +38,25 @@ module ActiveScaffold::DataStructures
38
38
  # then add columns to @set (unless they already exist)
39
39
  args.each { |a| @set << ActiveScaffold::DataStructures::Column.new(a.to_sym, @active_record_class) unless find_by_name(a) }
40
40
  end
41
- alias_method :<<, :add
41
+ alias << add
42
+
43
+ # add columns from association (belongs_to or has_one)
44
+ # these columns will use label translation from association model
45
+ # they will be excluded, so won't be included in action columns
46
+ # association columns will work for read actions only, not in form actions (create, update, subform)
47
+ def add_association_columns(association, *columns)
48
+ column = self[association]
49
+ raise ArgumentError, "unknown column #{association}" if column.nil?
50
+ raise ArgumentError, "column #{association} is not an association" if column.association.nil?
51
+ raise ArugmentError, "column #{association} is not singular association" unless column.association.singular?
52
+ raise ArugmentError, "column #{association} is polymorphic association" if column.association.polymorphic?
53
+
54
+ klass = column.association.klass
55
+ columns.each do |col|
56
+ next if find_by_name col
57
+ @set << ActiveScaffold::DataStructures::Column.new(col, klass, column.association)
58
+ end
59
+ end
42
60
 
43
61
  def exclude(*args)
44
62
  # only remove columns from _inheritable. we never want to completely forget about a column.
@@ -56,7 +74,7 @@ module ActiveScaffold::DataStructures
56
74
  column = @set.find { |c| c == name }
57
75
  column
58
76
  end
59
- alias_method :[], :find_by_name
77
+ alias [] find_by_name
60
78
 
61
79
  def each
62
80
  @set.each { |i| yield i }
@@ -63,7 +63,7 @@ module ActiveScaffold::DataStructures
63
63
  false
64
64
  end
65
65
 
66
- def sorted?
66
+ def sorted?(*)
67
67
  false
68
68
  end
69
69
  end
@@ -71,27 +71,15 @@ module ActiveScaffold::DataStructures
71
71
  class NestedInfoAssociation < NestedInfo
72
72
  def initialize(model, params)
73
73
  super
74
- @association = parent_model.reflect_on_association(params[:association].to_sym)
75
- @param_name = @association.active_record.name.foreign_key.to_sym
74
+ column = parent_scaffold.active_scaffold_config.columns[params[:association].to_sym]
75
+ @param_name = column.model.name.foreign_key.to_sym
76
76
  @parent_id = params[@param_name]
77
- iterate_model_associations(model)
77
+ @association = column.try(:association)
78
+ @child_association = association.reverse_association(model) if association
79
+ setup_constrained_fields
78
80
  end
79
81
 
80
- delegate :name, :to => :association
81
-
82
- def has_many?
83
- association.macro == :has_many
84
- end
85
-
86
- def habtm?
87
- association.macro == :has_and_belongs_to_many
88
- end
89
-
90
- delegate :belongs_to?, :to => :association
91
-
92
- def has_one?
93
- association.macro == :has_one
94
- end
82
+ delegate :name, :belongs_to?, :has_one?, :has_many?, :habtm?, :readonly?, :to => :association
95
83
 
96
84
  # A through association with has_one or has_many as source association
97
85
  # create cannot be called in nested through associations, and not-nested through associations
@@ -101,28 +89,25 @@ module ActiveScaffold::DataStructures
101
89
  def readonly_through_association?(columns)
102
90
  return false unless through_association?
103
91
  return true if association.through_reflection.options[:through]
104
- association.source_reflection.macro != :belongs_to && (
92
+ !association.source_reflection.belongs_to? && (
105
93
  !child_association || !columns.include?(child_association.through_reflection.name)
106
94
  )
107
95
  end
108
96
 
109
97
  def through_association?
110
- association.options[:through]
98
+ association.through?
111
99
  end
112
100
 
113
- def readonly?
114
- association.options[:readonly]
101
+ def sorted?(chain)
102
+ default_sorting(chain).present?
115
103
  end
116
104
 
117
- def sorted?
118
- association.options.key? :order
119
- end
120
-
121
- def default_sorting
122
- if association.options[:order] # TODO: remove when rails 3 compatibility is removed
123
- association.options[:order]
124
- elsif association.respond_to?(:scope) # rails 4
125
- association.klass.class_eval(&association.scope).values[:order] if association.scope.is_a? Proc
105
+ def default_sorting(chain)
106
+ return @default_sorting if defined? @default_sorting
107
+ if association.scope.is_a?(Proc) && chain.respond_to?(:values) && chain.values[:order]
108
+ @default_sorting = chain.values[:order]
109
+ @default_sorting = @default_sorting.map(&:to_sql) if @default_sorting[0].is_a? Arel::Nodes::Node
110
+ @default_sorting = @default_sorting.join(', ')
126
111
  end
127
112
  end
128
113
 
@@ -132,12 +117,12 @@ module ActiveScaffold::DataStructures
132
117
 
133
118
  protected
134
119
 
135
- def iterate_model_associations(model)
120
+ def setup_constrained_fields
136
121
  @constrained_fields = []
137
- constrained_fields << Array(association.foreign_key).map(&:to_sym) unless association.belongs_to?
138
- return if (reverse = association.reverse(model)).nil?
139
- @child_association = model.reflect_on_association(reverse)
140
- constrained_fields << @child_association.name unless @child_association == association
122
+ @constrained_fields << Array(association.foreign_key).map(&:to_sym) unless association.belongs_to?
123
+ if child_association && child_association != association
124
+ @constrained_fields << child_association.name
125
+ end
141
126
  end
142
127
  end
143
128
 
@@ -9,7 +9,7 @@ module ActiveScaffold::DataStructures
9
9
 
10
10
  def set_values(*args)
11
11
  @set = []
12
- add *args
12
+ add(*args)
13
13
  end
14
14
 
15
15
  # the way to add items to the set.
@@ -20,7 +20,7 @@ module ActiveScaffold::DataStructures
20
20
  @set << arg unless @set.include? arg # avoid duplicates
21
21
  end
22
22
  end
23
- alias_method :<<, :add
23
+ alias << add
24
24
 
25
25
  # the way to remove items from the set.
26
26
  def exclude(*args)
@@ -29,7 +29,7 @@ module ActiveScaffold::DataStructures
29
29
  # check respond_to? :to_sym, ActionColumns doesn't respond to to_sym
30
30
  @set.reject! { |c| c.respond_to?(:to_sym) && args.include?(c.to_sym) } # reject all items specified
31
31
  end
32
- alias_method :remove, :exclude
32
+ alias remove exclude
33
33
 
34
34
  # returns an array of items with the provided names
35
35
  def find_by_names(*names)
@@ -42,7 +42,7 @@ module ActiveScaffold::DataStructures
42
42
  item = @set.find { |c| c == name }
43
43
  item
44
44
  end
45
- alias_method :[], :find_by_name
45
+ alias [] find_by_name
46
46
 
47
47
  def each
48
48
  @set.each { |i| yield i }
@@ -2,19 +2,24 @@ module ActiveScaffold::DataStructures
2
2
  # encapsulates the column sorting configuration for the List view
3
3
  class Sorting
4
4
  include Enumerable
5
+ include ActiveScaffold::OrmChecks
5
6
 
6
7
  attr_accessor :constraint_columns
7
8
  attr_accessor :sorting_by_primary_key # enabled by default for postgres
9
+ attr_reader :model
10
+ alias active_record_class model
8
11
 
9
- def initialize(columns)
12
+ def initialize(columns, model)
10
13
  @columns = columns
11
14
  @clauses = []
12
15
  @constraint_columns = []
16
+ @model = model
13
17
  end
14
18
 
15
- def set_default_sorting(model)
19
+ def set_default_sorting
20
+ return unless active_record?
16
21
  # fallback to setting primary key ordering
17
- setup_primary_key_order_clause(model)
22
+ setup_primary_key_order_clause
18
23
  model_scope = model.send(:build_default_scope)
19
24
  order_clause = model_scope.order_values.join(',') if model_scope
20
25
  return unless order_clause
@@ -34,7 +39,7 @@ module ActiveScaffold::DataStructures
34
39
  direction = direction.to_s.upcase
35
40
  column = get_column(column_name)
36
41
  raise ArgumentError, "Could not find column #{column_name}" if column.nil?
37
- raise ArgumentError, 'Sorting direction unknown' unless [:ASC, :DESC].include? direction.to_sym
42
+ raise ArgumentError, 'Sorting direction unknown' unless %i[ASC DESC].include? direction.to_sym
38
43
  @clauses << [column, direction.untaint] if column.sortable?
39
44
  raise ArgumentError, "Can't mix :method- and :sql-based sorting" if mixed_sorting?
40
45
  end
@@ -77,8 +82,8 @@ module ActiveScaffold::DataStructures
77
82
  clause[1]
78
83
  end
79
84
 
80
- SORTING_STAGES = Hash[%w(reset ASC DESC reset).each_cons(2).to_a].freeze
81
- DEFAULT_SORTING_STAGES = Hash[%w(ASC DESC ASC).each_cons(2).to_a].freeze
85
+ SORTING_STAGES = Hash[%w[reset ASC DESC reset].each_cons(2).to_a].freeze
86
+ DEFAULT_SORTING_STAGES = Hash[%w[ASC DESC ASC].each_cons(2).to_a].freeze
82
87
  def next_sorting_of(column, sorted_by_default)
83
88
  stages = sorted_by_default ? DEFAULT_SORTING_STAGES : SORTING_STAGES
84
89
  stages[direction_of(column)] || 'ASC'
@@ -103,21 +108,30 @@ module ActiveScaffold::DataStructures
103
108
  @clauses.first
104
109
  end
105
110
 
111
+ def size
112
+ @clauses.size
113
+ end
114
+
106
115
  # builds an order-by clause
107
- def clause
116
+ def clause(grouped_columns_calculations = nil)
108
117
  return nil if sorts_by_method? || default_sorting?
109
118
 
110
119
  # unless the sorting is by method, create the sql string
111
120
  order = []
112
121
  each do |sort_column, sort_direction|
113
122
  next if constraint_columns.include? sort_column.name
114
- sql = sort_column.sort[:sql]
115
- next if sql.nil? || sql.empty?
123
+ sql = grouped_columns_calculations.try(:dig, sort_column.name) || sort_column.sort[:sql]
124
+ next if sql.blank?
125
+ sql = sql.to_sql if sql.respond_to?(:to_sql)
116
126
 
117
- order << Array(sql).map { |column| "#{column} #{sort_direction}" }.join(', ')
127
+ parts = Array(sql).map do |column|
128
+ mongoid? ? [column, sort_direction] : "#{column} #{sort_direction}"
129
+ end
130
+ order << parts
118
131
  end
119
132
 
120
133
  order << @primary_key_order_clause if @sorting_by_primary_key
134
+ order.flatten!(1)
121
135
  order unless order.empty?
122
136
  end
123
137
 
@@ -149,7 +163,7 @@ module ActiveScaffold::DataStructures
149
163
  def set_sorting_from_order_clause(order_clause, model_table_name = nil)
150
164
  clear
151
165
  order_clause.to_s.split(',').each do |criterion|
152
- unless criterion.blank?
166
+ if criterion.present?
153
167
  order_parts = extract_order_parts(criterion)
154
168
  add(order_parts[:column_name], order_parts[:direction]) unless different_table?(model_table_name, order_parts[:table_name]) || get_column(order_parts[:column_name]).nil?
155
169
  end
@@ -178,22 +192,22 @@ module ActiveScaffold::DataStructures
178
192
  end
179
193
 
180
194
  def extract_direction(direction_part)
181
- if direction_part.to_s.upcase == 'DESC'
195
+ if direction_part.to_s.casecmp('DESC').zero?
182
196
  'DESC'
183
197
  else
184
198
  'ASC'
185
199
  end
186
200
  end
187
201
 
188
- def postgres?(model)
202
+ def postgres?
189
203
  model.connection.try(:adapter_name) == 'PostgreSQL'
190
204
  end
191
205
 
192
- def setup_primary_key_order_clause(model)
206
+ def setup_primary_key_order_clause
193
207
  return unless model.column_names.include?(model.primary_key)
194
208
  set([model.primary_key, 'ASC'])
195
209
  @primary_key_order_clause = clause
196
- @sorting_by_primary_key = postgres?(model) # mandatory for postgres, so enabled by default
210
+ @sorting_by_primary_key = postgres? # mandatory for postgres, so enabled by default
197
211
  end
198
212
  end
199
213
  end
@@ -34,6 +34,8 @@ module ActiveScaffold
34
34
  end
35
35
  end
36
36
 
37
- config.assets.precompile << 'active_scaffold/indicator.gif' if Rails::VERSION::MAJOR >= 4
37
+ initializer 'active_scaffold.assets' do
38
+ config.assets.precompile << 'active_scaffold/indicator.gif'
39
+ end
38
40
  end
39
41
  end
@@ -1,7 +1,7 @@
1
1
  # wrap the action rendering for ActiveScaffold controllers
2
- module ActionController #:nodoc:
3
- class Base
4
- def render_with_active_scaffold(*args, &block)
2
+ module ActiveScaffold
3
+ module ActionController #:nodoc:
4
+ def render(*args, &block)
5
5
  if self.class.uses_active_scaffold? && params[:adapter] && @rendering_adapter.nil? && request.xhr?
6
6
  @rendering_adapter = true # recursion control
7
7
  # if we need an adapter, then we render the actual stuff to a string and insert it into the adapter template
@@ -11,9 +11,14 @@ module ActionController #:nodoc:
11
11
  :use_full_path => true, :layout => false, :content_type => :html
12
12
  @rendering_adapter = nil # recursion control
13
13
  else
14
- render_without_active_scaffold(*args, &block)
14
+ super(*args, &block)
15
15
  end
16
16
  end
17
- alias_method_chain :render, :active_scaffold
17
+ end
18
+ end
19
+
20
+ module ActionController
21
+ class Base
22
+ prepend ActiveScaffold::ActionController
18
23
  end
19
24
  end
@@ -1,16 +1,15 @@
1
- module ActionView
2
- class LookupContext
1
+ module ActiveScaffold
2
+ module LookupContext
3
3
  attr_accessor :last_template
4
4
 
5
- def find_template_with_last_template(name, prefixes = [], partial = false, keys = [], options = {})
6
- self.last_template = find_template_without_last_template(name, prefixes, partial, keys, options)
5
+ def find_template(name, prefixes = [], partial = false, keys = [], options = {})
6
+ self.last_template = super(name, prefixes, partial, keys, options)
7
7
  end
8
- alias_method_chain :find_template, :last_template
9
8
  end
10
9
  end
11
10
 
12
11
  # wrap the action rendering for ActiveScaffold views
13
- module ActionView::Helpers #:nodoc:
12
+ module ActiveScaffold #:nodoc:
14
13
  module RenderingHelper
15
14
  #
16
15
  # Adds two rendering options.
@@ -34,52 +33,11 @@ module ActionView::Helpers #:nodoc:
34
33
  #
35
34
  # Defining options[:label] lets you completely customize the list title for the embedded scaffold.
36
35
  #
37
- def render_with_active_scaffold(*args, &block)
36
+ # options[:xhr] force to load embedded scaffold with AJAX even when render_component gem is installed.
37
+ #
38
+ def render(*args, &block)
38
39
  if args.first.is_a?(Hash) && args.first[:active_scaffold]
39
- require 'digest/md5'
40
- options = args.first
41
-
42
- remote_controller = options[:active_scaffold]
43
- constraints = options[:constraints]
44
- conditions = options[:conditions]
45
- eid = Digest::MD5.hexdigest(params[:controller] + remote_controller.to_s + constraints.to_s + conditions.to_s)
46
- eid_info = session["as:#{eid}"] ||= {}
47
- if constraints
48
- eid_info['constraints'] = constraints
49
- else
50
- eid_info.delete 'constraints'
51
- end
52
- if conditions
53
- eid_info['conditions'] = conditions
54
- else
55
- eid_info.delete 'conditions'
56
- end
57
- if options[:label]
58
- eid_info['list'] = {'label' => options[:label]}
59
- else
60
- eid_info.delete 'list'
61
- end
62
- session.delete "as:#{eid}" if eid_info.empty?
63
- options[:params] ||= {}
64
- options[:params].merge! :eid => eid, :embedded => true
65
-
66
- id = "as_#{eid}-embedded"
67
- url_options = {:controller => remote_controller.to_s, :action => 'index'}.merge(options[:params])
68
-
69
- if controller.respond_to?(:render_component_into_view, true)
70
- controller.send(:render_component_into_view, url_options)
71
- else
72
- url = url_for(url_options)
73
- content_tag(:div, :id => id, :class => 'active-scaffold-component', :data => {:refresh => url}) do
74
- # parse the ActiveRecord model name from the controller path, which
75
- # might be a namespaced controller (e.g., 'admin/admins')
76
- model = remote_controller.to_s.sub(/.*\//, '').singularize
77
- content_tag(:div, :class => 'active-scaffold-header') do
78
- content_tag :h2, link_to(args.first[:label] || active_scaffold_config_for(model).list.label, url, :remote => true, :class => 'load-embedded')
79
- end
80
- end
81
- end
82
-
40
+ render_embedded args.first
83
41
  elsif args.first == :super
84
42
  @_view_paths ||= lookup_context.view_paths.clone
85
43
  @_last_template ||= lookup_context.last_template
@@ -102,30 +60,78 @@ module ActionView::Helpers #:nodoc:
102
60
  last_view_path = File.expand_path(File.dirname(File.dirname(lookup_context.last_template.inspect)), Rails.root)
103
61
  lookup_context.view_paths = view_paths.drop(view_paths.find_index { |path| path.to_s == last_view_path } + 1)
104
62
  end
105
- result = render_without_active_scaffold options
63
+ result = super options
106
64
  lookup_context.view_paths = @_view_paths if @_view_paths
107
65
  lookup_context.last_template = @_last_template if @_last_template
108
66
  result
109
67
  else
110
68
  @_view_paths ||= lookup_context.view_paths.clone
111
69
  last_template = lookup_context.last_template
112
- if args[0].is_a?(Hash)
113
- current_view = {:locals => args[0][:locals], :object => args[0][:object]}
114
- else # call is render 'partial', locals_hash
115
- current_view = {:locals => args[1]}
116
- end
70
+ current_view = if args[0].is_a?(Hash)
71
+ {:locals => args[0][:locals], :object => args[0][:object]}
72
+ else # call is render 'partial', locals_hash
73
+ {:locals => args[1]}
74
+ end
117
75
  view_stack << current_view if current_view
118
76
  lookup_context.view_paths = @_view_paths # reset view_paths in case a view render :super, and then render :partial
119
- result = render_without_active_scaffold(*args, &block)
77
+ result = super
120
78
  view_stack.pop if current_view.present?
121
79
  lookup_context.last_template = last_template
122
80
  result
123
81
  end
124
82
  end
125
- alias_method_chain :render, :active_scaffold
126
83
 
127
84
  def view_stack
128
85
  @_view_stack ||= []
129
86
  end
87
+
88
+ private
89
+
90
+ def render_embedded(options)
91
+ require 'digest/md5'
92
+
93
+ remote_controller = options[:active_scaffold]
94
+ # It is important that the EID hash remains short as to not contribute
95
+ # to a large session size and thus a possible cookie overflow exception
96
+ # when using rails CookieStore or EncryptedCookieStore. For example,
97
+ # when rendering many embedded scaffolds with constraints or conditions
98
+ # on a single page.
99
+ eid = Digest::MD5.hexdigest(params[:controller] + options.to_s)
100
+ eid_info = {loading: true}
101
+ eid_info[:constraints] = options[:constraints] if options[:constraints]
102
+ eid_info[:conditions] = options[:conditions] if options[:conditions]
103
+ eid_info[:label] = options[:label] if options[:label]
104
+ options[:params] ||= {}
105
+ options[:params].merge! :eid => eid, :embedded => eid_info
106
+
107
+ id = "as_#{eid}-embedded"
108
+ url_options = {controller: remote_controller.to_s, action: 'index', id: nil}.merge(options[:params])
109
+
110
+ if controller.respond_to?(:render_component_into_view, true) && !options[:xhr]
111
+ controller.send(:render_component_into_view, url_options)
112
+ else
113
+ url = url_for(url_options)
114
+ content_tag(:div, :id => id, :class => 'active-scaffold-component', :data => {:refresh => url}) do
115
+ # parse the ActiveRecord model name from the controller path, which
116
+ # might be a namespaced controller (e.g., 'admin/admins')
117
+ model = remote_controller.to_s.sub(/.*\//, '').singularize
118
+ content_tag(:div, :class => 'active-scaffold-header') do
119
+ content_tag :h2, link_to(options[:label] || active_scaffold_config_for(model).list.label, url, :remote => true, :class => 'load-embedded')
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
126
+
127
+ module ActionView
128
+ LookupContext.class_eval do
129
+ prepend ActiveScaffold::LookupContext
130
+ end
131
+
132
+ module Helpers
133
+ Base.class_eval do
134
+ include ActiveScaffold::RenderingHelper
135
+ end
130
136
  end
131
137
  end