FactoryBot creates records even when a record is provided - ruby

I use factorybot to create records in development sometimes. However, it's creating a bunch of extra data that I wasn't expecting.
I have two factories:
FactoryBot.define do
factory :user do
first_name { Faker::Name.first_name }
last_name { Faker::Name.last_name }
email { "#{first_name}.#{last_name}#example.com" }
end
factory :post do
user { create(:user) }
title { Faker::Lorem.sentence }
end
end
If I run FactoryBot.create(:post) in the rails console, it will create a new post with a new user. What I didn't expect is that if I do FactoryBot.create(:post, user: User.first), it would create a post associated with the first user, but still create a new record. So, I get this:
irb(main):001:0> User.count
=> 1
irb(main:002:0> FactoryBot.create(:post, user: User.first)
=> #<Post id: 1, title: 'Lorem Ipsum', host_id: 1>
irb(main:003:0> User.count
=> 2
Everything works, it just creates a new user record that isn't attached to anything. Is there anyway to stop that from happening?

You don't need to tell FactoryBot to create(:user). Just remove it.
factory :post do
user
title { Faker::Lorem.sentence }
end
https://devhints.io/factory_bot

Related

Creating a FactoryBot Hash model with complicated structure, including multiple nested attributes

I'm implementing a set of Cucumber driven API tests and stumbled into one specific model that has a few nested elements, like this:
factory :human_being, class: Hash do
human {
name {
first_name { Faker::Name.first_name }
last_name { Faker::Name.last_name }
}
age { rand(1..100) }
}
initialize_with { attributes }
end
The result I wanted to achieve should look like this:
{
:human => {
:name => {
:first_name => "mike",
:last_name => "blob"
},
:age => 16
}
}
I am invoking creation using FactoryBot.build(:human_being) and I get undefined method 'name' for #<FactoryBot::SyntaxRunner:0x00007fd640a39a80>
My env.rb has the World(FactoryBot::Syntax::Methods) / FactoryBot.find_definitions lines.
I've lurked through a few answers regarding the nested attributes / associations / traits but I didn't find a proper way to get what I want. Sorry if the post is duplicated and thanks in advance for help.
Okay, seems that I've found the solution right after posting the question. Currently the following model creates the Hash I described in the question:
factory :human_being, class: Hash do
human { {
name: {
first_name: Faker::Name.first_name,
last_name: Faker::Name.last_name,
},
age: rand(1..100),
sex: %w(male female other).sample
} }
initialize_with { attributes }
end
Although I'm not sure it is a completely correct answer from guidelines perspective, but at least it works for my exact case.
source (See 'Defining factories'): Because of the block syntax in Ruby, defining attributes as Hashes (for serialized/JSON columns, for example) requires two sets of curly brackets:
factory :program do
configuration { { auto_resolve: false, auto_define: true } }
end

Activeldap: add a value to a multivalued attribute in the active directory

i have a users and groups in my AD.
ou=users, dc=MLT, dc=local
ou=groups, dc=MLT, dc=local
i want to associate the users with the groups using activeldap.
i do that through adding the users distinguish name (user.dn) to an attribute in the groups called "member" which is multivalued attribute.
you can hier read the description of "member"
https://learn.microsoft.com/en-us/windows/win32/ad/group-objects
This is the Ruby code that I wrote :
config = {
host: 'c',
base: 'dc= MLT,dc=local',
bind_dn: ENV['AD_SERVICE_USER'],
password: ENV['AD_SERVICE_PASSWORD']
}
# Connect to LDAP server
ActiveLdap::Base.setup_connection config
class Group < ActiveLdap::Base
query = {
dn_attribute: 'cn',
prefix: 'ou=groups',
classes: %w[top group]
}
ldap_mapping query
end
class User < ActiveLdap::Base
query = {
dn_attribute: 'cn',
prefix: 'ou=users'
}
ldap_mapping query
end
# associate "Joe" to the "employee" group
group = Group.find(:first, attribute: 'cn', value: 'employee')
user = User.find(:first, attribute:'cn', value:'Joe')
# I used push to add the new user.dn because "member" should be a list of distinguished names.
group.member.push(user.dn)
begin
group.save
rescue ActiveLdap::SaveError
puts 'Could not save group!'
puts new_group.errors.full_messages
end
I became this error message:
undefined method `push' for #<ActiveLdap::DistinguishedName:0x00007f8d2054cb58>
can anyone tell me how can I add more than one value to member ????

How to get id from post route in Sinatra

I have a factory that is creating a student. All of the required fields are being filled in and created in the factory. When I post a new student, I am never getting that student, just the one created in the factory? How Can I get the id of the new student from the POST?
students.rb
FactoryGirl.define do
factory :student do
sequence(:id)
sequence(:first_name){|n| "Tom"}
sequence(:last_name) {|n| "Dick"}
sequence(:password) {|n| "test"}
sequence(:email){ |n| "person#{n}#example.com" }
school
end
end
student_spec.rb
require File.dirname(__FILE__) + '/spec_helper'
describe 'admin' do
before :all do
#student=FactoryGirl.create(:student)
end
it 'should save student information' do
params={
post:{
first_name: 'Abraham',
last_name: 'Lincoln',
address: '1400 Pennsylvania Ave.',
city: 'Washington',
state: 'D.C.',
zip: '11111',
email: 'alincoln#whitehouse.gov',
password: 'I cant tell a lie',
major: 'Law',
level: 'Graduate',
employment: 'President',
participation: 'Yes',
participation_research: 'No',
what_doing_now: 'Watching a Play',
ethnicity: 'White',
eth_other: 'none',
education_level: 'Doctorate',
hometown: 'Springfield, IL',
twitter_handle: '#TheRealAbe',
facebook_url: 'therealabe',
prior_education: 'None',
prior_AS: 'None',
prior_BS: 'None',
prior_MS: 'None',
prior_Other: 'None',
awards_honors: 'Ended Civil War',
scholarships: 'Full',
other_inbre_funding: 'yes',
deleted: 'false'
}
}
post '/admin/students/add/', params, {'rack.session'=>{student_id:#student.id, admin_authorized:true}}
updated_student = Student.get(#student.id)
expect(updated_student.first_name).to eql('Abraham')
#expect(updated_student.last_name).to eql('Dick')
#expect(updated_student.address).to eql('1400 Pennsylvania Ave.')
end
end
I can't know for certain without seeing the route, but it seems to me that you are calling POST twice, once in the route, and once within the params hash. However, without seeing your routes, I can't be sure if that's an issue.
Another thought comes from reading your spec. Are you attempting to create a new student(POST) or are you attempting to edit the student? If you are attempting to edit an existing student, this should probably be referring to a separate route, PUT or a PATCH by RESTful conventions. As was mentioned above, a POST route does not need an id, precisely because it is creating a new student, which is where it will create the id, and the id is set by the database, not manually.
a reference to the above: http://guides.rubyonrails.org/v2.3.11/routing.html
I hope this at least points in the right direction. :D

FactoryGirl Sequence not instantiating when creating an object with association

I have to write a few tests for a controller I have. I have 3 models : User, UserInfo and UserPaymentPreference. I am using FactoryGirl to make a UserPaymentPreference object like this:
factory :advertiser_payment_preference_sequence do
association :advertiser_user, factory: [:advertiser_user_sequence]
end
factory :advertiser_user_sequence, class: 'AdvertiserUser' do
deposit { rand(100..1000).round(2) }
sequence(:login) { |n| "#{Faker::Internet.domain_name}#{n}" }
user_type 'advertiser'
association :user_info, factory: :advertiser_user_info_sequence
trait :no_deposit do
deposit 0
end
end
factory :advertiser_user_info_sequence, class: 'AdvertiserUserInfo' do
sequence(:firstname) { |n| "#{Faker::Name.first_name}#{n}" }
sequence(:organization) { |n| "#{Faker::Company.name}#{n}" }
sequence(:phone) { |n| "#{Faker::PhoneNumber.cell_phone}#{n}" }
sequence(:fiscal_code) { |n| "#{Faker::Company.duns_number}#{n}" }
sequence(:address) { |n| "#{Faker::Address.street_address}#{n}" }
sequence(:city) { |n| "#{Faker::Address.city}#{n}" }
end
This works fine if I do something like FactoryGirl.build(:advertiser_user_info_sequence) it will create an object of type UserInfo with all the fields populated as they should be.
However when I try to make an AdvertiserPaymentPreference object by doing FactoryGirl.build(:advertiser_payment_preference_sequence) all the user_info fields will be "" instead of the value that it should have been created by the association.
describe 'Advertiser::Billing::SettingsController', type: :request do
let!(:campaign) { create :campaign }
let!(:user) { campaign.user }
let!(:user_info) { user.user_info }
let!(:advertiser_payment_preference) { create(:advertiser_payment_preference, advertiser_user_id: user.id)}
before :each do
login(user)
end
describe "PUT/PATCH /advertiser/billing/settings" do
context "it is a valid request" do
it "updates the resource" do
binding.pry
new_advertiser_payment_preference = build(:advertiser_payment_preference_sequence)
binding.pry
params = {}
params['user_info'] = to_api_advertiser_info(new_advertiser_user_info)
api_put '/advertiser/billing/profile', params
expect(response.status).to eq(200)
user_info.reload
expect(user_info.organization).to eq(new_advertiser_user_info.organization)
expect(user_info.phone).to eq(new_advertiser_user_info.phone)
expect(user_info.fiscal_code).to eq(new_advertiser_user_info.fiscal_code)
expect(user_info.country).to eq(new_advertiser_user_info.country)
expect(user_info.address).to eq(new_advertiser_user_info.address)
end
end
end
end
This is how the test for the controller looks like . When I call new_advertiser_payment_preference = build(:advertiser_payment_preference_sequence) I get an object user_info with "" on all fields that are made using advertser_user_info_sequence.

Update activerecord relation given a hash of multiple entries

I'm quite new to Rails, so be gentle :)
I have the following models set-up:
class User
has_many :it_certificates, :class_name => 'UserCertificate'
class UserCertificate
belongs_to :skill
Given the following input (in JSON)
{
"certificates":[
{ // update
"id":1,
"name":"Agile Web Dev 2",
"entity":"Agile Masters!",
"non_it":false,
"date_items":{
"month":10,
"year":2012
},
"skill": {
"id":57
}
},
{ // create
"name":"Agile Web Dev 1",
"entity":"Agile Masters!",
"non_it":false,
"date_items":{
"month":10,
"year":2011
},
"skill": {
"id":58
}
}
]
}
How's the easiest way to update the information for the relation it_certificates?
I've been looking to update_all but it doesn't match my needs (it only updates given fields with the same value).
So I've been struggling around with the approach of iterating over each of these records and then update them one-by-one.
I mean struggling because it looks to me there are lots of things I have to care of when the idea of Rails is the opposite.
Thanks in advance!
So, here's my solution for now:
def self.update_from_hash(data, user_id)
self.transaction do
data.each do |certificate|
if certificate[:id] == nil
# create
if !self.create(
:name => certificate[:name],
:entity => certificate[:entity],
:user_id => user_id,
:non_it => certificate[:non_it],
:skill_id => certificate[:skill][:id],
:date => self.build_date_from_items(certificate[:date_items][:month], certificate[:date_items][:year])
)
raise ActiveRecord::Rollback
end
else
# update
if !self.update(certificate[:id], {
:name => certificate[:name],
:entity => certificate[:entity],
:non_it => certificate[:non_it],
:skill_id => certificate[:skill][:id],
:date => self.build_date_from_items(certificate[:date_items][:month], certificate[:date_items][:year])
})
raise ActiveRecord::Rollback
end
end
end
end
return true
end
It works, but I'm still expecting a more elegant solution :)

Resources