acts_as_approvable 0.1.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. data/.gitignore +4 -0
  2. data/.rspec +2 -0
  3. data/.travis.yml +5 -0
  4. data/Appraisals +22 -0
  5. data/CHANGELOG +76 -0
  6. data/Gemfile +3 -0
  7. data/Gemfile.lock +84 -0
  8. data/MIT-LICENSE +2 -2
  9. data/README.md +146 -0
  10. data/Rakefile +90 -7
  11. data/TODO.md +30 -0
  12. data/VERSION +1 -0
  13. data/acts_as_approvable.gemspec +40 -0
  14. data/features/create_approval.feature +36 -0
  15. data/features/destroy_approval.feature +19 -0
  16. data/features/reset_approval.feature +13 -0
  17. data/features/step_definitions/cucumber_steps.rb +132 -0
  18. data/features/support/env.rb +14 -0
  19. data/features/support/large.txt +29943 -0
  20. data/features/support/second_large.txt +31798 -0
  21. data/features/update_approval.feature +48 -0
  22. data/gemfiles/Gemfile.ci +14 -0
  23. data/gemfiles/Gemfile.ci.lock +98 -0
  24. data/gemfiles/mysql2.gemfile +7 -0
  25. data/gemfiles/mysql2.gemfile.lock +86 -0
  26. data/gemfiles/rails2.gemfile +8 -0
  27. data/gemfiles/rails2.gemfile.lock +86 -0
  28. data/gemfiles/rails30.gemfile +9 -0
  29. data/gemfiles/rails30.gemfile.lock +124 -0
  30. data/gemfiles/rails31.gemfile +9 -0
  31. data/gemfiles/rails31.gemfile.lock +135 -0
  32. data/gemfiles/sqlite.gemfile +7 -0
  33. data/generators/acts_as_approvable/USAGE +3 -0
  34. data/generators/acts_as_approvable/acts_as_approvable_generator.rb +81 -0
  35. data/generators/acts_as_approvable/templates/approvals.js +71 -0
  36. data/generators/acts_as_approvable/templates/approvals_controller.rb +91 -0
  37. data/generators/acts_as_approvable/templates/create_approvals.rb +27 -0
  38. data/generators/acts_as_approvable/templates/initializer.rb +3 -0
  39. data/generators/acts_as_approvable/templates/jquery.form.js +101 -0
  40. data/generators/acts_as_approvable/templates/views/erb/_owner_select.html.erb +4 -0
  41. data/generators/acts_as_approvable/templates/views/erb/_table.html.erb +26 -0
  42. data/generators/acts_as_approvable/templates/views/erb/index.html.erb +17 -0
  43. data/generators/acts_as_approvable/templates/views/haml/_owner_select.html.haml +3 -0
  44. data/generators/acts_as_approvable/templates/views/haml/_table.html.haml +19 -0
  45. data/generators/acts_as_approvable/templates/views/haml/index.html.haml +15 -0
  46. data/init.rb +1 -0
  47. data/lib/acts_as_approvable.rb +96 -2
  48. data/lib/acts_as_approvable/approval.rb +205 -11
  49. data/lib/acts_as_approvable/error.rb +34 -0
  50. data/lib/acts_as_approvable/model.rb +60 -0
  51. data/lib/acts_as_approvable/model/class_methods.rb +63 -0
  52. data/lib/acts_as_approvable/model/create_instance_methods.rb +88 -0
  53. data/lib/acts_as_approvable/model/destroy_instance_methods.rb +38 -0
  54. data/lib/acts_as_approvable/model/instance_methods.rb +107 -0
  55. data/lib/acts_as_approvable/model/update_instance_methods.rb +61 -0
  56. data/lib/acts_as_approvable/ownership.rb +141 -0
  57. data/lib/acts_as_approvable/railtie.rb +7 -0
  58. data/lib/acts_as_approvable/version.rb +1 -8
  59. data/lib/generators/acts_as_approvable/USAGE +1 -0
  60. data/lib/generators/acts_as_approvable/acts_as_approvable_generator.rb +68 -0
  61. data/lib/generators/acts_as_approvable/base.rb +30 -0
  62. data/lib/generators/acts_as_approvable/templates/approvals.js +71 -0
  63. data/lib/generators/acts_as_approvable/templates/approvals_controller.rb +91 -0
  64. data/lib/generators/acts_as_approvable/templates/create_approvals.rb +27 -0
  65. data/lib/generators/acts_as_approvable/templates/jquery.form.js +101 -0
  66. data/lib/generators/erb/acts_as_approvable_generator.rb +33 -0
  67. data/lib/generators/erb/templates/_owner_select.html.erb +4 -0
  68. data/lib/generators/erb/templates/_table.html.erb +26 -0
  69. data/lib/generators/erb/templates/index.html.erb +17 -0
  70. data/lib/generators/haml/acts_as_approvable_generator.rb +33 -0
  71. data/lib/generators/haml/templates/_owner_select.html.haml +3 -0
  72. data/lib/generators/haml/templates/_table.html.haml +19 -0
  73. data/lib/generators/haml/templates/index.html.haml +15 -0
  74. data/lib/tasks/acts_as_approvable.rake +4 -0
  75. data/rails/init.rb +1 -0
  76. data/spec/acts_as_approvable/approval_spec.rb +614 -0
  77. data/spec/acts_as_approvable/model/class_methods_spec.rb +219 -0
  78. data/spec/acts_as_approvable/model/create_instance_methods_spec.rb +169 -0
  79. data/spec/acts_as_approvable/model/destroy_instance_methods_spec.rb +71 -0
  80. data/spec/acts_as_approvable/model/instance_methods_spec.rb +328 -0
  81. data/spec/acts_as_approvable/model/update_instance_methods_spec.rb +111 -0
  82. data/spec/acts_as_approvable/model_spec.rb +113 -0
  83. data/spec/acts_as_approvable/ownership/class_methods_spec.rb +134 -0
  84. data/spec/acts_as_approvable/ownership/instance_methods_spec.rb +32 -0
  85. data/spec/acts_as_approvable/ownership_spec.rb +52 -0
  86. data/spec/acts_as_approvable_spec.rb +31 -0
  87. data/spec/spec_helper.rb +51 -0
  88. data/spec/support/database.rb +49 -0
  89. data/spec/support/database.yml +12 -0
  90. data/spec/support/matchers.rb +87 -0
  91. data/spec/support/models.rb +67 -0
  92. data/spec/support/schema.rb +54 -0
  93. metadata +375 -58
  94. data/README.rdoc +0 -38
  95. data/lib/acts_as_approvable/approver.rb +0 -76
  96. data/lib/generators/acts_as_approvable/install_generator.rb +0 -28
  97. data/lib/generators/acts_as_approvable/templates/install.rb +0 -16
