Add Validation for Model creation - ruby-on-rails

I have a Ruby on Rails project with 3 models: User, Group and Task.
The idea is that users can be in different groups and groups can have different users, so member is a table in between connecting them.
The problem is that when doing the seeding:
puts "Creating Users"
admin_one = User.create!(email: "jose#test.io", password: "1234567")
admin_two = User.create!(email: "joe#test.io", password: "1234567")
puts "Creating Groups"
group_one = Group.create!(name: "Test Group One", description: "blablabla", admin: admin_one)
group_two = Group.create!(name: "Test Group Two", description: "blablabla", admin: admin_two)
puts "Creating Tasks"
task_one = Task.create!(name: "Task 1", user: user_one, group: group_one, assignee: admin_one)
task_two = Task.create!(name: "Task 2", user: admin_one, group: group_one, assignee: admin_one)
# Test if we can create tasks for users not part of the group
task_two = Task.create!(name: "Task 3", user: admin_one, group: group_one, assignee: admin_two)
I realized that even if admin_one is part of group_one, I can still create a Task with the assignee being admin_two (who is not related to that group at all).
Is there a way to set it so that the task assignee's group needs to be one that the user is in? Is a callback the only option or there are other ways to implement the logic?
Thanks!

You can use a custom validation for this:
# in task model
validate :assignee_in_correct_group
def assignee_in_correct_group
errors.add(:group_id, 'is not the same as assignee group id') unless assignee.groups.exist?(id: group.id)
end

Related

Rails. Create an object with its association in one transaction

I need to create an object with the associated item all in the step of creation.
Although I have tried creating first, and then second, this gives problems in the way that if the second fails, then I get with the first part half done.
Relationship one user has many companies
I mean something like
user = User.create!(
email: prospect.email,
first_name: prospect.first_name,
last_name: prospect.last_name,
#birthdate:prospect.user_birthday,
id_number: prospect.id_number,
phone: prospect.phone,
address: prospect.address,
password: prospect.id_number,
password_confirmation: prospect.id_number,
company = user.companies.create(
name: prospect.vat_company_name,
plan: prospect.plan,
address: prospect.address,
description: prospect.company_description,
email: prospect.email,
phone: prospect.phone,
network_id: prospect.network_id
)
current_company_id: company.id
)
which of course fails because maybe it can't be done directly.
I have tried build instead of create, but same result.
I also know that second create will fail because the first object doesn't exist yet.
How is the best way?
You can create them both separately and wrap them in a transaction:
ActiveRecord::Base.transaction do
user = User.create!(...)
company = Company.create!(...)
end
This way if one of them fails, the other doesn't end up being committed to the database.
If you are talking about separate instance storage then using a db transaction lock is the way to go forward as mentioned by Danial. But if you only need to create an associated record of active record then you can do with Active Record only. It would make sure that both records are saved.
user = User.new
email: prospect.email,
first_name: prospect.first_name,
last_name: prospect.last_name,
#birthdate:prospect.user_birthday,
id_number: prospect.id_number,
phone: prospect.phone,
address: prospect.address,
password: prospect.id_number,
password_confirmation: prospect.id_number
user.build_current_company
name: prospect.vat_company_name,
plan: prospect.plan,
address: prospect.address,
description: prospect.company_description,
email: prospect.email,
phone: prospect.phone,
network_id: prospect.network_id
user.save!
This will create both user and it's current company.
(I am taking an assumption that you have belongs_to :current_company,class_name: 'Company' in user.rb)

Rails 4 STI Model Couldn't find with 'id' when running functional tests

