How do I define associated Factories with FactoryBot? - ruby

Given two models, Alert and Zipcode, where one Alert must have 1 or more Zipcodes:
class Alert < ActiveRecord::Base
attr_accessible :descr, :zipcode
has_many :zipcode
validates :zipcode, :length => { :minimum => 1 }
end
class Zipcode < ActiveRecord::Base
attr_accessible :zip
belongs_to :alert
end
How do I write my FactoryBot factories so that:
Zipcode factories are defined in their own file
Alert factories are defined in their own file
Alert can rely on the factory defined by Zipcode?
All of the documentation and examples I read expect you to define the contained class inside the parent factory file, blob them all together, or make some other compromise or work-around. Isn't there a clean way to keep the spec factories separate?

The trick is to make sure the container class, that is, the one with a has_many statement in its definition, creates the contained class as an array in FactoryBot. For example:
In your spec/factories/zipcodes.rb:
FactoryBot.define do
factory :zipcode do
zip { 78701 + rand(99) }
end
end
And in spec/factories/alerts.rb:
FactoryBot.define do
factory :alert do
zipcode { Array.new(3) { FactoryBot.build(:zipcode) } }
end
end

Related

Calling a Associated Controller Method Not Working

I have 3 models of User, Role and UserRole with their respective controllers as UsersController, RolesController and UserRolesController.
I have a method in the UserRoles controller which I would want to access through the Users controller but I keep having errors as explained below.
I have tried various means of even moving the method def self.add_roles_to_user(user, role) from the UsersRoles controller into the UserRole model and call it but I keep having the same error.
I have gone through lots of similar questions and various blogs, including those on this platform such as Calling a method from controller, and others but to no good results.
class UserRole < ApplicationRecord
# many-to-many association using join table with roles and user
belongs_to :user, inverse_of: :user_roles
belongs_to :role, optional: true, inverse_of: :user_roles
end
class User < ApplicationRecord
has_many :user_roles, inverse_of: :user
has_many :roles, through: :user_roles
end
class Role < ApplicationRecord
# table associations between role and user
has_many :user_roles, inverse_of: :role
has_many :users, through: :user_roles
end
class UserRolesController < ApplicationController
def self.add_roles_to_user(user, role)
if ! user.nil?
if role.length > 0
role.each do |sel_role|
#u_role = UserRole.new
#u_role.user_id = user_id
#u_role.role_id = sel_role.role_id
#u_role.save
end
end
end
end
end
class Users::RegistrationsController < Devise::RegistrationsController
def create_user
respond_to do |format|
if #user.save
# add roles
UserRoles.add_user_roles(params[:user], params[:role])
end
end
end
end
I am calling the add_uer_to_role method in the User controller when I am adding or creating a new user.
What I have noticed is that I keep getting different errors based on how I call the method.
For example, I expect to have no errors when I call the method like; UserRoles.add_roles_to_user(params[:user], params[:role]) but it gives the error NameError (uninitialized constant Users::RegistrationsController::UserRoles):
Hoping a good samaritan will come to my aid as soon as possible. Thanks in advance
If it is a common function, you can define it in application controller and call it. Else you can define in helper.
Please verify Calling a method from another controller
You can use that function as a module and use it:
# lib/common_stuff.rb
module CommonStuff
def common_thing
# code
end
end
# app/controllers/my_controller.rb
require 'common_stuff'
class MyController < ApplicationController
include CommonStuff
# has access to common_thing
end

Creating multiple unique entries with transient FactoryGirl

