acts-as-approvable 0.6.1

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 (61) hide show
  1. data/.gitignore +6 -0
  2. data/Appraisals +18 -0
  3. data/CHANGELOG +10 -0
  4. data/Gemfile +3 -0
  5. data/Gemfile.lock +50 -0
  6. data/MIT-LICENSE +20 -0
  7. data/README.md +108 -0
  8. data/Rakefile +81 -0
  9. data/VERSION +1 -0
  10. data/acts-as-approvable.gemspec +31 -0
  11. data/gemfiles/rails2.gemfile +7 -0
  12. data/gemfiles/rails2.gemfile.lock +50 -0
  13. data/gemfiles/rails30.gemfile +8 -0
  14. data/gemfiles/rails30.gemfile.lock +90 -0
  15. data/gemfiles/rails31.gemfile +8 -0
  16. data/gemfiles/rails31.gemfile.lock +101 -0
  17. data/gemfiles/rails32.gemfile +8 -0
  18. data/gemfiles/rails32.gemfile.lock +99 -0
  19. data/generators/acts_as_approvable/USAGE +3 -0
  20. data/generators/acts_as_approvable/acts_as_approvable_generator.rb +86 -0
  21. data/generators/acts_as_approvable/templates/approvals_controller.rb +97 -0
  22. data/generators/acts_as_approvable/templates/create_approvals.rb +26 -0
  23. data/generators/acts_as_approvable/templates/initializer.rb +3 -0
  24. data/generators/acts_as_approvable/templates/views/erb/_owner_select.html.erb +4 -0
  25. data/generators/acts_as_approvable/templates/views/erb/_table.html.erb +26 -0
  26. data/generators/acts_as_approvable/templates/views/erb/index.html.erb +15 -0
  27. data/generators/acts_as_approvable/templates/views/haml/_owner_select.html.haml +3 -0
  28. data/generators/acts_as_approvable/templates/views/haml/_table.html.haml +19 -0
  29. data/generators/acts_as_approvable/templates/views/haml/index.html.haml +13 -0
  30. data/init.rb +1 -0
  31. data/lib/acts-as-approvable/version.rb +3 -0
  32. data/lib/acts_as_approvable/acts_as_approvable.rb +291 -0
  33. data/lib/acts_as_approvable/approval.rb +179 -0
  34. data/lib/acts_as_approvable/error.rb +31 -0
  35. data/lib/acts_as_approvable/ownership.rb +117 -0
  36. data/lib/acts_as_approvable/railtie.rb +7 -0
  37. data/lib/acts_as_approvable.rb +66 -0
  38. data/lib/generators/acts_as_approvable/USAGE +1 -0
  39. data/lib/generators/acts_as_approvable/acts_as_approvable_generator.rb +73 -0
  40. data/lib/generators/acts_as_approvable/templates/approvals_controller.rb +97 -0
  41. data/lib/generators/acts_as_approvable/templates/create_approvals.rb +26 -0
  42. data/lib/generators/acts_as_approvable.rb +0 -0
  43. data/lib/generators/erb/acts_as_approvable_generator.rb +44 -0
  44. data/lib/generators/erb/templates/_owner_select.html.erb +4 -0
  45. data/lib/generators/erb/templates/_table.html.erb +26 -0
  46. data/lib/generators/erb/templates/index.html.erb +15 -0
  47. data/lib/generators/haml/acts_as_approvable_generator.rb +44 -0
  48. data/lib/generators/haml/templates/_owner_select.html.haml +3 -0
  49. data/lib/generators/haml/templates/_table.html.haml +19 -0
  50. data/lib/generators/haml/templates/index.html.haml +13 -0
  51. data/lib/tasks/acts_as_approvable.rake +4 -0
  52. data/rails/init.rb +1 -0
  53. data/test/acts_as_approvable_model_test.rb +428 -0
  54. data/test/acts_as_approvable_ownership_test.rb +132 -0
  55. data/test/acts_as_approvable_schema_test.rb +13 -0
  56. data/test/acts_as_approvable_test.rb +8 -0
  57. data/test/database.yml +7 -0
  58. data/test/schema.rb +44 -0
  59. data/test/support.rb +19 -0
  60. data/test/test_helper.rb +60 -0
  61. metadata +225 -0
