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
@@ -22,8 +22,8 @@ module ActiveScaffold::Actions
22
22
  columns = active_scaffold_config.search.columns
23
23
  text_search = active_scaffold_config.search.text_search
24
24
  query = query.split(active_scaffold_config.search.split_terms) if active_scaffold_config.search.split_terms
25
- search_conditions = self.class.create_conditions_for_columns(query, columns, text_search)
26
- @filtered = !search_conditions.blank?
25
+ search_conditions = self.class.conditions_for_columns(query, columns, text_search)
26
+ @filtered = search_conditions.present?
27
27
  active_scaffold_conditions.concat search_conditions if @filtered
28
28
 
29
29
  references, outer_joins = columns.partition do |column|
@@ -1,7 +1,7 @@
1
1
  module ActiveScaffold::Actions
2
2
  module Show
3
3
  def self.included(base)
4
- base.before_filter :show_authorized_filter, :only => :show
4
+ base.before_action :show_authorized_filter, :only => :show
5
5
  end
6
6
 
7
7
  def show
@@ -42,14 +42,13 @@ module ActiveScaffold::Actions
42
42
  # May be overridden to customize show routine
43
43
  def do_show
44
44
  set_includes_for_columns(:show) if active_scaffold_config.actions.include? :list
45
- klass = beginning_of_chain.preload(active_scaffold_preload)
46
- @record = find_if_allowed(params[:id], :read, klass)
45
+ get_row
47
46
  end
48
47
 
49
48
  # The default security delegates to ActiveRecordPermissions.
50
49
  # You may override the method to customize.
51
50
  def show_authorized?(record = nil)
52
- (record || self).send(:authorized_for?, :crud_type => :read)
51
+ (record || self).authorized_for?(crud_type: :read, reason: true)
53
52
  end
54
53
 
55
54
  def show_ignore?(record = nil)
@@ -60,7 +59,7 @@ module ActiveScaffold::Actions
60
59
 
61
60
  def show_authorized_filter
62
61
  link = active_scaffold_config.show.link || active_scaffold_config.show.class.link
63
- raise ActiveScaffold::ActionNotAllowed unless send(link.security_method)
62
+ raise ActiveScaffold::ActionNotAllowed unless Array(send(link.security_method))[0]
64
63
  end
65
64
 
66
65
  def show_formats
@@ -2,7 +2,7 @@ module ActiveScaffold::Actions
2
2
  module Subform
3
3
  def edit_associated
4
4
  do_edit_associated
5
- render :action => 'edit_associated', :formats => [:js], :readonly => @column.association.options[:readonly]
5
+ render :action => 'edit_associated', :formats => [:js], :readonly => @column.association.readonly?
6
6
  end
7
7
 
8
8
  protected
@@ -18,13 +18,15 @@ module ActiveScaffold::Actions
18
18
  @column = active_scaffold_config.columns[params[:child_association]]
19
19
 
20
20
  # NOTE: we don't check whether the user is allowed to update this record, because if not, we'll still let them associate the record. we'll just refuse to do more than associate, is all.
21
- @record = @column.association.klass.find(params[:associated_id]) if params[:associated_id]
22
- if @record
23
- if (association = active_scaffold_config.columns[params[:child_association]].association.reverse)
24
- if @record.class.reflect_on_association(association).collection?
25
- @record.send(association) << @parent_record
21
+ if params[:associated_id]
22
+ @record = @column.association.klass.find(params[:associated_id])
23
+ if (reverse = @column.association.reverse_association)
24
+ if reverse.collection?
25
+ @record.send(reverse.name) << @parent_record
26
+ elsif @column.association.belongs_to?
27
+ @parent_record.send("#{@column.name}=", @record)
26
28
  else
27
- @record.send("#{association}=", @parent_record)
29
+ @record.send("#{reverse.name}=", @parent_record)
28
30
  end
29
31
  end
30
32
  else
@@ -1,7 +1,7 @@
1
1
  module ActiveScaffold::Actions
2
2
  module Update
3
3
  def self.included(base)
4
- base.before_filter :update_authorized_filter, :only => [:edit, :update]
4
+ base.before_action :update_authorized_filter, :only => %i[edit update]
5
5
  base.helper_method :update_refresh_list?
6
6
  end
