use a meaningful context for confirmation notifications
fixes #5711 also cleans up broadcast policy a bit * remove cruft * re-organize so that the DSL doesn't become methods on the model * add context method to the DSL that becomes asset_context on the message * only evaluate the policy block once, instead of every time a model that has a broadcast policy is saved test plan: * add an e-mail address to a user, and check the "I want to login" checkbox * the notification e-mail should link back to the correct domain Change-Id: I484d07963c86c81f9b0226fd942ef9a71ac5500c Reviewed-on: https://gerrit.instructure.com/8830 Tested-by: Hudson <hudson@instructure.com> Reviewed-by: Bracken Mosbacker <bracken@instructure.com> Reviewed-by: Brian Palmer <brianp@instructure.com>
This commit is contained in:
parent
dadfce7c87
commit
b48b122438
|
@ -37,7 +37,7 @@ class CommunicationChannelsController < ApplicationController
|
|||
@cc.workflow_state = 'unconfirmed'
|
||||
@cc.build_pseudonym_on_confirm = params[:build_pseudonym] == '1'
|
||||
if @cc.save
|
||||
@cc.send_confirmation!
|
||||
@cc.send_confirmation!(@domain_root_account)
|
||||
flash[:notice] = "Contact method registered!"
|
||||
render :json => @cc.to_json(:only => [:id, :user_id, :path, :path_type])
|
||||
else
|
||||
|
@ -172,7 +172,7 @@ class CommunicationChannelsController < ApplicationController
|
|||
new_cc ||= @user.communication_channels.build(:path => @pseudonym.unique_id)
|
||||
new_cc.user = @user
|
||||
new_cc.workflow_state = 'unconfirmed' if new_cc.retired?
|
||||
new_cc.send_confirmation! if new_cc.unconfirmed?
|
||||
new_cc.send_confirmation!(@root_account) if new_cc.unconfirmed?
|
||||
new_cc.save! if new_cc.changed?
|
||||
@pseudonym.communication_channel = new_cc
|
||||
end
|
||||
|
@ -220,7 +220,7 @@ class CommunicationChannelsController < ApplicationController
|
|||
@enrollment.re_send_confirmation!
|
||||
else
|
||||
@cc = @user.communication_channels.find(params[:id])
|
||||
@cc.send_confirmation!
|
||||
@cc.send_confirmation!(@domain_root_account)
|
||||
end
|
||||
render :json => {:re_sent => true}
|
||||
end
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<% define_content :link do %>
|
||||
http://<%= HostUrl.context_host(((asset.pseudonym || asset.user.pseudonym).account rescue nil)) %>/register/<%= asset.confirmation_code %>
|
||||
http://<%= HostUrl.context_host(asset_context) || HostUrl.default_host %>/register/<%= asset.confirmation_code %>
|
||||
<% end %>
|
||||
|
||||
<% define_content :subject do %>
|
||||
|
@ -9,7 +9,7 @@
|
|||
<%= t :body,
|
||||
"The email address, %{email} is being registered at %{website} for the user, %{user}.",
|
||||
:email => asset.path,
|
||||
:website => (HostUrl.context_host((asset.pseudonym || asset.user.pseudonym).account) rescue nil) || HostUrl.default_host,
|
||||
:website => HostUrl.context_host(asset_context) || HostUrl.default_host,
|
||||
:user => asset.user.name %>
|
||||
|
||||
<%= t :details, "To confirm this registration, please visit the following url:" %>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<%= t :body, <<-BODY, :confirmation_code => asset.confirmation_code, :user => asset.user.name, :website => (HostUrl.context_host((asset.pseudonym || asset.user.pseudonym).account) rescue nil) || HostUrl.default_host
|
||||
<%= t :body, <<-BODY, :confirmation_code => asset.confirmation_code, :user => asset.user.name, :website => HostUrl.context_host(asset_context) || HostUrl.default_host
|
||||
Confirm Canvas registration w/ code:
|
||||
|
||||
%{confirmation_code}
|
||||
|
|
|
@ -70,6 +70,7 @@ class CommunicationChannel < ActiveRecord::Base
|
|||
record.workflow_state == 'unconfirmed' and self.user.registered? and
|
||||
self.path_type == 'email'
|
||||
}
|
||||
p.context { @root_account }
|
||||
|
||||
p.dispatch :merge_email_communication_channel
|
||||
p.to { self }
|
||||
|
@ -86,8 +87,9 @@ class CommunicationChannel < ActiveRecord::Base
|
|||
self.path_type == 'sms' and
|
||||
!self.user.creation_pending?
|
||||
}
|
||||
p.context { @root_account }
|
||||
end
|
||||
|
||||
|
||||
def active_pseudonyms
|
||||
self.user.pseudonyms.active
|
||||
end
|
||||
|
@ -128,9 +130,11 @@ class CommunicationChannel < ActiveRecord::Base
|
|||
@request_password = false
|
||||
end
|
||||
|
||||
def send_confirmation!
|
||||
def send_confirmation!(root_account)
|
||||
@send_confirmation = true
|
||||
@root_account = root_account
|
||||
self.save!
|
||||
@root_account = nil
|
||||
@send_confirmation = false
|
||||
end
|
||||
|
||||
|
|
|
@ -20,7 +20,8 @@ class DelayedNotification < ActiveRecord::Base
|
|||
include Workflow
|
||||
belongs_to :asset, :polymorphic => true
|
||||
belongs_to :notification
|
||||
attr_accessible :asset, :notification, :recipient_keys
|
||||
belongs_to :asset_context, :polymorphic => true
|
||||
attr_accessible :asset, :notification, :recipient_keys, :asset_context
|
||||
|
||||
serialize :recipient_keys
|
||||
|
||||
|
@ -32,14 +33,15 @@ class DelayedNotification < ActiveRecord::Base
|
|||
state :errored
|
||||
end
|
||||
|
||||
def self.process(asset, notification, recipient_keys)
|
||||
dn = DelayedNotification.new(:asset => asset, :notification => notification, :recipient_keys => recipient_keys)
|
||||
def self.process(asset, notification, recipient_keys, asset_context)
|
||||
dn = DelayedNotification.new(:asset => asset, :notification => notification, :recipient_keys => recipient_keys,
|
||||
:asset_context => asset_context)
|
||||
dn.process
|
||||
end
|
||||
|
||||
def process
|
||||
tos = self.to_list
|
||||
res = self.notification.create_message(self.asset, tos) if self.asset && !tos.empty?
|
||||
res = self.notification.create_message(self.asset, tos, :asset_context => self.asset_context) if self.asset && !tos.empty?
|
||||
self.do_process unless self.new_record?
|
||||
res
|
||||
rescue => e
|
||||
|
|
|
@ -196,7 +196,7 @@ class Notification < ActiveRecord::Base
|
|||
:subject => self.subject,
|
||||
:to => to_path
|
||||
)
|
||||
|
||||
|
||||
message.body = self.body
|
||||
message.body = self.sms_body if c.respond_to?("path_type") && c.path_type == "sms"
|
||||
message.notification_name = self.name
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
class AddAssetContextToDelayedNotification < ActiveRecord::Migration
|
||||
tag :predeploy
|
||||
|
||||
def self.up
|
||||
add_column :delayed_notifications, :asset_context_type, :string
|
||||
add_column :delayed_notifications, :asset_context_id, :integer, :limit => 8
|
||||
end
|
||||
|
||||
def self.down
|
||||
remove_column :delayed_notifications, :asset_context_id
|
||||
remove_column :delayed_notifications, :asset_context_type
|
||||
end
|
||||
end
|
|
@ -25,12 +25,6 @@ describe Announcement do
|
|||
end
|
||||
|
||||
context "broadcast policy" do
|
||||
it "should have a broadcast policy" do
|
||||
announcement_model
|
||||
@a.should be_respond_to(:dispatch)
|
||||
@a.should be_respond_to(:to)
|
||||
end
|
||||
|
||||
it "should sanitize message" do
|
||||
announcement_model
|
||||
@a.message = "<a href='#' onclick='alert(12);'>only this should stay</a>"
|
||||
|
|
|
@ -35,17 +35,6 @@ describe AssignmentGroup do
|
|||
end
|
||||
|
||||
context "broadcast policy" do
|
||||
it "should have a broadcast policy" do
|
||||
assignment_group_model
|
||||
@ag.should be_respond_to(:dispatch)
|
||||
@ag.should be_respond_to(:to)
|
||||
end
|
||||
|
||||
# it "should have 1 policy defined" do
|
||||
# assignment_group_model
|
||||
# @ag.broadcast_policy_list.size.should eql(1)
|
||||
# end
|
||||
|
||||
context "grade weight changed" do
|
||||
# it "should have a 'Grade Weight Changed' policy" do
|
||||
# assignment_group_model
|
||||
|
|
|
@ -1028,12 +1028,6 @@ describe Assignment do
|
|||
end
|
||||
|
||||
context "broadcast policy" do
|
||||
it "should have a broadcast policy" do
|
||||
assignment_model
|
||||
@a.should be_respond_to(:dispatch)
|
||||
@a.should be_respond_to(:to)
|
||||
end
|
||||
|
||||
context "due date changed" do
|
||||
it "should create a message when an assignment due date has changed" do
|
||||
Notification.create(:name => 'Assignment Due Date Changed')
|
||||
|
|
|
@ -311,4 +311,20 @@ describe CommunicationChannel do
|
|||
# the unconfirmed should still be valid, even though a retired exists
|
||||
@cc.should be_valid
|
||||
end
|
||||
|
||||
context "notifications" do
|
||||
it "should forward the root account to the message" do
|
||||
notification = Notification.create!(:name => 'Confirm Email Communication Channel', :category => 'Registration')
|
||||
@user = User.create!
|
||||
@user.register!
|
||||
@cc = @user.communication_channels.create!(:path => 'user1@example.com')
|
||||
account = Account.create!
|
||||
HostUrl.stubs(:context_host).with(account).returns('someserver.com')
|
||||
HostUrl.stubs(:context_host).with(nil).returns('default')
|
||||
@cc.send_confirmation!(account)
|
||||
message = Message.find(:first, :conditions => { :communication_channel_id => @cc.id, :notification_id => notification.id })
|
||||
message.should_not be_nil
|
||||
message.body.should match /someserver.com/
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -117,12 +117,6 @@ describe Submission do
|
|||
end
|
||||
|
||||
context "broadcast policy" do
|
||||
it "should have a broadcast policy" do
|
||||
submission_spec_model
|
||||
@submission.should be_respond_to(:dispatch)
|
||||
@submission.should be_respond_to(:to)
|
||||
end
|
||||
|
||||
context "Assignment Submitted Late" do
|
||||
it "should create a message when the assignment is turned in late" do
|
||||
Notification.create(:name => 'Assignment Submitted Late')
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
require 'rake'
|
||||
require 'rake/testtask'
|
||||
require 'rake/rdoctask'
|
||||
|
||||
desc 'Default: run specs.'
|
||||
task :default => :spec
|
||||
|
||||
desc 'Run specs'
|
||||
task :spec do
|
||||
`spec spec/`
|
||||
end
|
||||
|
||||
desc 'Test the adheres_to_policy plugin.'
|
||||
Rake::TestTask.new(:test) do |t|
|
||||
t.libs << 'lib'
|
||||
t.pattern = 'test/**/*_test.rb'
|
||||
t.verbose = true
|
||||
end
|
||||
|
||||
desc 'Generate documentation for the adheres_to_policy plugin.'
|
||||
Rake::RDocTask.new(:rdoc) do |rdoc|
|
||||
rdoc.rdoc_dir = 'rdoc'
|
||||
rdoc.title = 'AdheresToPolicy'
|
||||
rdoc.options << '--line-numbers' << '--inline-source'
|
||||
rdoc.rdoc_files.include('README')
|
||||
rdoc.rdoc_files.include('lib/**/*.rb')
|
||||
end
|
|
@ -1,2 +1,2 @@
|
|||
require 'broadcast_policy'
|
||||
ActiveRecord::Base.send :extend, Instructure::Broadcast::Policy::ClassMethods
|
||||
ActiveRecord::Base.send :extend, Instructure::BroadcastPolicy::ClassMethods
|
|
@ -16,323 +16,331 @@
|
|||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
# This should work like this:
|
||||
#
|
||||
# class Account < ActiveRecord::Base
|
||||
# has_a_broadcast_policy
|
||||
#
|
||||
# set_broadcast_policy do
|
||||
# dispatch(:name)
|
||||
# to { some_list }
|
||||
# whenever { |obj| obj.something == condition }
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# Some useful examples:
|
||||
#
|
||||
# set_broadcast_policy do
|
||||
# dispatch :new_assignment
|
||||
# to { self.students }
|
||||
# whenever { |record| record.just_created? }
|
||||
# end
|
||||
#
|
||||
# set_broadcast_policy do
|
||||
# dispatch :assignment_change
|
||||
# to { self.students }
|
||||
# whenever { |record|
|
||||
# record.prior_version != self.version and true
|
||||
# # ... some field-wise comparison
|
||||
# }
|
||||
# end
|
||||
#
|
||||
# u = User.find(:first)
|
||||
# a = Account.find(:first)
|
||||
# a.check_policy(u)
|
||||
|
||||
module Instructure #:nodoc:
|
||||
module Broadcast #:nodoc:
|
||||
# This should work like this:
|
||||
#
|
||||
# class Account < ActiveRecord::Base
|
||||
# has_a_broadcast_policy
|
||||
#
|
||||
# set_broadcast_policy do
|
||||
# dispatch(:name)
|
||||
# to { some_list }
|
||||
# whenever { |obj| obj.something == condition }
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# Some useful examples:
|
||||
#
|
||||
# set_broadcast_policy do
|
||||
# dispatch :new_assignment
|
||||
# to { self.students }
|
||||
# whenever { |record| record.just_created? }
|
||||
# end
|
||||
#
|
||||
# set_broadcast_policy do
|
||||
# dispatch :assignment_change
|
||||
# to { self.students }
|
||||
# whenever { |record|
|
||||
# record.prior_version != self.version and true
|
||||
# # ... some field-wise comparison
|
||||
# }
|
||||
# end
|
||||
#
|
||||
# u = User.find(:first)
|
||||
# a = Account.find(:first)
|
||||
# a.check_policy(u)
|
||||
module Policy
|
||||
|
||||
class PolicyStorage
|
||||
|
||||
attr_accessor :dispatch, :to, :whenever
|
||||
|
||||
def initialize(dispatch)
|
||||
self.dispatch = dispatch
|
||||
end
|
||||
|
||||
# This should be called for an instance. It can only be sent out if the
|
||||
# condition is met, if there is a notification that we can find, and if
|
||||
# there is someone to send this to. At this point, a Message record is
|
||||
# created, which will be delayed, consolidated, dispatched to the right
|
||||
# server, and then finally sent through that server.
|
||||
#
|
||||
# This now sets a series of temporary flags while working for audit
|
||||
# reasons.
|
||||
def broadcast(record)
|
||||
if (record.skip_broadcasts rescue false)
|
||||
record.messages_failed[self.dispatch] = "Broadcasting explicitly skipped"
|
||||
return false
|
||||
end
|
||||
begin
|
||||
meets_condition = self.whenever.call(record)
|
||||
rescue
|
||||
meets_condition = false
|
||||
record.messages_failed[self.dispatch] = "Error thrown attempting to meet condition."
|
||||
return false
|
||||
end
|
||||
module BroadcastPolicy #:nodoc:
|
||||
|
||||
unless meets_condition
|
||||
record.messages_failed[self.dispatch] = "Did not meet condition."
|
||||
return false
|
||||
end
|
||||
notification = record.notifications.find_by_name(self.dispatch) rescue nil
|
||||
notification ||= Notification.find_by_name(self.dispatch)
|
||||
# logger.warn "Could not find notification for #{record.inspect}" unless notification
|
||||
unless notification
|
||||
record.messages_failed[self.dispatch] = "Could not find notification: #{self.dispatch}."
|
||||
return false
|
||||
end
|
||||
# self.consolidated_notifications[notification_name.to_s.titleize] rescue nil
|
||||
|
||||
begin
|
||||
to_list = self.to.call(record)
|
||||
rescue
|
||||
to_list = nil
|
||||
record.messages_failed[self.dispatch] = "Error thrown attempting to generate a recipient list."
|
||||
return false
|
||||
end
|
||||
unless to_list
|
||||
record.messages_failed[self.dispatch] = "Could not generate a recipient list."
|
||||
return false
|
||||
end
|
||||
to_list = Array[to_list].flatten
|
||||
n = DelayedNotification.send_later_if_production_enqueue_args(
|
||||
:process,
|
||||
{ :priority => Delayed::LOW_PRIORITY },
|
||||
record, notification, (to_list || []).compact.map(&:asset_string))
|
||||
n ||= DelayedNotification.new(:asset => record, :notification => notification, :recipient_keys => (to_list || []).compact.map(&:asset_string))
|
||||
if Rails.env.test?
|
||||
record.messages_sent[self.dispatch] = n.is_a?(DelayedNotification) ? n.process : n
|
||||
end
|
||||
n
|
||||
# notification.create_message(record, to_list)
|
||||
end
|
||||
|
||||
end # PolicyStorage
|
||||
|
||||
module ClassMethods #:nodoc:
|
||||
def has_a_broadcast_policy
|
||||
extend Instructure::Broadcast::Policy::SingletonMethods
|
||||
include Instructure::Broadcast::Policy::InstanceMethods
|
||||
after_save :broadcast_notifications # Must be defined locally...
|
||||
before_save :set_broadcast_flags
|
||||
end
|
||||
|
||||
# Uses the 'context' relationship as the governing relationship.
|
||||
# Canonically, this probably will look something like:
|
||||
# course -> professor -> section -> department -> account.
|
||||
# So, this method recurses the list, keeping the nearer values. So,
|
||||
# account can setup a series of default notifications, but a professor
|
||||
# can override these.
|
||||
|
||||
# Removing for now, until we memoize this
|
||||
# def consolidated_notifications
|
||||
# instance_notifications = Notification.find_all_by_context_type_and_context_id(self.class.to_s, self.id)
|
||||
# class_notifications = Notification.find_all_by_context_type_and_context_id(self.class.to_s, nil)
|
||||
# context_notifications = context.consolidated_notifications if
|
||||
# defined?(context) and context.respond_to?(:consolidated_notifications)
|
||||
# context_notifications ||= []
|
||||
#
|
||||
# cn = hashify(*instance_notifications)
|
||||
# cn.reverse_merge!(*class_notifications)
|
||||
# cn.reverse_merge!(*context_notifications)
|
||||
# cn
|
||||
# end
|
||||
#
|
||||
# def hashify(*list)
|
||||
# list.inject({}) {|h, v| h[v.name] = v; h}
|
||||
# end
|
||||
# protected :hashify
|
||||
|
||||
class PolicyList
|
||||
def initialize
|
||||
@notifications = []
|
||||
end
|
||||
|
||||
# This is where the DSL is defined.
|
||||
module SingletonMethods
|
||||
|
||||
def self.extended(klass)
|
||||
klass.send(:class_inheritable_accessor, :broadcast_policy_block)
|
||||
end
|
||||
|
||||
# This stores the policy for broadcasting changes on a class. It works like a
|
||||
# macro. The policy block will be stored in @broadcast_policy_block. Then, an
|
||||
# instance will use that to instantiate a Policy object.
|
||||
def set_broadcast_policy(&block)
|
||||
self.broadcast_policy_block = block
|
||||
end
|
||||
|
||||
end # SingletonMethods
|
||||
|
||||
module InstanceMethods
|
||||
|
||||
# Some generic flags for inside the policy
|
||||
attr_accessor :just_created, :prior_version
|
||||
|
||||
# Some flags for auditing policy matching
|
||||
def messages_sent
|
||||
@messages_sent ||= {}
|
||||
def populate(&block)
|
||||
self.instance_eval(&block)
|
||||
@current_notification = nil
|
||||
end
|
||||
|
||||
def broadcast(record)
|
||||
@notifications.each { |notification| notification.broadcast(record) }
|
||||
end
|
||||
|
||||
def dispatch(notification_name)
|
||||
titleized = notification_name.to_s.titleize.gsub(/sms/i, "SMS")
|
||||
@current_notification = @notifications.find { |notification| notification.dispatch == titleized }
|
||||
return if @current_notification
|
||||
@current_notification = NotificationPolicy.new(titleized)
|
||||
@notifications << @current_notification
|
||||
end
|
||||
|
||||
def current_notification
|
||||
raise "Must call dispatch in the policy block first" unless @current_notification
|
||||
@current_notification
|
||||
end
|
||||
protected :current_notification
|
||||
|
||||
def to(&block)
|
||||
self.current_notification.to = block
|
||||
end
|
||||
|
||||
def whenever(&block)
|
||||
self.current_notification.whenever = block
|
||||
end
|
||||
|
||||
def context(&block)
|
||||
self.current_notification.context = block
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class NotificationPolicy
|
||||
attr_accessor :dispatch, :to, :whenever, :context
|
||||
|
||||
def initialize(dispatch)
|
||||
self.dispatch = dispatch
|
||||
end
|
||||
|
||||
# This should be called for an instance. It can only be sent out if the
|
||||
# condition is met, if there is a notification that we can find, and if
|
||||
# there is someone to send this to. At this point, a Message record is
|
||||
# created, which will be delayed, consolidated, dispatched to the right
|
||||
# server, and then finally sent through that server.
|
||||
#
|
||||
# This now sets a series of temporary flags while working for audit
|
||||
# reasons.
|
||||
def broadcast(record)
|
||||
if (record.skip_broadcasts rescue false)
|
||||
record.messages_failed[self.dispatch] = "Broadcasting explicitly skipped"
|
||||
return false
|
||||
end
|
||||
|
||||
def clear_broadcast_messages
|
||||
@messages_sent = {}
|
||||
@messages_failed = {}
|
||||
end
|
||||
|
||||
# Whenever a requirement fails, this is stored here.
|
||||
def messages_failed
|
||||
@messages_failed ||= {}
|
||||
begin
|
||||
meets_condition = record.instance_eval &self.whenever
|
||||
rescue
|
||||
meets_condition = false
|
||||
record.messages_failed[self.dispatch] = "Error thrown attempting to meet condition."
|
||||
return false
|
||||
end
|
||||
|
||||
# This is called before_save
|
||||
def set_broadcast_flags
|
||||
@broadcasted = false
|
||||
unless @skip_broadcasts
|
||||
self.just_created = self.new_record?
|
||||
self.prior_version = generate_prior_version
|
||||
end
|
||||
unless meets_condition
|
||||
record.messages_failed[self.dispatch] = "Did not meet condition."
|
||||
return false
|
||||
end
|
||||
|
||||
def generate_prior_version
|
||||
obj = self.class.new
|
||||
self.attributes.each do |attr, value|
|
||||
obj.__send__("#{attr}=", value) rescue nil
|
||||
end
|
||||
self.changes.each do |attr, values|
|
||||
obj.__send__("#{attr}=", values[0]) rescue nil
|
||||
end
|
||||
obj.workflow_state = self.workflow_state_was if obj.respond_to?("workflow_state=") && self.respond_to?("workflow_state_was")
|
||||
obj
|
||||
notification = record.notifications.find_by_name(self.dispatch) rescue nil
|
||||
notification ||= Notification.find_by_name(self.dispatch)
|
||||
# logger.warn "Could not find notification for #{record.inspect}" unless notification
|
||||
unless notification
|
||||
record.messages_failed[self.dispatch] = "Could not find notification: #{self.dispatch}."
|
||||
return false
|
||||
end
|
||||
|
||||
# This is called after_save
|
||||
def broadcast_notifications
|
||||
return if @broadcasted
|
||||
@broadcasted = true
|
||||
raise ArgumentError, "Broadcast Policy block not supplied for #{self.class.to_s}" unless self.class.broadcast_policy_block
|
||||
# our common pattern is to do:
|
||||
# set_broadcast_policy do |p|
|
||||
# ...
|
||||
# end
|
||||
# note that p is really just self, in that block
|
||||
self.instance_eval &self.class.broadcast_policy_block
|
||||
self.broadcast_policy_list.each {|p| p.broadcast(self) }
|
||||
self.broadcast_policy_list.clear
|
||||
# self.consolidated_notifications[notification_name.to_s.titleize] rescue nil
|
||||
begin
|
||||
to_list = record.instance_eval &self.to
|
||||
rescue
|
||||
to_list = nil
|
||||
record.messages_failed[self.dispatch] = "Error thrown attempting to generate a recipient list."
|
||||
return false
|
||||
end
|
||||
unless to_list
|
||||
record.messages_failed[self.dispatch] = "Could not generate a recipient list."
|
||||
return false
|
||||
end
|
||||
to_list = Array[to_list].flatten
|
||||
|
||||
begin
|
||||
asset_context = record.instance_eval &self.context if self.context
|
||||
rescue
|
||||
record.messages_failed[self.dispatch] = "Error thrown attempting to get asset_context."
|
||||
return false
|
||||
end
|
||||
|
||||
def broadcast_policy_list
|
||||
@broadcast_policy_list ||= []
|
||||
n = DelayedNotification.send_later_if_production_enqueue_args(
|
||||
:process,
|
||||
{ :priority => Delayed::LOW_PRIORITY },
|
||||
record, notification, (to_list || []).compact.map(&:asset_string), asset_context)
|
||||
n ||= DelayedNotification.new(:asset => record, :notification => notification,
|
||||
:recipient_keys => (to_list || []).compact.map(&:asset_string),
|
||||
:asset_context => asset_context)
|
||||
if Rails.env.test?
|
||||
record.messages_sent[self.dispatch] = n.is_a?(DelayedNotification) ? n.process : n
|
||||
end
|
||||
n
|
||||
# notification.create_message(record, to_list)
|
||||
end
|
||||
end # NotificationPolicy
|
||||
|
||||
# If this is nil, we don't worry about trying to implement anything.
|
||||
def dispatch(notification_name)
|
||||
found = self.broadcast_policy_list.find {|bp| bp.dispatch == titleized(notification_name)}
|
||||
return found if found
|
||||
self.broadcast_policy_list << PolicyStorage.new(titleized(notification_name))
|
||||
end
|
||||
|
||||
def titleized(notification_name)
|
||||
notification_name.to_s.titleize.gsub(/sms/i, "SMS")
|
||||
end
|
||||
protected :titleized
|
||||
|
||||
def implementing_policy
|
||||
if not self.broadcast_policy_list.last
|
||||
# This really shouldn't happen, if a policy is setup right, but it
|
||||
# should be logged and silently fail.
|
||||
self.broadcast_policy_list << PolicyStorage.new("unknown")
|
||||
end
|
||||
self.broadcast_policy_list.last
|
||||
end
|
||||
|
||||
def to(&block)
|
||||
self.implementing_policy.to = block
|
||||
end
|
||||
|
||||
def whenever(&block)
|
||||
self.implementing_policy.whenever = block
|
||||
end
|
||||
|
||||
attr_accessor :skip_broadcasts
|
||||
|
||||
def save_without_broadcasting
|
||||
@skip_broadcasts = true
|
||||
self.save
|
||||
@skip_broadcasts = false
|
||||
end
|
||||
|
||||
def save_without_broadcasting!
|
||||
@skip_broadcasts = true
|
||||
self.save!
|
||||
@skip_broadcasts = false
|
||||
end
|
||||
|
||||
# The rest of the methods here should just be helper methods to make
|
||||
# writing a condition that much easier.
|
||||
def changed_in_state(state, opts={})
|
||||
fields = opts[:fields] || []
|
||||
fields = [fields] unless fields.is_a?(Array)
|
||||
module ClassMethods #:nodoc:
|
||||
def has_a_broadcast_policy
|
||||
extend Instructure::BroadcastPolicy::SingletonMethods
|
||||
include Instructure::BroadcastPolicy::InstanceMethods
|
||||
after_save :broadcast_notifications # Must be defined locally...
|
||||
before_save :set_broadcast_flags
|
||||
end
|
||||
|
||||
# Come back to this to debug some of the notifications
|
||||
# if fields == [:due_at]
|
||||
# require 'rubygems'
|
||||
# require 'ruby-debug'
|
||||
# debugger
|
||||
# 1 + 1
|
||||
# end
|
||||
|
||||
begin
|
||||
fields.map {|field| self.prior_version.send(field) != self.send(field) }.include?(true) and
|
||||
self.workflow_state == state.to_s and
|
||||
self.prior_version.workflow_state == state.to_s
|
||||
rescue Exception => e
|
||||
logger.warn "Could not check if a change was made: #{e.inspect}"
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def changed_in_states(states, opts={})
|
||||
!states.select{|s| changed_in_state(s, opts)}.empty?
|
||||
end
|
||||
# Uses the 'context' relationship as the governing relationship.
|
||||
# Canonically, this probably will look something like:
|
||||
# course -> professor -> section -> department -> account.
|
||||
# So, this method recurses the list, keeping the nearer values. So,
|
||||
# account can setup a series of default notifications, but a professor
|
||||
# can override these.
|
||||
|
||||
def remained_in_state(state)
|
||||
begin
|
||||
self.workflow_state == state.to_s and
|
||||
self.prior_version.workflow_state == state.to_s
|
||||
rescue Exception => e
|
||||
logger.warn "Could not check if a record remained in the same state: #{e.inspect}"
|
||||
false
|
||||
end
|
||||
# Removing for now, until we memoize this
|
||||
# def consolidated_notifications
|
||||
# instance_notifications = Notification.find_all_by_context_type_and_context_id(self.class.to_s, self.id)
|
||||
# class_notifications = Notification.find_all_by_context_type_and_context_id(self.class.to_s, nil)
|
||||
# context_notifications = context.consolidated_notifications if
|
||||
# defined?(context) and context.respond_to?(:consolidated_notifications)
|
||||
# context_notifications ||= []
|
||||
#
|
||||
# cn = hashify(*instance_notifications)
|
||||
# cn.reverse_merge!(*class_notifications)
|
||||
# cn.reverse_merge!(*context_notifications)
|
||||
# cn
|
||||
# end
|
||||
#
|
||||
# def hashify(*list)
|
||||
# list.inject({}) {|h, v| h[v.name] = v; h}
|
||||
# end
|
||||
# protected :hashify
|
||||
|
||||
end
|
||||
|
||||
# This is where the DSL is defined.
|
||||
module SingletonMethods
|
||||
|
||||
def self.extended(klass)
|
||||
klass.send(:class_inheritable_accessor, :broadcast_policy_list)
|
||||
end
|
||||
|
||||
# This stores the policy for broadcasting changes on a class. It works like a
|
||||
# macro. The policy block will be stored in @broadcast_policy.
|
||||
def set_broadcast_policy(&block)
|
||||
self.broadcast_policy_list = PolicyList.new
|
||||
self.broadcast_policy_list.populate(&block)
|
||||
end
|
||||
|
||||
end # SingletonMethods
|
||||
|
||||
module InstanceMethods
|
||||
|
||||
# Some generic flags for inside the policy
|
||||
attr_accessor :just_created, :prior_version
|
||||
|
||||
# Some flags for auditing policy matching
|
||||
def messages_sent
|
||||
@messages_sent ||= {}
|
||||
end
|
||||
|
||||
def clear_broadcast_messages
|
||||
@messages_sent = {}
|
||||
@messages_failed = {}
|
||||
end
|
||||
|
||||
# Whenever a requirement fails, this is stored here.
|
||||
def messages_failed
|
||||
@messages_failed ||= {}
|
||||
end
|
||||
|
||||
# This is called before_save
|
||||
def set_broadcast_flags
|
||||
@broadcasted = false
|
||||
unless @skip_broadcasts
|
||||
self.just_created = self.new_record?
|
||||
self.prior_version = generate_prior_version
|
||||
end
|
||||
|
||||
def changed_state(new_state=nil, old_state=nil)
|
||||
begin
|
||||
if new_state and old_state
|
||||
self.workflow_state == new_state.to_s and
|
||||
self.prior_version.workflow_state == old_state.to_s
|
||||
elsif new_state
|
||||
self.workflow_state.to_s == new_state.to_s and
|
||||
self.prior_version.workflow_state != self.workflow_state
|
||||
else
|
||||
self.workflow_state != self.prior_version.workflow_state
|
||||
end
|
||||
rescue Exception => e
|
||||
logger.warn "Could not check if a record changed state: #{e.inspect}"
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def generate_prior_version
|
||||
obj = self.class.new
|
||||
self.attributes.each do |attr, value|
|
||||
obj.__send__("#{attr}=", value) rescue nil
|
||||
end
|
||||
alias :changed_state_to :changed_state
|
||||
|
||||
|
||||
end # InstanceMethods
|
||||
end # Policy
|
||||
end # Adheres
|
||||
self.changes.each do |attr, values|
|
||||
obj.__send__("#{attr}=", values[0]) rescue nil
|
||||
end
|
||||
obj.workflow_state = self.workflow_state_was if obj.respond_to?("workflow_state=") && self.respond_to?("workflow_state_was")
|
||||
obj
|
||||
end
|
||||
|
||||
# This is called after_save
|
||||
def broadcast_notifications
|
||||
return if @broadcasted
|
||||
@broadcasted = true
|
||||
raise ArgumentError, "Broadcast Policy block not supplied for #{self.class.to_s}" unless self.class.broadcast_policy_list
|
||||
self.class.broadcast_policy_list.broadcast(self)
|
||||
end
|
||||
|
||||
attr_accessor :skip_broadcasts
|
||||
|
||||
def save_without_broadcasting
|
||||
@skip_broadcasts = true
|
||||
self.save
|
||||
@skip_broadcasts = false
|
||||
end
|
||||
|
||||
def save_without_broadcasting!
|
||||
@skip_broadcasts = true
|
||||
self.save!
|
||||
@skip_broadcasts = false
|
||||
end
|
||||
|
||||
# The rest of the methods here should just be helper methods to make
|
||||
# writing a condition that much easier.
|
||||
def changed_in_state(state, opts={})
|
||||
fields = opts[:fields] || []
|
||||
fields = [fields] unless fields.is_a?(Array)
|
||||
|
||||
# Come back to this to debug some of the notifications
|
||||
# if fields == [:due_at]
|
||||
# require 'rubygems'
|
||||
# require 'ruby-debug'
|
||||
# debugger
|
||||
# 1 + 1
|
||||
# end
|
||||
|
||||
begin
|
||||
fields.map {|field| self.prior_version.send(field) != self.send(field) }.include?(true) and
|
||||
self.workflow_state == state.to_s and
|
||||
self.prior_version.workflow_state == state.to_s
|
||||
rescue Exception => e
|
||||
logger.warn "Could not check if a change was made: #{e.inspect}"
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def changed_in_states(states, opts={})
|
||||
!states.select{|s| changed_in_state(s, opts)}.empty?
|
||||
end
|
||||
|
||||
def remained_in_state(state)
|
||||
begin
|
||||
self.workflow_state == state.to_s and
|
||||
self.prior_version.workflow_state == state.to_s
|
||||
rescue Exception => e
|
||||
logger.warn "Could not check if a record remained in the same state: #{e.inspect}"
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def changed_state(new_state=nil, old_state=nil)
|
||||
begin
|
||||
if new_state and old_state
|
||||
self.workflow_state == new_state.to_s and
|
||||
self.prior_version.workflow_state == old_state.to_s
|
||||
elsif new_state
|
||||
self.workflow_state.to_s == new_state.to_s and
|
||||
self.prior_version.workflow_state != self.workflow_state
|
||||
else
|
||||
self.workflow_state != self.prior_version.workflow_state
|
||||
end
|
||||
rescue Exception => e
|
||||
logger.warn "Could not check if a record changed state: #{e.inspect}"
|
||||
false
|
||||
end
|
||||
end
|
||||
alias :changed_state_to :changed_state
|
||||
|
||||
|
||||
end # InstanceMethods
|
||||
end # BroadcastPolicy
|
||||
end # Instructure
|
||||
|
|
|
@ -1,234 +0,0 @@
|
|||
#
|
||||
# Copyright (C) 2011 Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
# Commenting out the parts I'm now avoiding.
|
||||
module Instructure #:nodoc:
|
||||
module Broadcast #:nodoc:
|
||||
# This should work like this:
|
||||
#
|
||||
# class Account < ActiveRecord::Base
|
||||
# has_a_broadcast_policy
|
||||
#
|
||||
# set_broadcast_policy do
|
||||
# dispatch(:name)
|
||||
# to { some_list }
|
||||
# whenever { |obj| obj.something == condition }
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# Some useful examples:
|
||||
#
|
||||
# set_broadcast_policy do
|
||||
# dispatch :new_assignment
|
||||
# to { self.students }
|
||||
# whenever { |record| record.just_created? }
|
||||
# end
|
||||
#
|
||||
# set_broadcast_policy do
|
||||
# dispatch :assignment_change
|
||||
# to { self.students }
|
||||
# whenever { |record|
|
||||
# record.prior_version != self.version and true
|
||||
# # ... some field-wise comparison
|
||||
# }
|
||||
# end
|
||||
#
|
||||
# u = User.find(:first)
|
||||
# a = Account.find(:first)
|
||||
# a.check_policy(u)
|
||||
module Policy
|
||||
|
||||
# class PolicyStorage
|
||||
#
|
||||
# attr_accessor :dispatch, :to, :whenever
|
||||
#
|
||||
# def initialize(dispatch)
|
||||
# self.dispatch = dispatch
|
||||
# end
|
||||
#
|
||||
# # This should be called for an instance. It can only be sent out if the
|
||||
# # condition is met, if there is a notification that we can find, and if
|
||||
# # there is someone to send this to. At this point, a Message record is
|
||||
# # created, which will be delayed, consolidated, dispatched to the right
|
||||
# # server, and then finally sent through that server.
|
||||
#
|
||||
# def broadcast(record)
|
||||
# begin
|
||||
# meets_condition = self.whenever.call(record)
|
||||
# rescue
|
||||
# return false
|
||||
# end
|
||||
# return false unless meets_condition
|
||||
#
|
||||
# notification = Notification.find_by_name(self.dispatch)
|
||||
# return false unless notification
|
||||
# # self.consolidated_notifications[notification_name.to_s.titleize] rescue nil
|
||||
#
|
||||
# begin
|
||||
# to_list = self.to.call(record)
|
||||
# rescue
|
||||
# return false
|
||||
# end
|
||||
# return false unless to_list
|
||||
#
|
||||
# notification.create_message(record, to_list)
|
||||
# end
|
||||
#
|
||||
# end
|
||||
|
||||
module ClassMethods #:nodoc:
|
||||
def has_a_broadcast_policy
|
||||
# extend Instructure::Broadcast::Policy::SingletonMethods
|
||||
include Instructure::Broadcast::Policy::InstanceMethods
|
||||
after_save :broadcast_notifications # Must be defined locally...
|
||||
before_save :set_broadcast_flags
|
||||
end
|
||||
|
||||
# Uses the 'context' relationship as the governing relationship.
|
||||
# Canonically, this probably will look something like:
|
||||
# course -> professor -> section -> department -> account.
|
||||
# So, this method recurses the list, keeping the nearer values. So,
|
||||
# account can setup a series of default notifications, but a professor
|
||||
# can override these.
|
||||
|
||||
# Removing for now, until we memoize this
|
||||
# def consolidated_notifications
|
||||
# instance_notifications = Notification.find_all_by_context_type_and_context_id(self.class.to_s, self.id)
|
||||
# class_notifications = Notification.find_all_by_context_type_and_context_id(self.class.to_s, nil)
|
||||
# context_notifications = context.consolidated_notifications if
|
||||
# defined?(context) and context.respond_to?(:consolidated_notifications)
|
||||
# context_notifications ||= []
|
||||
#
|
||||
# cn = hashify(*instance_notifications)
|
||||
# cn.reverse_merge!(*class_notifications)
|
||||
# cn.reverse_merge!(*context_notifications)
|
||||
# cn
|
||||
# end
|
||||
#
|
||||
# def hashify(*list)
|
||||
# list.inject({}) {|h, v| h[v.name] = v; h}
|
||||
# end
|
||||
# protected :hashify
|
||||
|
||||
end
|
||||
|
||||
# This is where the DSL is defined.
|
||||
# module SingletonMethods
|
||||
#
|
||||
# attr_accessor :broadcast_policy_block
|
||||
#
|
||||
# # This stores the policy for broadcasting changes on a class. It works like a
|
||||
# # macro. The policy block will be stored in @broadcast_policy_block. Then, an
|
||||
# # instance will use that to instantiate a Policy object.
|
||||
# def set_broadcast_policy(&block)
|
||||
# self.broadcast_policy_block = block
|
||||
# end
|
||||
#
|
||||
# end
|
||||
|
||||
module InstanceMethods
|
||||
|
||||
attr_accessor :just_created, :prior_version
|
||||
|
||||
# This is called before_save
|
||||
def set_broadcast_flags
|
||||
self.just_created = self.new_record?
|
||||
self.prior_version = self.versions.current.model rescue nil
|
||||
end
|
||||
|
||||
# This is called after_save
|
||||
# def broadcast_notifications
|
||||
# self.instance_eval &self.class.broadcast_policy_block
|
||||
# self.broadcast_policy_list.each {|p| p.broadcast(self) }
|
||||
# end
|
||||
|
||||
# def broadcast_policy_list
|
||||
# @broadcast_policy_list ||= []
|
||||
# end
|
||||
|
||||
# If this is nil, we don't worry about trying to implement anything.
|
||||
# def dispatch(notification_name)
|
||||
# self.broadcast_policy_list << PolicyStorage.new(notification_name.to_s.titleize)
|
||||
# end
|
||||
|
||||
# def implementing_policy
|
||||
# if not self.broadcast_policy_list.last
|
||||
# # This really shouldn't happen, if a policy is setup right, but it
|
||||
# # should be logged and silently fail.
|
||||
# self.broadcast_policy_list << PolicyStorage.new("unknown")
|
||||
# end
|
||||
# self.broadcast_policy_list.last
|
||||
# end
|
||||
#
|
||||
# def to(&block)
|
||||
# self.implementing_policy.to = block
|
||||
# end
|
||||
#
|
||||
# def whenever(&block)
|
||||
# self.implementing_policy.whenever = block
|
||||
# end
|
||||
|
||||
# The rest of the methods here should just be helper methods to make
|
||||
# writing a condition that much easier.
|
||||
def changed_in_state(state, opts={})
|
||||
fields = opts[:fields] || []
|
||||
fields = [fields] unless fields.is_a?(Array)
|
||||
|
||||
begin
|
||||
fields.each {|field| self.prior_version.send(field) != self.send(field) }.compact == [true] and
|
||||
self.workflow_state == state.to_s and
|
||||
self.prior_version.workflow_state == state.to_s
|
||||
rescue Exception => e
|
||||
logger.warn "Could not check if a change was made: #{e.inspect}"
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def remained_in_state(state)
|
||||
begin
|
||||
self.workflow_state == state.to_s and
|
||||
self.prior_version.workflow_state == state.to_s
|
||||
rescue Exception => e
|
||||
logger.warn "Could not check if a record remained in the same state: #{e.inspect}"
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def changed_state(new_state=nil, old_state=nil)
|
||||
begin
|
||||
if new_state and old_state
|
||||
self.workflow_state == new_state.to_s and
|
||||
self.prior_version.workflow_state == old_state.to_s
|
||||
elsif new_state
|
||||
self.workflow_state == new_state.to_s and
|
||||
self.prior_version.workflow_state != self.workflow_state
|
||||
else
|
||||
self.workflow_state != self.prior_version.workflow_state
|
||||
end
|
||||
rescue Exception => e
|
||||
logger.warn "Could not check if a record changed state: #{e.inspect}"
|
||||
false
|
||||
end
|
||||
end
|
||||
alias :changed_state_to :changed_state
|
||||
|
||||
|
||||
end # InstanceMethods
|
||||
end # Policy
|
||||
end # Adheres
|
||||
end # Instructure
|
|
@ -1,53 +0,0 @@
|
|||
#
|
||||
# Copyright (C) 2011 Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
module Instructure #:nodoc:
|
||||
module Broadcast #:nodoc:
|
||||
module Policy #:nodoc:
|
||||
class PolicyStorage
|
||||
|
||||
attr_accessor :dispatch, :to, :whenever
|
||||
|
||||
def initialize(dispatch)
|
||||
self.dispatch = dispatch
|
||||
end
|
||||
|
||||
# This should be called for an instance. It can only be sent out if the
|
||||
# condition is met, if there is a notification that we can find, and if
|
||||
# there is someone to send this to. At this point, a Message record is
|
||||
# created, which will be delayed, consolidated, dispatched to the right
|
||||
# server, and then finally sent through that server.
|
||||
|
||||
def dispatch(record)
|
||||
meets_condition = self.whenever.call(record)
|
||||
return false unless meets_condition
|
||||
|
||||
notification = Notification.find_by_name(self.dispatch)
|
||||
return false unless notification
|
||||
# self.consolidated_notifications[notification_name.to_s.titleize] rescue nil
|
||||
|
||||
to_list = self.to.call(record)
|
||||
return false unless to_list
|
||||
|
||||
notification.create_message(record, to_list)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,90 +0,0 @@
|
|||
#
|
||||
# Copyright (C) 2011 Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
# This is a much simpler approach for now.
|
||||
module Instructure #:nodoc:
|
||||
module Broadcast #:nodoc:
|
||||
module Policy
|
||||
|
||||
module ClassMethods #:nodoc:
|
||||
def has_a_broadcast_policy
|
||||
include Instructure::Broadcast::Policy::InstanceMethods
|
||||
after_save :broadcast_notifications # Must be defined locally...
|
||||
before_save :set_broadcast_flags
|
||||
end
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
|
||||
attr_accessor :just_created, :prior_version
|
||||
|
||||
# This is called before_save
|
||||
def set_broadcast_flags
|
||||
self.just_created = self.new_record?
|
||||
self.prior_version = self.versions.current.model rescue nil
|
||||
end
|
||||
|
||||
# The rest of the methods here should just be helper methods to make
|
||||
# writing a condition that much easier.
|
||||
def changed_in_state(state, opts={})
|
||||
fields = opts[:fields] || []
|
||||
fields = [fields] unless fields.is_a?(Array)
|
||||
|
||||
begin
|
||||
fields.each {|field| self.prior_version.send(field) != self.send(field) }.compact == [true] and
|
||||
self.workflow_state == state.to_s and
|
||||
self.prior_version.workflow_state == state.to_s
|
||||
rescue Exception => e
|
||||
logger.warn "Could not check if a change was made: #{e.inspect}"
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def remained_in_state(state)
|
||||
begin
|
||||
self.workflow_state == state.to_s and
|
||||
self.prior_version.workflow_state == state.to_s
|
||||
rescue Exception => e
|
||||
logger.warn "Could not check if a record remained in the same state: #{e.inspect}"
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def changed_state(new_state=nil, old_state=nil)
|
||||
begin
|
||||
if new_state and old_state
|
||||
self.workflow_state == new_state.to_s and
|
||||
self.prior_version.workflow_state == old_state.to_s
|
||||
elsif new_state
|
||||
self.workflow_state == new_state.to_s and
|
||||
self.prior_version.workflow_state != self.workflow_state
|
||||
else
|
||||
self.workflow_state != self.prior_version.workflow_state
|
||||
end
|
||||
rescue Exception => e
|
||||
logger.warn "Could not check if a record changed state: #{e.inspect}"
|
||||
false
|
||||
end
|
||||
end
|
||||
alias :changed_state_to :changed_state
|
||||
|
||||
|
||||
end # InstanceMethods
|
||||
end # Policy
|
||||
end # Adheres
|
||||
end # Instructure
|
|
@ -20,9 +20,9 @@ require 'rubygems'
|
|||
require 'spec'
|
||||
require File.join(File.dirname(__FILE__), "/../lib/broadcast_policy")
|
||||
|
||||
include ::Instructure::Broadcast
|
||||
include ::Instructure
|
||||
|
||||
describe Policy, "set_broadcast_policy" do
|
||||
describe BroadcastPolicy, "set_broadcast_policy" do
|
||||
before(:each) do
|
||||
class AnotherModel
|
||||
class << self
|
||||
|
@ -33,7 +33,7 @@ describe Policy, "set_broadcast_policy" do
|
|||
true
|
||||
end
|
||||
end
|
||||
extend Policy::ClassMethods
|
||||
extend BroadcastPolicy::ClassMethods
|
||||
end
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in New Issue