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.
- data/.gitignore +6 -0
- data/Appraisals +18 -0
- data/CHANGELOG +10 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +50 -0
- data/MIT-LICENSE +20 -0
- data/README.md +108 -0
- data/Rakefile +81 -0
- data/VERSION +1 -0
- data/acts-as-approvable.gemspec +31 -0
- data/gemfiles/rails2.gemfile +7 -0
- data/gemfiles/rails2.gemfile.lock +50 -0
- data/gemfiles/rails30.gemfile +8 -0
- data/gemfiles/rails30.gemfile.lock +90 -0
- data/gemfiles/rails31.gemfile +8 -0
- data/gemfiles/rails31.gemfile.lock +101 -0
- data/gemfiles/rails32.gemfile +8 -0
- data/gemfiles/rails32.gemfile.lock +99 -0
- data/generators/acts_as_approvable/USAGE +3 -0
- data/generators/acts_as_approvable/acts_as_approvable_generator.rb +86 -0
- data/generators/acts_as_approvable/templates/approvals_controller.rb +97 -0
- data/generators/acts_as_approvable/templates/create_approvals.rb +26 -0
- data/generators/acts_as_approvable/templates/initializer.rb +3 -0
- data/generators/acts_as_approvable/templates/views/erb/_owner_select.html.erb +4 -0
- data/generators/acts_as_approvable/templates/views/erb/_table.html.erb +26 -0
- data/generators/acts_as_approvable/templates/views/erb/index.html.erb +15 -0
- data/generators/acts_as_approvable/templates/views/haml/_owner_select.html.haml +3 -0
- data/generators/acts_as_approvable/templates/views/haml/_table.html.haml +19 -0
- data/generators/acts_as_approvable/templates/views/haml/index.html.haml +13 -0
- data/init.rb +1 -0
- data/lib/acts-as-approvable/version.rb +3 -0
- data/lib/acts_as_approvable/acts_as_approvable.rb +291 -0
- data/lib/acts_as_approvable/approval.rb +179 -0
- data/lib/acts_as_approvable/error.rb +31 -0
- data/lib/acts_as_approvable/ownership.rb +117 -0
- data/lib/acts_as_approvable/railtie.rb +7 -0
- data/lib/acts_as_approvable.rb +66 -0
- data/lib/generators/acts_as_approvable/USAGE +1 -0
- data/lib/generators/acts_as_approvable/acts_as_approvable_generator.rb +73 -0
- data/lib/generators/acts_as_approvable/templates/approvals_controller.rb +97 -0
- data/lib/generators/acts_as_approvable/templates/create_approvals.rb +26 -0
- data/lib/generators/acts_as_approvable.rb +0 -0
- data/lib/generators/erb/acts_as_approvable_generator.rb +44 -0
- data/lib/generators/erb/templates/_owner_select.html.erb +4 -0
- data/lib/generators/erb/templates/_table.html.erb +26 -0
- data/lib/generators/erb/templates/index.html.erb +15 -0
- data/lib/generators/haml/acts_as_approvable_generator.rb +44 -0
- data/lib/generators/haml/templates/_owner_select.html.haml +3 -0
- data/lib/generators/haml/templates/_table.html.haml +19 -0
- data/lib/generators/haml/templates/index.html.haml +13 -0
- data/lib/tasks/acts_as_approvable.rake +4 -0
- data/rails/init.rb +1 -0
- data/test/acts_as_approvable_model_test.rb +428 -0
- data/test/acts_as_approvable_ownership_test.rb +132 -0
- data/test/acts_as_approvable_schema_test.rb +13 -0
- data/test/acts_as_approvable_test.rb +8 -0
- data/test/database.yml +7 -0
- data/test/schema.rb +44 -0
- data/test/support.rb +19 -0
- data/test/test_helper.rb +60 -0
- 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,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> </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,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
|
data/rails/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'acts_as_approvable'
|