7
7
 
@@ -135,28 +135,35 @@ module ActiveScaffold::Actions
135
135
  def do_update_column
136
136
  # delete from params so update :table won't break urls, also they shouldn't be used in sort links too
137
137
  value = params.delete(:value)
138
- column = params.delete(:column).to_sym
138
+ column = params.delete(:column)
139
139
  params.delete(:original_html)
140
140
  params.delete(:original_value)
141
141
  @column = active_scaffold_config.columns[column]
142
- @record = find_if_allowed(params[:id], :read)
142
+ @record = value_record = find_if_allowed(params[:id], :read)
143
143
  return unless @record.authorized_for?(:crud_type => :update, :column => column)
144
+ if @column.delegated_association
145
+ value_record = @record.send(@column.delegated_association.name)
146
+ value_record ||= @record.association(@column.delegated_association.name).build
147
+ return unless value_record.authorized_for?(:crud_type => :update, :column => column)
148
+ end
144
149
 
145
150
  value ||=
146
151
  unless @column.column.nil? || @column.column.null
147
152
  default_val = @column.column.default
148
- default_val = @column.column.cast_type.type_cast_from_user default_val if Rails.version >= '4.2.0'
153
+ default_val = ActiveScaffold::Core.column_type_cast default_val, @column.column if Rails.version >= '4.2.0'
149
154
  default_val == true ? false : default_val
150
155
  end
151
156
  unless @column.nil?
152
- value = column_value_from_param_value(@record, @column, value)
153
- value = [] if value.nil? && @column.form_ui && @column.plural_association?
157
+ value = column_value_from_param_value(value_record, @column, value)
158
+ value = [] if value.nil? && @column.form_ui && @column.association.try(:collection?)
154
159
  end
155
160
 
156
- @record.send("#{@column.name}=", value)
161
+ value_record.send("#{@column.name}=", value)
157
162
  before_update_save(@record)
158
- self.successful = @record.save
159
- if self.successful? && active_scaffold_config.actions.include?(:list)
163
+ self.successful = value_record.save
164
+ if !successful?
165
+ flash.now[:error] = value_record.errors.full_messages.presence
166
+ elsif active_scaffold_config.actions.include?(:list)
160
167
  if @column.inplace_edit_update == :table
161
168
  params.delete(:id)
162
169
  do_list
@@ -180,19 +187,19 @@ module ActiveScaffold::Actions
180
187
 
181
188
  # The default security delegates to ActiveRecordPermissions.
182
189
  # You may override the method to customize.
183
- def update_authorized?(record = nil)
184
- (!nested? || !nested.readonly?) && (record || self).authorized_for?(:crud_type => :update)
190
+ def update_authorized?(record = nil, column = nil)
191
+ (!nested? || !nested.readonly?) && (record || self).authorized_for?(crud_type: :update, column: column, reason: true)
185
192
  end
186
193
 
187
194
  def update_ignore?(record = nil)
188
- !self.authorized_for?(:crud_type => :update)
195
+ !authorized_for?(:crud_type => :update)
189
196
  end
190
197
 
191
198
  private
192
199
 
193
200
  def update_authorized_filter
194
201
  link = active_scaffold_config.update.link || active_scaffold_config.update.class.link
195
- raise ActiveScaffold::ActionNotAllowed unless send(link.security_method)
202
+ raise ActiveScaffold::ActionNotAllowed unless Array(send(link.security_method))[0]
196
203
  end
197
204
 
198
205
  def edit_formats
@@ -22,11 +22,15 @@ module ActiveScaffold
22
22
  mattr_accessor :default_permission
23
23
  @@default_permission = true
24
24
 
25
+ # if enabled, string returned on authorized methods will be interpreted as not authorized and used as reason
26
+ mattr_accessor :not_authorized_reason
27
+ @@not_authorized_reason = false
28
+
25
29
  # This is a module aimed at making the current_user available to ActiveRecord models for permissions.
26
30
  module ModelUserAccess
27
31
  module Controller
28
32
  def self.included(base)
29
- base.prepend_before_filter :assign_current_user_to_models
33
+ base.prepend_before_action :assign_current_user_to_models
30
34
  end
31
35
 
32
36
  # We need to give the ActiveRecord classes a handle to the current user. We don't want to just pass the object,
