RSpec and ActiveRecord : Examples failing while checking One To Many association - ruby

I have been developing a sample application after reading Rails 3 Tutorial book. In this application there are many Sections and each section has one Video.
Here are the artifacts of this application:
Models
class Video < ActiveRecord::Base
has_many :sections
end
class Section < ActiveRecord::Base
belongs_to :video
validates :name, presence: true, length: { maximum: 50 }
end
RSpec
require 'spec_helper'
describe Section do
let(:video) { Video.new(name: "Dummy Video Name", path: "/home/data/video/test.mov") }
before do
#section = video.sections.build(name: 'Test Section')
end
subject { #section }
# Check for attribute accessor methods
it { should respond_to(:name) }
it { should respond_to(:video) }
it { should respond_to(:video_id) }
its(:video) { should == video }
# Sanity check, verifying that the #section object is initially valid
it { should be_valid }
describe "when name is not present" do
before { #section.name = "" }
it { should_not be_valid }
end
describe "when name is too long" do
before { #section.name = "a" * 52 }
it { should_not be_valid }
end
describe "when video_id not present" do
before { #section.video_id = nil }
it { should_not be_valid }
end
...
end
And the schema definitions of both Models
..
create_table "sections", :force => true do |t|
t.string "name"
t.integer "video_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "videos", :force => true do |t|
t.string "name"
t.string "path"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
...
While running above rspec I am getting following error.
Failures:
1) Section
Failure/Error: it { should be_valid }
expected valid? to return true, got false
# ./spec/models/section_spec.rb:22:in `block (2 levels) in <top (required)>'
2) Section video
Failure/Error: its(:video) { should == video }
expected: #<Video id: nil, name: "Dummy Video Name", path: "/home/data/video/test.mov", created_at: nil, updated_at: nil>
got: nil (using ==)
# ./spec/models/section_spec.rb:19:in `block (2 levels) in <top (required)>'
I could map my requirement with User-Micropost relation describe in the book and aligned RSpec with them. I have limited knowledge on Rails and the whole echo system.
Please help me to resolve this issue and some reference to validation Models with RSpec(and shoulda) is highly appreciable.
Amit Patel

I am able to resolve this issue by saving video in before block.
Here is the snippet
before do
video.save
#section = video.sections.build(name: 'Test Section')
end
The only difference between the Micropost model spec of Rails 3 Tutorial book and the above one is , the former one is using FactoryGirl#create. From https://github.com/thoughtbot/factory_girl/wiki/How-factory_girl-interacts-with-ActiveRecord I found that FactoryGirl.create method actually creates and save instance internally. So it was working there while it was no working in my code.
If you have better insight with RSpec for ActiveRecord then please share with us.
Thanks.
Amit Patel

Related

Get Stripe charge information rails 5 create order

So I'm implementing Stripe and users are able to purchase successfully, however, I would like to get the charge information, last 4 card numbers, card type etc so I can generate receipts using https://github.com/excid3/receipts.
Here is what I have so far:
PaymentsController
class PaymentsController < ApplicationController
before_action :authenticate_user!
def create
token = params[:stripeToken]
#course = Course.find(params[:course_id])
#user = current_user
begin
charge = Stripe::Charge.create(
amount: (#course.price*100).to_i,
currency: "gbp",
source: token,
description: params[:stripeEmail],
receipt_email: params[:stripeEmail]
)
if charge.paid
Order.create(
course_id: #course.id,
user_id: #user.id,
Amount: #course.price
)
end
flash[:success] = "Your payment was processed successfully"
rescue Stripe::CardError => e
body = e.json_body
err = body[:error]
flash[:error] = "Unfortunately, there was an error processing your payment: #{err[:message]}"
end
redirect_to course_path(#course)
end
end
OrdersController
class OrdersController < ApplicationController
layout proc { user_signed_in? ? "dashboard" : "application" }
before_action :authenticate_user!
def index
#orders = Order.includes(:course).all
end
def show
#order = Order.find(params[:id])
respond_to do |format|
format.pdf {
send_data #order.receipt.render,
filename: "#{#order.created_at.strftime("%Y-%m-%d")}-aurameir-courses-receipt.pdf",
type: "application/pdf",
disposition: :inline
}
end
end
def create
end
def destroy
end
end
Order.rb
class Order < ApplicationRecord
belongs_to :course
belongs_to :user
validates :stripe_id, uniqueness: true
def receipt
Receipts::Receipt.new(
id: id,
subheading: "RECEIPT FOR CHARGE #%{id}",
product: "####",
company: {
name: "####",
address: "####",
email: "####",
logo: "####"
},
line_items: [
["Date", created_at.to_s],
["Account Billed", "#{user.full_name} (#{user.email})"],
["Product", "####"],
["Amount", "£#{amount / 100}.00"],
["Charged to", "#{card_type} (**** **** **** #{card_last4})"],
["Transaction ID", uuid]
],
font: {
normal: Rails.root.join('app/assets/fonts-converted/font-files/AvenirBook.ttf')
}
)
end
end
schema.rb
create_table "orders", force: :cascade do |t|
t.integer "user_id"
t.integer "course_id"
t.integer "stripe_id"
t.integer "amount"
t.string "card_last4"
t.string "card_type"
t.integer "card_exp_month"
t.integer "card_exp_year"
t.string "uuid"
t.index ["course_id"], name: "index_orders_on_course_id"
t.index ["user_id"], name: "index_orders_on_user_id"
end
How would I go about getting the charge information?
All the information you need is in the charge object you created.
if charge.paid
Order.create(
course_id: #course.id,
user_id: #user.id,
amount: #course.price,
card_last4: charge.source.last4,
card_type: charge.source.brand,
card_exp_month: charge.source.exp_month,
card_exp_year: charge.source.exp_year
)
end
See https://stripe.com/docs/api/ruby#charge_object for all the information available to you about a charge.
I guess you are not storing the Stripe Charge ID anywhere on successful payment.
My suggestion, store the charge ID in your Order record
if charge.paid
Order.create(
course_id: #course.id,
user_id: #user.id,
amount: #course.price,
stripe_charge_id: charge.id
)
end
In your receipt method you can fetch the stripe charge as follows:
def receipt
stripe_charge = Stripe::Charge.retrieve(stripe_charge_id)
...
end
The stripe_charge objects contains all the information and you can use whatever data you need.
Hope this helped.

before_action and nested attributes for models

The question is really simple.
Please, take a look at my schema:
ActiveRecord::Schema.define(version: 20170812094528) do
create_table "items", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "parent_id"
end
create_table "parents", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "item_id"
end
end
This is how models looks like:
class Parent < ApplicationRecord
has_one :item
accepts_nested_attributes_for :item
before_create :set_something
def set_something
self.item.build
end
end
and
class Item < ApplicationRecord
belongs_to :parent
end
The question: When creating a Parent, why does it raise the following error?
undefined method `build' for nil:NilClass
How should I set this up so that I can add and item record at the same time as creating a parent?
Try using
def set_something
build_item
end
The model Parent has_one :item. In Rails, the has_one association adds a helper method build_association(attributes = {}) to the owner class, and not association.build(attributes = {}). This is the reason that you are getting the error undefined method 'build' for nil:NilClass.
Please see the has_one documentation for more details on has_one association.

Railstutorial.org 6.2.4 Format Validation

Working my way through Hartl's Railstutorial.org, an I run into an issue getting the test to work on the Format validation with the email.
My user.rb is as follows:
class User < ActiveRecord::Base
validates :name, presence: true, length: { maximum:50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z]+\z/i
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX }
end
user_spec.rb is:
require 'spec_helper'
describe User do
before do
#user = User.new(name: "Example User", email: "user#example.com")
end
subject { #user }
it { should respond_to(:name) }
it { should respond_to(:email)}
it { should be_valid}
describe "when name is not present" do
before { #user.name = " " }
it { should_not be_valid }
end
describe "when email is not present" do
before { #user.email=" "}
it { should_not be_valid }
end
describe "when name is too long" do
before { #user.name="a"*51}
it {should_not be_valid}
end
describe "when email format is invalid" do
it "should be invalid" do
addresses = %w[user#foo,com user_at_foo.org
example.user#foo. foo#bar_baz.com foo#bar+baz.com]
addresses.each do |invalid_address|
#user.email = invalid_address
expect(#user).not_to be_valid
end
end
end
describe "when email format is valid" do
it "should be valid" do
addresses = %w[user#foo.COM A_US-ER#f.b.org frst.lst#foo.jp a+b#baz.cn]
addresses.each do |valid_address|
#user.email = valid_address
expect(#user).to be_valid
end
end
end
end
My error listing is as follows:
Failures:
1) User
Failure/Error: it { should be_valid}
expected #<User id: nil, name: "Example User", email: "user#example.com",
created_at: nil, updated_at: nil> to be valid, but got errors: Email is invalid
# ./spec/models/user_spec.rb:13:in 'block (2 levels) in <top (required)>'
2) User when email format is valid should be valid
Failure/Error: expect(#user).to be_valid
expected #<User id: nil, name: "Example User", email: "user#foo.COM". cre
ated_at: nil, updated_at: nil> to be valid, but got errors. Email is invalid
# ./spec/models/user_spec.rb:45:in 'block (4 levels) in <top (required)>'
# ./spec/models/user_spec.rb:43:in 'each'
# ./spec/models/user_spec.rb:43:in 'block (3 levels) in >top (required)>'
Finished in 0.03 seconds
8 examples, 2 failures
Failed examples:
rspec ./spec/models/user_spec.rb:13 # User
rspec ./spec/models/user_spec.rb:41 # User when email format is valid should be
valid
I am sure that I am missing something minor (when I have this much trouble figuring it out, it usually is minor). Would greatly appreciate any help I can get.
The exact code in the tutorial is as follows:
class User < ActiveRecord::Base
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX }
end
your VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z]+\z/i lacks some letters.

Rails ActiveRecord Ignoring Params and Saving Nil Data

This is stumping me; For some reason the db is saving the record with nil fields instead of my params. Can anyone help me understand why ActiveRecord isn't using my params?
db migration:
class CreateRoutes < ActiveRecord::Migration
def change
create_table :routes do |t|
t.integer :user_id
t.string :start_address
t.string :end_address
t.text :waypoints
t.text :schedule
t.integer :duration
t.timestamps
end
add_index :routes, :user_id
end
end
route.rb:
class Route < ActiveRecord::Base
attr_accessor :start_address, :end_address, :waypoints, :schedule, :duration
belongs_to :user
#serialize :waypoints, :schedule
validates :user_id, presence: true
validates :start_address, presence: true
validates :end_address, presence: true
validates :schedule, presence: true
validates :duration, presence: true, numericality: { only_integer: true, greater_than: 0 }
end
routes_controller.rb:
class RoutesController < ApplicationController
.
.
.
def create
logger.debug "\n\n*** #{params[:route]} ***"
#route = current_user.routes.build(params[:route])
logger.debug "*** The route is #{#route.inspect} ***\n\n"
if #route.save
flash[:success] = "Route saved!"
redirect_to user_path(current_user)
else
render 'new'
end
end
.
.
.
end
logger output:
*** {"start_address"=>"123 Sample St.", "end_address"=>"321 Elpmas St.", "waypoints"=>"None", "schedule"=>"Mondays", "duration"=>"15"} ***
*** The route is #<Route id: nil, user_id: 1, start_address: nil, end_address: nil, waypoints: nil, schedule: nil, duration: nil, created_at: nil, updated_at: nil> ***
The attr_accessors will overwrite the accessors generated by ActiveRecord, causing them to not be persisted in the DB--they'll be like plain old Ruby properties/members instead of the meta-programmed magic of ActiveRecord.
DB properties (persistent properties) can have things like attr_accessible, though.

Attribute Not Being Added to Object

I'm trying to add an attribute to a model object. Direct access works but when I print the entire object or encode it into JSON, that attribute is left out. Can someone tell me what I'm doing wrong?
Here is my rails console output:
irb(main):010:0> b=ChatMessage.new(:user_id=>4,:room_id=>1,:message=>"Hello World")
=> #<ChatMessage id: nil, room_id: 1, user_id: 4, message: "Hello World", created_at: nil, updated_at: nil>
irb(main):011:0> b.sender_nickname="bbb"
=> "bbb"
irb(main):012:0> b.sender_nickname
=> "bbb"
irb(main):013:0> b
=> #<ChatMessage id: nil, room_id: 1, user_id: 4, message: "Hello World", created_at: nil, updated_at: nil>
Here is my model code:
class ChatMessage < ActiveRecord::Base
attr_accessor :sender_nickname
def self.get_last_message_id
last_message=ChatMessage.all.last
last_message.nil? ? 0 : last_message.id
end
def self.get_all_messages_after(room_id,message_id)
ChatMessage.where("room_id = ? AND id > ?",room_id,message_id)
end
end
edit:
Here is the migration file for chat_messages table.
I'm not really looking to save sender_nickname. So it's more like a virtual attribute (but is still in db through association). And I might need to add other attributes later that aren't in the db. Is it possible to do it without using association?
def self.up
create_table :chat_messages do |t|
t.integer :room_id
t.integer :user_id
t.string :message
t.timestamps
end
end
as far as I know to_json will only take the attributes in the model and serialize (as in chat_message.attributes, not attr_accessor).
You properbly got a sender, or user model, or anything like that.
What I would do is to make a relation to the sender, user or what its called, with a belong_to, and then use this code to convert it to json:
chat_message.to_json(:include => { :sender => { :only => :nickname } })
It may also work with you code, and then just:
chat_message.to_json(:include => { :sender_nickname })
There also some documentation here: http://api.rubyonrails.org/classes/ActiveModel/Serializers/JSON.html
Hope it helps :)

Resources