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,60 @@
1
+ require 'acts_as_approvable/model/class_methods'
2
+ require 'acts_as_approvable/model/instance_methods'
3
+ require 'acts_as_approvable/model/create_instance_methods'
4
+ require 'acts_as_approvable/model/update_instance_methods'
5
+ require 'acts_as_approvable/model/destroy_instance_methods'
6
+
7
+ module ActsAsApprovable
8
+ ##
9
+ # The meat of {ActsAsApprovable}. This applies methods for the configured approval events
10
+ # and configures the required relationships.
11
+ module Model
12
+ # Declare this in your model to require approval on new records or changes to fields.
13
+ #
14
+ # @param [Hash] options the options for this models approval workflow.
15
+ # @option options [Symbol,Array] :on The events to require approval on (`:create`, `:update` or `:destroy`).
16
+ # @option options [String] :state_field The local field to store `:create` approval state.
17
+ # @option options [Array] :ignore A list of fields to ignore. By default we ignore `:created_at`, `:updated_at` and
18
+ # the field specified in `:state_field`.
19
+ # @option options [Array] :only A list of fields to explicitly require approval on. This list supercedes `:ignore`.
20
+ def acts_as_approvable(options = {})
21
+ extend ClassMethods
22
+ include InstanceMethods
23
+
24
+ cattr_accessor :approvable_on
25
+ self.approvable_on = Array.wrap(options.delete(:on) { [:create, :update, :destroy] })
26
+
27
+ cattr_accessor :approvable_field
28
+ self.approvable_field = options.delete(:state_field)
29
+
30
+ cattr_accessor :approvable_ignore
31
+ ignores = Array.wrap(options.delete(:ignore) { [] })
32
+ ignores.push('created_at', 'updated_at', primary_key, self.approvable_field)
33
+ self.approvable_ignore = ignores.compact.uniq.map(&:to_s)
34
+
35
+ cattr_accessor :approvable_only
36
+ self.approvable_only = Array.wrap(options.delete(:only) { [] }).uniq.map(&:to_s)
37
+
38
+ cattr_accessor :approvals_disabled
39
+ self.approvals_disabled = false
40
+
41
+ has_many :approvals, :as => :item, :dependent => :destroy
42
+
43
+ if approvable_on?(:update)
44
+ include UpdateInstanceMethods
45
+ before_update :approvable_update, :if => :approvable_update?
46
+ end
47
+
48
+ if approvable_on?(:create)
49
+ include CreateInstanceMethods
50
+ before_create :approvable_create, :if => :approvable_create?
51
+ end
52
+
53
+ if approvable_on?(:destroy)
54
+ include DestroyInstanceMethods
55
+ end
56
+
57
+ after_save :approvable_save, :if => :approvals_enabled?
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,63 @@
1
+ module ActsAsApprovable
2
+ module Model
3
+ ##
4
+ # Class methods available after acts_as_approvable has been called
5
+ module ClassMethods
6
+ ##
7
+ # Returns true if the approval queue is active at both the local and global
8
+ # level. Note that the global level supercedes the local level.
9
+ def approvals_enabled?
10
+ global_approvals_on? and approvals_on?
11
+ end
12
+
13
+ def approvals_disabled?
14
+ not approvals_enabled?
15
+ end
16
+
17
+ ##
18
+ # Enable the approval queue for this model.
19
+ def approvals_on
20
+ self.approvals_disabled = false
21
+ end
22
+
23
+ ##
24
+ # Disable the approval queue for this model.
25
+ def approvals_off
26
+ self.approvals_disabled = true
27
+ end
28
+
29
+ def approvals_on?
30
+ not self.approvals_disabled
31
+ end
32
+
33
+ def global_approvals_on?
34
+ ActsAsApprovable.enabled?
35
+ end
36
+
37
+ ##
38
+ # Returns true if the model is configured to use the approval queue on the
39
+ # given event (`:create` or `:update`).
40
+ def approvable_on?(event)
41
+ self.approvable_on.include?(event)
42
+ end
43
+
44
+ ##
45
+ # Returns a list of fields that require approval.
46
+ def approvable_fields
47
+ return self.approvable_only unless self.approvable_only.empty?
48
+ column_names - self.approvable_ignore
49
+ end
50
+
51
+ ##
52
+ # Execute a code block while the approval queue is temporarily disabled. The
53
+ # queue state will be returned to it's previous value, either on or off.
54
+ def without_approval(&block)
55
+ enable = self.approvals_on?
56
+ approvals_off
57
+ yield(self)
58
+ ensure
59
+ approvals_on if enable
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,88 @@
1
+ module ActsAsApprovable
2
+ module Model
3
+ ##
4
+ # Instance methods that apply to the `:create` event specifically.
5
+ module CreateInstanceMethods
6
+ ##
7
+ # Retrieve approval record for the creation event.
8
+ #
9
+ # @return [Approval]
10
+ def approval
11
+ approvals.find_by_event('create')
12
+ end
13
+
14
+ ##
15
+ # Get the approval state of the current record from either the local state
16
+ # field or, if no state field exists, the creation approval object.
17
+ #
18
+ # @return [String] one of `'pending'`, `'approved`' or `'rejected'`.
19
+ def approval_state
20
+ if self.class.approvable_field
21
+ send(self.class.approvable_field)
22
+ elsif approval.present?
23
+ approval.state
24
+ end
25
+ end
26
+
27
+ ##
28
+ # Set the records local approval state.
29
+ #
30
+ # @param [String] state one of `'pending'`, `'approved`' or `'rejected'`.
31
+ def set_approval_state(state)
32
+ return unless self.class.approvable_field
33
+ send("#{self.class.approvable_field}=".to_sym, state)
34
+ end
35
+
36
+ ##
37
+ # Returns true if the record is pending approval.
38
+ def pending?
39
+ approval_state.present? ? approval_state == 'pending' : approval.try(:pending?)
40
+ end
41
+
42
+ ##
43
+ # Returns true if the record has been approved.
44
+ def approved?
45
+ approval_state.present? ? approval_state == 'approved' : approval.try(:approved?)
46
+ end
47
+
48
+ ##
49
+ # Returns true if the record has been rejected.
50
+ def rejected?
51
+ approval_state.present? ? approval_state == 'rejected' : approval.try(:rejected?)
52
+ end
53
+
54
+ ##
55
+ # Approves the record through {Approval#approve!}
56
+ #
57
+ # @return [Boolean]
58
+ def approve!
59
+ return unless approvable_on?(:create) && approval.present?
60
+ approval.approve!
61
+ end
62
+
63
+ ##
64
+ # Rejects the record through {Approval#reject!}
65
+ #
66
+ # @return [Boolean]
67
+ def reject!
68
+ return unless approvable_on?(:create) && approval.present?
69
+ approval.reject!
70
+ end
71
+
72
+ def reset!
73
+ return unless approvable_on?(:create) && approval.present?
74
+ approval.reset!
75
+ end
76
+
77
+ private
78
+ def approvable_create?
79
+ approvals_enabled? and approvable_on?(:create)
80
+ end
81
+
82
+ def approvable_create
83
+ @approval = approvals.build(:event => 'create', :state => 'pending')
84
+ set_approval_state('pending')
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,38 @@
1
+ module ActsAsApprovable
2
+ module Model
3
+ ##
4
+ # Instance methods that apply to the `:destroy` event specifically.
5
+ module DestroyInstanceMethods
6
+ ##
7
+ # Retrieve approval records for the destruction event.
8
+ #
9
+ # @param [Boolean] all toggle for returning all or pending approvals
10
+ # @return [Approval]
11
+ def destroy_approvals(all = true)
12
+ all ? approvals.find_all_by_event('destroy') : approvals.find_all_by_event_and_state('destroy', 0)
13
+ end
14
+
15
+ ##
16
+ # Returns true if there are any pending `:destroy` event approvals
17
+ def pending_destruction?
18
+ not destroy_approvals(false).empty?
19
+ end
20
+
21
+ ##
22
+ # Add a `:destrory` approval event to the queue if approvals are enabled, otherwise
23
+ # destroy the record.
24
+ def destroy
25
+ approvable_destroy? ? request_destruction : super
26
+ end
27
+
28
+ private
29
+ def approvable_destroy?
30
+ approvals_enabled? and approvable_on?(:destroy)
31
+ end
32
+
33
+ def request_destruction
34
+ approvals.create!(:event => 'destroy', :state => 'pending', :original => attributes)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,107 @@
1
+ module ActsAsApprovable
2
+ module Model
3
+ ##
4
+ # Instance methods that apply to both `:update` and `:create` events.
5
+ module InstanceMethods
6
+ ##
7
+ # Returns true if the approval queue is active at both the local and global
8
+ # level. Note that the global level supercedes the local level.
9
+ def approvals_enabled?
10
+ global_approvals_on? and model_approvals_on? and approvals_on?
11
+ end
12
+
13
+ ##
14
+ # Returns the inverse of `#approvals_enabled?`
15
+ def approvals_disabled?
16
+ not approvals_enabled?
17
+ end
18
+
19
+ ##
20
+ # Turn off approvals for this instance.
21
+ def approvals_off
22
+ @approvals_disabled = true
23
+ end
24
+
25
+ ##
26
+ # Turn on approvals for this instance.
27
+ def approvals_on
28
+ @approvals_disabled = false
29
+ end
30
+
31
+ ##
32
+ # Returns true if the approval queue is on for this instance.
33
+ def approvals_on?
34
+ not @approvals_disabled
35
+ end
36
+
37
+ ##
38
+ # Returns true if the approval queue is on for this model.
39
+ def model_approvals_on?
40
+ self.class.approvals_on?
41
+ end
42
+
43
+ ##
44
+ # Returns true if the approval queue is on globally.
45
+ def global_approvals_on?
46
+ ActsAsApprovable.enabled?
47
+ end
48
+
49
+ ##
50
+ # Returns true if the model is configured to use the approval queue on the
51
+ # given event (`:create` or `:update`).
52
+ def approvable_on?(event)
53
+ self.class.approvable_on?(event)
54
+ end
55
+
56
+ ##
57
+ # A filter that is run before the record can be approved. Returning false
58
+ # stops the approval process from completing.
59
+ def before_approve(approval); end
60
+
61
+ ##
62
+ # A filter that is run after the record has been approved.
63
+ def after_approve(approval); end
64
+
65
+ ##
66
+ # A filter that is run before the record can be rejected. Returning false
67
+ # stops the rejection process from completing.
68
+ def before_reject(approval); end
69
+
70
+ ##
71
+ # A filter that is run after the record has been rejected.
72
+ def after_reject(approval); end
73
+
74
+ ##
75
+ # Execute a code block while the approval queue is temporarily disabled. The
76
+ # queue state will be returned to it's previous value, either on or off.
77
+ def without_approval(&block)
78
+ enable = approvals_on? # If we use #approvals_enabled? the global state might be incorrectly applied.
79
+ approvals_off
80
+ yield(self)
81
+ ensure
82
+ approvals_on if enable
83
+ end
84
+
85
+ def save_without_approval(*args) #:nodoc:
86
+ without_approval { |i| save(*args) }
87
+ end
88
+
89
+ def save_without_approval!(*args) #:nodoc:
90
+ without_approval { |i| save!(*args) }
91
+ end
92
+
93
+ def destroy_without_approval #:nodoc:
94
+ without_approval { |i| destroy }
95
+ end
96
+
97
+ def destroy_without_approval! #:nodoc:
98
+ without_approval { |i| destroy! }
99
+ end
100
+
101
+ private
102
+ def approvable_save
103
+ @approval.save if @approval.present? && @approval.new_record?
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,61 @@
1
+ module ActsAsApprovable
2
+ module Model
3
+ ##
4
+ # Instance methods that apply to the :update event specifically.
5
+ module UpdateInstanceMethods
6
+ ##
7
+ # Retrieve all approval records for `:update` events.
8
+ def update_approvals(all = true)
9
+ all ? approvals.find_all_by_event('update') : approvals.find_all_by_event_and_state('update', 0)
10
+ end
11
+
12
+ ##
13
+ # Returns true if the record has any `#update_approvals` that are pending
14
+ # approval.
15
+ def pending_changes?
16
+ !update_approvals(false).empty?
17
+ end
18
+
19
+ ##
20
+ # Returns true if any notable (eg. not ignored) fields have been changed.
21
+ def changed_notably?
22
+ notably_changed.any?
23
+ end
24
+
25
+ ##
26
+ # Returns an array of any notable (eg. not ignored) fields that have not
27
+ # been changed.
28
+ #
29
+ # @return [Array] a list of changed fields.
30
+ def notably_changed
31
+ approvable_fields.select { |field| changed.include?(field) }
32
+ end
33
+
34
+ ##
35
+ # Returns a list of fields that require approval.
36
+ def approvable_fields
37
+ self.class.approvable_fields
38
+ end
39
+
40
+ private
41
+ def approvable_update?
42
+ approvals_enabled? and approvable_on?(:update) and changed_notably?
43
+ end
44
+
45
+ def approvable_update
46
+ changed = {}
47
+ originals = {}
48
+
49
+ notably_changed.each do |attr|
50
+ original, changed_to = changes[attr]
51
+
52
+ write_attribute(attr.to_s, original)
53
+ changed[attr] = changed_to
54
+ originals[attr] = original
55
+ end
56
+
57
+ @approval = approvals.build(:event => 'update', :state => 'pending', :object => changed, :original => originals)
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,141 @@
1
+ module ActsAsApprovable
2
+ ##
3
+ # This module provides the {Approval} class with the ability to assign records
4
+ # as an "owner" of the approval. This is especially useful for tracking purposes
5
+ # when you require it, and can be beneficial when you have an approval queue with
6
+ # a high rate of insertions.
7
+ #
8
+ # By default the ownership functionality will reference a model named `User` and
9
+ # will allow any user to take ownership of an approval.
10
+ module Ownership
11
+ ##
12
+ # Configure approvals to allow ownership by a User model.
13
+ #
14
+ # If a block is given it will be applied to Approval at the class level,
15
+ # allowing you to override functionality on the fly.
16
+ #
17
+ # @param [Hash] options a hash of options for configuration
18
+ # @option options [Object] :model the model being used for Approval records (defaults to `Approval`).
19
+ # @option options [Object] :owner the model being used for owner records (defaults to `User`).
20
+ # @option options [Object] :source class used to override retrieval of owner records.
21
+ def self.configure(options = {}, &block)
22
+ approval = options.delete(:model) { Approval }
23
+ owner = options.delete(:owner) { User }
24
+
25
+ approval.send(:include, self)
26
+
27
+ ActsAsApprovable.owner_class = owner
28
+ ActsAsApprovable.owner_source = options.delete(:source)
29
+
30
+ approval.send(:belongs_to, :owner, :class_name => owner.to_s, :foreign_key => :owner_id)
31
+ end
32
+
33
+ def self.included(base)
34
+ base.send(:include, InstanceMethods)
35
+ base.extend(ClassMethods)
36
+ end
37
+
38
+ ##
39
+ # Instance methods for approval ownership.
40
+ module InstanceMethods
41
+ ##
42
+ # Set the owner and save the record.
43
+ #
44
+ # @return [Boolean]
45
+ def assign(owner)
46
+ raise ActsAsApprovable::Error::InvalidOwner unless self.class.available_owners.include?(owner)
47
+ self.owner = owner
48
+ save
49
+ end
50
+
51
+ ##
52
+ # Removed any assigned owner and save the record.
53
+ #
54
+ # @return [Boolean]
55
+ def unassign
56
+ self.owner = nil
57
+ save
58
+ end
59
+ end
60
+
61
+ ##
62
+ # Class methods for approval ownership.
63
+ module ClassMethods
64
+ ##
65
+ # Get the model that represents an owner.
66
+ #
67
+ # @see ActsAsApprovable::Ownership.configure
68
+ def owner_class
69
+ ActsAsApprovable.owner_class
70
+ end
71
+
72
+ ##
73
+ # Source class used to override Owner retrieval methods
74
+ #
75
+ # @see ActsAsApprovable::Ownership.configure
76
+ def owner_source
77
+ ActsAsApprovable.owner_source
78
+ end
79
+
80
+ ##
81
+ # Attempt to run a method on the configured #owner_source class. If it does
82
+ # not exist yield to the given block.
83
+ def with_owner_source(method, *args)
84
+ if owner_source && owner_source.singleton_class.method_defined?(method)
85
+ owner_source.send(method, *args)
86
+ else
87
+ yield
88
+ end
89
+ end
90
+
91
+ ##
92
+ # A list of records that can be assigned to an approval.
93
+ #
94
+ # This method can be overriden by the configured #owner_source.
95
+ def available_owners
96
+ with_owner_source(:available_owners) { owner_class.all }
97
+ end
98
+
99
+ ##
100
+ # Build an array from {#available_owners} usable by Rails' `#options_for_select`.
101
+ # Each element in the array is built with {#option_for_owner}.
102
+ #
103
+ # @return [Array]
104
+ def options_for_available_owners(with_prompt = false)
105
+ owners = available_owners.map { |owner| option_for_owner(owner) }
106
+ owners.unshift(['(none)', nil]) if with_prompt
107
+ owners
108
+ end
109
+
110
+ ##
111
+ # A list of owners that have assigned approvals.
112
+ #
113
+ # This method can be overriden by the configured #owner_source.
114
+ def assigned_owners
115
+ with_owner_source(:assigned_owners) { all(:select => 'DISTINCT(owner_id)', :conditions => 'owner_id IS NOT NULL', :include => :owner).map(&:owner) }
116
+ end
117
+
118
+ ##
119
+ # Build an array from {#assigned_owners} usable by Rails' `#options_for_select`.
120
+ # Each element in the array is built with {#option_for_owner}.
121
+ #
122
+ # @return [Array]
123
+ def options_for_assigned_owners(with_prompt = false)
124
+ owners = assigned_owners.map { |owner| option_for_owner(owner) }
125
+ owners.unshift(['All Users', nil]) if with_prompt
126
+ owners
127
+ end
128
+
129
+ ##
130
+ # Helper method that takes an owner record and returns an array for Rails'
131
+ # `#options_for_select`.
132
+ #
133
+ # This method can be overriden by the configured #owner_source.
134
+ #
135
+ # @return [Array] a 2-index array with a display string and value.
136
+ def option_for_owner(owner)
137
+ with_owner_source(:option_for_owner, owner) { [owner.to_str, owner.id] }
138
+ end
139
+ end
140
+ end
141
+ end