user2361174
user2361174

Reputation: 1932

Rails faker gem produces same product name

I'm trying to use rails Faker gem to produce unique product names to make sample Item models in the database. I've used Faker multiple times but for some reason I can't produce new product names. I've made the nameMaker function to avoid possible early repeats, but I get a record invalidation just after one insert. Does anyone know how I could fix this?

seed.rb:

98.times do |n|
    name = Item.nameMaker
    description = Faker::Lorem.sentence(1)
    price = Item.priceMaker
    item = Item.create!(
        name: name,
        description: description,
        price: price)
    end

item.rb:

class Item < ActiveRecord::Base
    validates :name, presence: true, length: { maximum: 100 }
    validates :description, presence: true,
        length: { maximum: 1000 }
    VALID_PRICE_REGEX = /\A\d+(?:\.\d{0,3})?\z/
    validates :price, presence: true,
        :format => { with: VALID_PRICE_REGEX },
        :numericality => {:greater_than => 0}
    validates_uniqueness_of :name  

    def Item.nameMaker
        loop do
            name = Item.newName
            break if Item.find_by(name: name).nil?
        end
        return name
    end

    def Item.newName
        Faker::Commerce.product_name
    end 
end

Upvotes: 2

Views: 3068

Answers (3)

Hanmaslah
Hanmaslah

Reputation: 746

To get a unique name, enclose the faker in brackets. Eg

 name { Faker::Commerce.product_name }

To achieve this, you could also make use of factory girl and when you want to create 98 different Items, you could have something like

    factories/item.rb

FactoryGirl.define do
  factory :item do
    name { Faker::Commerce.product_name }
    description { Faker::Lorem.sentence(1) }
    price Faker::Commerce.price
  end
end

in your spec file

let(:item) { create_list(:item, 98) }

Upvotes: 5

user2361174
user2361174

Reputation: 1932

I figured it out after some experimentation, apparently the loop in some ways acts as like a function in terms of scoping. If you initialize a local variable in a loop, the function outside of the loop will not see it. In this case name always returning the string Item from the Item.nameMaker function. Thus the first attempt would always succeed and the second one would obtain the validation restriction.

def Item.nameMaker
    loop do
        name = Faker::Commerce.product_name # 'Random Product Name'
        puts "Name: #{name}" # "Name: Random Product Name"
        item = Item.find_by(name: name)
        if item.nil?
        puts "#{name} not found" # "Random Product Name not found"
        break
        else 
        end
    end
    puts "Returning Name #{name}" # "Returning Name Item"
    return name
end 

I managed to fix this by initializing the local variable outside of the loop. By doing this the entire function now has visibility to the same local variable for some reason.

def Item.nameMaker
    name = "" #initializing
    loop do
        name = Faker::Commerce.product_name # 'Random Product Name'
        puts "Name: #{name}" # "Name: Random Product Name"
        item = Item.find_by(name: name)
        if item.nil?
        puts "#{name} not found" # "Random Product Name not found"
        break
        else 
        end
    end
    puts "Returning Name #{name}" # "Returning Random Product Name"
    return name
end 

Upvotes: 0

Mr.D
Mr.D

Reputation: 7873

You can add validates_uniqueness_of :name in your model. When you run seed method if there is already exists same name, it will throw error and skip to the next.

There is possibility that you will not have exactly 98 Items. You can increase number of times or edit Faker itself.

Upvotes: 0

Related Questions