@@ -0,0 +1,7 @@
1
+ module ActsAsApprovable
2
+ class Railtie < Rails::Railtie
3
+ initializer 'acts_as_approvable.configure_rails_initialization' do |app|
4
+ ActiveRecord::Base.send :extend, ActsAsApprovable::Model
5
+ end
6
+ end
7
+ end
@@ -1,10 +1,3 @@
1
1
  module ActsAsApprovable
2
- module Version
3
- MAJOR = 0
4
- MINOR = 1
5
- TINY = 1
6
- PRE = nil
7
-
8
- STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
9
- end
2
+ VERSION = File.read(File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'VERSION'))).chomp
10
3
  end
@@ -0,0 +1 @@
1
+ Generates ApprovalsController, a migration the create the Approval table, and an initializer for the plugin.
@@ -0,0 +1,68 @@
1
+ require 'rails/generators/active_record'
2
+ require 'generators/acts_as_approvable/base'
3
+
4
+
5
+ class ActsAsApprovableGenerator < Rails::Generators::Base
6
+ include ActsAsApprovable::Generators::Base
7
+
8
+ source_root File.expand_path('../templates', __FILE__)
9
+
10
+ class_option :base, :type => :string, :default => 'ApplicationController', :desc => 'Base class for the ApprovalsController'
11
+ class_option :owner, :type => :string, :optional => true, :desc => 'Model that can own approvals'
12
+ class_option :scripts, :type => :boolean, :optional => true, :default => false
13
+
14
+ desc 'Generates ApprovalsController, a migration the create the Approval table, and an initializer for the plugin.'
15
+
16
+ def check_class_collisions
17
+ class_collisions '', 'ApprovalsController'
18
+ end
19
+
20
+ def create_controller_file
21
+ template 'approvals_controller.rb', File.join('app/controllers', 'approvals_controller.rb')
22
+ end
23
+
24
+ def create_migration_file
25
+ number = ActiveRecord::Generators::Base.next_migration_number('db/migrate')
26
+ template 'create_approvals.rb', "db/migrate/#{number}_create_approvals.rb"
27
+ end
28
+
29
+ def create_initializer_file
30
+ initializer('acts_as_approvable.rb') do
31
+ data = ''
32
+
33
+ if owner?
34
+ data << 'ActsAsApprovable::Ownership.configure'
35
+ data << "(:owner => #{owner})" if owner != 'User'
36
+ end
37
+
38
+ data << "\n"
39
+ end
40
+ end
41
+
42
+ def create_script_files
43
+ return unless scripts?
44
+
45
+ template 'jquery.form.js', 'public/javascripts/jquery.form.js'
46
+ template 'approvals.js', 'public/javascripts/approvals.js'
47
+ end
48
+
49
+ hook_for :template_engine
50
+
51
+ def add_routes
52
+ resource = []
53
+ resource << 'resources :approvals, :only => [:index] do'
54
+ resource << ' collection do'
55
+ resource << ' get \'index\''
56
+ resource << ' get \'history\''
57
+ resource << ' get \'mine\'' if owner?
58
+ resource << ' end'
59
+ resource << ' member do'
60
+ resource << ' post \'approve\''
61
+ resource << ' post \'reject\''
62
+ resource << ' post \'assign\'' if owner?
63
+ resource << ' end'
64
+ resource << ' end'
65
+
66
+ route(resource.join("\n"))
67
+ end
68
+ end
@@ -0,0 +1,30 @@
1
+ module ActsAsApprovable
2
+ module Generators
3
+ module Base
4
+ protected
5
+ def owner?
6
+ options[:owner].present?
7
+ end
8
+
9
+ def owner
10
+ options[:owner] == 'owner' ? 'User' : options[:owner]
11
+ end
12
+
13
+ def scripts?
14
+ options[:scripts]
15
+ end
16
+
17
+ def collection_actions
18
+ actions = [:index, :history]
19
+ actions << :mine if owner?
20
+ actions.map { |a| ":#{a}" }
21
+ end
22
+
23
+ def member_actions
24
+ actions = [:approve, :reject]
25
+ actions << :assign if owner?
26
+ actions.map { |a| ":#{a}" }
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,71 @@
1
+ $(function() {
2
+ // Assignment
3
+ $('form.assignment').each(function() {
4
+ var form = $(this),
5
+ spinner = $('.spinner', this);
6
+
7
+ form.ajaxForm({
8
+ dataType: 'json',
9
+ beforeSubmit: function() {
10
+ spinner.show();
11
+ },
12
+ success: function(data, status, jqx) {
13
+ spinner.hide();
14
+
15
+ if (!data.success) {
16
+ alert('There was an issue assigning this approval!')
17
+ }
18
+ },
19
+ error: function(jqx, status, error) {
20
+ spinner.hide();
21
+ alert('There was an issue assigning this approval!')
22
+ },
23
+ });
24
+
25
+ $('select', this).change(function() {
26
+ form.submit();
27
+ });
28
+ });
29
+
30
+ // Approval and Rejection
31
+ var actionLinks = $('td.actions a');
32
+
33
+ actionLinks.click(function() {
34
+ if ($(this).hasClass('disabled')) return false;
35
+
36
+ var verbing = ($(this).hasClass('approve') ? 'approving' : 'rejecting'),
37
+ row = $(this).parents('tr'),
38
+ settings = {
39
+ dataType: 'json',
40
+ url: $(this).attr('href'),
41
+ beforeSubmit: function() {
42
+ actionLinks.addClass('disabled');
43
+ },
44
+ success: function(data) {
45
+ actionLinks.removeClass('disabled');
46
+
47
+ if (!data.success) {
48
+ if (data.message) {
49
+ alert(data.message);
50
+ } else {
51
+ alert('There was an issue ' + verbing + ' the approval.');
52
+ }
53
+ } else {
54
+ row.fadeOut('fast', row.remove);
55
+ }
56
+ },
57
+ error: function() {
58
+ actionLinks.removeClass('disabled');
59
+ alert('There was an issue ' + verbing + ' the approval.');
60
+ }
61
+ };
62
+
63
+ if ($(this).hasClass('reject')) {
64
+ var reason = prompt('Reason for rejection');
65
+ if (reason) settings['data'] = {reason: reason};
66
+ }
67
+
68
+ $.ajax(settings);
69
+ return false;
70
+ });
71
+ });
@@ -0,0 +1,91 @@
1
+ class ApprovalsController < <%= options[:base] %>
2
+ before_filter :setup_conditions, :only => [<%= collection_actions.join(', ') %>]
3
+ before_filter :setup_partial, :only => [<%= collection_actions.join(', ') %>]
4
+ before_filter :find_approval, :only => [<%= member_actions.join(', ') %>]
5
+
6
+ def index
7
+ state = params[:state] =~ /^-?\d+$/ ? params[:state].to_i : Approval.enumerate_state('pending')
8
+ @conditions[:state] = state if state > -1
9
+
10
+ @approvals = Approval.all(:conditions => @conditions, :order => 'created_at ASC')
11
+ end
12
+
13
+ def history
14
+ @conditions[:state] = Approval.enumerate_states('approved', 'rejected')
15
+
16
+ @approvals = Approval.all(:conditions => @conditions, :order => 'created_at DESC')
17
+ render :index
18
+ end
19
+
20
+ <% if owner? %> def mine
21
+ @conditions[:owner_id] = current_user.id
22
+
23
+ @approvals = Approval.all(:conditions => @conditions, :order => 'created_at ASC')
24
+ render :index
25
+ end
26
+
27
+ def assign
28
+ json_wrapper do
29
+ if params[:approval][:owner_id].empty?
30
+ @approval.unassign
31
+ else
32
+ user = <%= options[:owner] %>.find(params[:approval][:owner_id])
33
+ @approval.assign(user)
34
+ end
35
+ end
36
+ end
37
+
38
+ <% end %> def approve
39
+ json_wrapper do
40
+ <% if owner? %> @approval.owner = current_user if respond_to?(:current_user)
41
+ <% end %> @approval.approve!
42
+ end
43
+ end
44
+
45
+ def reject
46
+ json_wrapper do
47
+ <% if owner? %> @approval.owner = current_user if respond_to?(:current_user)
48
+ <% end %> @approval.reject!(params[:reason])
49
+ end
50
+ end
51
+
52
+ private
53
+ def json_wrapper
54
+ json = {:success => false}
55
+
56
+ begin
57
+ json[:success] = yield
58
+ rescue ActsAsApprovable::Error => e
59
+ json[:message] = e.message
60
+ rescue
61
+ json[:message] = 'An unknown error occured'
62
+ end
63
+
64
+ render :json => json
65
+ end
66
+
67
+ def setup_conditions
68
+ @conditions ||= {}
69
+
70
+ <% if owner? %> @conditions[:owner_id] = params[:owner_id] if params[:owner_id].present?
71
+ <% end %> @conditions[:item_type] = params[:item_type] if params[:item_type].present?
72
+ end
73
+
74
+ # Check for the selected models partial, use the generic one if it doesn't exist
75
+ def setup_partial
76
+ @table_partial = @conditions.fetch(:item_type) { 'table' }
77
+
78
+ if @table_partial != 'table'
79
+ partial_path = Rails.root.join('app', 'views', 'approvals', "_#{@table_partial}.html.#{view_language}")
80
+ @table_partial = 'table' unless File.exist?(partial_path)
81
+ end
82
+ end
83
+
84
+ def find_approval
85
+ @approval = Approval.find(params[:id])
86
+ end
87
+
88
+ def view_language
89
+ ActsAsApprovable.view_language
90
+ end
91
+ end
@@ -0,0 +1,27 @@
1
+ class CreateApprovals < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :approvals do |t|
4
+ t.string :item_type, :null => false
5
+ t.integer :item_id, :null => false
6
+ t.string :event, :null => false
7
+ t.integer :state, :null => false, :default => 0
8
+ <% if options[:owner] %> t.integer :owner_id
9
+ <% end %> t.text :object, :limit => 16777216
10
+ t.text :original, :limit => 16777216
11
+ t.text :reason
12
+
13
+ t.timestamps
14
+ end
15
+
16
+ add_index :approvals, [:state, :event]
17
+ add_index :approvals, [:item_type, :item_id]
18
+ <% if options[:owner] %> add_index :approvals, [:owner_id]
19
+ <% end %> end
20
+
21
+ def self.down
22
+ remove_index :approvals, [:state, :event]
23
+ remove_index :approvals, [:item_type, :item_id]
24
+ <% if options[:owner] %> remove_index :approvals, [:owner_id]
25
+ <% end %> drop_table :approvals
26
+ end
27
+ end
@@ -0,0 +1,101 @@
1
+ ;(function($){$.fn.ajaxSubmit=function(options){if(!this.length){log('ajaxSubmit: skipping submit process - no element selected');return this;}
2
+ if(typeof options=='function'){options={success:options};}
3
+ var action=this.attr('action');var url=(typeof action==='string')?$.trim(action):'';url=url||window.location.href||'';if(url){url=(url.match(/^([^#]+)/)||[])[1];}
4
+ options=$.extend(true,{url:url,success:$.ajaxSettings.success,type:this[0].getAttribute('method')||'GET',iframeSrc:/^https/i.test(window.location.href||'')?'javascript:false':'about:blank'},options);var veto={};this.trigger('form-pre-serialize',[this,options,veto]);if(veto.veto){log('ajaxSubmit: submit vetoed via form-pre-serialize trigger');return this;}
5
+ if(options.beforeSerialize&&options.beforeSerialize(this,options)===false){log('ajaxSubmit: submit aborted via beforeSerialize callback');return this;}
6
+ var n,v,a=this.formToArray(options.semantic);if(options.data){options.extraData=options.data;for(n in options.data){if(options.data[n]instanceof Array){for(var k in options.data[n]){a.push({name:n,value:options.data[n][k]});}}
7
+ else{v=options.data[n];v=$.isFunction(v)?v():v;a.push({name:n,value:v});}}}
8
+ if(options.beforeSubmit&&options.beforeSubmit(a,this,options)===false){log('ajaxSubmit: submit aborted via beforeSubmit callback');return this;}
9
+ this.trigger('form-submit-validate',[a,this,options,veto]);if(veto.veto){log('ajaxSubmit: submit vetoed via form-submit-validate trigger');return this;}
10
+ var q=$.param(a);if(options.type.toUpperCase()=='GET'){options.url+=(options.url.indexOf('?')>=0?'&':'?')+q;options.data=null;}
11
+ else{options.data=q;}
12
+ var $form=this,callbacks=[];if(options.resetForm){callbacks.push(function(){$form.resetForm();});}
13
+ if(options.clearForm){callbacks.push(function(){$form.clearForm();});}
14
+ if(!options.dataType&&options.target){var oldSuccess=options.success||function(){};callbacks.push(function(data){var fn=options.replaceTarget?'replaceWith':'html';$(options.target)[fn](data).each(oldSuccess,arguments);});}
15
+ else if(options.success){callbacks.push(options.success);}
16
+ options.success=function(data,status,xhr){var context=options.context||options;for(var i=0,max=callbacks.length;i<max;i++){callbacks[i].apply(context,[data,status,xhr||$form,$form]);}};var fileInputs=$('input:file',this).length>0;var mp='multipart/form-data';var multipart=($form.attr('enctype')==mp||$form.attr('encoding')==mp);if(options.iframe!==false&&(fileInputs||options.iframe||multipart)){if(options.closeKeepAlive){$.get(options.closeKeepAlive,fileUpload);}
17
+ else{fileUpload();}}
18
+ else{$.ajax(options);}
19
+ this.trigger('form-submit-notify',[this,options]);return this;function fileUpload(){var form=$form[0],s,g,id,$io,io,xhr,sub,n,timedOut,timeoutHandle;if($(':input[name=submit],:input[id=submit]',form).length){alert('Error: Form elements must not have name or id of "submit".');return;}
20
+ s=$.extend(true,{},$.ajaxSettings,options);s.context=s.context||s;$io,id='jqFormIO'+(new Date().getTime());if(s.iframeTarget){$io=$(s.iframeTarget);n=$io.attr('name');if(n==null)
21
+ $io.attr('name',id);else
22
+ id=n;}
23
+ else{$io=$('<iframe name="'+id+'" src="'+s.iframeSrc+'" />');$io.css({position:'absolute',top:'-1000px',left:'-1000px'});}
24
+ io=$io[0];xhr={aborted:0,responseText:null,responseXML:null,status:0,statusText:'n/a',getAllResponseHeaders:function(){},getResponseHeader:function(){},setRequestHeader:function(){},abort:function(status){var e=(status==='timeout'?'timeout':'aborted');log('aborting upload... '+e);this.aborted=1;$io.attr('src',s.iframeSrc);xhr.error=e;s.error&&s.error.call(s.context,xhr,e,e);g&&$.event.trigger("ajaxError",[xhr,s,e]);s.complete&&s.complete.call(s.context,xhr,e);}};g=s.global;if(g&&!$.active++){$.event.trigger("ajaxStart");}
25
+ if(g){$.event.trigger("ajaxSend",[xhr,s]);}
26
+ if(s.beforeSend&&s.beforeSend.call(s.context,xhr,s)===false){if(s.global){$.active--;}
27
+ return;}
28
+ if(xhr.aborted){return;}
29
+ sub=form.clk;if(sub){n=sub.name;if(n&&!sub.disabled){s.extraData=s.extraData||{};s.extraData[n]=sub.value;if(sub.type=="image"){s.extraData[n+'.x']=form.clk_x;s.extraData[n+'.y']=form.clk_y;}}}
30
+ function doSubmit(){var t=$form.attr('target'),a=$form.attr('action');form.setAttribute('target',id);if(form.getAttribute('method')!='POST'){form.setAttribute('method','POST');}
31
+ if(form.getAttribute('action')!=s.url){form.setAttribute('action',s.url);}
32
+ if(!s.skipEncodingOverride){$form.attr({encoding:'multipart/form-data',enctype:'multipart/form-data'});}
33
+ if(s.timeout){timeoutHandle=setTimeout(function(){timedOut=true;cb(true);},s.timeout);}
34
+ var extraInputs=[];try{if(s.extraData){for(var n in s.extraData){extraInputs.push($('<input type="hidden" name="'+n+'" value="'+s.extraData[n]+'" />').appendTo(form)[0]);}}
35
+ if(!s.iframeTarget){$io.appendTo('body');io.attachEvent?io.attachEvent('onload',cb):io.addEventListener('load',cb,false);}
36
+ form.submit();}
37
+ finally{form.setAttribute('action',a);if(t){form.setAttribute('target',t);}else{$form.removeAttr('target');}
38
+ $(extraInputs).remove();}}
39
+ if(s.forceSync){doSubmit();}
40
+ else{setTimeout(doSubmit,10);}
41
+ var data,doc,domCheckCount=50,callbackProcessed;function cb(e){if(xhr.aborted||callbackProcessed){return;}
42
+ if(e===true&&xhr){xhr.abort('timeout');return;}
43
+ var doc=io.contentWindow?io.contentWindow.document:io.contentDocument?io.contentDocument:io.document;if(!doc||doc.location.href==s.iframeSrc){if(!timedOut)
44
+ return;}
45
+ io.detachEvent?io.detachEvent('onload',cb):io.removeEventListener('load',cb,false);var status='success',errMsg;try{if(timedOut){throw'timeout';}
46
+ var isXml=s.dataType=='xml'||doc.XMLDocument||$.isXMLDoc(doc);log('isXml='+isXml);if(!isXml&&window.opera&&(doc.body==null||doc.body.innerHTML=='')){if(--domCheckCount){log('requeing onLoad callback, DOM not available');setTimeout(cb,250);return;}}
47
+ var docRoot=doc.body?doc.body:doc.documentElement;xhr.responseText=docRoot?docRoot.innerHTML:null;xhr.responseXML=doc.XMLDocument?doc.XMLDocument:doc;if(isXml)
48
+ s.dataType='xml';xhr.getResponseHeader=function(header){var headers={'content-type':s.dataType};return headers[header];};if(docRoot){xhr.status=Number(docRoot.getAttribute('status'))||xhr.status;xhr.statusText=docRoot.getAttribute('statusText')||xhr.statusText;}
49
+ var scr=/(json|script|text)/.test(s.dataType.toLowerCase());if(scr||s.textarea){var ta=doc.getElementsByTagName('textarea')[0];if(ta){xhr.responseText=ta.value;xhr.status=Number(ta.getAttribute('status'))||xhr.status;xhr.statusText=ta.getAttribute('statusText')||xhr.statusText;}
50
+ else if(scr){var pre=doc.getElementsByTagName('pre')[0];var b=doc.getElementsByTagName('body')[0];if(pre){xhr.responseText=pre.textContent?pre.textContent:pre.innerHTML;}
51
+ else if(b){xhr.responseText=b.innerHTML;}}}
52
+ else if(s.dataType=='xml'&&!xhr.responseXML&&xhr.responseText!=null){xhr.responseXML=toXml(xhr.responseText);}
53
+ try{data=httpData(xhr,s.dataType,s);}
54
+ catch(e){status='parsererror';xhr.error=errMsg=(e||status);}}
55
+ catch(e){log('error caught',e);status='error';xhr.error=errMsg=(e||status);}
56
+ if(xhr.aborted){log('upload aborted');status=null;}
57
+ if(xhr.status){status=(xhr.status>=200&&xhr.status<300||xhr.status===304)?'success':'error';}
58
+ if(status==='success'){s.success&&s.success.call(s.context,data,'success',xhr);g&&$.event.trigger("ajaxSuccess",[xhr,s]);}
59
+ else if(status){if(errMsg==undefined)
60
+ errMsg=xhr.statusText;s.error&&s.error.call(s.context,xhr,status,errMsg);g&&$.event.trigger("ajaxError",[xhr,s,errMsg]);}
61
+ g&&$.event.trigger("ajaxComplete",[xhr,s]);if(g&&!--$.active){$.event.trigger("ajaxStop");}
62
+ s.complete&&s.complete.call(s.context,xhr,status);callbackProcessed=true;if(s.timeout)
63
+ clearTimeout(timeoutHandle);setTimeout(function(){if(!s.iframeTarget)
64
+ $io.remove();xhr.responseXML=null;},100);}
65
+ var toXml=$.parseXML||function(s,doc){if(window.ActiveXObject){doc=new ActiveXObject('Microsoft.XMLDOM');doc.async='false';doc.loadXML(s);}
66
+ else{doc=(new DOMParser()).parseFromString(s,'text/xml');}
67
+ return(doc&&doc.documentElement&&doc.documentElement.nodeName!='parsererror')?doc:null;};var parseJSON=$.parseJSON||function(s){return window['eval']('('+s+')');};var httpData=function(xhr,type,s){var ct=xhr.getResponseHeader('content-type')||'',xml=type==='xml'||!type&&ct.indexOf('xml')>=0,data=xml?xhr.responseXML:xhr.responseText;if(xml&&data.documentElement.nodeName==='parsererror'){$.error&&$.error('parsererror');}
68
+ if(s&&s.dataFilter){data=s.dataFilter(data,type);}
69
+ if(typeof data==='string'){if(type==='json'||!type&&ct.indexOf('json')>=0){data=parseJSON(data);}else if(type==="script"||!type&&ct.indexOf("javascript")>=0){$.globalEval(data);}}
70
+ return data;};}};$.fn.ajaxForm=function(options){if(this.length===0){var o={s:this.selector,c:this.context};if(!$.isReady&&o.s){log('DOM not ready, queuing ajaxForm');$(function(){$(o.s,o.c).ajaxForm(options);});return this;}
71
+ log('terminating; zero elements found by selector'+($.isReady?'':' (DOM not ready)'));return this;}
72
+ return this.ajaxFormUnbind().bind('submit.form-plugin',function(e){if(!e.isDefaultPrevented()){e.preventDefault();$(this).ajaxSubmit(options);}}).bind('click.form-plugin',function(e){var target=e.target;var $el=$(target);if(!($el.is(":submit,input:image"))){var t=$el.closest(':submit');if(t.length==0){return;}
73
+ target=t[0];}
74
+ var form=this;form.clk=target;if(target.type=='image'){if(e.offsetX!=undefined){form.clk_x=e.offsetX;form.clk_y=e.offsetY;}else if(typeof $.fn.offset=='function'){var offset=$el.offset();form.clk_x=e.pageX-offset.left;form.clk_y=e.pageY-offset.top;}else{form.clk_x=e.pageX-target.offsetLeft;form.clk_y=e.pageY-target.offsetTop;}}
75
+ setTimeout(function(){form.clk=form.clk_x=form.clk_y=null;},100);});};$.fn.ajaxFormUnbind=function(){return this.unbind('submit.form-plugin click.form-plugin');};$.fn.formToArray=function(semantic){var a=[];if(this.length===0){return a;}
76
+ var form=this[0];var els=semantic?form.getElementsByTagName('*'):form.elements;if(!els){return a;}
77
+ var i,j,n,v,el,max,jmax;for(i=0,max=els.length;i<max;i++){el=els[i];n=el.name;if(!n){continue;}
78
+ if(semantic&&form.clk&&el.type=="image"){if(!el.disabled&&form.clk==el){a.push({name:n,value:$(el).val()});a.push({name:n+'.x',value:form.clk_x},{name:n+'.y',value:form.clk_y});}
79
+ continue;}
80
+ v=$.fieldValue(el,true);if(v&&v.constructor==Array){for(j=0,jmax=v.length;j<jmax;j++){a.push({name:n,value:v[j]});}}
81
+ else if(v!==null&&typeof v!='undefined'){a.push({name:n,value:v});}}
82
+ if(!semantic&&form.clk){var $input=$(form.clk),input=$input[0];n=input.name;if(n&&!input.disabled&&input.type=='image'){a.push({name:n,value:$input.val()});a.push({name:n+'.x',value:form.clk_x},{name:n+'.y',value:form.clk_y});}}
83
+ return a;};$.fn.formSerialize=function(semantic){return $.param(this.formToArray(semantic));};$.fn.fieldSerialize=function(successful){var a=[];this.each(function(){var n=this.name;if(!n){return;}
84
+ var v=$.fieldValue(this,successful);if(v&&v.constructor==Array){for(var i=0,max=v.length;i<max;i++){a.push({name:n,value:v[i]});}}
85
+ else if(v!==null&&typeof v!='undefined'){a.push({name:this.name,value:v});}});return $.param(a);};$.fn.fieldValue=function(successful){for(var val=[],i=0,max=this.length;i<max;i++){var el=this[i];var v=$.fieldValue(el,successful);if(v===null||typeof v=='undefined'||(v.constructor==Array&&!v.length)){continue;}
86
+ v.constructor==Array?$.merge(val,v):val.push(v);}
87
+ return val;};$.fieldValue=function(el,successful){var n=el.name,t=el.type,tag=el.tagName.toLowerCase();if(successful===undefined){successful=true;}
88
+ if(successful&&(!n||el.disabled||t=='reset'||t=='button'||(t=='checkbox'||t=='radio')&&!el.checked||(t=='submit'||t=='image')&&el.form&&el.form.clk!=el||tag=='select'&&el.selectedIndex==-1)){return null;}
89
+ if(tag=='select'){var index=el.selectedIndex;if(index<0){return null;}
90
+ var a=[],ops=el.options;var one=(t=='select-one');var max=(one?index+1:ops.length);for(var i=(one?index:0);i<max;i++){var op=ops[i];if(op.selected){var v=op.value;if(!v){v=(op.attributes&&op.attributes['value']&&!(op.attributes['value'].specified))?op.text:op.value;}
91
+ if(one){return v;}
92
+ a.push(v);}}
93
+ return a;}
94
+ return $(el).val();};$.fn.clearForm=function(){return this.each(function(){$('input,select,textarea',this).clearFields();});};$.fn.clearFields=$.fn.clearInputs=function(){return this.each(function(){var t=this.type,tag=this.tagName.toLowerCase();if(t=='text'||t=='password'||tag=='textarea'){this.value='';}
95
+ else if(t=='checkbox'||t=='radio'){this.checked=false;}
96
+ else if(tag=='select'){this.selectedIndex=-1;}});};$.fn.resetForm=function(){return this.each(function(){if(typeof this.reset=='function'||(typeof this.reset=='object'&&!this.reset.nodeType)){this.reset();}});};$.fn.enable=function(b){if(b===undefined){b=true;}
97
+ return this.each(function(){this.disabled=!b;});};$.fn.selected=function(select){if(select===undefined){select=true;}
98
+ return this.each(function(){var t=this.type;if(t=='checkbox'||t=='radio'){this.checked=select;}
99
+ else if(this.tagName.toLowerCase()=='option'){var $sel=$(this).parent('select');if(select&&$sel[0]&&$sel[0].type=='select-one'){$sel.find('option').selected(false);}
100
+ this.selected=select;}});};function log(){if($.fn.ajaxSubmit.debug){var msg='[jquery.form] '+Array.prototype.join.call(arguments,'');if(window.console&&window.console.log){window.console.log(msg);}
101
+ else if(window.opera&&window.opera.postError){window.opera.postError(msg);}}};})(jQuery);
@@ -0,0 +1,33 @@
1
+ require 'generators/acts_as_approvable/base'
2
+
3
+ module Erb
4
+ module Generators
5
+ class ActsAsApprovableGenerator < Rails::Generators::Base
6
+ include ActsAsApprovable::Generators::Base
7
+
8
+ source_root File.expand_path('../templates', __FILE__)
9
+
10
+ class_option :owner, :type => :string, :optional => true, :desc => 'Model that can own approvals'
11
+ class_option :scripts, :type => :boolean, :optional => true, :default => false
12
+
13
+ def copy_view_files
14
+ template 'index.html.erb', 'app/views/approvals/index.html.erb'
15
+ template '_table.html.erb', 'app/views/approvals/_table.html.erb'
16
+ template '_owner_select.html.erb', 'app/views/approvals/_owner_select.html.erb' if owner?
17
+ end
18
+
19
+ protected
20
+ def format
21
+ :html
22
+ end
23
+
24
+ def handler
25
+ :erb
26
+ end
27
+
28
+ def filename_with_extensions(name)
29
+ [name, format, handler].compact.join('.')
30
+ end
31
+ end
32
+ end
33
+ end