@@ -91,22 +95,37 @@ module ActiveScaffold
91
95
  # options[:crud_type] should be a CRUD verb (:create, :read, :update, :destroy)
92
96
  # options[:column] should be the name of a model attribute
93
97
  # options[:action] is the name of a method
98
+ # options[:reason] if returning reason is expected, it will return array with authorized and reason, or nil if no reason
94
99
  def authorized_for?(options = {})
95
- raise ArgumentError, "unknown crud type #{options[:crud_type]}" if options[:crud_type] && ![:create, :read, :update, :delete].include?(options[:crud_type])
100
+ raise ArgumentError, "unknown crud type #{options[:crud_type]}" if options[:crud_type] && !%i[create read update delete].include?(options[:crud_type])
96
101
 
102
+ not_authorized_reason = ActiveRecordPermissions.not_authorized_reason
97
103
  # collect other possibly-related methods that actually exist
98
104
  methods = cached_authorized_for_methods(options)
99
105
  return ActiveRecordPermissions.default_permission if methods.empty?
100
- return send(methods.first) if methods.one?
106
+ if methods.one?
107
+ result = send(methods.first)
108
+ # if not_authorized_reason enabled interpret String as reason for not authorized
109
+ authorized, reason = not_authorized_reason && result.is_a?(String) ? [false, result] : result
110
+ # return array with reason only if requested with options[:reason]
111
+ return options[:reason] ? [authorized, reason] : authorized
112
+ end
101
113
 
102
114
  # if any method returns false, then return false
103
- return false if methods.any? { |m| !send(m) }
115
+ methods.each do |method|
116
+ result = send(method)
117
+ # if not_authorized_reason enabled interpret String as reason for not authorized
118
+ authorized, reason = not_authorized_reason && result.is_a?(String) ? [false, result] : [result, nil]
119
+ next if authorized
120
+ # return array with reason only if requested with options[:reason]
121
+ return options[:reason] ? [authorized, reason] : authorized
122
+ end
104
123
  true
105
124
  end
106
125
 
107
126
  def cached_authorized_for_methods(options)
108
127
  key = "#{options[:crud_type]}##{options[:column]}##{options[:action]}"
109
- if self.is_a? Class
128
+ if is_a? Class
110
129
  self.class_security_methods ||= {}
111
130
  self.class_security_methods[key] ||= authorized_for_methods(options)
112
131
  else
@@ -33,26 +33,25 @@ module ActiveScaffold
33
33
  module AttributeParams
34
34
  protected
35
35
 
36
- # workaround to update counters when belongs_to changes on persisted record on Rails 3
37
36
  # workaround to update counters when polymorphic has_many changes on persisted record
38
- # TODO: remove when rails3 support is removed and counter cache for polymorphic has_many association works on rails4
37
+ # TODO: remove when rails4 support is removed or counter cache for polymorphic has_many association works on rails4
39
38
  def hack_for_has_many_counter_cache(parent_record, column, value)
40
39
  association = parent_record.association(column.name)
41
40
  counter_attr = association.send(:cached_counter_attribute_name)
42
41
  difference = value.select(&:persisted?).size - parent_record.send(counter_attr)
43
42
 
44
43
  if parent_record.new_record?
45
- if Rails.version == '4.2.0'
44
+ if Rails.version >= '4.2.0'
46
45
  parent_record.send "#{column.name}=", value
47
46
  parent_record.send "#{counter_attr}_will_change!"
48
- else # < 4.2 or > 4.2.0
47
+ else # < 4.2
49
48
  parent_record.send "#{counter_attr}=", difference
50
49
  parent_record.send "#{column.name}=", value
51
50
  end
52
51
  else
53
52
  # don't decrement counter for deleted records, on destroy they will update counter
54
53
  difference += (parent_record.send(column.name) - value).size
55
- association.send :update_counter, difference unless difference == 0
54
+ association.send :update_counter, difference unless difference.zero?
56
55
  end
57
56
 
58
57
  # update counters on old parents if belongs_to is changed
@@ -63,21 +62,18 @@ module ActiveScaffold
63
62
  parent_record.send "#{column.name}=", value if parent_record.persisted?
64
63
  end
65
64
 
