I am new to Rails and am following the 'Agile Web Development with Rails' book. Currently I am testing my cart and line items.
In my fixtures I have 2 line items. I delete one in the test, so one more remains in the cart.
However I think due to my session mechanism the controller now uses a new empty cart instead of the fixture cart. This caused my test to fail.
In my *application_controller.rb*
def current_cart
Cart.find(session[:cart_id])
rescue ActiveRecord::RecordNotFound
cart = Cart.create
session[:cart_id] = cart.id
cart
en
In my *line_items_controller.rb* I have:
def destroy
#line_item = LineItem.find(params[:id])
#line_item.destroy
respond_to do |format|
if current_cart.line_items.empty?
format.html { redirect_to(store_url, :notice => 'Your cart is empty') }
else
format.html { redirect_to(current_cart, :notice => 'Item removed' )}
end
format.xml { head :ok }
end
end
And in my *functional\line_items_controller_test.rb* I have:
test "should destroy line_item" do
assert_difference('LineItem.count', -1) do
delete :destroy, :id => #line_item.to_param
end
assert(!#cart.line_items.empty?,'Cart should not be empty')
if #cart.line_items.empty?
assert_redirected_to store_url #this was the redirection result
else
assert_redirected_to cart_path(#cart.id) #but should be this redirection
end
end
The code works in real environment, just that the test fails.
How can I modify my test & code, so fixture-cart and my session mechanism can work together and pass the test?
The controller action takes a second hash of session variables to use for the request.
delete :destroy, { :id => #line_item.to_param }, { :cart_id => #cart.id }
Related
I have a Rails 3.2.18 app where I want to ship details of a call (includes name, age, and other information) to a recipient's phone that is already a field in the database.
So for instance a call has a unit assigned, and each unit has a medic (employee) assigned. In the medic model there's a phone field 281-444-555 (example number). What I want to be able to do in the calls controller is to send a SMS on create and update with the details of that call so it arrives on their phone as SMS.
I'm currently doing notifications to phones by using Email to SMS gateway 2813334444#vtext.com (example) using ActionMailer and it works "ok". But I really want to leverage Twilio.
Here's how I'm doing the mailer action to notify the medics of calls on create/update
calls_controller
def create
parse_times!
#call = Call.new(params[:call])
#call.dispatched_by = current_user.username
if #call.save
#call.send_mail(:new_call)
redirect_to calls_path, notice: "Call #{#call.incident_number} was successfully created.".html_safe
else
render :new
end
end
def update
parse_times!
#call = Call.find(params[:id])
respond_to do |format|
if #call.update_attributes(params[:call])
unless #call.call_status == "close"
#call.send_mail(:update_call)
end
format.html { redirect_to #call, notice: "Call #{#call.incident_number} was successfully updated.".html_safe }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #call.errors, status: :unprocessable_entity }
end
end
end
call_mailer
def new_call(medic, call)
#call = call
#medic = medic
mail to: [#medic.medic_sms, #medic.medic_email], :cc => "noreply#company.com", subject: "New Call: #{#call.incident_number}"
end
def update_call(medic, call)
#call = call
#medic = medic
mail to: [#medic.medic_sms, #medic.medic_email], subject: "Updated Call: #{#call.incident_number}"
end
call model (mailer method)
def send_mail(mail_type)
units.each do |unit|
CallMailer.send(mail_type, unit.incharge, self).deliver
CallMailer.send(mail_type, unit.attendant, self).deliver
end
end
end
This is working just fine for mailing the phones and emails of the medics, but I would like to add something similar using Twilio where I can ship the call details to them via SMS in the create and update action.
If anyone can point me in the right direction, I'd appreciate it. I have a Twilio account already and would like to put it to good use.
Update 08/03/14
I think I figured this out and got it working in a basic fashion. But I'd like to see if there's someway to cleanly pass #call object data into the :body => section. Right now I'm having to iterate over the specific fields I want to send (which are about 10 different fields). It would be nice if I could create a partial or template and pass it to :body => similar to how ActionMailer works. Any thoughts?
calls_controller.rb (working)
def update
parse_times!
#call = Call.find(params[:id])
respond_to do |format|
if #call.update_attributes(params[:call])
unless #call.call_status == "close"
unless #call.unit_ids.empty?
send_sms
end
#call.send_mail(:update_call)
end
format.html { redirect_to #call, notice: "Call #{#call.incident_number} was successfully updated.".html_safe }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #call.errors, status: :unprocessable_entity }
end
end
end
private
def send_sms
account_sid = 'AC5CCCCC'
auth_token = 'ATTTTT'
#client = Twilio::REST::Client.new account_sid, auth_token
#client.account.messages.create(
:from => '2814084444',
:to => #call.units.first.incharge.medic_phone,
:body => "incident_number #{#call.incident_number} patient name #{#call.patient_name}"
)
#client.account.messages.create(
:from => '2814084444',
:to => #call.units.first.attendant.medic_phone,
:body => "incident_number #{#call.incident_number} patient name #{#call.patient_name}"
)
end
end
Ok, I have this figured out now. I needed to do string interpolation in the :body element for Twilio to send the information out. Here is my final code and it's working and updated with a conditional to only fire Twilio if certain conditions are met.
calls_controller.rb
def update
parse_times!
#call = Call.find(params[:id])
respond_to do |format|
if #call.update_attributes(params[:call])
if !(#call.call_status == "close") && !(#call.unit_ids.empty?)
send_update_sms
#call.send_mail(:update_call)
end
format.html { redirect_to #call, notice: "Call #{#call.incident_number} was successfully updated.".html_safe }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #call.errors, status: :unprocessable_entity }
end
end
end
def send_update_sms
account_sid = 'AC5CCCC'
auth_token = 'ATTTT'
#client = Twilio::REST::Client.new account_sid, auth_token
#client.account.messages.create(
:from => '28140844444',
:to => #call.units.first.incharge.medic_phone,
:body => "Updated: #{#call.incident_number}/#{#call.units.map(&:unit_name).join(", ")}/#{#call.patient_name}/#{#call.patient_age}/#{#call.patient_sex.try(:sex)}/#{#call.nature.try(:determinant)}/#{#call.special_equipments.map(&:name).join(", ")}/#{#call.traffic_type}/#{transfer_from_address}/#{transfer_to_address} CHECK EMAIL FOR FULL CALL INFO"
)
#client.account.messages.create(
:from => '2814084444',
:to => #call.units.first.attendant.medic_phone,
:body => "Updated: #{#call.incident_number}/#{#call.units.map(&:unit_name).join(", ")}/#{#call.patient_name}/#{#call.patient_age}/#{#call.patient_sex.try(:sex)}/#{#call.nature.try(:determinant)}/#{#call.special_equipments.map(&:name).join(", ")}/#{#call.traffic_type}/#{transfer_from_address}/#{transfer_to_address} CHECK EMAIL FOR FULL CALL INFO"
)
end
def transfer_from_address
if #call.transferred_from.nil?
#call.transfer_from_other
else
#call.transferred_from.try(:facility_name) + ' ' + #call.transferred_from.try(:facility_address)
end
end
def transfer_to_address
if #call.transferred_to.nil?
#call.transfer_to_other
else
#call.transferred_to.try(:facility_name) + ' ' + #call.transferred_to.try(:facility_address)
end
end
I am sending an email whenever a new record is created in my Rails 3 application.
def new_resource_notification(resource)
#resource = resource
mail(:to => "admin#domain.com", :bcc => User.all.map(&:email), :subject => "New item added")
end
Resource Controller
# POST /resources
# POST /resources.json
def create
#resource = Resource.create( params[:resource] )
#resource.uploadedip_string = request.env['REMOTE_ADDR']
respond_to do |format|
if #resource.save
UserMailer.new_resource_notification(#resource).deliver
format.html { redirect_to #resource, notice: 'Resource was successfully created.' }
format.json { render json: #resource, status: :created, location: #resource }
else
format.html { render action: "new" }
format.json { render json: #resource.errors, status: :unprocessable_entity }
end
end
end
I am using Devise for the user authentication an have added a field called email_subscribe. I've also added a simple checkbox in the user profile view to enable and disable the subscription.
Everything is working correctly at the moment. All users receive an email when a new record is created and users can set and unset their subscription. What I am trying to do now is make the two work together.
How do I only send an email to the users who's email_subscribe is true?
mails = User.where(:email_subscriber => true)
mail(:to => "admin#domain.com", :bcc => mails, subject => "New item added")
I seem to have trouble handling error messages. Here's my method:
def destroy
#user = User.find(current_user)
#authorization = Authorization.find(params[:id])
if #user.authorizations.count > 1
#authorization.destroy
redirect_to(user_path(current_user))
else
...
end
end
I don't want a user to delete their last authorization (say, Facebook) because then the user won't have any authorizations associated with it and the account is lost. I want to send an error message that says why the destroy method fails, but I'm just not sure how. Everything I've tried just doesn't work.
I've tried things like #user.errors.add => "error message"
But it just shows up blank. I think my problem is with using render. If I try, for example:
respond_to do |format|
#user.errors[:base] << "Sorry, you can't delete your only authorized service."
format.html render :template => "users/show"
end
I get a problem because rails starts looking for the partials in users/show inside the authorizations directory for some reason, presumably because I'm calling the authorizations controller from a users view.
Here's how I display the flash in my views:
def show_flash
[:notice, :errors, :alert].collect do |key|
msg = (flash[key].to_s + " (close)")
content_tag(:div, (content_tag(:a, msg, :class => "#{key}", :href => "#", :onclick => "$('messages').slideUp(); return false;")), :id => key, :class => "flash_#{key}") unless flash[key].blank?
end.join
end
I can get notices to appear just fine.
So, this seems to be working:
respond_to do |format|
format.html {redirect_to(#user, :alert => "Sorry, you can't delete your only authorized service.")}
end
But, this does not:
respond_to do |format|
format.html {redirect_to(#user, :errors => "Sorry, you can't delete your only authorized service.")}
end
I'm storing my shopping cart in the database and have a current_cart method in my application helper.
private
def current_cart
Cart.find(session[:cart_id])
rescue ActiveRecord::RecordNotFound
cart = Cart.create
session[:cart_id] = cart.id
cart
end
Each cart has_many line_items. When a customer is ready to check out, they are sent to order/new ... when the order returns successful from the payment gateway though, everything is fine. But I'm testing the order failure and it seems there's a problem with my create controller, because the #order object isn't being saved and rails is trying to render order#new again. It's not even getting to try and see if the payment was successful. I'm not sure what' wrong, as it was working when I tested it a few days ago.
def create
#order = current_cart.build_order(params[:order])
#order.ip_address = request.remote_ip
#order.total_tokens = #order.calculate_total_tokens
#order.user_id = current_user
if #order.save
if #order.purchase
render :action => "success"
else
render :action => "failure"
end
else
render :action => "new"
end
end
def new
#cart = current_cart
if #cart.line_items.empty?
redirect_to store_url, :notice => "Your cart is empty."
return
end
#order = Order.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #order }
end
end
I'm getting this error when it tries to render the page, obviously because the order hasn't been saved:
NoMethodError in Orders#create
Showing /Users/justin/Code/trackable/app/views/orders/_order_cart.html.erb where line #1 raised:
undefined method `line_items' for nil:NilClass
Extracted source (around line #1):
1: <% unless #cart.line_items.empty? %>
2: <div id="checkout_cart">
3: <% #cart.line_items.each do |l| %>
4: <%= l.quantity %>
Not sure what's going on
So, adding #cart = current_cart to the #create controller seems to have fixed the problem. ActiveMerchant was throwing a validation error and for some reason, when it did that, it was losing #cart ... it wasn't doing that earlier when I was testing ActiveRecord validations, so I'm still not quite sure what I changed that screwed everything up.
I am testing a controller in RSpec2 and for both my create and update actions, when passed invalid params, the controller should render either the "new" or "edit" templates respectively. It is doing that, but my test never passes.
describe "with invalid params" do
before(:each) do
User.stub(:new) { mock_user(:valid? => false, :save => false) }
end
it "re-renders the 'new' template" do
post :create, :company_id => mock_company.id
response.should render_template("new")
end
end
Results in this:
re-renders the 'new' template
expecting <"new"> but rendering with <"">
Here is the controller action:
respond_to do |format|
if #user.save
format.html {
flash[:notice] = "#{#user.full_name} was added to #{#company.name}."
redirect_to company_users_url(#company)
}
else
logger.debug #user.errors
format.html{
render :new
}
end
end
This problem also seems to be isolated to this controller. I have almost identical code running another controller and it is fine. I am not sure where the problem could be.
Update:
Here are the two mock methods
def mock_user(stubs={})
#mock_user ||= mock_model(User, stubs).as_null_object
end
def mock_company(stubs={})
(#mock_company ||= mock_model(Company).as_null_object).tap do |company|
company.stub(stubs) unless stubs.empty?
end
end
Turned out it was a problem with stubbing and CanCan. CanCan was loading the resources and uses some different methods than what I thought.