Rspec factory_girl ActiveMerchant undefined method `credit_card=' - ruby-on-rails-3

I'm attempting to test the creation of an Reservation model that involves processing a payment with ActiveMerchant upon creation.
The initial setup for payment processing involved following the ActiveMerchant Railscasts. Payments are working fine within the app. (http://railscasts.com/episodes/145-integrating-active-merchant)
I've tried creating the credit_card object within the Reservation factory and within it's own ":valid_credit_card" factory...
The basic test is just attempting to verify the Reservation can be created.
Test results:
1) Reservation should have a valid factory
Failure/Error: #current_reservation = Factory.create(:reservation)
NoMethodError:
undefined method `credit_card=' for #<Reservation:0xb5f6173c>
# ./spec/models/reservation_spec.rb:11
The Reservation belongs_to a user and has_many rooms through reservation_sets
Factory.define :reservation do |f|
f.association :user
f.rooms { |a| [a.association(:room)] }
f.arrival Time.now + 2.weeks
f.nights 2
f.phone "555-123-1234"
f.credit_card :valid_credit_card
end
Factory.define :valid_credit_card, :class => ActiveMerchant::Billing::CreditCard do |f|
expiration_date = Time.zone.now + 1.year
f.type "visa"
f.number "4111111111111111"
f.verification_value "333"
f.month expiration_date.strftime("%m")
f.year expiration_date.strftime("%y")
f.first_name "Bob"
f.last_name "Smith"
end
And the spec/models/reservation_spec.rb. Using #credit_card Factory.build causes errors about "saving" the credit_card.
If I remove the line f.credit_card :valid_credit_card I get the NoMethodError for :month
even though :month is listed in attr_accessor. The creation of a reservation within the app does work.
1) Reservation should have a valid factory
Failure/Error: #current_reservation = Factory.create(:reservation)
NoMethodError:
undefined method `month' for nil:NilClass
describe Reservation do
before :each do
#smith = Factory.create(:user)
#room = Factory.create(:room)
##credit_card = Factory.build(:valid_credit_card)
end
it "should have a valid factory" do
#current_reservation = Factory.create(:reservation)
#current_reservation.should be_valid
end
end
What am I overlooking / doing incorrectly...?
Reservation model excerpts
class Reservation < ActiveRecord::Base
# relationships
belongs_to :user
has_many :reservation_sets,
:dependent => :destroy
has_many :rooms,
:through => :reservation_sets
has_many :transactions,
:class_name => 'ReservationTransaction',
:dependent => :destroy
attr_accessor :card_number, :card_verification, :card_expires_on, :card_type, :ip_address, :rtype, :month, :year
# other standard validations
validate :validate_card, :on => :create
# other reservation methods...
# gets paid upon reservation creation
def pay_deposit
# Generate active merchant object
ReservationTransaction.gateway =
ActiveMerchant::Billing::AuthorizeNetGateway.new({
:login => rooms[0].user.gateway_login,
:password => rooms[0].user.gateway_password
})
response = ReservationTransaction.gateway.purchase(deposit_price_in_cents, credit_card, purchase_options)
t = transactions.create!(:action => "purchase", :amount => deposit_price_in_cents, :response => response)
if response.success?
update_attribute(:reserved_at, Time.now)
# update state
payment_captured!
else
transaction_declined!
errors.add :base, response.message
end
t.card_number = credit_card.display_number
t.save!
response.success?
end
def validate_card
unless credit_card.valid?
credit_card.errors.full_messages.each do |message|
errors.add :base, message #_to_base message
end
end
end
def credit_card
#credit_card ||= ActiveMerchant::Billing::CreditCard.new(
:type => card_type,
:number => card_number,
:verification_value => card_verification,
:month => card_expires_on.month,
:year => card_expires_on.year,
:first_name => first_name,
:last_name => last_name
)
end
And the create action from the Reservation Controller
def create
#reservation = Reservation.new(params[:reservation])
#reservation.arrival = session[:arrival]
#reservation.nights = session[:nights]
#reservation.number_kids = session[:number_kids]
#reservation.number_adults = session[:number_adults]
session[:creating_reservation] = 1
#reservation.user_id = #reservation.rooms[0].user_id
session[:owner] = #reservation.user_id
#rooms = Room.all
#reservation.ip_address = request.remote_ip
# get room owner...
#owner = User.find(#reservation.user_id)
respond_to do |format|
if #reservation.save
if #reservation.pay_deposit
#set cc...
#reservation.transactions[0].card_number = #reservation.send(:credit_card).display_number
ReservationMailer.reservation_created(#reservation).deliver
ReservationMailer.reservation_notice(#reservation).deliver
session[:arrival] = nil
session[:reservation_id] = #reservation.id
if #owner
thanks_path = "#{#owner.permalink}/reservations/#{#reservation.id}"
else
thanks_path = #reservation
end
format.html { redirect_to #reservation, :notice => 'Reservation was successfully created.' }
format.json { render :json => #reservation, :status => :created, :location => #reservation }
# also trigger email sending or wherever that is
# receipt email and order notification
#
else
# set flash or show message problem w/ transaction
format.html { render :action => "new" }
end
else
format.html { render :action => "new" }
format.json { render :json => #reservation.errors, :status => :unprocessable_entity }
end
end
end

It looks like you are trying to assign credit_card a value, but you don't really have a class accessor. So where you are trying to call f.credit_card :valid_credit_card isn't going to work.
I would remove the f.credit_card :valid_credit_card from your factory and look into using rspec stubs, then you could do something like the following in your rspec test:
mock_cc = ActiveMerchant::Billing::CreditCard.new(
:type => card_type,
:number => card_number,
:verification_value => card_verification,
:month => card_expires_on.month,
:year => card_expires_on.year,
:first_name => first_name,
:last_name => last_name
)
Reservation.stub(:credit_card).and_return(mock_cc)
This would make it so when your model called credit_card it would return a mocked object.

Related

Failing RSpec test with strong parameters

I am migrating my Rails 4 app (still using protected attributes gem) to Rails 5.1.4. In the course of this action, I need to rewrite a lot of code to replace protected attributes with strong parameters.
I am currently stuck on one specific controller where my RSpec tests fail, and I don't know how to implement the controller and test logic such that things are correct and tests pass.
The app has an admin backend where users can add (and thus upload) photos to an album. The respective Admin::PhotosController handles the photos of an album.
Here's the relevant exerpt from my app:
def create
# #organizer_account is set by an before_filter
#album = #organizer_account.albums.find_by_id(params[:album_id])
#photo = #album.photos.new(photo_params)
#photo.organizer_account_id = #organizer_account.id
authorize! :create, #photo
respond_to do |format|
if #photo.save
format.html {
render :json => [#photo.to_jq_file].to_json, :content_type => 'text/html', :layout => false
}
format.json {
files = [ #photo.to_jq_file ]
render :json => {:files => [#photo.to_jq_file] }, :status => :created, :location => admin_album_photo_path(#album, #photo)
}
else
format.html {
render action: "new"
}
format.json {
render json: #photo.errors, status: :unprocessable_entity
}
end
end
end
I have defined the following strong parameters:
private
def photo_params
params.require(:photo).permit(:id, :album_id, :organizer_account_id, :file)
end
The failing RSpec test is as follows:
require 'spec_helper'
describe Admin::PhotosController, :type => :controller do
render_views
describe "post 'create'" do
describe "with valid parameters" do
before(:each) do
#organizer_account = FactoryBot.create(:organizer_account)
#user = FactoryBot.create(:user)
#user.organizer_account_id = #organizer_account.id
#user.add_role :admin, #organizer_account
#user.save
sign_in #user
#album = #organizer_account.albums.create(:title => "Album 1")
#photo_attrs = FactoryBot.attributes_for(:photo)
request.env["HTTP_REFERER"] = new_admin_album_path
controller.request.host = #organizer_account.subdomain + ".lvh.me"
end
it "should create a new photo record", :focus => true do
lambda {
post :create, params: {:photo => #photo_attrs, :album_id => #album.id }
}.should change(#organizer_account.albums.find_by_id(#album.id).photos, :count).by(1)
end
end
end
end
I strongly assume that the issue is in parameters are a) passed
post :create, params: {:photo => #photo_attrs, :album_id => #album.id }
and then processed
#photo = #album.photos.new(photo_params)
While the params hash passed by the test has all the required entries
params: {"photo"=><ActionController::Parameters {"file"=>[#<ActionDispatch::Http::UploadedFile:0x00000010dd7560 #tempfile=#<Tempfile:C:/Users/PATRIC~1/AppData/Local/Temp/RackMultipart20180520-11424-avge07.gif>, #original_filename="image6.gif", #content_type="image/gif", #headers="Content-Disposition: form-data; name=\"photo[file][]\"; filename=\"image6.gif\"\r\nContent-Type: image/gif\r\nContent-Length: 46844\r\n">]} permitted: false>, "album_id"=>"1561", "controller"=>"admin/photos", "action"=>"create"}
the photo_params is empty:
photo_params: {}
Update #1: Definition of factory for photo
FactoryBot.define do
factory :photo, :class => Photo do
file Rack::Test::UploadedFile.new(Rails.root + 'spec/fixtures/photos/apfelkuchen.jpg', "image/jpg")
end
end
Update #2: Photo model with file attachment and image processing config
class Photo < ActiveRecord::Base
require 'rmagick'
include Magick
belongs_to :album
belongs_to :organizer_account
before_destroy { |photo| photo.file.destroy }
validates :album_id, :presence => true
validates :organizer_account_id, :presence => true
has_attached_file :file,
# The following tyles and convert options lead to breaking RSpec tests. If commented, RSpec tests pass.
:styles => {
:mini => "50x50#",
:thumb => "160x160#",
:large => "1200x1200>"
},
:convert_options => {
:mini => "-quality 75 -strip",
:thumb => "-quality 75 -strip"
}
validates :file, :presence => true
end

Got error while copying existing record in new one in rails3?

Hi i am creating an action to copy an existing record in a new one in rails3 which is correctly populates the values from previous record to new one but at the time i submit the form it gives the error. Here is my code sample
class SalaryStructuresController < ApplicationController
def new
#salary_structure = SalaryStructure.new
#salary_structure.salary_structure_line_items.build
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #salary_structure }
end
end
def create
#salary_structure = SalaryStructure.new(params[:salary_structure])
#salary_structure.company_id = current_company.id
#salary_structure.created_by = current_user.id
respond_to do |format|
if #salary_structure.valid?
#salary_structure.save_with_variable_payheads
flash[:success]= "Salary structure successfully created."
format.html { redirect_to(#salary_structure) }
format.xml { render :xml => #salary_structure, :status => :created, :location => #salary_structure }
else
format.html { render :action => "new" }
format.xml { render :xml => #salary_structure.errors, :status => :unprocessable_entity }
end
end
end
#action to clone the salary structure
def copy
#payheads = current_company.payheads
#users = current_company.users
#source_salary_structure = SalaryStructure.find(params[:id])
#salary_structure = SalaryStructure.new(#source_salary_structure.attributes)
#source_salary_structure.salary_structure_line_items.each do |line_item|
salary_item = SalaryStructureLineItem.new(line_item.attributes)
#salary_structure.salary_structure_line_items << salary_item
end
render :action => "new"
end
end
My model:
class SalaryStructure < ActiveRecord::Base
has_many :salary_structure_line_items
belongs_to :user
# has_many :payheads
accepts_nested_attributes_for :salary_structure_line_items, :reject_if => lambda {|p| p[:payhead_id].blank? && p[:amount].blank? }, :allow_destroy => true
#validation
validates_presence_of :effective_from_date, :for_employee
validates_presence_of :salary_structure_line_items
validates_associated :salary_structure_line_items
attr_accessible :id, :effective_from_date, :salary_structure_line_items_attributes, :amount, :total, :pay_head_type, :for_employee, :pay_head, :created_by, :updated_at, :created_at, :company_id,
:salary_structure_line_items_attributes
end
When i submit the form (press save) i got the error on salary_structure_id:
ActiveRecord::RecordNotFound in SalaryStructuresController#create
Couldn't find SalaryStructureLineItem with ID=11 for SalaryStructure with ID=
Even in parameters salary_structure_id is present :
"commit"=>"Save",
"salary_structure"=>{"salary_structure_line_items_attributes"=>{"0"=>{"amount"=>"3000.0",
"_destroy"=>"",
"salary_structure_id"=>"4",
"id"=>"11",
"payhead_id"=>"1"},
"1"=>{"amount"=>"500.0",
"_destroy"=>"",
"salary_structure_id"=>"4",
"id"=>"12",
"payhead_id"=>"2"}
i am unable to trace where i am missing something, please help me.
I have created clone file in very easy way here i have created a new action in my controller
def copy_salary_structure
#users = current_company.users.without_salary_structure
#payheads = current_company.payheads.where(:optional => false)
#old_salary_structure = SalaryStructure.find_by_id(params[:id])
#salary_structure = SalaryStructure.new
#salary_structure.company_id = #old_salary_structure.company_id
#salary_structure.created_by = current_user.id
#old_salary_structure.salary_structure_line_items.each do |line_item|
salary_structure_line_item = SalaryStructureLineItem.new(
:payhead_id => line_item.payhead_id,
:amount => line_item.amount
)
#salary_structure.salary_structure_line_items << salary_structure_line_item
end
end
and i have create a view form with same name from which i can review the record and then save it easily

Update nested attributes before saving to database

Long time reader of Stackoverflow but have never found myself in a position to ask a question (that hasn't already been answered). I guess there's a first time for everything so here it goes...
System Info:
Ruby Version = 1.8.7
Rails Version = 3.2.2
Situation:
We have an application with a user registration system in place. In order to hook up and populate all of our tables correctly, we are utilizing Complex/Nested Forms within the registration view. I actually have the nested forms working perfectly, everything is being populated as it should, its awesome really.
Here is the problem: I need to set one of the value of one of the nested attributes AFTER the form post but BEFORE the records are saved.
Here is a quick example so you can see what I'm talking about a little bit better:
A user registers with our site. When they register a record is created in the Users data table. Each user is also classified as a team_mate (join table) and assigned to their very own individual team (at first). But, a 'team' (table) also has an 'alias' field in it which, on the initial creation of the user we would like to set to the users first name (without having to have them enter their first name into an 'alias' field on the form).
So, I guess the question would be: How to I manually set the value of a nested attribute after the form post and before the records are saved to the database?
A (simplistic) example of the table schema looks is as follows:
Users (id, first_name, last_name, created_at, updated_at)
Team_mates(id, user_id, team_id, created_at, updated_at) - join table
Teams(id, alias, created_at, updated_at)
Models:
User.rb
class User < ActiveRecord::Base
has_many :team_mates, :dependent => :destroy
has_many :teams, :through => :team_mates, :foreign_key => :team_id
accepts_nested_attributes_for :team_mates, :allow_destroy => true
before_save :set_defaults
private
def set_defaults
#want to set :users => :team_mates_attributes => :team_attributes => :alias to #user.first_name here
# Would prefer to handle this here instead of in the controller.
end
end
Team.rb
class Team < ActiveRecord::Base
has_many :team_mates, :dependent => :destroy
has_many :users, :through => :team_mates, :foreign_key => :user_id
end
Team_mate.rb
class TeamMate < ActiveRecord::Base
belongs_to :user
belongs_to :team
accepts_nested_attributes_for :team, :allow_destroy => true
end
Controller
Users_controller.rb
class UsersController < ApplicationController
def new
#user = User.new
#user.emails.build(:is_default_email => 1)
#user.build_login
#user.team_mates.build.build_team(:alias => 'Clinton444', :created_at => Time.new, :updated_at => Time.new)
respond_to do |format|
format.html
format.json { render :json => #match }
end
end
def create
#user = User.new(params[:user])
#user.attributes = ({ "user" => { "team_mates" => { "team" => { "alias" => #user.first_name } } } }) #--this doesn't work...
#user.attributes = ({ :user => { :team_mates => { :team => { :alias => #user.first_name } } } }) #--neither does this...
respond_to do |format|
if #user.save
format.html { redirect_to(#user, :notice => 'User was successfully created.') }
format.json { render :json => #user, :status => :created, :location => #user }
else
format.html { render :action => "new" }
format.json { render :json => #user.errors, :status => :unprocessable_entity }
end
end
end
View
new.html.haml
= form_for(#user, :html => {:class => 'form-horizontal'}) do |f|
- if #user.errors.any?
.alert
%h2
= pluralize(#user.errors.count, "error")
prohibited this post from being saved:
%ul
- #user.errors.full_messages.each do |msg|
%li
= msg
%fieldset
.control-group
= f.label :first_name, :class => "control-label"
.controls
=f.text_field :first_name, :class => "span8"
.control-group
= f.label :last_name, :class => "control-label"
.controls
=f.text_field :last_name, :class => "span8"
= f.fields_for :emails do |e|
=e.hidden_field :is_default_email, :class => "span8"
.control-group
= e.label :email, :class => "control-label"
.controls
=e.text_field :email, :class => "span8"
= f.fields_for :team_mates do |tm|
= tm.fields_for :team do |t|
=t.hidden_field :alias, :class => "span8"
=t.hidden_field :created_at, :class => "span8"
=t.hidden_field :updated_at, :class => "span8"
= f.fields_for :login do |e|
.control-group
= e.label :user_login, :class => "control-label"
.controls
=e.text_field :user_login, :class => "span8"
.control-group
= e.label :password_encrypted, :class => "control-label"
.controls
=e.text_field :password_encrypted, :class => "span8"
.control-group
.controls
=f.submit :class => 'btn btn-primary btn-medium'
And finally
Rails server output on form post
Parameters: {"user"=>{"team_mates_attributes"=>{"0"=>{"team_attributes"=>{"created_at"=>"Wed Jun 06 09:52:19 -0600 2012", "alias"=>"asfs444", "updated_at"=>"Wed Jun 06 09:52:19 -0600 2012"}}}, "first_name"=>"lkjlkjlsdfslkjeowir", "last_name"=>"ouisodifuoixv", "emails_attributes"=>{"0"=>{"is_default_email"=>"1", "email"=>"lpisfsopf#psflsjdk.com"}}, "login_attributes"=>{"user_login"=>"lkjsdfooiusfd", "password_encrypted"=>"[FILTERED]"}}, "utf8"=>"✓", "commit"=>"Create User", "authenticity_token"=>"CQLQ93/0VlncSzMlmtLPHgaVrrvjuHFN+lN6CYCsiR8="}
After looking at the models you might be wondering where emails/logins are coming from. They're built within the model on our system, but are not really part of this question so I omitted the code for them. They are working, so the problem isn't on that side.
Check http://archives.ryandaigle.com/articles/2009/2/1/what-s-new-in-edge-rails-nested-attributes
To support both the creation of new objects and the editing of
existing ones we have to use an array of hashes for one-to-many
associations or a single hash for one-to-one associations. If no :id
property exists then it is assumed to represent a nested model to
create.
Not 100% sure.. I haven't used\tested it before, but this should give you an idea
#user.teams.each do |team|
team.team_mates do |team_mate|
# To edit existing
team_mate.team_attributes = [ { :id => team.id, :alias => #user.first_name } ]
# To create new
team_mate.team_attributes = [ { :alias => #user.first_name } ]
team_mate.save
end
end

Multiple (n) identical nested forms generated square-times(n*n) when validation fails

User has two addresses shipping(:address_type=0) and billing(:address_type=1)
User form with 2 classic nested forms for each address type are generated square times every submit and failed validation.
Models:
class User < ActiveRecord::Base
has_many :addresses, :dependent => :destroy
accepts_nested_attributes_for :addresses
validates_associated :addresses
end
class Address < ActiveRecord::Base
belongs_to :user
validates :user, :address_type, :first_name, :last_name, :street
end
Controller
class UsersController < ApplicationController
public
def new
#user = User.new
#shipping_address = #user.addresses.build({:address_type => 0})
#billing_address = #user.addresses.build({:address_type => 1})
end
def create
#user = User.new(params[:user])
if #user.save
#fine
else
render => :new
end
end
Uncomplete Form
=form_for #user, :html => { :multipart => true } do |ff|
=ff.fields_for :addresses, #shipping_address do |f|
=f.hidden_field :address_type, :value => 0
=ff.fields_for :addresses, #billing_address do |f|
=f.hidden_field :address_type, :value => 1
=ff.submit
The form should look like this:
=form_for #user, :html => { :multipart => true } do |ff|
=ff.fields_for :addresses do |f|
Nothing else.
Addressess is already a collection, so you should have just one rendering of it.
Also that ":addresses, #shipping_address" makes it to render addresses AND shipping address, even if it's included in #user.addresses.
The addressess built in new action will show there because they are in the addresses collection.
EDIT:
If you need only these two addresses, you can sort it and pass it to fields_for directly:
=form_for #user, :html => { :multipart => true } do |ff|
=ff.fields_for ff.object.addresses.sort{|a,b| a.address_type <=> b.address_type } do |f|
That should do it.
Surprised? I guess not but I was. I found it am I correct? And its stupid and simple.
There is no #shipping_address nor #billing_address when validation fails and rendering the new action (the form) again. But #user has already 2 addresses builded and nested form behave correctly to render each twice for first time failed validation.
def create
#user = User.new(params[:user])
if #user.save
#fine
else
#user.addresses.clear
#user_address = #user.addresses.build({:address_type => 0})
#user_address.attributes = params[:user][:addresses_attributes]["0"]
#billing_address = #user.addresses.build({:address_type => 1})
#billing_address.attributes = params[:user][:addresses_attributes]["1"]
render => :new
end
end

Filter controller condition by value in database?

I have the following code in one of my controllers (in a Rails 3.1 application) which works well:
def index
##calls = Call.all
#calls = Call.where(:destination => '12345678').limit(25)
respond_to do |format|
format.html # index.html.erb
format.json { render :json => #calls }
end
end
I'm trying to work out the best way of proceeding from here, basically each user has their own destination code (in this case it's 12345678).
Is it possible for the users to have a value in a model which can be passed into the controller?
An example
def index
##calls = Call.all
#calls = Call.where(:destination => '<% #user.destination %>').limit(25)
respond_to do |format|
format.html # index.html.erb
format.json { render :json => #calls }
end
end
I realise that the above code wouldn't work but what would be a workaround to achieve the same thing?
Update with a little more information:
I have two models, one is calls and the other is users.
I want to be able to do something like this:
#calls = Call.where(:destination => #user.destination_id).limit(25)'
Where :destination is part of the Calls model and destination_id is part of the users model. Each user has a different destination_id value.
Routes
Outofhours::Application.routes.draw do
ActiveAdmin.routes(self)
devise_for :admin_users, ActiveAdmin::Devise.config
get "log_out" => "sessions#destroy", :as => "log_out"
get "log_in" => "sessions#new", :as => "log_in"
get "sign_up" => "users#new", :as => "sign_up"
resources :users
resources :sessions
resources :calls
root :to => 'dashboards#index'
resources :dashboards
end
user model
class User < ActiveRecord::Base
attr_accessible :email, :company, :destination_id, :password, :password_confirmation
attr_accessor :password
before_save :encrypt_password
validates_confirmation_of :password
validates_presence_of :password, :on => :create
validates_presence_of :email
validates_uniqueness_of :email
validates_uniqueness_of :company
validates_uniqueness_of :destination_id
def self.authenticate(email, password)
user = find_by_email(email)
if user && user.password_hash == BCrypt::Engine.hash_secret(password, user.password_salt)
user
else
nil
end
end
def encrypt_password
if password.present?
self.password_salt = BCrypt::Engine.generate_salt
self.password_hash = BCrypt::Engine.hash_secret(password, password_salt)
end
end
end
call model
class Call < ActiveRecord::Base
end
You could pass the destination to the controller in the params array. This way you could access it in the controller like this
def index
##calls = Call.all
#calls = Call.where(:destination => current_user.destination_id).limit(25)
respond_to do |format|
format.html # index.html.erb
format.json { render :json => #calls }
end
end