65
+ # rails 4 needs this hack for polymorphic has_many
66
66
  # TODO: remove when hack_for_has_many_counter_cache is not needed
67
67
  def hack_for_has_many_counter_cache?(parent_record, column)
68
- return unless column.association.try(:macro) == :has_many && parent_record.association(column.name).send(:has_cached_counter?)
69
- if Rails.version < '4.0' # rails 3 needs this hack always
70
- true
71
- else # rails 4 needs this hack for polymorphic has_many
72
- column.association.options[:as]
73
- end
68
+ column.association.counter_cache_hack? && parent_record.association(column.name).send(:has_cached_counter?)
74
69
  end
75
70
 
76
71
  # workaround for updating counters twice bug on rails4 (https://github.com/rails/rails/pull/14849)
72
+ # rails 4 needs this hack for non-polymorphic belongs_to, when selecting record, not creating new one (value is Hash)
73
+ # rails 5 needs this hack for belongs_to, when selecting record, not creating new one (value is Hash)
77
74
  # TODO: remove when pull request is merged and no version with bug is supported
78
- def counter_cache_hack?(column, value)
79
- return unless Rails.version >= '4.0' && !value.is_a?(Hash)
80
- column.association.try(:belongs_to?) && column.association.options[:counter_cache] && !column.association.options[:polymorphic]
75
+ def counter_cache_hack?(association, value)
76
+ !params_hash?(value) && association.belongs_to? && association.counter_cache_hack?
81
77
  end
82
78
 
83
79
  # Takes attributes (as from params[:record]) and applies them to the parent_record. Also looks for
@@ -110,7 +106,7 @@ module ActiveScaffold
110
106
  value = update_column_from_params(parent_record, column, attributes[column.name], avoid_changes)
111
107
  end
112
108
  rescue => e
113
- logger.error "#{e.class.name}: #{e.message} -- on the ActiveScaffold column = :#{column.name} for #{parent_record.inspect}#{" with value #{value}" if value}"
109
+ Rails.logger.error "#{e.class.name}: #{e.message} -- on the ActiveScaffold column = :#{column.name} for #{parent_record.inspect}#{" with value #{value}" if value}"
114
110
  raise
115
111
  end
116
112
  end
@@ -120,14 +116,20 @@ module ActiveScaffold
120
116
 
121
117
  def update_column_from_params(parent_record, column, attribute, avoid_changes = false)
122
118
  value = column_value_from_param_value(parent_record, column, attribute, avoid_changes)
123
- if avoid_changes && column.plural_association?
119
+ if avoid_changes && column.association
124
120
  parent_record.association(column.name).target = value
125
- elsif counter_cache_hack?(column, attribute)
121
+ parent_record.send("#{column.association.foreign_key}=", value.try(:id)) if column.association.belongs_to?
122
+ elsif column.association && counter_cache_hack?(column.association, attribute)
126
123
  parent_record.send "#{column.association.foreign_key}=", value.try(:id)
127
124
  parent_record.association(column.name).target = value
125
+ elsif column.association.try(:collection?) && column.association.through? && !column.association.through_reflection.collection?
126
+ through = column.association.through_reflection.name
127
+ through_record = parent_record.send(through)
128
+ through_record ||= parent_record.send "build_#{through}"
129
+ through_record.send "#{column.association.source_reflection.name}=", value
128
130
  else
129
131
  begin
130
- if hack_for_has_many_counter_cache?(parent_record, column)
132
+ if column.association && hack_for_has_many_counter_cache?(parent_record, column)
131
133
  hack_for_has_many_counter_cache(parent_record, column, value)
132
134
  else
133
135
  parent_record.send "#{column.name}=", value
@@ -137,7 +139,7 @@ module ActiveScaffold
137
139
  parent_record.association(column.name).target = value if column.association
138
140
  end
139
141
  end
140
- if column.association && [:has_one, :has_many].include?(column.association.macro) && column.association.reverse
142
+ if column.association.try(:reverse_association).try(:belongs_to?)
141
143
  Array(value).each { |v| v.send("#{column.association.reverse}=", parent_record) if v.new_record? }
142
144
  end
143
145
  value
@@ -146,10 +148,10 @@ module ActiveScaffold
146
148
  def column_value_from_param_value(parent_record, column, value, avoid_changes = false)
