active_scaffold 3.4.1 → 3.4.2

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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +16 -0
  3. data/app/assets/javascripts/active_scaffold.js.erb +7 -6
  4. data/app/assets/javascripts/jquery/active_scaffold.js +18 -15
  5. data/app/assets/javascripts/prototype/active_scaffold.js +10 -9
  6. data/app/assets/stylesheets/active_scaffold_jquery_ui.css.erb +4 -0
  7. data/app/assets/stylesheets/active_scaffold_layout.css +3 -3
  8. data/app/views/active_scaffold_overrides/_form_association_footer.html.erb +2 -2
  9. data/app/views/active_scaffold_overrides/_list_inline_adapter.html.erb +2 -2
  10. data/app/views/active_scaffold_overrides/destroy.js.erb +1 -1
  11. data/app/views/active_scaffold_overrides/on_action_update.js.erb +1 -1
  12. data/app/views/active_scaffold_overrides/on_create.js.erb +1 -1
  13. data/app/views/active_scaffold_overrides/on_mark.js.erb +1 -1
  14. data/app/views/active_scaffold_overrides/on_update.js.erb +1 -1
  15. data/app/views/active_scaffold_overrides/update_column.js.erb +2 -2
  16. data/lib/active_scaffold/actions/common_search.rb +2 -2
  17. data/lib/active_scaffold/actions/core.rb +8 -5
  18. data/lib/active_scaffold/actions/create.rb +2 -1
  19. data/lib/active_scaffold/actions/list.rb +4 -3
  20. data/lib/active_scaffold/actions/mark.rb +2 -1
  21. data/lib/active_scaffold/actions/show.rb +1 -1
  22. data/lib/active_scaffold/actions/update.rb +2 -1
  23. data/lib/active_scaffold/attribute_params.rb +1 -0
  24. data/lib/active_scaffold/config/base.rb +1 -1
  25. data/lib/active_scaffold/config/list.rb +1 -1
  26. data/lib/active_scaffold/config/search.rb +1 -1
  27. data/lib/active_scaffold/constraints.rb +1 -1
  28. data/lib/active_scaffold/data_structures/column.rb +16 -5
  29. data/lib/active_scaffold/extensions/action_view_rendering.rb +6 -6
  30. data/lib/active_scaffold/extensions/reverse_associations.rb +82 -46
  31. data/lib/active_scaffold/extensions/unsaved_associated.rb +2 -1
  32. data/lib/active_scaffold/finder.rb +6 -6
  33. data/lib/active_scaffold/helpers/form_column_helpers.rb +1 -1
  34. data/lib/active_scaffold/helpers/search_column_helpers.rb +1 -1
  35. data/lib/active_scaffold/helpers/view_helpers.rb +10 -1
  36. data/lib/active_scaffold/marked_model.rb +12 -6
  37. data/lib/active_scaffold/tableless.rb +6 -4
  38. data/lib/active_scaffold/version.rb +1 -1
  39. data/test/misc/attribute_params_test.rb +3 -0
  40. data/test/mock_app/app/models/person.rb +1 -1
  41. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c53462e9f8376026d9967b86c6e853504641002e
4
- data.tar.gz: c7f5e3fdf9da980b02791d0d99d50f6377095070
3
+ metadata.gz: f13a22b391abab0509c40d617c528902fda68c27
4
+ data.tar.gz: a4bc30e40eb947290999544fcdec3cce07fe3236
5
5
  SHA512:
6
- metadata.gz: f31c9d71a8ef33e63eae3987cd37a6bbd0327746db8af9f9c241e959be1a765fbd28da7edf25a41785add40a532f9963995052becf89c31870b65eac90cc3db4
7
- data.tar.gz: af95b26715e8eaa8808dea19c2ea4b88650893892d1cd863f34e64f3c9db168570313d194cb1166b0e4c2bc1d2843261733672060f7a432a3ff45d520db58b1e
6
+ metadata.gz: 56f0cf576372ed2f10ddd03c6a798738237e5a3bda87e01d62b6bd3f8fe4831b7579589aa0fa50a6989c9b372e8e9c23dfd1643d682231fe356091e93f30c0d0
7
+ data.tar.gz: e8d69d8f3c33c9167829635378146560e2b771d8ab495db3adf2966d4af0061394594541020696f72b5cfb7f21577934656d26667288ba32abd318680ba7338c
data/CHANGELOG CHANGED
@@ -1,3 +1,19 @@
1
+ = 3.4.2 (not released yet)
2
+ - fix tableless on latest rails4 versions
3
+ - respond with 406 for html request on js only actions
4
+ - fix JS issue on IE8
5
+ - support jquery-ui-rails 5
6
+ - update_columns fixes on subforms refreshing
7
+ - fix dynamic links menu in group links
8
+ - fix id_condition (/:id/index) for models with different primary key
9
+ - fix parent_controller param when rendering subforms on render_field
10
+ - fix session usage on rails >= 4.1
11
+ - fix marked records on rails >= 4.1
12
+ - add display_dynamic_action_group helper to help building js and html to display a dynamic action group
13
+ - display error when has_one association with :select form_ui fails to set, save continued but change was lost
14
+ - fix each_record_in_scope for rails 4 (fixes mark all records)
15
+ - trigger as:element_created for created record
16
+
1
17
  = 3.4.1
2
18
  - fix search value when both field_search and search are enabled and a search with field_search is active
3
19
  - fix cancan permissions check on subform
@@ -7,12 +7,13 @@
7
7
  <% require_asset "jquery/date_picker_bridge" %>
8
8
  <% require_asset "jquery/draggable_lists" %>
9
9
  <% elsif Jquery.const_defined? 'Ui' %>
10
- <% require_asset "jquery.ui.core" %>
11
- <% require_asset "jquery.ui.effect" %>
12
- <% require_asset "jquery.ui.sortable" %>
13
- <% require_asset "jquery.ui.draggable" %>
14
- <% require_asset "jquery.ui.droppable" %>
15
- <% require_asset "jquery.ui.datepicker" %>
10
+ <% jquery_ui_prefix = Jquery::Ui::Rails::VERSION < '5.0.0' ? 'jquery.ui.' : 'jquery-ui/' %>
11
+ <% require_asset "#{jquery_ui_prefix}core" %>
12
+ <% require_asset "#{jquery_ui_prefix}effect" %>
13
+ <% require_asset "#{jquery_ui_prefix}sortable" %>
14
+ <% require_asset "#{jquery_ui_prefix}draggable" %>
15
+ <% require_asset "#{jquery_ui_prefix}droppable" %>
16
+ <% require_asset "#{jquery_ui_prefix}datepicker" %>
16
17
  <% require_asset "jquery-ui-timepicker-addon" %>