I have a FactoryGirl Object that creates a Category in my case (it is associated with an image)
class Image < ActiveRecord::Base
has_many :image_categories, dependent: :destroy
has_many :categories, through: :image_categories
validates :categories, presence: { message: 'Choose At Least 1 Category' }
end
class Category < ActiveRecord::Base
has_many :image_categories
has_many :images, through: :image_categories
validates :name, presence: { message: "Don't forget to add a Category" }
validates_uniqueness_of :name, message: 'Category name %{value} already exists'
end
FactoryGirl.define do
factory :category do
name 'My Category'
end
end
FactoryGirl.define do
factory :image do
title 'Test Title'
description 'Test Description'
transient do
categories_count 1
end
categories { build_list(:category, categories_count) }
end
end
When creating an image with 1 category everything is fine, but if i try and save with 2 categories the second entry gets saved as nil, I guess thats because of my validation of unique names.
So my question is how can I use transients to create a list of 2 unique categories
Hope this makes sense
Thanks
Direct from Factory Girl Documentation: http://www.rubydoc.info/gems/factory_girl/file/GETTING_STARTED.md#Transient_Attributes
FactoryGirl.define do
# post factory with a `belongs_to` association for the user
factory :post do
title "Through the Looking Glass"
user
end
# user factory without associated posts
factory :user do
name "John Doe"
# user_with_posts will create post data after the user has been created
factory :user_with_posts do
# posts_count is declared as a transient attribute and available in
# attributes on the factory, as well as the callback via the evaluator
transient do
posts_count 2
end
# the after(:create) yields two values; the user instance itself and the
# evaluator, which stores all values from the factory, including transient
# attributes; `create_list`'s second argument is the number of records
# to create and we make sure the user is associated properly to the post
after(:create) do |user, evaluator|
create_list(:post, evaluator.posts_count, user: user)
end
end
end
end

Override a Mongoid model's setters and getters

Is there a way to override a setter or getter for a model in Mongoid? Something like:
class Project
include Mongoid::Document
field :name, :type => String
field :num_users, type: Integer, default: 0
key :name
has_and_belongs_to_many :users, class_name: "User", inverse_of: :projects
# This will not work
def name=(projectname)
#name = projectname.capitalize
end
end
where the name method can be overwritten without using virtual fields?
better use
def name=(projectname)
super(projectname.capitalize)
end
the method
self[:name] = projectname.capitalize
can be dangerous, cause overloading with it can cause endless recursion
def name=(projectname)
self[:name] = projectname.capitalize
end
I had a similar issue with needing to override the "user" setter for a belongs_to :user relationship. I came up with this solution for not only this case but for wrapping any method already defined within the same class.
class Class
def wrap_method(name, &block)
existing = self.instance_method(name)
define_method name do |*args|
instance_exec(*args, existing ? existing.bind(self) : nil, &block)
end
end
This allows you to do the following in your model class:
wrap_method :user= do |value, wrapped|
wrapped.call(value)
#additional logic here
end

Rails Model callback: Passing field as parameter to callback classes?

I want to implement before_validaton callback in a separate class so that it can be reused by multiple model classes.
Here in callback i want to strip field passed as parameter but i am not sure how to pass parameter to callback class. Also i want to pass this as reference rather than by value(not sure if this concept is in Ruby Rails). I am following the link http://guides.rubyonrails.org/active_record_validations_callbacks.html#callback-classes
Here is code which is not completely correct, please help for same
class StripFieldsCallback
def self.before_validation(field)
field = field.strip
end
end
class User < ActiveRecord::Base
validates_uniqueness_of :name, :case_sensitive => false
validates_length_of :name, :maximum => 50
before__validation StripFieldsCallback(name)
end
If i define method in model in itself rather than defining in separate callback class code is like this (which works fine)
class User < ActiveRecord::Base
validates_uniqueness_of :name, :case_sensitive => false
validates_length_of :name, :maximum => 50
before__validation :strip_blanks
protected
def strip_blanks
self.name = self.name.strip
end
end
Of course it is not good to replicate methods in all of models so i want to define method in callback classes.
You may do this or use normalize_attributes gem
module StripFieldsCallback
def before_validation_z(field)
write_attribute(field, read_attribute(field).strip) if read_attribute(field)
end
end
class User < ActiveRecord::Base
include StripFieldsCallback
before_validation lambda{|data| data.before_validation_z(:name)}
end

How to ensure a model is saved with the right account in rails?

If I have the following models setup:
class Member < ActiveRecord::Base
has_many :children
end
class Child < ActiveRecord::Base
belongs_to :member
has_many :photos
end
class Photo < ActiveRecord::Base
belongs_to :child
end
When a new Photo is created, what is the best way to ensure that it is associated with a child in the member's account?
I have login working properly, and a current_member helper method, which doesn't seem to be available in the models
So, from what I gather, the "Rails Way™" of doing this would be to put the conditions in the controllers.
ex:
unless current_member.children.collect { |child| child.id.to_s }.include?(#photo.child_id)
#photo.errors.add :child_id "this is not your child"
end

Resources