147
149
  # convert the value, possibly by instantiating associated objects
148
150
  form_ui = column.form_ui || column.column.try(:type)
149
- if form_ui && self.respond_to?("column_value_for_#{form_ui}_type", true)
151
+ if form_ui && respond_to?("column_value_for_#{form_ui}_type", true)
150
152
  send("column_value_for_#{form_ui}_type", parent_record, column, value)
151
- elsif value.is_a?(Hash)
152
- column_value_from_param_hash_value(parent_record, column, value, avoid_changes)
153
+ elsif params_hash? value
154
+ column_value_from_param_hash_value(parent_record, column, params_hash(value), avoid_changes)
153
155
  else
154
156
  column_value_from_param_simple_value(parent_record, column, value)
155
157
  end
@@ -171,10 +173,14 @@ module ActiveScaffold
171
173
  new_value
172
174
  end
173
175
 
176
+ def column_value_for_month_type(parent_record, column, value)
177
+ Date.parse("#{value}-01")
178
+ end
179
+
174
180
  def column_value_from_param_simple_value(parent_record, column, value)
175
- if column.singular_association?
181
+ if column.association.try :singular?
176
182
  if value.present?
177
- if column.polymorphic_association?
183
+ if column.association.polymorphic?
178
184
  class_name = parent_record.send(column.association.foreign_type)
179
185
  class_name.constantize.find(value) if class_name.present?
180
186
  else
@@ -182,22 +188,23 @@ module ActiveScaffold
182
188
  column.association.klass.find(value)
183
189
  end
184
190
  end
185
- elsif column.plural_association?
191
+ elsif column.association.try :collection?
186
192
  column_plural_assocation_value_from_value(column, Array(value))
187
193
  elsif column.number? && column.options[:format] && column.form_ui != :number
188
194
  column.number_to_native(value)
189
195
  else
190
196
  # convert empty strings into nil. this works better with 'null => true' columns (and validations),
191
- # and 'null => false' columns should just convert back to an empty string.
192
- # ... but we can at least check the ConnectionAdapter::Column object to see if nulls are allowed
193
- value = nil if value.is_a?(String) && value.empty? && !column.column.nil? && column.column.null
197
+ # for 'null => false' columns is just converted to default value from column
198
+ if value.is_a?(String) && value.empty? && !column.column.nil?
199
+ value = column.column.null ? nil : column.column.default
200
+ end
194
201
  value
195
202
  end
196
203
  end
197
204
 
198
205
  def column_plural_assocation_value_from_value(column, value)
199
206
  # it's an array of ids
200
- if value && !value.empty?
207
+ if value.present?
201
208
  ids = value.select(&:present?)
202
209
  ids.empty? ? [] : column.association.klass.find(ids)
203
210
  else
@@ -206,32 +213,35 @@ module ActiveScaffold
206
213
  end
207
214
 
208
215
  def column_value_from_param_hash_value(parent_record, column, value, avoid_changes = false)
209
- if column.singular_association?
216
+ if column.association.try :singular?
210
217
  manage_nested_record_from_params(parent_record, column, value, avoid_changes)
211
- elsif column.plural_association?
218
+ elsif column.association.try :collection?
212
219
  # HACK: to be able to delete all associated records, hash will include "0" => ""
213
- value.collect { |_, val| manage_nested_record_from_params(parent_record, column, val, avoid_changes) unless val.blank? }.compact
220
+ values = value.values.reject(&:blank?)
221
+ values.collect { |val| manage_nested_record_from_params(parent_record, column, val, avoid_changes) }.compact
214
222
  else
215
223
  value
216
224
  end
217
225
  end
218
226
 
219
227
  def manage_nested_record_from_params(parent_record, column, attributes, avoid_changes = false)
220
- return nil unless build_record_from_params(attributes, column, parent_record)
228
+ return nil unless build_record_from_params?(attributes, column, parent_record)
221
229
  record = find_or_create_for_params(attributes, column, parent_record)
222
230
  if record
223
231
  record_columns = active_scaffold_config_for(column.association.klass).subform.columns
224
- record_columns.constraint_columns = [column.association.reverse]
232
+ prev_constraints = record_columns.constraint_columns
233
+ record_columns.constraint_columns = [column.association.reverse].compact
225
234
  update_record_from_params(record, record_columns, attributes, avoid_changes)