So I have a Request model (I know it's a terrible name), and 2 single inherited models TenantRequest and PropertyRequest. Now I have fixtures for all 3. So I wrote functional controller tests for my requests_controller and my tenant_requests_controller, which both work fine. But for some reason, my property_controller tests show me the following error for every setup:
1) Error:
PropertyRequestsControllerTest#test_should_get_edit:
ActiveRecord::RecordNotFound: Couldn't find Request with 'id'=298486374
test/controllers/property_requests_controller_test.rb:12:in `block in <class:PropertyRequestsControllerTest>'
This is the tenant_requests.yml:
one:
title: This is the title of the tenant request
body: This is the body
user: regular
email: jim#retail.com
type: TenantRequest
contact_name: Jim
Here is my property_request.yml:
one:
title: This is the title of the property request
body: This is the body for property
user: broker
email: sue#broker.com
type: PropertyRequest
contact_name: Sue
budget: 1234
city: New York
region: Manhattan
created_at: now
updated_at: now
status: open
company: Walmart
contact_position: Boss
contact_phone: 555-555-5555
squarefeet: 12345
broker: true
parking: true
onsite_tour: true
part_of_town: Downtown
time_to_reach: 7pm
budget: 1234
Here is the property_requests_controller_test:
require 'test_helper'
class PropertyRequestsControllerTest < ActionController::TestCase
setup do
#regular = users(:jim)
#broker = users(:sue)
#analyst = users(:kev)
#admin = users(:lin)
sign_in :user, #analyst
#myrequest = property_requests(:one)
end
test "should get index" do
get :index
assert_response :success
assert_not_nil assigns(:requests)
end
test "should get new" do
get :new
assert_response :success
end
test "should create request successfully" do
assert_difference('Request.count') do
post :create, request: { contact_name: 'Sue', body: 'this is the body', email: 'sue#broker.com', title: 'newly created property request', type: 'PropertyRequest' }
end
assert_redirected_to property_request_path(PropertyRequest.last)
end
If you need more information, please let me know and I can add it. Thank you,
According to this blog post, fixture files have a one-to-one relationship with database tables. There could be a conflict occurring from having files for each child class. Try placing all fixtures into requests.yml.
Your fixture file is named property_request.yml (singular), while you're calling property_requests(:one) (plural) in the test itself. The Rails Guides show fixture files given pluralized names, so I would rename the file to property_requests.yml to make them match and conform to Rails' conventions (and hope that's the issue).

Rails 3.2.13 + Faker + Devise + Rolify

I'm building a test app and came across a problem with Faker gem:
I've created Use model with Devise and used Rolify gem to create roles and then later on will use CanCan to limit user's usage permissions.
So, with Faker I've created a file for my rake task:
namespace :db do
desc "Fill dummy DB"
task :populate => :environment do
require "populator"
require "faker"
password = "password"
User.populate 198 do |user|
user.firstname = Faker::Name.first_name
user.lastname = Faker::Name.last_name
user.email = Faker::Internet.email
user.encrypted_password = User.new(:password => password).encrypted_password
user.phone = Faker::PhoneNumber.phone_number
user.address1 = Faker::Address.street_address
user.city = Faker::Address.city
user.state = Faker::Address.state_abbr
user.zip = Faker::Address.zip_code
user.latitude = Faker::Address.latitude
user.longitude = Faker::Address.longitude
end
end
end
This code works and it creates 198 dummy users...but when I looked into my users_roles table - nothing's there, users don't have roles assigned to them. I've been trying to figure out how to assign a role to user through Faker, but no luck.
I've tried adding user.role_id = User.add_role :user , but no luck.
Thank you in advance.
I discourage you from using populator gem. There are three reasons to that:
Populator is no longer maintained, last commit was over two years ago. The official repository still says "Rails 3 support is currently being worked on. Stay tuned." with Rails 4 released over a month ago.
It does not have data validation. It is up to you to ensure you’re adding proper data values. And this may end up really messy.
It's really easy to make it programatically giving you more control over custom cases and therefore making you a better developer =)
You can construct your database populator with user role adding with code below:
namespace :db do
desc "Fill dummy DB"
task :populate => :environment do
198.times do |n|
user = User.new(
firstname: Faker::Name.first_name,
lastname: Faker::Name.last_name,
email: Faker::Internet.email,
password: "password",
phone: Faker::PhoneNumber.phone_number,
address1: Faker::Address.street_address,
city: Faker::Address.city,
state: Faker::Address.state_abbr,
zip: Faker::Address.zip_code,
latitude: Faker::Address.latitude,
longitude: Faker::Address.longitude )
user.add_role :user
user.save
end
end
end
Please note as I:
deleted require statements (at least on my system aren't necessary)
moved password into a block
am adding a role using function, not assignment (=) BEFORE saving a user - not possible using populator
Using this construct, you can make more handy things with the User instance, ex. prevent sending a confirmation email when using Devise's :confirmable by invoking user.skip_confirmation! before saving - what you might find useful.
Consider as well generating emails basing on first_name and last_name already generated by Faker before to have user's name and email inline. To achieve that you may replace
Faker::Internet.email
by
"#{user.first_name}.#{user.last_name}#{n+1}#example.com"
taking advantage of block numerator and therefore creating an unique email.

RSpec: Flush table / destroy objects after each context

How do you delete the objects (in the database and in the memory) you created
after each test
AND after each context? (in a context it could make sense to build tests on each other)
Is there a method to do this automatically?
I have the following problem:
Each test saves entries to the database. The next test then depends on these entries. Even if I wanted to build tests that are dependent on other tests, I couldn't, because the order in which the tests get executed is not controllable.
factories.rb:
sequence(:name) { |n| "purchaser #{n}" }
organization_spec.rb:
context "when no supplier exists" do
it "finds no associated suppliers" do
purchaser = create(:organization_purchaser)
purchaser.partners.empty?.should == true
end
end
context "when one supplier exists" do
it "finds one associated suppliers" do
purchaser = create(:organization_purchaser)
supplier = create(:organization_supplier)
partnership = create(:partnership, organization: purchaser, partner: supplier)
purchaser.partners.last.name.should == "purchaser 1"
end
end
context "when two suppliers exist" do
it "finds two associated suppliers" do
purchaser = create(:organization_purchaser)
2.times do |i|
supplier = create(:organization_supplier)
partnership = create(:partnership, organization: purchaser, partner: supplier)
end
purchaser.partners.last.name.should == "purchaser 2"
end
end
RSpec output:
Organization
#suppliers_for_purchaser
responds
when no supplier exists
finds no associated suppliers
when two suppliers exist
finds two associated suppliers
when one supplier exists
finds one associated suppliers (FAILED - 1)
Failures:
1) Organization#suppliers_for_purchaser when one supplier exists finds one associated suppliers
Failure/Error: purchaser.partners.last.name.should == "purchaser 1"
expected: "purchaser 1"
got: "purchaser 3" (using ==)
You should use Database Cleaner
All you have to do is add the following code to your Rspec configuration file spec_helper.rb
config.before(:suite) do
DatabaseCleaner.strategy = :transaction
DatabaseCleaner.clean_with(:truncation)
end
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
UPDATE
As of Rails 5.1 this is not needed if you use config.use_transactional_tests
https://github.com/rails/rails/pull/19282
Did you try adding a before method ?
describe MyController do
before(:each) do
User.delete_all
MyOtherModel.delete_all
...
end
I think your question is more along the lines of how can I reset a factory girl sequence? Those arent stored in the database, even if you delete all you will still have the issue. When testing something like this I find it easer to just override the factory girl sequence..
it "finds one associated suppliers" do
purchaser = create(:organization_purchaser)
supplier = create(:organization_supplier , name: "My First Supplier")
partnership = create(:partnership, organization: purchaser, partner: supplier)
purchaser.partners.last.name.should == "My First Supplier"
end
The other things you probably do is something like
it "finds one associated suppliers" do
purchaser = create(:organization_purchaser)
supplier = create(:organization_supplier)
partnership = create(:partnership, organization: purchaser, partner: supplier)
purchaser.partners.last.should == supplier
end
or even
it "finds one associated suppliers" do
purchaser = create(:organization_purchaser)
supplier = create(:organization_supplier)
partnership = create(:partnership, organization: purchaser, partner: supplier)
purchaser.partners.last.name.should == supplier.name
end

Factory Girl: using sequence inline vs not inline

There are (at least?) two ways to use a sequence in factory girl:
Factory.sequence :my_id do |n|
"#{n}"
end
Factory.define :my_object do |mo|
mo.id Factory.next :my_id
end
and simply doing it inline:
Factory.define :my_object do |mo|
mo.sequence(:id) { |n| "#{n}" }
end
My question is this. If I use the inline version in two different factories, will there be two different sequences that both start at 1 and increment in tandem...meaning that if I create one of each type of factory object they will both have id 1?
If I use the externally defined sequence in two different factories am I guaranteed to get unique ids across the two objects? Meaning will the ids of each object be different?
I am trying to confirm if the behavior above is accurate because I'm working with a completely goofy data model trying to get rspec & factory girl to play nice with it. The designer of the database set things up so that different objects have to have ids generated that are unique across a set of unrelated objects. Changing the data model at this point is not a feasible solution though I'd really love to drag this stuff back onto the Rails.
When using externally defined sequences in two different factories you will see incrementing ids across the factories. However, when using inline sequences each factory will have their own sequence.
I created the example rake task below to illustrate this. It displays the following results:
*** External FactoryGirl Sequence Test Results ***
User Name: Name 1
User Name: Name 2
User Name: Name 3
User Name: Name 4
Role: Name 5
Role: Name 6
Role: Name 7
Role: Name 8
*** Internal FactoryGirl Sequence Test Results ***
User Name: Name 1
User Name: Name 2
User Name: Name 3
User Name: Name 4
Role: Role 1
Role: Role 2
Role: Role 3
Role: Role 4
As you can see, when using external sequences the number continues to increase as you move from the user to the role. However when using an inline sequence the increments are independent of each other.
The following schema files were used for this example:
create_table "users", :force => true do |t|
t.string "name"
t.string "email"
end
create_table "roles", :force => true do |t|
t.string "name"
end
The example rake task is:
require 'factory_girl_rails'
namespace :sequencetests do
Rake::Task[:environment].invoke
task :external do
FactoryGirl.factories.clear
desc "Factory Girl Sequence Test using an externally defined sequence"
puts "*** External FactoryGirl Sequence Test Results ***"
FactoryGirl.define do
sequence :name do |n|
"Name #{n}"
end
factory :user do |u|
name
end
factory :role do |r|
name
end
end
users = buildit(:user)
roles = buildit(:role)
puts( showit(users, "User Name: "))
puts( showit(roles, "Role: "))
end
task :inline do
FactoryGirl.factories.clear
puts "*** Internal FactoryGirl Sequence Test Results ***"
desc "Factory Girl Sequence Test using an inline sequence"
FactoryGirl.define do
factory :user do |u|
u.sequence(:name) {|n| "Name #{n}" }
end
factory :role do |r|
r.sequence(:name) {|n| "Role #{n}" }
end
end
users = buildit(:user)
roles = buildit(:role)
puts( showit(users, "User Name: "))
puts( showit(roles, "Role: "))
end
end
task sequencetests: ['sequencetests:external', 'sequencetests:inline']
def buildit(what)
items = []
4.times do
items << FactoryGirl.build(what)
end
items
end
def showit(items, prefix = "Name: ")
results = ""
items.each do |item|
results += "#{prefix}#{item.name}\n"
end
results
end
I hope this helps explain the different possibilities when using sequences in FactoryGirl.
Yes, the inline versions will create 2 independent sequences, each starting at 1

Resources