) for a twilio application. For some reason, I am now getting the following error message: ActiveRecord::RecordInvalid (Validation failed: State is invalid).
When I simply do c = Call.new c.valid? I am told that it is invalid, and then that the "state is invalid" when I print the error messages.
class Call < ActiveRecord::Base
include CallCenter
belongs_to :user
attr_accessible :to, :from, :called, :caller
attr_accessible :account_sid, :call_sid, :call_status, :digits
call_flow :state, :initial => :initial do
state :initial do
event :incoming_call, :to => :greeting
end
state :greeting do
# TODO if user has a current question id, transition to play_question
# TODO else transition to play_lecture
event :greeted, :to => :advance_user
event :no_classroom, :to => :gather_classroom_number
response do |x|
if user.classroom_id == nil
x.Say "It looks like you are not assigned to a classroom. Let's take
care of that now."
x.Redirect flow_url(:no_classroom)
else
x.Say "Welcome back! Let's get back to your classes."
x.Redirect flow_url(:greeted)
end
end
end
state :gather_classroom_number do
event :received_number, :to => :evaluate_classroom_number
response do |x|
x.Gather :numDigits => '3', :action => flow_url(:received_number) do
x.Say "Please enter the 3 digit classroom number given by your teacher.
If you do not know this number, press 999 to move on."
end
end
end
state :evaluate_classroom_number do
event :no_number, :to => :advance_user
event :correct_number, :to => :advance_user
event :wrong_number, :to => :gather_classroom_number
response do |x|
if digits == '999'
x.Say "Ok, we'll move on."
x.Redirect flow_url(:no_number)
elsif user.assign_classroom == nil
x.Say "Looks like you entered the wrong classroom number.
Let's try it again."
x.Redirect flow_url(:wrong_number)
else
user.assign_classroom
user.save
x.Redirect flow_url(:correct_number)
end
end
end
state :determine_current_segment do
event :going_to_lecture, :to => :play_lecture
event :going_to_question, :to => :play_question
response do |x|
# x.Say "Determining current segment."
if user.on_lecture?
x.Redirect flow_url(:going_to_lecture)
else
x.Redirect flow_url(:going_to_question)
end
end
end
state :play_lecture do
event :lecture_finished, :to => :repeat_or_advance
response do |x|
x.Gather :numDigits => '1', :action => flow_url(:lecture_finished) do
x.Play user.lecture.soundfile.url
user_lecture.save
end
#HOLD_MUSIC.sort_by { rand }.each do |url|
# x.Play url
#end
end
end
# In this state, we should have access to digits
state :repeat_or_advance do
event :request_repeat, :to => :determine_current_segment
event :request_advance, :to => :advance_user
response do |x|
#x.Say "You pressed #{digits}."
if digits == "1"
x.Say "Ok, we'll repeat this for you."
x.Redirect flow_url(:request_repeat)
else
x.Redirect flow_url(:request_advance)
end
end
end
state :play_question do
event :submit_answer, :to => :check_if_correct
response do |x|
x.Gather :numDigits => '1', :action => flow_url(:submit_answer) do
x.Play user.question.soundfile.url
end
end
end
state :check_if_correct do
event :answer_correct, :to => :advance_user
event :answer_incorrect, :to => :question_explanation
response do |x|
x.Say "You pressed #{digits}."
answer.assert_correct
answer.save
if digits == user.question.answer.to_s #you will need to write a function that checks if its correct or not
x.Say "Great, that's right. Now we'll move onto the next question."
x.Redirect flow_url(:answer_correct) #then send to next question, perhaps by
#first writing a state that changes current question to the next question. Then resend to current question.
else
x.Redirect flow_url(:answer_incorrect)
end
end
end
state :question_explanation do
event :explanation_end, :to => :advance_user
response do |x|
x.Play user.question.explanationfile.url
x.Redirect flow_url(:explanation_end)
end
end
state :advance_user do
event :advancing_user, :to => :determine_current_segment
response do |x|
user.advance
user.save #don't know why this is not working
# x.Say "We have advanced you to the next part."
x.Redirect flow_url(:advancing_user)
end
end
#write logic for
#if question is correct, and
# if there is another question,
# go to the next question
# if there is not another question
# go to congrats,
#and do you want to go to another lecture
# if question is incorrect, play explanation
# then play next question
# end
state :ended do
after(:success) do
update_attributes(:ended_at => Time.now)
end
end
state any do
event :hang_up, :to => :ended
end
end
# ===============
# = Call Center =
# ===============
def run(event)
send(event)
render
end
def flow_url(event)
params = {
:event => event.to_s
}
uri = URI.join(ENV['TWILIO_TUNNEL_URL'], "calls/flow")
uri.query = params.to_query
uri.to_s
end
def redirect_to(event, *args)
account.calls.get(self.call_sid).update({:url => flow_url(event)})
end
def wait_time
Time.now - (self.waiting_at || self.created_at)
end
# ========
# = REST =
# ========
def account
client.account
end
def client
self.class.client
end
def self.client
@client ||= Twilio::REST::Client.new ENV['TWILIO_ACCOUNT_SID'], ENV['TWILIO_AUTH_TOKEN']
end
# ===========
# = Routing =
# ===========
MAX_WAIT_TIME = 60
HOLD_MUSIC = [
].freeze
serialize :conference_history, Array
def can_connect?(call)
!conference_history.include?(call.from)
end
def call_conference_name(call)
[self.call_sid, call.call_sid].sort.join('::')
end
def connect(call)
self.conference_name = call_conference_name(call)
self.conference_history << call.from
self.save!
self.redirect_and_put_in_conference!
end
def location
if caller_states = Carmen::states(self.caller_country)
title_and_abbreviation = caller_states.detect { |title, abbr| abbr == self.caller_state }
if title_and_abbreviation
return "Someone in #{self.caller_city.capitalize}, #{title_and_abbreviation.first}"
end
end
"Someone in #{self.caller_city.capitalize}, #{self.caller_state}"
rescue Carmen::StatesNotSupported, Carmen::NonexistentCountry
"An Unknown Caller"
end
def as_json(options = {})
{
:location => location,
:conference_name => conference_name
}
end
end