235
+ record_columns.constraint_columns = prev_constraints
226
236
  record.unsaved = true
227
237
  end
228
238
  record
229
239
  end
230
240
 
231
- def build_record_from_params(params, column, record)
241
+ def build_record_from_params?(params, column, record)
232
242
  current = record.send(column.name)
233
243
  klass = column.association.klass
234
- (column.plural_association? && !column.show_blank_record?(current)) || !attributes_hash_is_empty?(params, klass)
244
+ (column.association.collection? && !column.show_blank_record?(current)) || !attributes_hash_is_empty?(params, klass)
235
245
  end
236
246
 
237
247
  # Attempts to create or find an instance of the klass of the association in parent_column from the
@@ -261,12 +271,11 @@ module ActiveScaffold
261
271
  end
262
272
  end
263
273
 
264
- def save_record_to_association(record, association_name, value)
265
- association = record.class.reflect_on_association(association_name) if association_name
274
+ def save_record_to_association(record, association, value)
266
275
  if association.try(:collection?)
267
- record.send(association_name) << value
276
+ record.send(association.name) << value
268
277
  elsif association
269
- record.send("#{association_name}=", value)
278
+ record.send("#{association.name}=", value)
270
279
  end
271
280
  end
272
281
 
@@ -274,24 +283,26 @@ module ActiveScaffold
274
283
  # This isn't a literal emptiness - it's an attempt to discern whether the user intended it to be empty or not.
275
284
  def attributes_hash_is_empty?(hash, klass)
276
285
  # old style date form management... ignore them too
277
- part_ignore_column_types = [:datetime, :date, :time]
286
+ part_ignore_column_types = [:datetime, :date, :time, Time, Date]
278
287
 
279
288
  hash.all? do |key, value|
280
289
  # convert any possible multi-parameter attributes like 'created_at(5i)' to simply 'created_at'
281
290
  parts = key.to_s.split('(')
282
291
  column_name = parts.first
283
- column = klass.columns_hash[column_name]
292
+ column = ActiveScaffold::OrmChecks.columns_hash(klass)[column_name]
293
+ column_type = ActiveScaffold::OrmChecks.column_type(klass, column_name) if column
284
294
 
285
- # booleans and datetimes will always have a value. so we ignore them when checking whether the hash is empty.
295
+ # datetimes will always have a value. so we ignore them when checking whether the hash is empty.
286
296
  # this could be a bad idea. but the current situation (excess record entry) seems worse.
287
- next true if column && parts.length > 1 && part_ignore_column_types.include?(column.type)
297
+ next true if column && parts.length > 1 && part_ignore_column_types.include?(column_type)
288
298
 
289
299
  # defaults are pre-filled on the form. we can't use them to determine if the user intends a new row.
300
+ # booleans always have value, so they are ignored if not changed from default
290
301
  default_value = column_default_value(column_name, klass, column)
291
302
  casted_value = column ? ActiveScaffold::Core.column_type_cast(value, column) : value
292
303
  next true if casted_value == default_value
293
304
 
294
- if value.is_a?(Hash)
305
+ if params_hash? value
295
306
  attributes_hash_is_empty?(value, klass)
296
307
  elsif value.is_a?(Array)
297
308
  value.all?(&:blank?)
@@ -303,10 +314,18 @@ module ActiveScaffold
303
314
 
304
315
  def column_default_value(column_name, klass, column)
305
316
  return unless column
306
- if Rails.version < '4.2'
307
- column.default
308
- else
309
- column.type_cast_from_database(column.default)
317
+ if ActiveScaffold::OrmChecks.mongoid? klass
318
+ column.default_val
319
+ elsif ActiveScaffold::OrmChecks.active_record? klass
320
+ if Rails.version < '4.2'
321
+ column.default
322
+ elsif Rails.version < '5.0'
323
+ column.type_cast_from_database(column.default)
324
+ else
325
+ column_type = ActiveScaffold::OrmChecks.column_type(klass, column_name)
326
+ cast_type = ActiveModel::Type.lookup column_type
327
+ cast_type ? cast_type.deserialize(column.default) : column.default
328
+ end
310
329
  end
311
330
  end
312
331
  end