active_scaffold 3.4.1 → 3.4.2

Sign up to get free protection for your applications and to get access to all the features.
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