rspec model attribute change not saving properly - ruby-on-rails

I am using factory girl to create a model with inventory_count = 3. In my test, I want to test a case for when inventory_count = 0..so here's what I did:
before(:each) do
#user = FactoryGirl.create(:user)
#piece = FactoryGirl.create(:piece)
#lineup = #user.lineup
end
it 'should have status \'Waiting List\' if the piece doesn\'t have available inventory' do
#piece.available_count = 0
#lineup.pieces << #piece
piece_lineup = #lineup.piece_lineups.find_by_piece_id(#piece.id)
piece_lineup.set_status
piece_lineup.status.should == 'Waiting List'
end
I put a debugger after #piece.available_count = 0 and it is = 0, but when it gets down to the next line it switches back to the old value. I tried adding a .save to #piece but it still didn't work. Am i doing something wrong? Should I be creating the new value model in factorygirl instead of trying to do it on the fly?

I believe it would be better practice to write:
#piece = FactoryGirl.create(:piece, :available_count => 0)
to create a piece model with its count already 0.
Also, you're question begins mentioning inventory_count. Did you actually mean available_count? Just want to make sure you didn't have a typo.

The change isn't making it to the DB. Try
#piece.update_attributes!(available_count: 0)
EDIT: To skip the callback that is resetting the available_count, use update_column(:available_count, 0)

Related

Iterating through only persisted objects

This is the code for my create display:
def create
#display = #department.displays.new(display_params)
#token = #display.build_token(value: (max_token + 1) , status: 0)
if #display.save
....
end
max_token is the method called to find the largest number of token in the display tokens.
def max_token
#tokens = #department.displays.map do |display|
display.token.value
end
#tokens.max
end
Problem
I've created a new display for the department with the code in the create method.
#display = #department.displays.new(display_params)
But it is not saved yet, as the #display.save is called only after the max_token method.
But when the max_token method is called, the code
#tokens = #department.displays.map do |display|
is also displaying the unsaved display of the department.
And since the token of the display has not been set yet, as it is not saved, throws a nil value error.
My Solution
This is what i've tried upto now, but I want to know if there's a better method.
def max_token
#tokens = #department.displays.map do |display|
if display.token.nil?
display.token.value
else
0
end
end
#tokens.max
end
If you're not worried about the uniqueness of value at the DB layer, you can simply filter out displays with a nil value for token:
def max_token
#department.displays.where.not(token: nil).map do |display|
display.token.value
end.max
end
(This is also assuming you don't actually need to assign #tokens as a side effect of max_token.)
Try to create a new separated Display first then assign it to the #department after max_token get called so the new Display won't be included in #department.displays.map
def create
#display = Displays.new(display_params)
#token = #display.build_token(value: (max_token + 1) , status: 0)
#department.displays << #display
if #display.save
....
end

Increment field within validator

I have a custom validator that checks if the user has entered the correct SMS code. When the user enters the wrong code I need to log the failed attempt and limit their retries to 3 per code.
I have created the following validator that works however the field is not being incremented.
def token_match
if token != User.find(user_id).verification_token
User.find(user_id).increment!(:verification_fails)
errors.add(:sms_code, "does not match")
end
end
The problem is as soon as I add the error the previous statement is rolled back. If I comment out the errors.add line then the increment works however there is no higher level validation performed.
Change your custom validator to be:
def token_match
if token != User.find(user_id).verification_token
errors.add(:sms_code, "does not match")
end
end
and add in your model after_validation callback to be like this:
after_validation: increase_fails_count
def increase_fails_count
unless self.errors[:sms_code].empty?
user = User.find_by(:id => user_id)
user.increment!(:verification_fails)
user.save
end
end
You can use #update_columns in your validator. It writes directly to db.
u = User.find(user_id)
u.update_columns(verification_fails: u.verification_fails + 1)
This worked for me. But if for some reason it doesn't work for you, maybe you can try running it in a new thread,which creates a new db connection:
Thread.new do
num = User.find(user_id).verification_fails
ActiveRecord::Base.connection_pool.with_connection { |con| con.exec_query("UPDATE users SET verification_fails = #{num} WHERE id = #{user_id}") }
end.join

Rails - Not hitting while loop when creating classes

I have a create method in Rails where I am trying to create multiple objects in a while loop. For some reason it doesn't seem to be hitting the while loop so no objects are being created. The code is below:
def create
#user = User.find(params[:participant][:user_id])
#activity = Activity.find(params[:activity_id])
weeks = #activity.weeks
i = 1
while i <= weeks do
puts "Test"
participant = Participant.new
participant.user_id = #user.id
participant.activity_id = #activity.id
participant.attended = false
participant.paid = false
participant.week = i
participant.save
i = i+1
end
redirect_to user_activities_path(#user, :id => #activity.id)
end
The form I am using to submit is working fine as I can see from the console, and the redirect_to method at the end is working, so it just seems to be missing the loop. If it helps, the value of weeks is 10. Any help would be greatly appreciated.
If multiple Test have been output, try participant.save!, i think the participant save might fail, like some column not valid, so no objects are being created.
Please check if activity record is get fetched. I think your 3rd statement should be as follow.
#activity = Activity.find(params[:participant][:activity_id])

How do I define a static value for a parameter of a new class?

I created a model for a thing called a Lesson that has :content and a :user_id. For early builds of this app, I want the content to be changing, based on my entry, and for the user_id to always = 1 so that it's clean in the DB and there's not a nil value.
How do I go about this?
In my lessons_controller.rb I have this:
def create
#lesson = Lesson.new(params[:lesson])
if #lesson.save
... do something
else
... do something else
I'm guessing this would be the best place to define that the user_id = 1 but how should I go about that?
You can just set #lesson.user_id = 1 in the line after you create it with new, and before you save it.
Another way to do it would be to set a hook in the lesson model -
before_validation :on => :create do |lesson|
lesson.user_id = 1
end

Controller show action code is flawed, but error is silent

Here is my User controller show action
def show
#public_groups = Group.public
#groups_member = #user.groups_as_member
#groups_as_owner = #user.groups_as_owner
#random_items = []
#assignments = []
unless #groups_member.nil?
until #random_items.count == 5 do
random_groups = #groups_member.sort_by{rand}.slice(0,5)
random_groups.each do |group|
assignments = Assignment.where(:group_id => group.id).limit(5).all
#assignments = Assignment.find_by_group_id(group.id)
y = Post.find_by_id(assignments.rand.post_id)
#random_items << y
end
end
end
end
I think it might be the way I am declaring the instance variable arrays #random_items and #assignments. I have no idea what the problem is though because my development and production servers don't give any compilation errors or anything.
When I comment out the big block of logic starting with the array declarations the site works.
I'd suggest you to perform a refactoring before you can find an error. Some principles before:
Any dataflow is about model layer responsibility
instance variables are use to share objects between ActionPack layers (controller and view)
use object's attributes instead of instance variables to easy to test
use associations and minimize Arel method and just find in controllers
With according to your code, it can be rewritten with:
# User model
def random_items
return unless groups_as_member
random_groups = groups_member.sort_by{rand}.slice(0,5)
random_groups.each do |group|
return if randorm_groups.length > 5
assignments = group.assignments.limit(5)
if y = Post.rand_by_post(assignments)
random_groups << y
end
end
return random_groups
end
# Post model
def self.rand_by_post(assignments)
find_by_id(assignments.rand.post_id)
end
Once you have the logic clear, you can find the bug and cover it with tests.

Resources