@@ -0,0 +1,73 @@
1
+ require 'rails/generators/active_record'
2
+
3
+ class ActsAsApprovableGenerator < Rails::Generators::Base
4
+ source_root File.expand_path('../templates', __FILE__)
5
+
6
+ class_option :base, :type => :string, :default => 'ApplicationController', :desc => 'Base class for the ApprovalsController'
7
+ class_option :owner, :type => :string, :optional => true, :desc => 'Model that can own approvals'
8
+
9
+ desc 'Generates ApprovalsController, a migration the create the Approval table, and an initializer for the plugin.'
10
+
11
+ def check_class_collisions
12
+ class_collisions '', 'ApprovalsController'
13
+ end
14
+
15
+ def create_controller_file
16
+ template 'approvals_controller.rb', File.join('app/controllers', 'approvals_controller.rb')
17
+ end
18
+
19
+ def create_migration_file
20
+ number = ActiveRecord::Generators::Base.next_migration_number('db/migrate')
21
+ template 'create_approvals.rb', "db/migrate/#{number}_create_approvals.rb"
22
+ end
23
+
24
+ def create_initializer_file
25
+ initializer('acts_as_approvable.rb') do
26
+ data = ''
27
+
28
+ if owner?
29
+ data << 'ActsAsApprovable::Ownership.configure'
30
+ data << "(:owner => #{options[:owner]})" if options[:owner] != 'User'
31
+ end
32
+
33
+ data << "\n"
34
+ end
35
+ end
36
+
37
+ hook_for :template_engine
38
+
39
+ def add_routes
40
+ resource = []
41
+ resource << 'resources :approvals, :only => [:index] do'
42
+ resource << ' collection do'
43
+ resource << ' get \'index\''
44
+ resource << ' get \'history\''
45
+ resource << ' get \'mine\'' if owner?
46
+ resource << ' end'
47
+ resource << ' member do'
48
+ resource << ' post \'approve\''
49
+ resource << ' post \'reject\''
50
+ resource << ' post \'assign\'' if owner?
51
+ resource << ' end'
52
+ resource << ' end'
53
+
54
+ route(resource.join("\n"))
55
+ end
56
+
57
+ protected
58
+ def owner?
59
+ options[:owner].present?
60
+ end
61
+
62
+ def collection_actions
63
+ actions = [:index, :history]
64
+ actions << :mine if owner?
65
+ actions.map { |a| ":#{a}" }
66
+ end
67
+
68
+ def member_actions
69
+ actions = [:approve, :reject]
70
+ actions << :assign if owner?
71
+ actions.map { |a| ":#{a}" }
72
+ end
73
+ end
@@ -0,0 +1,97 @@
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
+ around_filter :json_wrapper, :only => [<%= member_actions.join(', ') %>]
6
+
7
+ def index
8
+ state = params[:state] =~ /^\d+$/ ? params[:state].to_i : Approval.enumerate_state('pending')
9
+ @conditions[:state] = state if state > -1
10
+
11
+ @approvals = Approval.all(:conditions => @conditions, :order => 'created_at ASC')
12
+ end
13
+
14
+ def history
15
+ @conditions[:state] = Approval.enumerate_states('approved', 'rejected')
16
+
17
+ @approvals = Approval.all(:conditions => @conditions, :order => 'created_at DESC')
18
+ render :index
19
+ end
20
+
21
+ <% if owner? %> def mine
22
+ @conditions[:owner_id] = current_user.id
23
+
24
+ @approvals = Approval.all(:conditions => @conditions, :order => 'created_at ASC')
25
+ render :index
26
+ end
27
+
28
+ def assign
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
+
37
+ <% end %> def approve
38
+ <% if owner? %> @approval.owner = current_user if respond_to?(:current_user)
39
+ <% end %> @approval.approve!
40
+ end
41
+
42
+ def reject
43
+ <% if owner? %> @approval.owner = current_user if respond_to?(:current_user)
44
+ <% end %> @approval.reject!(params[:reason])
45
+ end
46
+
47
+ private
48
+ def json_wrapper
49
+ json = {:success => false}
50
+
51
+ begin
52
+ json[:success] = yield
53
+ rescue ActsAsApprovable::Error => e
54
+ json[:message] = e.message
55
+ rescue
56
+ json[:message] = 'An unknown error occured'
57
+ end
58
+
59
+ respond_to do |format|
60
+ format.html do
61
+ flash[:error] = json[:message] if json[:message]
62
+ redirect_to :action => :index
63
+ end
64
+ format.json { render :json => json }
65
+ end
66
+ end
67
+
68
+ def setup_conditions
69
+ @conditions ||= {}
70
+
71
+ <% if owner? %> if params[:owner_id]
72
+ @conditions[:owner_id] = params[:owner_id]
73
+ @conditions[:owner_id] = nil if params[:owner_id] == 0
74
+ end
75
+ <% end %> if params[:item_type]
76
+ @conditions[:item_type] = params[:item_type]
77
+ end
78
+ end
79
+
80
+ # Check for the selected models partial, use the generic one if it doesn't exist
81
+ def setup_partial
82
+ @table_partial = @conditions.fetch(:item_type) { 'table' }
83
+
84
+ if @table_partial != 'table'
85
+ partial_path = Rails.root.join('app', 'views', 'approvals', "_#{@table_partial}.html.#{view_language}")
86
+ @table_partial = 'table' unless File.exist?(partial_path)
87
+ end
88
+ end
89
+
90
+ def find_approval
91
+ @approval = Approval.find(params[:id])
92
+ end
93
+
94
+ def view_language
95
+ ActsAsApprovable.view_language
96
+ end
97
+ end
@@ -0,0 +1,26 @@
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
10
+ t.text :reason
11
+
12
+ t.timestamps
13
+ end
14
+
15
+ add_index :approvals, [:state, :event]
16
+ add_index :approvals, [:item_type, :item_id]
17
+ <% if options[:owner] %> add_index :approvals, [:owner_id]
18
+ <% end %> end
19
+
20
+ def self.down
21
+ remove_index :approvals, [:state, :event]
22
+ remove_index :approvals, [:item_type, :item_id]
23
+ <% if options[:owner] %> remove_index :approvals, [:owner_id]
24
+ <% end %> drop_table :approvals
25
+ end
26
+ end
File without changes
@@ -0,0 +1,44 @@
1
+ module Erb
2
+ module Generators
3
+ class ActsAsApprovableGenerator < Rails::Generators::Base
4
+ source_root File.expand_path('../templates', __FILE__)
5
+
6
+ class_option :owner, :type => :string, :optional => true, :desc => 'Model that can own approvals'
7
+
8
+ def copy_view_files
9
+ template 'index.html.erb', 'app/views/approvals/index.html.erb'
10
+ template '_table.html.erb', 'app/views/approvals/_table.html.erb'
11
+ template '_owner_select.html.erb', 'app/views/approvals/_owner_select.html.erb' if owner?
12
+ end
13
+
14
+ protected
15
+ def format
16
+ :html
17
+ end
18
+
19
+ def handler
20
+ :erb
21
+ end
22
+
23
+ def filename_with_extensions(name)
24
+ [name, format, handler].compact.join('.')
25
+ end
26
+
27
+ def owner?
28
+ options[:owner].present?
29
+ end
30
+
31
+ def collection_actions
32
+ actions = [:index, :history]
33
+ actions << :mine if owner?
34
+ actions.map { |a| ":#{a}" }
35
+ end
36
+
37
+ def member_actions
38
+ actions = [:approve, :reject]
39
+ actions << :assign if owner?
40
+ actions.map { |a| ":#{a}" }
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,4 @@
1
+ <%% form_for(approval, :url => assign_approval_path(approval.id), :html => {:class => 'assignment'}) do |f| %>
2
+ <%%= f.select(:owner_id, options_for_select(Approval.options_for_available_owners(true), approval.owner_id)) %>
3
+ <%%= f.submit('Set') %>
4
+ <%% end %>
@@ -0,0 +1,26 @@
1
+ <table>
2
+ <thead>
3
+ <tr>
4
+ <th>Type</th>
5
+ <th>Record</th>
6
+ <% if owner? %> <th>Owner</th>
7
+ <% end %> <th>State</th>
8
+ <th>&nbsp;</th>
9
+ </tr>
10
+ </thead>
11
+ <tbody>
12
+ <%% @approvals.each do |approval| %>
13
+ <tr>
14
+ <td><%%= approval.item_type.classify %></td>
15
+ <td><%%= approval.item.try(:to_s) || approval.item.id %></td>
16
+ <% if owner? %> <td><%%= render :partial => 'owner_select', :locals => {:approval => approval} %></td>
17
+ <% end %> <td><%%= approval.state %></td>
18
+ <td class='actions'>
19
+ <%%= link_to('Approve', approve_approval_path(approval)) %>
20
+ <%%= link_to('Reject', reject_approval_path(approval)) %>
21
+ </td>
22
+ </tr>
23
+ <%% end %>
24
+ </tbody>
25
+ </table>
26
+
@@ -0,0 +1,15 @@
1
+ <h1>Approval Queue</h1>
2
+
3
+ <%% form_for(:approval, :url => approvals_path, :html => {:method => :get}) do |f| %>
4
+ <%% unless @conditions[:state].is_a?(Array) # History page shouldn't allow selecting different states %>
5
+ <%%= f.label(:state) %>
6
+ <%%= f.select(:state, options_for_select(Approval.options_for_state, @conditions[:state])) %>
7
+ <%% end %>
8
+ <% if owner? %> <%%= f.label(:owner_id) %>
9
+ <%%= f.select(:owner_id, options_for_select(Approval.options_for_assigned_owners, @conditions[:owner_id]), :prompt => 'All Users') %>
10
+ <% end %> <%%= f.label(:item_type) %>
11
+ <%%= f.select(:item_type, options_for_select(Approval.options_for_type, @conditions[:item_type]), :prompt => 'All Types') %>
12
+ <%%= f.submit('Filter') %>
13
+ <%% end %>
14
+
15
+ <%%= render :partial => @table_partial %>
@@ -0,0 +1,44 @@
1
+ module Haml
2
+ module Generators
3
+ class ActsAsApprovableGenerator < Rails::Generators::Base
4
+ source_root File.expand_path('../templates', __FILE__)
5
+
6
+ class_option :owner, :type => :string, :optional => true, :desc => 'Model that can own approvals'
7
+
8
+ def copy_view_files
9
+ template 'index.html.haml', 'app/views/approvals/index.html.haml'
10
+ template '_table.html.haml', 'app/views/approvals/_table.html.haml'
11
+ template '_owner_select.html.haml', 'app/views/approvals/_owner_select.html.haml' if owner?
12
+ end
13
+
14
+ protected
15
+ def format
16
+ :html
17
+ end
18
+
19
+ def handler
20
+ :haml
21
+ end
22
+
23
+ def filename_with_extensions(name)
24
+ [name, format, handler].compact.join('.')
25
+ end
26
+
27
+ def owner?
28
+ options[:owner].present?
29
+ end
30
+
31
+ def collection_actions
32
+ actions = [:index, :history]
33
+ actions << :mine if owner?
34
+ actions.map { |a| ":#{a}" }
35
+ end
36
+
37
+ def member_actions
38
+ actions = [:approve, :reject]
39
+ actions << :assign if owner?
40
+ actions.map { |a| ":#{a}" }
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,3 @@
1
+ - form_for(approval, :url => assign_approval_path(approval.id), :html => {:class => 'assignment'}) do |f|
2
+ = f.select(:owner_id, options_for_select(Approval.options_for_available_owners(true), approval.owner_id))
3
+ = f.submit('Set')
@@ -0,0 +1,19 @@
1
+ %table
2
+ %thead
3
+ %tr
4
+ %th Type
5
+ %th Record
6
+ <% if owner? %> %th Owner
7
+ <% end %> %th State
8
+ %th
9
+ %tbody
10
+ - @approvals.each do |approval|
11
+ %tr
12
+ %td= approval.item_type.classify
13
+ %td= approval.item.try(:to_s) || approval.item.id
14
+ <% if owner? %> %td= render :partial => 'owner_select', :locals => {:approval => approval}
15
+ <% end %> %td= approval.state
16
+ %td.actions
17
+ = link_to('Approve', approve_approval_path(approval))
18
+ = link_to('Reject', reject_approval_path(approval))
19
+
@@ -0,0 +1,13 @@
1
+ %h1 Approval Queue
2
+
3
+ - form_for(:approval, :url => approvals_path, :html => {:method => :get}) do |f|
4
+ - unless @conditions[:state].is_a?(Array) # History page shouldn't allow selecting different states
5
+ = label_tag('state', 'State')
6
+ = select_tag('state', options_for_select(Approval.options_for_state, @conditions[:state]))
7
+ <% if owner? %> = label_tag('owner_id', 'Owner')
8
+ = select('owner_id', options_for_select(Approval.options_for_assigned_owners(true), @conditions[:owner_id]))
9
+ <% end %> = label_tag('item_type', 'Type')
10
+ = select_tag('item_type', options_for_select(Approval.options_for_type(true), @conditions[:item_type]))
11
+ %button{:type => 'Submit'} Filter
12
+
13
+ = render :partial => @table_partial
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :acts_as_approvable do
3
+ # # Task goes here
4
+ # end
data/rails/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'acts_as_approvable'