17
18
  <% require_asset "jquery/date_picker_bridge" %>
18
19
  <% require_asset "jquery/draggable_lists" %>
@@ -38,7 +38,7 @@ jQuery(document).ready(function($) {
38
38
  jQuery(document).on('focus', ':input', function() { ActiveScaffold.last_focus = this; });
39
39
  jQuery(document).on('blur', ':input', function(e) { ActiveScaffold.last_focus = e.relatedTarget; });
40
40
  jQuery(document).click(function(event) {
41
- jQuery('.action_group.dyn ul').hide(); // only hide so action links loading still work
41
+ jQuery('.action_group.dyn > ul').hide(); // only hide so action links loading still work
42
42
  });
43
43
  jQuery(document).on('ajax:beforeSend', 'form.as_form', function(event) {
44
44
  var as_form = jQuery(this).closest("form");
@@ -193,10 +193,10 @@ jQuery(document).ready(function($) {
193
193
  return true;
194
194
  } else return false;
195
195
  });
196
- jQuery(document).on('ajax:complete', '.action_group.dyn ul a', function(event) {
196
+ jQuery(document).on('ajax:complete', '.action_group.dyn > ul a', function(event) {
197
197
  var action_link = ActiveScaffold.find_action_link(event.target);
198
198
  if (action_link.loading_indicator) action_link.loading_indicator.css('visibility','hidden');
199
- jQuery(event.target).closest('.action_group.dyn ul').remove();
199
+ jQuery(event.target).closest('.action_group.dyn > ul').remove();
200
200
  });
201
201
 
202
202
  jQuery(document).on('change', 'input.update_form:not(.recordselect), textarea.update_form, select.update_form, .checkbox-list.update_form input:checkbox', function(event) {
@@ -252,8 +252,8 @@ jQuery(document).ready(function($) {
252
252
  });
253
253
 
254
254
  jQuery(document).on('click', '.active-scaffold .sub-form a.destroy', function(event) {
255
- event.preventDefault();
256
- ActiveScaffold.delete_subform_record($(this).data('delete-id'));
255
+ event.preventDefault();
256
+ ActiveScaffold.delete_subform_record($(this).data('delete-id'));
257
257
  });
258
258
 
259
259
  jQuery(document).on("click", '.hover_click', function(event) {
@@ -495,7 +495,7 @@ var ActiveScaffold = {
495
495
  replace: function(element, html) {
496
496
  if (typeof(element) == 'string') element = '#' + element;
497
497
  element = jQuery(element);
498
- var new_element = typeof(html) == 'string' ? jQuery.parseHTML(html.trim(), true) : html;
498
+ var new_element = typeof(html) == 'string' ? jQuery.parseHTML(jQuery.trim(html), true) : html;
499
499
  new_element = jQuery(new_element);
500
500
  if (element.length) {
501
501
  element.replaceWith(new_element);
@@ -583,13 +583,13 @@ var ActiveScaffold = {
583
583
  create_record_row: function(active_scaffold_id, html, options) {
584
584
  if (typeof(active_scaffold_id) == 'string') active_scaffold_id = '#' + active_scaffold_id;
585
585
  tbody = jQuery(active_scaffold_id).find('tbody.records').first();
586
+ var new_row;
586
587
 
587
588
  if (options.insert_at == 'top') {
588
589
  tbody.prepend(html);
589
- var new_row = tbody.children('tr.record:first-child');
590
+ new_row = tbody.children('tr.record:first-child');
590
591
  } else if (options.insert_at == 'bottom') {
591
592
  var rows = tbody.children('tr.record, tr.inline-adapter');
592
- var new_row = null;
593
593
  if (rows.length > 0) {
594
594
  new_row = rows.last().after(html).next();
595
595
  } else {
@@ -613,6 +613,7 @@ var ActiveScaffold = {
613
613
  this.hide_empty_message(tbody);
614
614
  this.increment_record_count(tbody.closest('div.active-scaffold'));
615
615
  this.highlight(new_row);
616
+ new_row.trigger('as:element_created');
616
617
  },
617
618
 
618
619
  create_record_row_from_url: function(action_link, url, options) {
@@ -663,14 +664,16 @@ var ActiveScaffold = {
663
664
  },
664
665
 
665
666
  display_dynamic_action_group: function(link, html) {
667
+ var container;
666
668
  if (typeof(link) == 'string') link = jQuery('#' + link);
667
- link.next('ul').remove();
668
- if (link.closest('td.actions').length) link.closest('td').addClass('action_group dyn');
669
- else {
670
- if (link.parent('div.actions').length) link.wrap($('<div>'));
671
- link.parent().addClass('action_group dyn');
669
+ if (link.closest('td.actions').length) {
670
+ container = link.closest('td').addClass('action_group dyn');
671
+ } else {
672
+ if (link.parent('div.actions').length) link.wrap(jQuery('<div>'));
673
+ container = link.parent().addClass('action_group dyn');
672
674
  }
673
- link.after(html);
675
+ container.find('> ul').remove();
676
+ container.append(html);
674
677
  },
675
678
 
676
679
  scroll_to: function(element, checkInViewport) {
@@ -939,7 +942,7 @@ var ActiveScaffold = {
939
942
  complete: function(event) {
940
943
  element.nextAll('img.loading-indicator').css('visibility','hidden');
941
944
  ActiveScaffold.enable_form(as_form);
942
- if (ActiveScaffold.last_focus) $(ActiveScaffold.last_focus).focus().select();
945
+ if (ActiveScaffold.last_focus) jQuery(ActiveScaffold.last_focus).focus().select();
943
946
  },
944
947
  error: function (xhr, status, error) {
945
948
  var as_div = element.closest("div.active-scaffold");
@@ -13,13 +13,13 @@ if (!Element.Methods.highlight) Element.addMethods({highlight: Prototype.emptyFu
13
13
 
14
14
  document.observe("dom:loaded", function() {
15
15
  document.on('click', function(event) {
16
- $$('.action_group.dyn ul').invoke('remove');
16
+ $$('.action_group.dyn > ul').invoke('hide');
17
17
  });
18
- document.on('ajax:complete', '.action_group.dyn ul a', function() {
18
+ document.on('ajax:complete', '.action_group.dyn > ul a', function() {
19
19
  var source = event.findElement();
20
20
  var action_link = ActiveScaffold.find_action_link(source);
21
21
  if (action_link.loading_indicator) action_link.loading_indicator.css('visibility','hidden');
22
- $(source).up('.action_group.dyn ul').remove();
22
+ $(source).up('.action_group.dyn > ul').remove();
23
23
  });
24
24
  document.on('ajax:create', 'form.as_form', function(event) {
25
25
  var source = event.findElement();
@@ -568,15 +568,16 @@ var ActiveScaffold = {
568
568
  },
569
569
 
570
570
  display_dynamic_action_group: function(link, html) {
571
+ var container;
571
572
  link = $(link);
572
- link.next('ul').remove();
573
- link.up('td').addClassName('action_group dyn');
574
- if (link.up('td.actions')) link.up('td').addClassName('action_group dyn');
575
- else {
573
+ if (link.up('td.actions')) {
574
+ container = link.up('td').addClassName('action_group dyn');
575
+ } else {
576
576
  if (link.up().hasClassName('actions')) link.wrap('div');
577
- link.up().addClassName('action_group dyn');
577
+ container = link.up().addClassName('action_group dyn');
578
578
  }
579
- link.insert({after: html});
579
+ container.down('> ul').remove();
580
+ container.insert({bottom: html});
580
581
  },
581
582
 
582
583
  scroll_to: function(element, checkInViewport) {
@@ -2,6 +2,10 @@
2
2
  <% require_asset "jquery-ui" %>
3
3
  <% require_asset "jquery-ui-theme" %>
4
4
  <% elsif Jquery.const_defined? 'Ui' %>
5
+ <% if Jquery::Ui::Rails::VERSION < '5.0.0' %>
5
6
  <% require_asset "jquery.ui.datepicker" %>
7
+ <% else %>
8
+ <% require_asset "jquery-ui/datepicker" %>
9
+ <% end %>
6
10
  <% require_asset "jquery-ui-theme" %>
7
11
  <% end %>
@@ -261,7 +261,7 @@ min-width: 150px;
261
261
  right: 0px;
262
262
  z-index: 2;
263
263
  }
264
- .active-scaffold .actions .action_group.dyn ul {
264
+ .active-scaffold .actions .action_group.dyn > ul {
265
265
  width: auto;
266
266
  display: block;
267
267
  }
@@ -296,7 +296,7 @@ text-align: left;
296
296
  background-position: 5px 50%;
297
297
  background-repeat: no-repeat;
298
298
  }
299
- .active-scaffold .actions .action_group.dyn ul li a {
299
+ .active-scaffold .actions .action_group.dyn > ul li a {
300
300
  padding-left: 5px;
301
301
  }
302
302
 
@@ -309,7 +309,7 @@ border-top-width: 0px;
309
309
  display: none;
310
310
  }
311
311
 
312
- .active-scaffold .actions .action_group:hover ul,
312
+ .active-scaffold .actions .action_group:hover > ul,
313
313
  .active-scaffold .actions .action_group ul li:hover > ul,
314
314
  .active-scaffold .actions .action_group ul ul li:hover ul {
315
315
  display: block;
@@ -13,8 +13,8 @@ return unless show_add_new or show_add_existing
13
13
 
14
14
  temporary_id = generated_id(parent_record) if parent_record.new_record?
15
15
  controller_path = active_scaffold_controller_for(parent_record.class).controller_path
16
- edit_associated_url = params_for(:controller => controller_path, :action => 'edit_associated', :child_association => column.name, :associated_id => '--ID--', :scope => scope, :id => parent_record.to_param, :generated_id => temporary_id, :parent_controller => controller.controller_path) if show_add_existing
17
- add_new_url = params_for(:controller => controller_path, :action => 'edit_associated', :child_association => column.name, :scope => scope, :id => parent_record.to_param, :generated_id => temporary_id, :parent_controller => controller.controller_path) if show_add_new
16
+ edit_associated_url = params_for(:controller => controller_path, :action => 'edit_associated', :child_association => column.name, :associated_id => '--ID--', :scope => scope, :id => parent_record.to_param, :generated_id => temporary_id, :parent_controller => params[:parent_controller] || controller.controller_path) if show_add_existing
17
+ add_new_url = params_for(:controller => controller_path, :action => 'edit_associated', :child_association => column.name, :scope => scope, :id => parent_record.to_param, :generated_id => temporary_id, :parent_controller => params[:parent_controller] || controller.controller_path) if show_add_new
18
18
 
19
19
  -%>
20
20
  <div class="footer-wrapper">
@@ -1,12 +1,12 @@
1
1
  <%# nested_id, allows us to remove a nested scaffold programmatically %>
2
2
  <tr class="inline-adapter" id="<%= element_row_id :action => :nested %>">
3
3
  <td class="inline-adapter-cell">
4
- <% if controller.send(:successful?) %>
4
+ <% if successful? %>
5
5
  <div class="<%= "#{params[:action]}-view" if params[:action] %> <%= "#{nested? ? nested.name : id_from_controller(params[:controller])}-view" %> view">
6
6
  <%= link_to(as_(:close), '', :class => 'inline-adapter-close as_cancel', :remote => true, :title => as_(:close)) -%>
7
7
  <%= payload -%>
8
8
  </div>
9
9
  <% end %>
10
+ <%= javascript_tag("setTimeout(function() { var action_link = ActiveScaffold.ActionLink.get('#{element_row_id(:action => :nested)}'); if (action_link) { action_link.update_flash_messages('#{escape_javascript(render(:partial => 'messages').strip)}');#{' action_link.close(); ActiveScaffold.scroll_to(action_link.scaffold(), ActiveScaffold.config.scroll_on_close == "checkInViewport");' unless successful?} } }, 10);") %>
10
11
  </td>
11
12
  </tr>
12
- <%= javascript_tag("setTimeout(function() { var action_link = ActiveScaffold.ActionLink.get('#{element_row_id(:action => :nested)}'); if (action_link) { action_link.update_flash_messages('#{escape_javascript(render(:partial => 'messages').strip)}');#{' action_link.close(); ActiveScaffold.scroll_to(action_link.scaffold(), ActiveScaffold.config.scroll_on_close == "checkInViewport");' unless controller.send(:successful?)} } }, 10);") %>
@@ -1,5 +1,5 @@
1
1
  <% messages_id = active_scaffold_messages_id %>
2
- <% if controller.send(:successful?) %>
2
+ <% if successful? %>
3
3
  <% if render_parent? %>
4
4
  <% if render_parent_action == :row %>
5
5
  <%# TODO: That s not working with delete.... %>
@@ -1,4 +1,4 @@
1
- <% if controller.send :successful? %>
1
+ <% if successful? %>
2
2
  <% if @record %>
3
3
  <%= render :partial => 'update_messages' %>
4
4
  <% row = escape_javascript(render(:partial => 'list_record', :locals => {:record => @record})) -%>
@@ -8,7 +8,7 @@ insert_at ||= :top -%>
8
8
  action_link = ActiveScaffold.find_action_link('<%= form_selector %>');
9
9
  action_link.update_flash_messages('<%= escape_javascript(render(:partial => 'messages')) %>');
10
10
  <% end -%>
11
- <% if controller.send :successful? -%>
11
+ <% if successful? -%>
12
12
  <% if render_parent? %>
13
13
  <% if nested_singular_association? %>
14
14
  action_link.close(true);
@@ -1,6 +1,6 @@
1
1
  <%
2
2
  checked = all_marked? unless local_assigns.has_key? :checked
3
- options = {:checked => checked, :include_mark_all => true, :include_checkboxes => params[:id].nil?}
3
+ options = {:checked => checked, :include_mark_all => true, :include_checkboxes => @include_checkboxes}
4
4
  %>
5
5
  ActiveScaffold.mark_records('<%= active_scaffold_tbody_id %>',<%= options.to_json.html_safe %>);
6
6
  <%= render :partial => 'update_messages' %>
@@ -2,7 +2,7 @@ try {
2
2
  <% form_selector = "#{element_form_id(:action => :update, :id => @record.try(:id) || params[:id])}" %>
3
3
  var action_link = ActiveScaffold.find_action_link('<%= form_selector %>');
4
4
  action_link.update_flash_messages('<%= escape_javascript(render(:partial => 'messages')) %>');
5
- <% if controller.send :successful? %>
5
+ <% if successful? %>
6
6
  <% if params[:dont_close] %>
7
7
  <% row_selector = element_row_id(:action => :list, :id => @record.id) %>
8
8
  ActiveScaffold.update_row('<%= row_selector %>', '<%= escape_javascript(render(:partial => 'list_record', :locals => {:record => @record})) %>');
@@ -1,10 +1,10 @@
1
1
  <% @column_span_id ||= element_cell_id(:id => @record.id.to_s, :action => 'update_column', :name => @column.name) -%>
2
- <% unless controller.send :successful? -%>
2
+ <% unless successful? -%>
3
3
  alert('<%= escape_javascript(@record.errors.full_messages.join("\n")) %>');
4
4
  <% @record.reload -%>
5
5
  <% end -%>
6
6
  <% if @column.inplace_edit
7
- ipe_update = @column.inplace_edit_update if controller.send :successful? -%>
7
+ ipe_update = @column.inplace_edit_update if successful? -%>
8
8
  <% case ipe_update
9
9
  when :row -%>
10
10
  ActiveScaffold.update_row('<%= element_row_id(:action => :list) %>', '<%= escape_javascript render('row', :record => @record) %>');
@@ -29,14 +29,14 @@ module ActiveScaffold::Actions
29
29
 
30
30
  def store_search_params_into_session
31
31
  if active_scaffold_config.store_user_settings
32
- active_scaffold_session_storage[:search] = params.delete :search if params[:search]
32
+ active_scaffold_session_storage['search'] = params.delete :search if params[:search]
33
33
  else
34
34
  @search_params = params.delete :search
35
35
  end
36
36
  end
37
37
 
38
38
  def search_params
39
- @search_params || active_scaffold_session_storage[:search]
39
+ @search_params || active_scaffold_session_storage['search']
40
40
  end
41
41
 
42
42
  def global_search_ignore?
@@ -17,8 +17,12 @@ module ActiveScaffold::Actions
17
17
  def render_field
18
18
  if request.get?
19
19
  render_field_for_inplace_editing
20
+ respond_to do |format|
21
+ format.js { render :action => 'render_field_inplace', :layout => false }
22
+ end
20
23
  else
21
24
  render_field_for_update_columns
25
+ respond_to { |format| format.js }
22
26
  end
23
27
  end
24
28
 
@@ -38,7 +42,6 @@ module ActiveScaffold::Actions
38
42
  def render_field_for_inplace_editing
39
43
  @column = active_scaffold_config.columns[params[:update_column]]
40
44
  @record = find_if_allowed(params[:id], :crud_type => :update, :column => params[:update_column])
41
- render :action => 'render_field_inplace', :layout => false
42
45
  end
43
46
 
44
47
  def render_field_for_update_columns
@@ -62,7 +65,7 @@ module ActiveScaffold::Actions
62
65
  end
63
66
 
64
67
  # check permissions and support overriding to_param
65
- record = find_if_allowed(id, :update) if id
68
+ record = find_if_allowed(id, :read) if id
66
69
  # call update_record_from_params with new_model
67
70
  # in other case some associations can be saved
68
71
  @record = new_model
@@ -70,7 +73,7 @@ module ActiveScaffold::Actions
70
73
  apply_constraints_to_record(@record) unless @scope
71
74
  @record = update_record_from_params(@record, @main_columns, hash, true)
72
75
  else
73
- @record = params[:id] ? find_if_allowed(params[:id], :update) : new_model
76
+ @record = params[:id] ? find_if_allowed(params[:id], :read) : new_model
74
77
  if @record.new_record?
75
78
  apply_constraints_to_record(@record) unless @scope
76
79
  else
@@ -125,11 +128,11 @@ module ActiveScaffold::Actions
125
128
  end
126
129
 
127
130
  def each_marked_record(&block)
128
- active_scaffold_config.model.find(marked_records.to_a).each &block
131
+ active_scaffold_config.model.as_marked.each &block
129
132
  end
130
133
 
131
134
  def marked_records
132
- active_scaffold_session_storage[:marked_records] ||= Set.new
135
+ active_scaffold_session_storage['marked_records'] ||= {}
133
136
  end
134
137
 
135
138
  def default_formats
@@ -101,7 +101,8 @@ module ActiveScaffold::Actions
101
101
  apply_constraints_to_record(@record, :allow_autosave => true)
102
102
  create_association_with_parent(@record) if nested?
103
103
  before_create_save(@record)
104
- self.successful = [@record.valid?, @record.associated_valid?].all? # this syntax avoids a short-circuit
104
+ # errors to @record can be added by update_record_from_params when association fails to set and ActiveRecord::RecordNotSaved is raised
105
+ self.successful = [@record.errors.empty? && @record.valid?, @record.associated_valid?].all? # this syntax avoids a short-circuit
105
106
  create_save(@record) unless options[:skip_save]
106
107
  end
107
108
  rescue ActiveRecord::ActiveRecordError => ex
@@ -132,7 +132,8 @@ module ActiveScaffold::Actions
132
132
  def each_record_in_scope
133
133
  do_search if respond_to? :do_search, true
134
134
  set_includes_for_columns
135
- append_to_query(beginning_of_chain, finder_options).each {|record| yield record}
135
+ # where(nil) is needed because we need a relation
136
+ append_to_query(beginning_of_chain.where(nil), finder_options).each {|record| yield record}
136
137
  end
137
138
 
138
139
  # The default security delegates to ActiveRecordPermissions.
@@ -151,11 +152,11 @@ module ActiveScaffold::Actions
151
152
  def process_action_link_action(render_action = :action_update, crud_type_or_security_options = nil)
152
153
  if request.get?
153
154
  # someone has disabled javascript, we have to show confirmation form first
154
- @record = find_if_allowed(params[:id], :read) if params[:id] && params[:id].to_i > 0
155
+ @record = find_if_allowed(params[:id], :read) if params[:id]
155
156
  respond_to_action(:action_confirmation)
156
157
  else
157
158
  @action_link = active_scaffold_config.action_links[action_name]
158
- if params[:id] && params[:id].to_i > 0
159
+ if params[:id]
159
160
  crud_type_or_security_options ||= (request.post? || request.put?) ? :update : :delete
160
161
  get_row(crud_type_or_security_options)
161
162
  unless @record.nil?
@@ -27,12 +27,13 @@ module ActiveScaffold::Actions
27
27
  end
28
28
 
29
29
  def mark_respond_to_js
30
- if params[:id]
30
+ if params.delete(:id) # so find_page doesn't filter by :id
31
31
  do_search if respond_to? :do_search, true
32
32
  set_includes_for_columns if active_scaffold_config.actions.include? :list
33
33
  @page = find_page(:pagination => active_scaffold_config.mark.mark_all_mode != :page)
34
34
  render :action => 'on_mark'
35
35
  else
36
+ @include_checkboxes = true
36
37
  render :action => 'on_mark', :locals => {:checked => mark?}
37
38
  end
38
39
  end
@@ -11,7 +11,7 @@ module ActiveScaffold::Actions
11
11
  do_show
12
12
  respond_to_action(:show)
13
13
  else
14
- @record = find_if_allowed(params[:id], :read) if params[:id] && params[:id].to_i > 0
14
+ @record = find_if_allowed(params[:id], :read) if params[:id]
15
15
  action_confirmation_respond_to_html(:destroy)
16
16
  end
17
17
  end
@@ -101,7 +101,8 @@ module ActiveScaffold::Actions
101
101
  active_scaffold_config.model.transaction do
102
102
  @record = update_record_from_params(@record, active_scaffold_config.update.columns, attributes) unless options[:no_record_param_update]
103
103
  before_update_save(@record)
104
- self.successful = [@record.valid?, @record.associated_valid?].all? # this syntax avoids a short-circuit
104
+ # errors to @record can be added by update_record_from_params when association fails to set and ActiveRecord::RecordNotSaved is raised
105
+ self.successful = [@record.errors.empty? && @record.valid?, @record.associated_valid?].all? # this syntax avoids a short-circuit
105
106
  if successful?
106
107
  @record.save! and @record.save_associated!
107
108
  after_update_save(@record)
@@ -66,6 +66,7 @@ module ActiveScaffold
66
66
  begin
67
67
  parent_record.send "#{column.name}=", value
68
68
  rescue ActiveRecord::RecordNotSaved
69
+ parent_record.errors.add column.name, :invalid
69
70
  parent_record.association(column.name).target = value if column.association
70
71
  end
71
72
  end
@@ -53,7 +53,7 @@ module ActiveScaffold::Config
53
53
  @params = params
54
54
  # the configuration object for this action
55
55
  @conf = conf
56
- @action = action
56
+ @action = action.to_s
57
57
  end
58
58
 
59
59
  def [](key)
@@ -237,7 +237,7 @@ module ActiveScaffold::Config
237
237
  attr_writer :label
238
238
  # This label has alread been localized.
239
239
  def label
240
- self[:label] || @label || @conf.label
240
+ self['label'] || @label || @conf.label
241
241
  end
242
242
 
243
243
  def per_page
@@ -41,7 +41,7 @@ module ActiveScaffold::Config
41
41
  def columns
42
42
  # we want to delay initializing to the @core.columns set for as long as possible. Too soon and .search_sql will not be available to .searchable?
43
43
  unless @columns
44
- self.columns = @core.columns.collect{|c| c.name if @core.columns._inheritable.include?(c.name) and c.searchable? and c.column and c.column.text?}.compact
44
+ self.columns = @core.columns.collect{|c| c.name if @core.columns._inheritable.include?(c.name) and c.searchable? and c.text?}.compact
45
45
  end
46
46
  @columns
47
47
  end
@@ -5,7 +5,7 @@ module ActiveScaffold
5
5
 
6
6
  # Returns the current constraints
7
7
  def active_scaffold_constraints
8
- @active_scaffold_constraints ||= active_scaffold_session_storage[:constraints] || {}
8
+ @active_scaffold_constraints ||= active_scaffold_session_storage['constraints'] || {}
9
9
  end
10
10
 
11
11
  # For each enabled action, adds the constrained columns to the ActionColumns object (if it exists).
@@ -291,6 +291,10 @@ module ActiveScaffold::DataStructures
291
291
  @number
292
292
  end
293
293
 
294
+ def text?
295
+ @text
296
+ end
297
+
294
298
  # this is so that array.delete and array.include?, etc., will work by column name
295
299
  def ==(other) #:nodoc:
296
300
  # another column
@@ -338,11 +342,18 @@ module ActiveScaffold::DataStructures
338
342
  end
339
343
  end
340
344
 
341
- self.number = @column.try(:number?)
342
- @options = {:format => :i18n_number} if self.number?
343
- @form_ui = :checkbox if @column and @column.type == :boolean
344
- @form_ui = :textarea if @column and @column.type == :text
345
- @form_ui = :number if @column and self.number?
345
+ @text = @column.nil? || [:string, :text].include?(@column.type)
346
+ if @column
347
+ @form_ui = case @column.type
348
+ when :boolean then :checkbox
349
+ when :text then :textarea
350
+ end
351
+ if @column.number?
352
+ @number = true
353
+ @form_ui = :number
354
+ @options = {:format => :i18n_number}
355
+ end
356
+ end
346
357
  @allow_add_existing = true
347
358
  @form_ui = self.class.association_form_ui if @association && self.class.association_form_ui
348
359
 
@@ -45,19 +45,19 @@ module ActionView::Helpers #:nodoc:
45
45
  eid = Digest::MD5.hexdigest(params[:controller] + remote_controller.to_s + constraints.to_s + conditions.to_s)
46
46
  eid_info = session["as:#{eid}"] ||= {}
47
47
  if constraints
48
- eid_info[:constraints] = constraints
48
+ eid_info['constraints'] = constraints
49
49
  else
50
- eid_info.delete :constraints
50
+ eid_info.delete 'constraints'
51
51
  end
52
52
  if conditions
53
- eid_info[:conditions] = conditions
53
+ eid_info['conditions'] = conditions
54
54
  else
55
- eid_info.delete :conditions
55
+ eid_info.delete 'conditions'
56
56
  end
57
57
  if options[:label]
58
- eid_info[:list] = {:label => options[:label]}
58
+ eid_info['list'] = {'label' => options[:label]}
59
59
  else
60
- eid_info.delete :list
60
+ eid_info.delete 'list'
61
61
  end
62
62
  session.delete "as:#{eid}" if eid_info.empty?
63
63
  options[:params] ||= {}
@@ -1,12 +1,20 @@
1
- module ActiveRecord
2
- module Reflection
3
- class AssociationReflection #:nodoc:
1
+ module ActiveScaffold
2
+ module ReverseAssociation
3
+ module CommonMethods
4
+ def self.included(base)
5
+ base.class_eval { attr_writer :reverse }
6
+ base.alias_method_chain :inverse_of, :autodetect
7
+ end
8
+
9
+ def inverse_of_with_autodetect
10
+ inverse_of_without_autodetect || autodetect_inverse
11
+ end
12
+
4
13
  def inverse_for?(klass)
5
14
  inverse_class = inverse_of.try(:active_record)
6
15
  inverse_class.present? && (inverse_class == klass || klass < inverse_class)
7
16
  end
8
17
 
9
- attr_writer :reverse
10
18
  def reverse(klass = nil)
11
19
  unless defined? @reverse
12
20
  @reverse ||= inverse_of.try(:name)
@@ -14,56 +22,84 @@ module ActiveRecord
14
22
  @reverse || (autodetect_inverse(klass).try(:name) unless klass.nil?)
15
23
  end
16
24
 
17
- def inverse_of_with_autodetect
18
- inverse_of_without_autodetect || autodetect_inverse
25
+ def autodetect_inverse(klass = nil)
26
+ return nil if klass.nil? && options[:polymorphic]
27
+ klass ||= self.klass
28
+
29
+ # name-based matching (association name vs self.active_record.to_s)
30
+ matches = self.reverse_matches(klass)
31
+ if matches.length > 1
32
+ matches.find_all do |assoc|
33
+ self.active_record.to_s.underscore.include? assoc.name.to_s.pluralize.singularize
34
+ end
35
+ end
36
+
37
+ matches.first
19
38
  end
20
- alias_method_chain :inverse_of, :autodetect
39
+ end
21
40
 
41
+ module AssociationReflection
42
+ def self.included(base)
43
+ base.send :include, ActiveScaffold::ReverseAssociation::CommonMethods
44
+ end
45
+
22
46
  protected
47
+ def reverse_matches(klass)
48
+ reverse_matches = []
23
49
 
24
- def autodetect_inverse(klass = nil)
25
- return nil if klass.nil? && options[:polymorphic]
26
- klass ||= self.klass
27
- reverse_matches = []
28
-
29
- # stage 1 filter: collect associations that point back to this model and use the same foreign_key
30
- klass.reflect_on_all_associations.each do |assoc|
31
- next if assoc == self
32
- if self.options[:through]
33
- # only iterate has_many :through associations
34
- next unless assoc.options[:through]
35
- next unless assoc.through_reflection.klass == self.through_reflection.klass
36
- else
37
- # skip over has_many :through associations
38
- next if assoc.options[:through]
39
- next unless assoc.options[:polymorphic] or assoc.class_name == self.active_record.name
40
-
41
- case [assoc.macro, self.macro].find_all{|m| m == :has_and_belongs_to_many}.length
42
- # if both are a habtm, then match them based on the join table
43
- when 2
44
- next unless assoc.options[:join_table] == self.options[:join_table]
45
-
46
- # if only one is a habtm, they do not match
47
- when 1
48
- next
49
-
50
- # otherwise, match them based on the foreign_key
51
- when 0
52
- next unless assoc.foreign_key.to_sym == self.foreign_key.to_sym
53
- end
54
- end
55
-
56
- reverse_matches << assoc
57
- end
50
+ # collect associations that point back to this model and use the same foreign_key
51
+ klass.reflect_on_all_associations.each do |assoc|
52
+ next if assoc == self
53
+ # skip over has_many :through associations
54
+ next if assoc.options[:through]
55
+ next unless assoc.options[:polymorphic] or assoc.class_name == self.active_record.name
58
56
 
59
- # stage 2 filter: name-based matching (association name vs self.active_record.to_s)
60
- reverse_matches.find_all do |assoc|
61
- self.active_record.to_s.underscore.include? assoc.name.to_s.pluralize.singularize
62
- end if reverse_matches.length > 1
57
+ case [assoc.macro, self.macro].find_all{|m| m == :has_and_belongs_to_many}.length
58
+ # if both are a habtm, then match them based on the join table
59
+ when 2
60
+ next unless assoc.options[:join_table] == self.options[:join_table]
61
+
62
+ # if only one is a habtm, they do not match
63
+ when 1
64
+ next
65
+
66
+ # otherwise, match them based on the foreign_key
67
+ when 0
68
+ next unless assoc.foreign_key.to_sym == self.foreign_key.to_sym
69
+ end
63
70
 
64
- reverse_matches.first
71
+ reverse_matches << assoc
65
72
  end
73
+ reverse_matches
74
+ end
75
+ end
76
+
77
+ module ThroughReflection
78
+ def self.included(base)
79
+ base.send :include, ActiveScaffold::ReverseAssociation::CommonMethods unless base < ActiveScaffold::ReverseAssociation::CommonMethods
80
+ end
66
81
 
82
+ protected
83
+
84
+ def reverse_matches(klass)
85
+ reverse_matches = []
86
+
87
+ # collect associations that point back to this model and use the same foreign_key
88
+ klass.reflect_on_all_associations.each do |assoc|
89
+ next if assoc == self
90
+ # only iterate has_many :through associations
91
+ next unless assoc.options[:through]
92
+ next unless assoc.class_name == self.active_record.name
93
+ next unless assoc.through_reflection.class_name == self.through_reflection.class_name
94
+
95
+ reverse_matches << assoc
96
+ end
97
+ reverse_matches
98
+ end
67
99
  end
100
+
68
101
  end
69
102
  end
103
+
104
+ ActiveRecord::Reflection::AssociationReflection.send :include, ActiveScaffold::ReverseAssociation::AssociationReflection
105
+ ActiveRecord::Reflection::ThroughReflection.send :include, ActiveScaffold::ReverseAssociation::ThroughReflection
@@ -4,7 +4,8 @@ class ActiveRecord::Base
4
4
  return true if path.include?(self) # prevent recursion (if associated and parent are new records)
5
5
  path << self
6
6
  # using [].all? syntax to avoid a short-circuit
7
- with_unsaved_associated { |a| [a.valid?, a.associated_valid?(path)].all? }
7
+ # errors to associated record can be added by update_record_from_params when association fails to set and ActiveRecord::RecordNotSaved is raised
8
+ with_unsaved_associated { |a| [a.errors.empty? && a.valid?, a.associated_valid?(path)].all? }
8
9
  end
9
10
 
10
11
  def save_associated
@@ -18,7 +18,7 @@ module ActiveScaffold
18
18
  where_clauses = []
19
19
  columns.each do |column|
20
20
  Array(column.search_sql).each do |search_sql|
21
- where_clauses << "#{search_sql} #{(column.column.nil? || column.column.text?) ? ActiveScaffold::Finder.like_operator : '='} ?"
21
+ where_clauses << "#{search_sql} #{column.text? ? ActiveScaffold::Finder.like_operator : '='} ?"
22
22
  end
23
23
  end
24
24
  phrase = where_clauses.join(' OR ')
@@ -26,7 +26,7 @@ module ActiveScaffold
26
26
  tokens.collect do |value|
27
27
  columns.inject([phrase]) do |condition, column|
28
28
  Array(column.search_sql).size.times do
29
- condition.push((column.column.nil? || column.column.text?) ? like_pattern.sub('?', value) : column.column.type_cast(value))
29
+ condition.push(column.text? ? like_pattern.sub('?', value) : column.column.type_cast(value))
30
30
  end
31
31
  condition
32
32
  end
@@ -62,7 +62,7 @@ module ActiveScaffold
62
62
  when :select, :multi_select, :country, :usa_state, :chosen, :multi_chosen
63
63
  ["%{search_sql} in (?)", Array(value)]
64
64
  else
65
- if column.column.nil? || column.column.text?
65
+ if column.text?
66
66
  ["%{search_sql} #{ActiveScaffold::Finder.like_operator} ?", like_pattern.sub('?', value)]
67
67
  else
68
68
  ["%{search_sql} = ?", column.column.type_cast(value)]
@@ -97,7 +97,7 @@ module ActiveScaffold
97
97
 
98
98
  def condition_for_range(column, value, like_pattern = nil)
99
99
  if !value.is_a?(Hash)
100
- if column.column.nil? || column.column.text?
100
+ if column.text?
101
101
  ["%{search_sql} #{ActiveScaffold::Finder.like_operator} ?", like_pattern.sub('?', value)]
102
102
  else
103
103
  ["%{search_sql} = ?", column.column.type_cast(value)]
@@ -320,12 +320,12 @@ module ActiveScaffold
320
320
  conditions_for_collection, # from the dev
321
321
  conditions_from_params, # from the parameters (e.g. /users/list?first_name=Fred)
322
322
  conditions_from_constraints, # from any constraints (embedded scaffolds)
323
- active_scaffold_session_storage[:conditions] # embedding conditions (weaker constraints)
323
+ active_scaffold_session_storage['conditions'] # embedding conditions (weaker constraints)
324
324
  ].reject(&:blank?)
325
325
  end
326
326
 
327
327
  def id_condition
328
- {:id => params[:id]} if params[:id]
328
+ {active_scaffold_config.model.primary_key => params[:id]} if params[:id]
329
329
  end
330
330
 
331
331
  # returns a single record (the given id) but only if it's allowed for the specified security options.
@@ -130,7 +130,7 @@ module ActiveScaffold
130
130
  ActiveSupport::Deprecation.warn "Relying on @record is deprecated, include :object in options with record.", caller if record.nil? # TODO Remove when relying on @record is removed
131
131
  record ||= @record # TODO Remove when relying on @record is removed
132
132
  subform_controller = controller.class.active_scaffold_controller_for(record.class) if scope
133
- form_columns = @main_columns.try(:names)
133
+ form_columns = @main_columns.try(:names) if scope.nil? || subform_controller == controller.class
134
134
  form_columns ||= current_form_columns(record, scope, subform_controller)
135
135
  if force || (form_columns && column.update_columns && (column.update_columns & form_columns).present?)
136
136
  url_params = params_for(:action => 'render_field', :column => column.name, :id => record.to_param)
@@ -156,7 +156,7 @@ module ActiveScaffold
156
156
  end
157
157
 
158
158
  def active_scaffold_search_range_string?(column)
159
- (column.column && column.column.text?) || column.search_ui == :string
159
+ column.text? || column.search_ui == :string
160
160
  end
161
161
 
162
162
  def active_scaffold_search_range_comparator_options(column)
@@ -96,6 +96,15 @@ module ActiveScaffold
96
96
  end
97
97
  end
98
98
 
99
+ def display_dynamic_action_group(action_link, links, record_or_ul_options = nil, ul_options = nil)
100
+ ul_options = record_or_ul_options if ul_options.nil? && record_or_ul_options.is_a?(Hash)
101
+ record = record_or_ul_options unless record_or_ul_options.is_a?(Hash)
102
+ html = content_tag :ul, ul_options do
103
+ links.map { |link| content_tag :li, link }.join('').html_safe
104
+ end
105
+ raw "ActiveScaffold.display_dynamic_action_group('#{get_action_link_id action_link, record}', '#{escape_javascript html}');"
106
+ end
107
+
99
108
  def display_action_links(action_links, record, options, &block)
100
109
  options[:level_0_tag] ||= nil
101
110
  options[:options_level_0_tag] ||= nil
@@ -512,7 +521,7 @@ module ActiveScaffold
512
521
 
513
522
  def column_empty?(column_value)
514
523
  empty = column_value.nil?
515
- empty ||= column_value != false && column_value.blank?
524
+ empty ||= false != column_value && column_value.blank?
516
525
  empty ||= ['&nbsp;', active_scaffold_config.list.empty_field_text].include? column_value if String === column_value
517
526
  return empty
518
527
  end
@@ -3,31 +3,37 @@ module ActiveScaffold
3
3
  # This is a module aimed at making the make session_stored marked_records available to ActiveRecord models
4
4
 
5
5
  def self.included(base)
6
- base.extend ClassMethods
7
- base.scope :as_marked, lambda { where(:id => base.marked_records.to_a) }
6
+ base.class_eval do
7
+ extend ClassMethods
8
+ scope :as_marked, lambda { where(primary_key => marked_record_ids) }
9
+ end
8
10
  end
9
11
 
10
12
  def as_marked
11
- marked_records.include?(self.id)
13
+ marked_records.include?(self.id.to_s)
12
14
  end
13
15
 
14
16
  def as_marked=(value)
15
17
  value = [true, 'true', 1, '1', 'T', 't'].include?(value.class == String ? value.downcase : value)
16
18
  if value == true
17
- marked_records << self.id if !as_marked
19
+ marked_records[self.id.to_s] = true if !as_marked
18
20
  else
19
- marked_records.delete(self.id)
21
+ marked_records.delete(self.id.to_s)
20
22
  end
21
23
  end
22
24
 
23
25
  module ClassMethods
24
26
  def marked_records
25
- Thread.current[:marked_records] ||= Set.new
27
+ Thread.current[:marked_records] ||= {}
26
28
  end
27
29
 
28
30
  def marked_records=(marked)
29
31
  Thread.current[:marked_records] = marked
30
32
  end
33
+
34
+ def marked_record_ids
35
+ marked_records.keys
36
+ end
31
37
  end
32
38
 
33
39
  # Instance-level access to the marked_records
@@ -195,13 +195,15 @@ class ActiveScaffold::Tableless < ActiveRecord::Base
195
195
  raise 'destroy must be implemented in a Tableless model'
196
196
  end
197
197
 
198
- def create_record #:nodoc:
198
+ def _create_record #:nodoc:
199
199
  run_callbacks(:create) {}
200
200
  end
201
- alias_method :create, :create_record # for rails3
201
+ alias_method :create_record, :_create_record # for rails4 < 4.0.6, < 4.1.2
202
+ alias_method :create, :_create_record # for rails3
202
203
 
203
- def update_record(*) #:nodoc:
204
+ def _update_record(*) #:nodoc:
204
205
  run_callbacks(:update) {}
205
206
  end
206
- alias_method :update, :update_record # for rails3
207
+ alias_method :update_record, :_update_record # for rails4 < 4.0.6, < 4.1.2
208
+ alias_method :update, :_update_record # for rails3
207
209
  end
@@ -2,7 +2,7 @@ module ActiveScaffold
2
2
  module Version
3
3
  MAJOR = 3
4
4
  MINOR = 4
5
- PATCH = 1
5
+ PATCH = 2
6
6
 
7
7
  STRING = [MAJOR, MINOR, PATCH].compact.join('.')
8
8
  end
@@ -319,4 +319,7 @@ class Controller
319
319
  def logger
320
320
  @logger ||= Logger.new(STDOUT)
321
321
  end
322
+ def flash
323
+ @flash ||= ActionDispatch::Flash::FlashHash.new
324
+ end
322
325
  end
@@ -2,7 +2,7 @@ class Person < ActiveRecord::Base
2
2
  has_many :buildings, :foreign_key => :owner_id
3
3
  has_one :floor, :foreign_key => :tenant_id
4
4
  has_one :address, :through => :floor
5
- has_one :home, :through => :floor, :source => :building
5
+ has_one :home, :through => :floor, :source => :building, :class_name => 'Building'
6
6
 
7
7
  has_many :contacts, :as => :contactable
8
8
  has_one :car, :dependent => :destroy
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_scaffold
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.4.1
4
+ version: 3.4.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Many, see README
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-07-02 00:00:00.000000000 Z
11
+ date: 2014-08-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: shoulda