ajax_scaffold_plugin 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. data/CHANGELOG +3 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README +13 -0
  4. data/app/views/ajax_scaffold/_column_headings.rhtml +20 -0
  5. data/app/views/ajax_scaffold/_column_totals.rhtml +10 -0
  6. data/app/views/ajax_scaffold/_form.rhtml +5 -0
  7. data/app/views/ajax_scaffold/_form_messages.rhtml +5 -0
  8. data/app/views/ajax_scaffold/_messages.rhtml +10 -0
  9. data/app/views/ajax_scaffold/_new_edit.rhtml +40 -0
  10. data/app/views/ajax_scaffold/_pagination_links.rhtml +26 -0
  11. data/app/views/ajax_scaffold/_row.rhtml +46 -0
  12. data/app/views/ajax_scaffold/cancel.rjs +12 -0
  13. data/app/views/ajax_scaffold/create.rjs +20 -0
  14. data/app/views/ajax_scaffold/destroy.rjs +11 -0
  15. data/app/views/ajax_scaffold/edit.rjs +16 -0
  16. data/app/views/ajax_scaffold/list.rhtml +1 -0
  17. data/app/views/ajax_scaffold/new.rjs +14 -0
  18. data/app/views/ajax_scaffold/table.rhtml +63 -0
  19. data/app/views/ajax_scaffold/update.rjs +19 -0
  20. data/init.rb +27 -0
  21. data/install.rb +1 -0
  22. data/lib/ajax_scaffold_plugin.rb +505 -0
  23. data/lib/base/ajax_scaffold.rb +180 -0
  24. data/public/blank.html +33 -0
  25. data/public/images/add.gif +0 -0
  26. data/public/images/arrow_down.gif +0 -0
  27. data/public/images/arrow_up.gif +0 -0
  28. data/public/images/indicator-small.gif +0 -0
  29. data/public/images/indicator.gif +0 -0
  30. data/public/javascripts/ajax_scaffold.js +126 -0
  31. data/public/javascripts/dhtml_history.js +961 -0
  32. data/public/javascripts/rico_corner.js +781 -0
  33. data/public/stylesheets/ajax_scaffold.css +482 -0
  34. data/tasks/ajax_scaffold_tasks.rake +4 -0
  35. data/test/ajax_scaffold_test.rb +8 -0
  36. metadata +90 -0
