Defining factories with chained associations - ruby

I want to create a :membership factory and then create a :comment factory that in this specific case "rolls up" to the same Group that the Membership does. It shouldn't always point to the same Group, so I'm defining my factories like this:
factory :membership do
user
group
end
factory :decision do
group
end
factory :comment do
decision
end
And then I'm creating those two objects like this:
membership = create(:membership)
decision = create(:decision, group: membership.group)
comment = create(:comment, decision: decision)
This works, but it's a minimal example. I'd like to be able to create the Membership and then pass the Membership as an argument to the Comment constructor, making the second line unnecessary. I've had a look at the factory_girl docs and I can't figure out how to change my factory definitions to do this. Is there a way?

Pass the Membership to the Comment factory in a transient attribute. In a before(:create) callback, create a Decision from the Membership and add the Decision to the Comment:
factory :comment do
transient do
membership
end
before(:create) do |comment, evaluator|
decision = create(:decision, group: evaluator.membership.group)
comment.decision = decision
end
end

Related

How to skip model's method(after_save method) when creating object with Factory Girl

I have the model A that has many model B, in model B has its method to do something after it is saved. How to skip it?
You should be able to skip a callback using factory_girl's after(:build) callback.
FactoryGirl.define do
factory :model_b do
after(:build) {|model_b| model_b.class.skip_callback(:save, :after, :your_callback)}
end
end
The factory_girl wiki has a great example file showing how to use some of the options the library provides.

What is a Ruby factory method?

I understand that a factory method is a class method that utilises the self keyword and instantiates an object of it's own class. I don't understand how this is useful or how it can extend the functionality of initialize method.
I'm working on a project creating a command line address book that asks me to use a factory pattern on the Person class so that I can create a Trainee or Instructor (subclasses) with different attributes.
A factory class is a clean way to have a single factory method that produces various kind of objects.
It takes a parameter, a parameter that tells the method which kind of object to create. For example to generate an Employee or a Boss, depending on the symbol that is passed in:
class Person
def initialize(attributes)
end
end
class Boss
def initialize(attributes)
end
end
class Employee
def initialize(attributes)
end
end
class PersonFactory
TYPES = {
employee: Employee,
boss: Boss
}
def self.for(type, attributes)
(TYPES[type] || Person).new(attributes)
end
end
and then:
employee = PersonFactory.for(:employee, name: 'Danny')
boss = PersonFactory.for(:boss, name: 'Danny')
person = PersonFactory.for(:foo, name: 'Danny')
I also wrote a more detailed blog post about that topic: The Factory Pattern
The Factory Method Pattern at least allows you to give an expressive name to what could otherwise be a complicated or opaque constructor. For instance if you have a constructor that takes a bunch of parameters, it may not be clear why to the caller, having a named Factory method or methods could potentially hide the complexity of the object creation and make your code more expressive of what is actually going on.
So in your case a bad design may be:
trainee = Person.new true
or
instructor = Person.new false
Where true or false branches to creating an instructor or trainee.
This could be improved by using a Factory method to clarify what is going on:
trainee = Person.create_trainee
instructor = Person.create_instructor
Why bother with factory methods?
(A) To simplify things:
Creating objects can be complicated, and
you may need to do this multiple times.
It's hard to remember:
# ugh - too much work!
driver = Person.new
engine = Brrrm.new
engine.turbo_charged = true
engine.max_rpm = 100000
car = Porsche.new
car.driver = driver
car.engine = engine
# preference - less to remember
ben = PersonFactory.create("ben")
car = PorscheFactory.create(ben)
# and you get the following for free, without remembering:
car.turbo_charged # => true
car.engine # => brrrm
car.driver # => ben_koshy
car.driver.personality # => :excellent_dude
# you can mix and match default values with options.
# generally speaking you want to inject as much as you can
# i.e. inverting dependencies. I make these illustrates to
# explain a concept, not as an example of great coding.
(B) To allow for overridding / stubbing
If you are writing testable code, you might want to create your own specialised 'crash dummy vehicle' so you can test collisions etc. If you have a factory method / object, then you can do this easily. This is a somewhat adavanced topic - google "creating a seam" or "dependency injection" for more info.

Can an Abstract Factory be responsible for "creating or finding an existing" item?