data/CHANGELOG ADDED
@@ -0,0 +1,3 @@
1
+ 3.2.0 ==========
2
+
3
+ * Initial release
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2006 Richard White
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,13 @@
1
+ ********************************************************************************
2
+ ** For all documentation see the project website: http://www.ajaxscaffold.com **
3
+ ********************************************************************************
4
+
5
+ Ajax Scaffold plugin by Scott Rutherford (scott@caronsoftware.com) & Richard White (rrwhite@gmail.com)
6
+
7
+ Uses DhtmlHistory by Brad Neuberg (bkn3@columbia.edu)
8
+ http://codinginparadise.org
9
+
10
+ Uses Querystringby by Adam Vandenberg
11
+ http://adamv.com/dev/javascript/querystring
12
+
13
+ Released under the MIT license (included)
@@ -0,0 +1,20 @@
1
+ <% for scaffold_column in @scaffold_columns %>
2
+ <% column_sort_direction = column_sort_direction(scaffold_column.name, params) %>
3
+ <% sort_params = params.merge(:controller => @scaffold_controller, :action => 'update_'+@prefix+'table',
4
+ :sort => scaffold_column.name, :sort_direction => column_sort_direction, :page => 1) %>
5
+ <% column_header_id = scaffold_column_header_id(sort_params.merge(:column_name => scaffold_column.name)) %>
6
+ <th id="<%= column_header_id %>" <%= "class=\"sorted #{current_sort_direction(params)}\"" if scaffold_column.name == current_sort(params) %>>
7
+ <% if scaffold_column.sortable? %>
8
+ <% href = url_for(sort_params) %>
9
+ <%= link_to_remote scaffold_column.label,
10
+ { :url => sort_params,
11
+ :before => "addAjaxTableUrlToHistory('#{href}')",
12
+ :loading => "Element.addClassName('#{column_header_id}','loading');",
13
+ :update => scaffold_content_id(sort_params) },
14
+ { :href => href } %>
15
+ <% else %>
16
+ <p><%= scaffold_column.label %></p>
17
+ <% end %>
18
+ </th>
19
+ <% end %>
20
+ <th></th>
@@ -0,0 +1,10 @@
1
+ <tr class="ajax-scaffold-totals">
2
+ <% for scaffold_column in @scaffold_columns %>
3
+ <td>
4
+ <% if show_totals_for_column?(scaffold_column.name) %><p><%= scaffold_column.total %></p><% else %>&nbsp;<% end %>
5
+ </td>
6
+ <% end %>
7
+ <% unless @no_edit && @no_delete %>
8
+ <td class="actions">&nbsp;</td>
9
+ <% end %>
10
+ </tr>
@@ -0,0 +1,5 @@
1
+ <fieldset>
2
+ <div class="row">
3
+ <%= all_input_tags(scaffold_class, scaffold_name, { :exclude => %w(created_on created_at updated_on updated_at) }) %>
4
+ </div>
5
+ </fieldset>
@@ -0,0 +1,5 @@
1
+ <%= render :partial => scaffold_partial_path('messages') %>
2
+
3
+ <% unless eval("@"+params[:scaffold_id]+".nil?") %>
4
+ <%= error_messages_for "#{params[:scaffold_id]}" %>
5
+ <% end %>
@@ -0,0 +1,10 @@
1
+ <% for name in [:info, :warning, :error] %>
2
+ <% if @flash[name] %>
3
+ <p class="<%= "#{name}-message message" %>" >
4
+ <%= @flash[name] %>
5
+ <% if request.xhr? %>
6
+ <a href="#" onclick="Element.remove(this.parentNode); return false;">Close</a>
7
+ <% end %>
8
+ </p>
9
+ <% end %>
10
+ <% end %>
@@ -0,0 +1,40 @@
1
+ <% if not request.xhr? %>
2
+ <table class="ajax-scaffold" cellpadding="0" cellspacing="0">
3
+ <tbody>
4
+ <% end %>
5
+ <tr id="<%= element_row_id(@options) %>" <%= "style=\"display:none;\"" if request.xhr? %>>
6
+ <td id="<%= element_cell_id(@options) %>" class="<%= @options[:action] %>" colspan="<%= num_columns %>">
7
+
8
+ <%= form_remote_tag :url => @options.merge(:controller => @scaffold_controller),
9
+ :loading => "Element.show('#{loading_indicator_id(@options)}'); Form.disable('#{element_form_id(@options)}');",
10
+ :html => { :href => url_for(@options.merge(:controller => @scaffold_controller)),
11
+ :id => element_form_id(@options) } %>
12
+
13
+ <%= hidden_field_tag "scaffold_id", @options[:scaffold_id] %>
14
+
15
+ <h4><%= Inflector.titleize(@options[:action]) %> <%= @scaffold_singular_name.titleize if @suffix.empty? %></h4>
16
+
17
+ <% if request.xhr? %>
18
+ <div id="<%= element_messages_id(@options) %>" class="messages-container"></div>
19
+ <% else %>
20
+ <%= render :partial => scaffold_partial_path('form_messages') %>
21
+ <% end %>
22
+
23
+ <%= render :partial => scaffold_partial_path('form'), :locals => { :scaffold_class => @scaffold_class, :scaffold_name => @scaffold_singular_name } %>
24
+
25
+ <p class="form-footer">
26
+ <%= submit_tag Inflector.titleize(@options[:action]), :class => "submit" %>
27
+ <% cancel_params = @options.merge(:controller => @scaffold_controller, :action => 'cancel'+@suffix, :referring_action => @options[:action]) %>
28
+ <%= link_to_remote "Cancel",
29
+ { :url => cancel_params,
30
+ :loading => "Element.show('#{loading_indicator_id(@options)}');" },
31
+ { :href => url_for(cancel_params) } %>
32
+ <%= loading_indicator_tag @options %>
33
+ </p>
34
+ <%= end_form_tag %>
35
+ </td>
36
+ </tr>
37
+ <% if not request.xhr? %>
38
+ </tbody>
39
+ </table>
40
+ <% end %>
@@ -0,0 +1,26 @@
1
+ <% pagination_params = params.merge(:controller => @scaffold_controller, :action => 'update_'+@prefix+'table') %>
2
+ <% indicator_params = pagination_params.merge(:action => 'pagination') %>
3
+ <% previous_url = url_for(pagination_params.merge(:page => paginator.current.previous)) %>
4
+ <% next_url = url_for(pagination_params.merge(:page => paginator.current.next)) %>
5
+ <% current_url = url_for(pagination_params.merge(:page => paginator.current)) %>
6
+
7
+ <script>
8
+ addAjaxTableUrlToHistory('<%= current_url %>')
9
+ </script>
10
+
11
+ <%= loading_indicator_tag indicator_params %>
12
+ <%= link_to_remote('Previous',
13
+ { :url => pagination_params.merge(:page => paginator.current.previous),
14
+ :before => "addAjaxTableUrlToHistory('#{previous_url}')",
15
+ :loading => "Element.show('#{loading_indicator_id(indicator_params)}');",
16
+ :update => scaffold_content_id(pagination_params) },
17
+ { :href => previous_url,
18
+ :class => "previous"}) if @paginator.current.previous %>
19
+ <%= pagination_ajax_links @paginator, pagination_params %>
20
+ <%= link_to_remote('Next',
21
+ { :url => pagination_params.merge(:page => paginator.current.next),
22
+ :before => "addAjaxTableUrlToHistory('#{next_url}')",
23
+ :loading => "Element.show('#{loading_indicator_id(indicator_params)}');",
24
+ :update => scaffold_content_id(pagination_params) },
25
+ { :href => next_url,
26
+ :class => "next"}) if @paginator.current.next %>
@@ -0,0 +1,46 @@
1
+ <% # The following is used when the browser doesn't have javascript enabled %>
2
+ <% eval("#{Inflector.underscore(@scaffold_class)} = row") %>
3
+ <% classAttr = cycle("", "class=\"even\"") %>
4
+ <% @options = params.merge(:controller => @scaffold_controller, :action => "view", :id => row.send("#{@scaffold_class.primary_key}")) %>
5
+
6
+ <tr <%= classAttr %> id="<%= element_row_id(@options) %>" <%= "style=\"display: none;\"" if hidden %>>
7
+ <% for scaffold_column in @scaffold_columns %>
8
+ <% column_value = eval(scaffold_column.eval) rescue nil %>
9
+ <% if show_totals_for_column?(scaffold_column.name) %>
10
+ <% scaffold_column.add_to_total(column_value) %>
11
+ <% end %>
12
+ <td class="<%= column_class(scaffold_column.name, column_value, current_sort(params), scaffold_column.class_name) %>" >
13
+ <%= format_column(column_value, scaffold_column.sanitize?) %>
14
+ </td>
15
+ <% end %>
16
+ <% unless @no_edit && @no_delete %>
17
+ <td class="actions">
18
+ <table cellpadding="0" cellspacing="0">
19
+ <tr>
20
+ <td class="indicator-container">
21
+ <%= loading_indicator_tag(@options) %>
22
+ </td>
23
+ <% unless @no_edit %>
24
+ <td>
25
+ <% edit_options = @options.merge(:action => 'edit'+@suffix) %>
26
+ <%= link_to_remote "Edit",
27
+ { :url => edit_options,
28
+ :loading => "Element.show('#{loading_indicator_id(@options)}');" },
29
+ { :href => url_for(edit_options) } %>
30
+ </td>
31
+ <% end %>
32
+ <% unless @no_delete %>
33
+ <td>
34
+ <% delete_options = @options.merge(:action => 'destroy'+@suffix) %>
35
+ <%= link_to_remote "Delete",
36
+ { :url => delete_options,
37
+ :confirm => 'Are you sure?',
38
+ :loading => "Element.show('#{loading_indicator_id(@options)}');" },
39
+ { :href => url_for( delete_options ) } %>
40
+ </td>
41
+ <% end %>
42
+ </tr>
43
+ </table>
44
+ </td>
45
+ <% end %>
46
+ </tr>
@@ -0,0 +1,12 @@
1
+ @options = { :scaffold_id => params[:scaffold_id], :action => params[:referring_action], :id => params[:id] }
2
+
3
+ if @successful
4
+ page.remove element_row_id(@options)
5
+ if @options[:action] == "update"+@suffix
6
+ page.show element_row_id(@options.merge(:action => "view"))
7
+ elsif @options[:action] == "create"+@suffix
8
+ page << "AjaxScaffold.displayMessageIfEmpty('#{scaffold_tbody_id(@options)}','#{empty_message_id(@options)}');"
9
+ end
10
+ else
11
+ page.hide loading_indicator_id(@options)
12
+ end
@@ -0,0 +1,20 @@
1
+ @options = { :scaffold_id => params[:scaffold_id], :action => "view" }
2
+ @create_options = @options.merge(:action => "create"+@suffix, :id => params[:id])
3
+
4
+ if @successful
5
+ @options[:id] = eval("@"+params[:scaffold_id]+".id")
6
+ page.insert_html :bottom, scaffold_tbody_id(@options), :partial => scaffold_partial_path('row'),
7
+ :object => eval("@"+params[:scaffold_id]), :locals => { :hidden => true }
8
+ page << "new TableRow.MoveAfter('#{element_row_id(@create_options)}', '#{element_row_id(@options)}');"
9
+ page.remove element_row_id(@create_options)
10
+ page.show element_row_id(@options)
11
+ page << "AjaxScaffold.stripe('#{scaffold_tbody_id(@options)}');"
12
+ page << "AjaxScaffold.removeSortClasses('#{@options[:scaffold_id]}');"
13
+ page.replace_html scaffold_messages_id(@options), :partial => scaffold_partial_path('messages')
14
+ else
15
+ page.replace_html element_messages_id(@create_options), :partial => scaffold_partial_path('form_messages')
16
+ page << "Form.enable('#{element_form_id(@create_options)}');"
17
+ page.hide loading_indicator_id(@create_options)
18
+ end
19
+
20
+
@@ -0,0 +1,11 @@
1
+ @options = { :scaffold_id => params[:scaffold_id], :action => "view", :id => params[:id] }
2
+
3
+ if @successful
4
+ page.remove element_row_id(@options)
5
+ page << "AjaxScaffold.displayMessageIfEmpty('#{scaffold_tbody_id(@options)}','#{empty_message_id(@options)}');"
6
+ page << "AjaxScaffold.stripe('#{scaffold_tbody_id(@options)}');"
7
+ else
8
+ page.hide loading_indicator_id(@options)
9
+ end
10
+
11
+ page.replace_html scaffold_messages_id(@options), :partial => scaffold_partial_path('messages')
@@ -0,0 +1,16 @@
1
+ @options = { :scaffold_id => params[:scaffold_id], :action => "update"+@suffix, :id => params[:id] }
2
+ @view_options = @options.merge(:action => "view")
3
+
4
+ if @successful
5
+ page.hide element_row_id(@view_options)
6
+ page.insert_html :bottom, scaffold_tbody_id(@options), :partial => scaffold_partial_path('new_edit')
7
+ page << "new TableRow.MoveAfter('#{element_row_id(@view_options)}', '#{element_row_id(@options)}');"
8
+ page.show element_row_id(@options)
9
+ page << "Form.focusFirstElement('#{element_form_id(@options)}');"
10
+ page.visual_effect :highlight, element_cell_id(@options)
11
+ page.replace_html element_messages_id(@options), :partial => scaffold_partial_path('form_messages')
12
+ else
13
+ page.replace_html scaffold_messages_id(@options), :partial => scaffold_partial_path('messages')
14
+ end
15
+
16
+ page.hide loading_indicator_id(@view_options)
@@ -0,0 +1 @@
1
+ <%= render_component :controller => @scaffold_controller, :action => @prefix+'table', :params => params %>
@@ -0,0 +1,14 @@
1
+ @options = { :scaffold_id => params[:scaffold_id], :action => "create"+@suffix, :id => generate_temporary_id }
2
+ @new_options = @options.merge(:action => "new"+@suffix, :id => nil)
3
+
4
+ if @successful
5
+ page.insert_html :top, scaffold_tbody_id(@options), :partial => scaffold_partial_path('new_edit')
6
+ page.show element_row_id(@options)
7
+ page << "Form.focusFirstElement('#{element_form_id(@options)}');"
8
+ page.visual_effect :highlight, element_cell_id(@options)
9
+ page.replace_html element_messages_id(@options), :partial => scaffold_partial_path('form_messages')
10
+ else
11
+ page.replace_html scaffold_messages_id(@options), :partial => scaffold_partial_path('messages')
12
+ end
13
+
14
+ page.hide loading_indicator_id(@new_options), empty_message_id(@options)
@@ -0,0 +1,63 @@
1
+ <% unless @width.empty? %>
2
+ <style>
3
+ .ajax-scaffold-<%= params[:scaffold_id] %> {
4
+ width: <%= @width %>;
5
+ }
6
+ </style>
7
+ <% end %>
8
+
9
+ <% if @show_wrapper %>
10
+ <div id="<%= params[:scaffold_id] %>" class="ajax-scaffold ajax-scaffold-<%= params[:scaffold_id] %>">
11
+ <div id="<%= scaffold_content_id(params) %>">
12
+ <% end %>
13
+ <div class="ajax-scaffold-header">
14
+ <% unless @no_create %>
15
+ <div class="actions">
16
+ <% new_params = params.merge(:controller => @scaffold_controller, :action => "new"+@suffix) %>
17
+ <%= loading_indicator_tag(new_params) %>
18
+ <%= link_to_remote "Create New",
19
+ { :url => new_params,
20
+ :loading => "Element.show('#{loading_indicator_id(new_params)}');" },
21
+ { :href => url_for(new_params), :class => "create" } %>
22
+ </div>
23
+ <% end %>
24
+ <h2><%= @scaffold_plural_name.titleize %></h2>
25
+ </div>
26
+ <table cellpadding="0" cellspacing="0">
27
+ <thead>
28
+ <tr>
29
+ <%= render :partial => scaffold_partial_path('column_headings') %>
30
+ </tr>
31
+ </thead>
32
+ <tbody>
33
+ <tr>
34
+ <td colspan="<%= num_columns %>" class="messages-container">
35
+ <div id="<%= scaffold_messages_id(params) %>">
36
+ <%= render :partial => scaffold_partial_path('messages') %>
37
+ </div>
38
+ <p id="<%= empty_message_id(params) %>" class="empty-message" <%= " style=\"display:none;\" " if !@collection.empty? %>>
39
+ No Entries
40
+ </p>
41
+ </td>
42
+ </tr>
43
+ </tbody>
44
+ <tbody id="<%= scaffold_tbody_id(params) %>">
45
+ <% if !@collection.empty? %>
46
+ <%= render :partial => scaffold_partial_path('row'), :collection => @collection, :locals => { :hidden => false } %>
47
+ <% if show_column_totals? %>
48
+ <%= render :partial => scaffold_partial_path('column_totals') %>
49
+ <% end %>
50
+ <% end %>
51
+ </tbody>
52
+ </table>
53
+ <div class="ajax-scaffold-footer">
54
+ <%= render :partial => scaffold_partial_path('pagination_links'), :locals => { :paginator => @paginator } %>
55
+ </div>
56
+ <% if @show_wrapper %>
57
+ </div>
58
+ </div>
59
+
60
+ <script type="text/javascript">
61
+ Rico.Corner.round('<%= params[:scaffold_id] %>', {color: '#005CB8', bgColor: '#fff', compact: true});
62
+ </script>
63
+ <% end %>
@@ -0,0 +1,19 @@
1
+ @options = { :scaffold_id => params[:scaffold_id], :action => "view", :id => params[:id] }
2
+ @update_options = @options.merge(:action => "update"+@suffix)
3
+
4
+ if @successful
5
+ page.remove element_row_id(@options)
6
+ page.insert_html :bottom, scaffold_tbody_id(@options), :partial => scaffold_partial_path('row'),
7
+ :object => eval("@"+params[:scaffold_id]), :locals => { :hidden => true }
8
+ page << "new TableRow.MoveAfter('#{element_row_id(@update_options)}', '#{element_row_id(@options)}');"
9
+ page.remove element_row_id(@update_options)
10
+ page.show element_row_id(@options)
11
+ page << "AjaxScaffold.stripe('#{scaffold_tbody_id(@options)}');"
12
+ page << "AjaxScaffold.removeSortClasses('#{@options[:scaffold_id]}');"
13
+ page.replace_html scaffold_messages_id(@options), :partial => scaffold_partial_path('messages')
14
+ else
15
+ page.replace_html element_messages_id(@update_options), :partial => scaffold_partial_path('form_messages')
16
+ page << "Form.enable('#{element_form_id(@update_options)}');"
17
+ page.hide loading_indicator_id(@update_options)
18
+ end
19
+
data/init.rb ADDED
@@ -0,0 +1,27 @@
1
+ # Include hook code here
2
+ require 'ajax_scaffold_plugin'
3
+
4
+ ActionController::Base.send(:include, AjaxScaffold)
5
+ ActionView::Base.send(:include, AjaxScaffold::Helper)
6
+
7
+ # copy all the files over to the main rails app, want to avoid .svn
8
+ source = File.join(directory,'/app/views/ajax_scaffold')
9
+ dest = File.join(RAILS_ROOT, '/app/views/ajax_scaffold')
10
+ FileUtils.mkdir(dest) unless File.exist?(dest)
11
+ FileUtils.cp_r(Dir.glob(source+'/*.*'), dest)
12
+
13
+ source = File.join(directory,'/public')
14
+ dest = RAILS_ROOT + '/public'
15
+ FileUtils.cp_r(Dir.glob(source+'/*.*'), dest)
16
+
17
+ source = File.join(directory,'/public/stylesheets')
18
+ dest = RAILS_ROOT + '/public/stylesheets'
19
+ FileUtils.cp_r(Dir.glob(source+'/*.*'), dest)
20
+
21
+ source = File.join(directory,'/public/javascripts')
22
+ dest = RAILS_ROOT + '/public/javascripts'
23
+ FileUtils.cp_r(Dir.glob(source+'/*.*'), dest)
24
+
25
+ source = File.join(directory,'/public/images')
26
+ dest = RAILS_ROOT + '/public/images'
27
+ FileUtils.cp_r(Dir.glob(source+'/*.*'), dest)
data/install.rb ADDED
@@ -0,0 +1 @@
1
+ # Install hook code here
@@ -0,0 +1,505 @@
1
+ require 'base/ajax_scaffold'
2
+
3
+ module AjaxScaffold # :nodoc:
4
+
5
+ class ScaffoldColumn
6
+ attr_reader :name, :eval, :sort_sql, :label, :class_name, :sort, :total
7
+
8
+ # Only options[:name] is required. It will infer the eval and sort values
9
+ # based on the given class.
10
+ def initialize(klass, options)
11
+ @name = options[:name]
12
+ @eval = options[:eval].nil? ? "#{Inflector.underscore(klass.to_s)}.#{@name}" : options[:eval]
13
+ @label = options[:label].nil? ? Inflector.titleize(@name) : options[:label]
14
+ @sortable = options[:sortable].nil? ? true : options[:sortable]
15
+ @sort_sql = options[:sort_sql].nil? ? "#{klass.table_name}.#{@name}" : options[:sort_sql] unless !@sortable
16
+ @class_name = options[:class_name].nil? ? "" : options[:class_name]
17
+ @sanitize = options[:sanitize].nil? ? true : options[:sanitize]
18
+
19
+ #Plugin Specific Below
20
+ @sort = options[:sort]
21
+ @total = 0
22
+ end
23
+
24
+ def add_to_total(value)
25
+ @total += value.to_f
26
+ end
27
+ end
28
+
29
+ module Common
30
+ def current_page(params)
31
+ session[params[:scaffold_id]][:page]
32
+ end
33
+ end
34
+
35
+ # extend the class that include this with the methods in ClassMethods
36
+ def self.included(base)
37
+ super
38
+ base.extend(ClassMethods)
39
+ end
40
+
41
+ # Start Helper module
42
+ module Helper
43
+ include AjaxScaffold::Common
44
+
45
+ # default html for an input tag
46
+ def default_input_block
47
+ Proc.new { |record, column| "<div class=\"form-element\">\n "+
48
+ "<label for=\"#{record}_#{column.name}\">#{column.human_name}</label>\n "+
49
+ "#{input(record, column.name, { :class => 'text-input' } )}\n</div>\n" }
50
+ end
51
+
52
+ # create input tags for each field in a model
53
+ def all_input_tags(record, record_name, options)
54
+ input_block = options[:input_block] || default_input_block
55
+
56
+ if !options[:exclude].blank?
57
+ filtered_content_columns = record.content_columns.reject { |column| options[:exclude].include?(column.name) }
58
+ else
59
+ filtered_content_columns = record.content_columns
60
+ end
61
+
62
+ filtered_content_columns.collect{ |column| input_block.call(record_name, column) }.join("\n")
63
+ end
64
+ end
65
+
66
+ module Controller
67
+
68
+ def sort_collection_by_method(collection, column, order)
69
+ # sort in place (sort!) using block
70
+ collection.sort!{|line1, line2| sort_lines_by_column(line1, line2, column, order)}
71
+ end
72
+
73
+ def sort_lines_by_column(line1, line2, column, sort_order)
74
+ # Compare each lines column values with <=>
75
+ value1 = eval_method_string(line1, column)
76
+ value2 = eval_method_string(line2, column)
77
+ if (value1.nil?)
78
+ res = -1
79
+ elsif (value2.nil?)
80
+ res = 1
81
+ else
82
+ res = (value1 != nil ? value1 : "") <=> (value2 != nil ? value2 : "")
83
+ end
84
+ # Reverse result if not asc
85
+ sort_order == 'asc' ? res : res * -1
86
+ end
87
+
88
+ def eval_method_string(object, method_str)
89
+ # need to eval each section of the method string as this could return nil
90
+ # at any point.
91
+ methods = method_str.split('.')
92
+ methods.each do |m|
93
+ object = eval("object.#{m}") rescue nil
94
+ return object if object.nil?
95
+ end
96
+ object
97
+ end
98
+
99
+ end
100
+
101
+ module Helper
102
+ include AjaxScaffold::Common
103
+
104
+ def show_totals_for_column?(column)
105
+ @scaffold_column_totals.include?(column)
106
+ end
107
+
108
+ def show_column_totals?
109
+ @scaffold_column_totals.size > 0
110
+ end
111
+
112
+ # return the base path to search for a partial
113
+ def scaffold_partial_path(partial)
114
+ @scaffold_plural_name+'/'+partial
115
+ end
116
+
117
+ # number of columns in a table
118
+ def num_columns
119
+ @scaffold_columns.length + 1
120
+ end
121
+
122
+ end
123
+
124
+ module ClassMethods
125
+
126
+ # Adds a swath of actions to the controller.
127
+ def ajax_scaffold(model_id, options = {})
128
+ options.assert_valid_keys(:class_name, :except, :rows_per_page, :suffix, :totals, :width, :rel_width)
129
+
130
+ # set up a few variables for use in the code generation
131
+ singular_name = model_id.to_s
132
+ class_name = options[:class_name] || singular_name.camelize
133
+ plural_name = singular_name.pluralize
134
+ rows_per_page = options[:rows_per_page] || 25
135
+ suffix = options[:suffix] ? "_#{singular_name}" : ""
136
+ prefix = options[:suffix] ? "#{plural_name}_" : ""
137
+ totals = options[:totals].nil? ? nil : options[:totals].join(',').to_s
138
+ width = options[:width].nil? ? (options[:rel_width].nil? ? nil : options[:rel_width]*100 ) : options[:width]
139
+
140
+ if (!width.nil?)
141
+ dimensions = options[:width].nil? ? "%" : "px"
142
+ width = width.to_s + dimensions
143
+ end
144
+
145
+ no_create, no_edit, no_delete = false, false, false
146
+ if (options[:except])
147
+ no_create = options[:except].include?('create')
148
+ no_edit = options[:except].include?('edit')
149
+ no_delete = options[:except].include?('delete')
150
+ end
151
+
152
+ module_eval <<-"end_eval", __FILE__, __LINE__
153
+ include AjaxScaffold::Common
154
+ include AjaxScaffold::Controller
155
+
156
+ verify :method => :post, :only => [ :destroy#{suffix}, :create#{suffix}, :update#{suffix} ],
157
+ :redirect_to => { :action => :#{prefix}table }
158
+
159
+ after_filter :clear_flashes
160
+
161
+ @@#{prefix}scaffold_columns = nil
162
+ @@#{prefix}scaffold_total_columns = nil
163
+ @@#{prefix}scaffold_columns_hash = nil
164
+
165
+ def #{prefix}scaffold_total_columns
166
+ csv_array = "#{totals}".length >= 1 ? CSV.parse_line("#{totals}") : []
167
+ @@#{prefix}scaffold_total_columns ||= csv_array
168
+ end
169
+
170
+ def #{prefix}scaffold_columns
171
+ @@#{prefix}scaffold_columns ||= build_#{prefix}scaffold_columns
172
+ end
173
+
174
+ def #{prefix}scaffold_columns_hash
175
+ @@#{prefix}scaffold_columns_hash ||= build_#{prefix}scaffold_columns_hash
176
+ end
177
+
178
+ def #{prefix}table
179
+ self.#{prefix}table_setup
180
+
181
+ render#{suffix}_template(:action => 'table')
182
+ end
183
+
184
+ def #{prefix}table_setup
185
+ update_params :default_scaffold_id => "#{singular_name}", :default_sort => nil, :default_sort_direction => default_#{prefix}sort_direction
186
+
187
+ @show_wrapper = true if @show_wrapper.nil?
188
+ @width = "#{width}"
189
+ sort_sql = #{prefix}scaffold_columns_hash[current_sort(params)].sort_sql rescue default_#{prefix}sort
190
+ sort = #{prefix}scaffold_columns_hash[current_sort(params)].sort rescue nil
191
+ sort_direction = current_sort_direction(params)
192
+
193
+ klass = Inflector.camelize("#{singular_name}").constantize
194
+ order = sort.nil? ? sort_sql : sort
195
+ order += " " + sort_direction
196
+ options = { :order => order,
197
+ :conditions => conditions_for_#{plural_name}_collection,
198
+ :direction => current_sort_direction(params),
199
+ :per_page => #{rows_per_page} }
200
+
201
+ count = count_#{plural_name}_collection(klass, options)
202
+ @paginator = Paginator.new(self, count, #{rows_per_page}, current_page(params))
203
+
204
+ if (sort.nil?)
205
+ @collection = page_and_sort_#{plural_name}_collection(klass, options, @paginator)
206
+ else
207
+ @collection = page_and_sort_#{plural_name}_collection_with_method(klass, options, @paginator)
208
+ end
209
+ end
210
+
211
+ # if multiple tables (suffix) then return to index (can be changed by overriding)
212
+ # if single table return to list
213
+ def #{prefix}return_to_main
214
+ action = "#{suffix}" == "" ? "#{prefix}list" : "index"
215
+ redirect_to :action => action
216
+ end
217
+
218
+ def update_#{prefix}table
219
+ @show_wrapper = false # don't show the outer wrapper elements if we are just updating an existing scaffold
220
+ if request.xhr?
221
+ #{prefix}table_setup
222
+ render#{suffix}_template(:action => 'table')
223
+ else
224
+ #{prefix}return_to_main
225
+ end
226
+ end
227
+
228
+ def #{prefix}list
229
+ render#{suffix}_template(:action => 'list.rhtml', :layout => true)
230
+ end
231
+ end_eval
232
+
233
+ if(suffix=="")
234
+ module_eval <<-"end_eval", __FILE__, __LINE__
235
+ def index
236
+ redirect_to(:action => :list)
237
+ end
238
+ end_eval
239
+ end
240
+
241
+ # now only create these methods if not excluded
242
+ unless (no_create)
243
+ module_eval <<-"end_eval", __FILE__, __LINE__
244
+ def new#{suffix}
245
+ @#{singular_name} = #{class_name}.new
246
+ @successful = true
247
+
248
+ return render#{suffix}_template(:action => 'new.rjs') if request.xhr?
249
+
250
+ # Javascript disabled fallback
251
+ if @successful
252
+ @options = { :action => "create" }
253
+ render#{suffix}_template(:action => "_new_edit.rhtml", :layout => true)
254
+ else
255
+ #{prefix}return_to_main
256
+ end
257
+ end
258
+
259
+ def create#{suffix}
260
+ begin
261
+ do_create#{suffix}
262
+ rescue
263
+ flash[:error], @successful = $!.to_s, false
264
+ end
265
+
266
+ return render#{suffix}_template(:action => 'create.rjs') if request.xhr?
267
+
268
+ if @successful
269
+ #{prefix}return_to_main
270
+ else
271
+ @options = { :scaffold_id => params[:scaffold_id], :action => "create" }
272
+ render#{suffix}_template(:action => "_new_edit.rhtml", :layout => true)
273
+ end
274
+ end
275
+
276
+ def do_create#{suffix}
277
+ @#{singular_name} = #{class_name}.new(params[:#{singular_name}])
278
+ @successful = @#{singular_name}.save
279
+ end
280
+ end_eval
281
+ end
282
+
283
+ unless (no_edit)
284
+ module_eval <<-"end_eval", __FILE__, __LINE__
285
+ def edit#{suffix}
286
+ begin
287
+ @#{singular_name} = #{class_name}.find(params[:id])
288
+ @successful = !@#{singular_name}.nil?
289
+ rescue
290
+ flash[:error], @successful = $!.to_s, false
291
+ end
292
+
293
+ return render#{suffix}_template(:action => 'edit.rjs') if request.xhr?
294
+
295
+ if @successful
296
+ @options = { :scaffold_id => params[:scaffold_id], :action => "update", :id => params[:id] }
297
+ render#{suffix}_template(:action => "_new_edit.rhtml", :layout => true)
298
+ else
299
+ #{prefix}return_to_main
300
+ end
301
+ end
302
+
303
+ def update#{suffix}
304
+ begin
305
+ do_update#{suffix}
306
+ rescue
307
+ flash[:error], @successful = $!.to_s, false
308
+ end
309
+
310
+ return render#{suffix}_template(:action => 'update.rjs') if request.xhr?
311
+
312
+ if @successful
313
+ #{prefix}return_to_main
314
+ else
315
+ @options = { :action => "update" }
316
+ render#{suffix}_template(:action => "_new_edit.rhtml", :layout => true)
317
+ end
318
+ end
319
+
320
+ def do_update#{suffix}
321
+ @#{singular_name} = #{class_name}.find(params[:id])
322
+ @successful = @#{singular_name}.update_attributes(params[:#{singular_name}])
323
+ end
324
+ end_eval
325
+ end
326
+
327
+ unless (no_delete)
328
+ module_eval <<-"end_eval", __FILE__, __LINE__
329
+ def destroy#{suffix}
330
+ begin
331
+ @successful = #{class_name}.find(params[:id]).destroy
332
+ rescue
333
+ flash[:error], @successful = $!.to_s, false
334
+ end
335
+
336
+ return render#{suffix}_template(:action => 'destroy.rjs') if request.xhr?
337
+
338
+ # Javascript disabled fallback
339
+ #{prefix}return_to_main
340
+ end
341
+ end_eval
342
+ end
343
+
344
+ module_eval <<-"end_eval", __FILE__, __LINE__
345
+ def cancel#{suffix}
346
+ @successful = true
347
+
348
+ return render#{suffix}_template(:action => 'cancel.rjs') if request.xhr?
349
+
350
+ # Javascript disabled fallback
351
+ #{prefix}return_to_main
352
+ end
353
+
354
+ protected
355
+ # Override this for custom selection conditions
356
+ def conditions_for_#{plural_name}_collection
357
+ nil
358
+ end
359
+
360
+ # Override to change the default sort from plural_name.id
361
+ def default_#{prefix}sort
362
+ "#{plural_name}.id"
363
+ end
364
+
365
+ # Override to change the default sort direction
366
+ def default_#{prefix}sort_direction
367
+ "asc"
368
+ end
369
+
370
+ # Returns the total number of items in the collection
371
+ def count_#{plural_name}_collection(model, options)
372
+ count_collection_for_pagination(model, options)
373
+ end
374
+
375
+ # Per model collection method, using sort_collection_by_method
376
+ def page_and_sort_#{plural_name}_collection_with_method(model, options, paginator)
377
+ # call: sort_collection_by_method(collection, column, order)
378
+ order_parts = options[:order].split(' ')
379
+ collection = model.find(:all, :conditions => options[:conditions],
380
+ :joins => options[:join] || options[:joins],
381
+ :include => options[:include],
382
+ :select => options[:select])
383
+
384
+ sort_collection_by_method(collection, order_parts[0], order_parts[1]).slice(paginator.current.offset,options[:per_page])
385
+ end
386
+
387
+ # Per model collection method delegates to Rails as default
388
+ def page_and_sort_#{plural_name}_collection(model, options, paginator)
389
+ find_collection_for_pagination(model, options, paginator)
390
+ end
391
+
392
+ private
393
+ def render#{suffix}_template( options = {} )
394
+ @scaffold_columns ||= #{prefix}scaffold_columns
395
+ @scaffold_column_totals = #{prefix}scaffold_total_columns
396
+ @scaffold_class = #{class_name}
397
+ @scaffold_singular_name, @scaffold_plural_name = "#{singular_name}", "#{plural_name}"
398
+ @scaffold_controller = "/" + self.class.to_s.sub(/Controller/, "").underscore
399
+ @no_create = #{no_create}
400
+ @no_edit = #{no_edit}
401
+ @no_delete = #{no_delete}
402
+ @suffix = "#{suffix}"
403
+ @prefix = "#{prefix}"
404
+
405
+ # check for template in following order:
406
+ # views/scaffold_class/template
407
+ # views/controller_class/template
408
+ # views/ajax_scaffold/template
409
+
410
+ action = options[:action]
411
+ layout = options[:layout] || false
412
+ if template_exists?("#{plural_name}/\#{action}")
413
+ render(:action => "../#{plural_name}/\#{action}", :layout => layout)
414
+
415
+ elsif template_exists?("\#{self.class.controller_path}/\#{action}")
416
+ render(:action => action, :layout => layout)
417
+
418
+ else
419
+ render(:template => "ajax_scaffold/\#{action}", :layout => layout)
420
+ end
421
+ end
422
+
423
+ def build_#{prefix}scaffold_columns
424
+ scaffold_columns = Array.new
425
+ #{class_name}.content_columns.each do |column|
426
+ scaffold_columns << ScaffoldColumn.new(#{class_name}, { :name => column.name })
427
+ end
428
+ scaffold_columns
429
+ end
430
+
431
+ def build_#{prefix}scaffold_columns_hash
432
+ scaffold_columns_hash = Hash.new
433
+ #{prefix}scaffold_columns.each do |scaffold_column|
434
+ scaffold_columns_hash[scaffold_column.name] = scaffold_column
435
+ end
436
+ scaffold_columns_hash
437
+ end
438
+
439
+ end_eval
440
+
441
+ # end ajax_scaffold method
442
+ end
443
+ # end ClassMethods
444
+ end
445
+
446
+ end
447
+
448
+ # override render here to ensure that the correct partial is picked up
449
+ module ::ActionView #:nodoc:
450
+ class Base
451
+
452
+ # Renders the template present at <tt>template_path</tt> (relative to the template_root).
453
+ # The hash in <tt>local_assigns</tt> is made available as local variables.
454
+ def render(options = {}, old_local_assigns = {}, &block) #:nodoc:
455
+ if options.is_a?(String)
456
+ render_file(options, true, old_local_assigns)
457
+ elsif options == :update
458
+ update_page(&block)
459
+ elsif options.is_a?(Hash)
460
+ options[:locals] ||= {}
461
+ options[:use_full_path] = options[:use_full_path].nil? ? true : options[:use_full_path]
462
+
463
+ if options[:file]
464
+ render_file(options[:file], options[:use_full_path], options[:locals])
465
+ elsif options[:partial] && options[:collection]
466
+ partial = parse_partial_path(options[:partial])
467
+ render_partial_collection(partial, options[:collection], options[:spacer_template], options[:locals])
468
+ elsif options[:partial]
469
+ partial = parse_partial_path(options[:partial])
470
+ render_partial(partial, ActionView::Base::ObjectWrapper.new(options[:object]), options[:locals])
471
+ elsif options[:inline]
472
+ render_template(options[:type] || :rhtml, options[:inline], nil, options[:locals] || {})
473
+ end
474
+ end
475
+ end
476
+
477
+ def parse_partial_path(partial_path)
478
+ path, partial_name = partial_pieces(partial_path)
479
+
480
+ if partial_path.include?('/')
481
+ unless (erb_template_exists?("#{path}/_#{partial_name}"))
482
+ path, partial_name = partial_pieces(partial_name)
483
+ partial_path = "#{path}/#{partial_name}"
484
+ end
485
+ end
486
+
487
+ unless (erb_template_exists?("#{path}/_#{partial_name}"))
488
+ partial_path = "ajax_scaffold/#{partial_name}"
489
+ end
490
+
491
+ partial_path
492
+ end
493
+ end
494
+
495
+ module Helpers
496
+ module AssetTagHelper
497
+ # produces all the includes in one shot
498
+ def ajax_scaffold_includes
499
+ js = javascript_include_tag(:defaults, 'dhtml_history', 'rico_corner', 'ajax_scaffold')
500
+ css = stylesheet_link_tag('ajax_scaffold')
501
+ js + "\n" + css
502
+ end
503
+ end
504
+ end
505
+ end