My Ruby code has a Concrete Factory, which builds some complex objects:
author = Author::Factory.build(email: "john#example.com")
class Author
class Factory < BaseFactory
def self.build(email: nil)
# ... Some data preparation and defaults
Author.new(
email: email
# Map and assign more attributes
)
end
end
end
Now, I've run into a situation where I either need to build a new one,
or assign one from an existing collection. In
database-terms: an UPSERT, or in ActiveRecord: find_or_create_by.
And I am not sure if this:
Is a proper task for an Abstract Factory and
If the proper way to implement this is by passing the collection, or
to make the Factory itself responsible for fetching it.
Passing it in:
author = Author::Factory.build(email: "john#example.com", existing: authors)
class Author
class Factory < BaseFactory
def self.build(email: nil)
author = existing.find {|author| author.email == email }
# If not found, prepare and build a new one, like above.
end
end
end
Letting the Factory find it:
author = Author::Factory.build(email: "john#example.com")
class Author
class Factory < BaseFactory
def self.build(email: nil)
author = Author.find_in_existing_with(email: email)
# If not found, prepare and build a new one, like above.
end
end
end
So: Should a Factory every be responsible for finding-or-building?
And if so, must the Factory be responsible for fetching the items that
it must match against, or should the caller pass them along?
Factory is a creational pattern, so clients will expect fresh new instances out of it.
Sure, what the Factory does internally is of no concern to consuming code. But if Author is a domain entity, I fail to see how an Author-building object could be used by consumers for anything else than the "real world" addition of a new author in the system.
Unless you want be semantically unfair and trick callers by reusing existing authors instead of instantiating new ones. But that doesn't look like something you would typically do in production.

Sharing a class instance between two classes

I have two different classes that both represent objects that need to be persisted to my database and now I want to share the database client object between the two classes. I want to avoid instantiating the client object more than once.
Currently I do this by using a global variable
$client = Mysql2::Client.new(:database => "myDb", :user => "user", :password => "password", :host => "localhost")
class Person
def save
$client.query("INSERT INTO persons")
end
end
class Car
def save
$client.query("INSERT INTO cars")
end
end
This works, but I am wondering if there are more correct ways to do this and why they are more correct?
You can inherit from a parent class. This allows you to share common functionality across objects and follows DRY (do not repeat yourself) programming principles. It will also allow you to protect your DB connection with locks, resuces, queues, pools, and whatever else you may want to do without having to worry about it in your children classes
class Record
#table_name = nil
##client = Mysql2::Client.new(:database => "myDb", :user => "user", :password => "password", :host => "localhost")
def save
##client.query("INSERT INTO #{#table_name}") if #table_name
end
end
class Person < Record
#table_name = "persons"
end
class Car < Record
#table_name = "cars"
end
While we are on the subject, you should look at using ActiveRecord for handling your database models and connections. It already does pretty much anything you'll need and will be more compatible with other gems already out there. It can be used without rails.
As an alternative on using inheritance, why not consider a simple Singleton pattern? This could make your models cleaner, by separating the responsibility outside your classes. And eliminating the need for inheritance.
The example below illustrates this. Only one, single instance of the DataManager class can exist. So, you'll only instantiate it once - but can use it everywhere:
require 'singleton'
class DataManager
include Singleton
attr_accessor :last_run_query
def initialize()
if #client.nil?
p "Initialize the Mysql client here - note that this'll only be called once..."
end
end
def query(args)
# do your magic here
#last_run_query = args
end
end
Next, calling it using the .instance accessor is a breeze - and will always point to one single instance, like so:
# Fetch, or create a new singleton instance
first = DataManager.instance
first.query('drop table mother')
p first.last_run_query
# Again, fetch or create a new instance
# this'll actually just fetch the first instance from above
second = DataManager.instance
p second.last_run_query
# last line prints: "drop table mother"
For the record, the Singleton pattern can have some downsides and using it frequently results in a never-ending debate on whether you should use it or not. But in my opinion it's a decent alternative to your specific question.

setting instance variables in factorygirl

Say I have a model like
class Vehicle < ActiveRecore::Base
after_initialize :set_ivars
def set_ivars
#my_ivar = true
end
end
and somewhere else in my code I do something like
#vehicle.instance_variable_set(:#my_ivar, false)
and then use this ivar to determine what validations get run.
How do I pass this Ivar into FactoryGirl?
FactoryGirl.define do
factory :vehicle do
association1
association2
end
end
How do I encode an ivar_set into the above, after create, before save?
How do I pass it into a FactoryGirl.create()?
FactoryGirl.define do
factory :vehicle do
association1
association2
ignore do
my_ivar true
end
after(:build) do |model, evaluator|
model.instance_variable_set(:#my_ivar, evaluator.my_ivar)
end
end
end
FactoryGirl.create(:vehicle).my_ivar #=> true
FactoryGirl.create(:vehicle, my_ivar: false).my_ivar #=> false
A bit late answer, nonetheless I had the need to setup an instance variable on a model. And since the above answer didn't work for the latest version of factory bot I did a bit of research and found out that the following approach works for me:
FactoryGirl.define do
factory :vehicle do
association1
association2
end
transient do
my_ivar { true }
end
after(:build) do |model, evaluator|
model.instance_variable_set(:#my_ivar, evaluator.my_ivar)
end
end
It's almost identical to the above answer but instead of ignore it uses transient keyword, I assume this is an in-place replacement for ignore.
What it does is that it allows to define a variable you can pass on to the factory but that doesn't end up being set on the resulting object. That in turn gives you an opportunity to do logic based upon it. Like we do in this example (albeit a simple one) where we set an instance variable based on the provided transient variable.
Note that the transient variable is set and available in the evaluator variable.
References:
Transient Attributes - Factory bot documentation

Resources