How do I get my torrents to work?
I'm still new to Ruby/Rails and need your help to make sure the seed is good before running the command.rails db:seed
Here is my seed:
require 'faker'
puts "Creating 10 fake user heros..."
10.times do
user = User.create!(
username: Faker::DcComics.hero,
password: "123456",
email: "#{user.username}@gmail.com",
bio: "Hi, I'm #{user.username}. Faker::Movie.quote",
avatar: Faker::LoremFlickr.image(size: "900x900", search_terms: ['dccomics'])
)
puts "#{user.username} created!"
end
puts "Creating 10 fake hero services..."
10.times do
service = Service.create!(
name: "#{Faker::Verb.ing_form} service",
description: "I'll use my #{Faker::Superhero.power} to accomplish the mission",
address: "#{Faker::Address.street_address}, #{Faker::Address.city}",
photos: Faker::LoremFlickr.image(size: "900x900", search_terms: ["#{Faker::Verb.ing_form}"]),
user_id: rand(user.id)
)
puts "#{service.name} created!"
end
puts "Finished!"
I think I'll run into a few problems in particular:
- My interpolation method: I'm not sure if I can use it in Faker
- I want to use the same username in the given
username
andemail
, what am I doing? - My User model doesn't have an incarnation in the database table, however
has_one_attached :avatar
, can I give it a seed? - Related question about my service model (
has_many_attached :photos
) . How can I generate multiple images for it? - I want to randomly select a service model for me from
user_id
the services created above , I really don't know how to proceed...
My interpolation method: I'm not sure if I can use it in Faker
Interpolation works with any expression. even multiple statements. "#{ a = 1 + 1; b = 2 + 2; a + b}"
will produce "6"
. But don't do that, it's confusing.
name: "#{Faker::Verb.ing_form} service",
equivalent to
name: Faker::Verb.ing_form.to_s + " service"
In some places, you are missing interpolation.
bio = "Hi, I'm #{user.username}. Faker::Movie.quote"
That should be...
bio = "Hi, I'm #{user.username}. #{Faker::Movie.quote}"
Elsewhere, interpolation is not required.
search_terms: ["#{Faker::Verb.ing_form}"]
Faker::Verb.ing_form
A string is already returned, no interpolation is required.
search_terms: [Faker::Verb.ing_form]
I want to use the same username as provided in the email, am I doing it right?
No, you must user
create it before you can reference it . If you try, you will get something similar undefined method `username' for nil:NilClass
. user
Yes nil
, there is nil
also no method called username
.
Instead, you can create a user, set their email, and save.
user = User.new(
username: Faker::DcComics.hero,
password: "123456",
avatar: Faker::LoremFlickr.image(size: "900x900", search_terms: ['dccomics'])
)
user.email = "#{user.username}@gmail.com"
user.bio = "Hi, I'm #{user.username}. #{Faker::Movie.quote}"
user.save!
Alternatively, you can create the username first and store it in a variable for reference.
username = Faker::DcComics.hero
user = User.create!(
username: username,
password: "123456",
email: "#{username}@gmail.com",
bio: "Hi, I'm #{username}. #{Faker::Movie.quote}",
avatar: Faker::LoremFlickr.image(size: "900x900", search_terms: ['dccomics'])
)
My user model has no avatar in the database table, but has_one_attached: avatar, can I seed it?
I'm not very familiar with attachments in Rails , but I think it should work as you wrote.
I want to feed my service model from one of the random user_id created above, I don't really know how to proceed...
Simple thing is to use .sample
user = User.all.sample
We can make it a little better by pluck
just getting the ID .
user = User.pluck(:id).sample
However, this will load all users. For small test databases this is not a problem. For production code, this is a huge waste. Instead, we can use order by random() limit 1
. In Rails 6, it's a bit clunky.
User.order(Arel.sql('RANDOM()')).pluck(:id).first
See this answer for an explanation.
Note that if you already have users loaded, pass the user instead of its id. This avoids the service having to load the user again.
10.times do
user = User.order(Arel.sql('RANDOM()')).pluck(:id).first
service = Service.create!(
...
user: user
)
end
But there is a better way...
Related question about my service model (has_many_attached:photos). How can I generate multiple images for it?
Here we make a real leap. Instead of generating static test data, use a Factory. FactoryBot makes it easy to define "factories" using Faker to generate test data. You are halfway there. With FactoryBotRails , FactoryBot learns how to use Rails models.
The user factory looks like this.
FactoryBot.define do
factory :user do
username { Faker::DcComics.hero }
password { "123456" }
email { "#{username}@gmail.com" }
bio { "Hi, I'm #{username}. #{Faker::Movie.quote}" }
avatar { Faker::LoremFlickr.image(size: "900x900", search_terms: ['dccomics']) }
end
end
Note that we can do it on a email
basis username
. Properties can reference other properties. This is because properties are actually little functions like this:
class UserFactory do
def username
@username ||= Faker::DDcComics.hero
end
def email
@email ||= "#{username}@gmail.com"
end
end
and make 10 of them...
users = FactoryBot.create_list(:user, 10)
You can override the default value. If you want users to use a certain email address...
user = FactoryBot.create(:user, email: "[email protected]")
Since you can keep users on the go, you no longer need to seed specific data.
Serving now. Let's take a look at the service factory. To provide the service, we need photos.
factory :service do
name { "#{Faker::Verb.ing_form} service" }
description { "I'll use my #{Faker::Superhero.power} to accomplish the mission" }
address { "#{Faker::Address.street_address}, #{Faker::Address.city}" }
user
# photos is presumably has-many_attached and so takes an Array.
photos {
[
Faker::LoremFlickr.image(
size: "900x900",
search_terms: [Faker::Verb.ing_form]
)
]
}
end
Take a look user
. FactoryBot will automatically populate with users manufactured using your user factory. Fixed an issue with serving with random users.
Our final step is to dry our plant by defining a photo .
factory :photo, class: Faker::LoremFlickr do
size { "900x900" }
search_terms { Faker::Lorem.words }
initialize_with do
Faker::LoremFlickr.image(attributes)
end
end
This is not a Rails model, so we need to teach FactoryBot how to make a photo by giving it its class and how to initialize it . attributes
is a hash containing size and search_terms.
Also note that I used more general search terms. Factories that require specific search terms will provide their own factories.
We can make any number of photos.
photos = FactoryBot.build_list(:photos, 3)
Using photo factories, we can dry users and service factories. this is all.
FactoryBot.define do
factory :photo, class: Faker::LoremFlickr do
size { "900x900" }
search_terms { Faker::Lorem.words }
initialize_with do
Faker::LoremFlickr.image(attributes)
end
end
factory :user do
username { Faker::DcComics.hero }
password { "123456" }
email { "#{username}@gmail.com" }
bio { "Hi, I'm #{username}. #{Faker::Movie.quote}" }
avatar { build(:photo, search_terms: ['dccomics']) }
end
factory :service do
name { "#{Faker::Verb.ing_form} service" }
description { "I'll use my #{Faker::Superhero.power} to accomplish the mission" }
address { "#{Faker::Address.street_address}, #{Faker::Address.city}" }
user
photos {
build_list(:photo, 3, search_terms: [Faker::Verb.ing_form])
}
end
end
Now, when you need a service to test, you can ask for one.
service = FactoryBot.create(:service)
Need a service by another name?
service = FactoryBot.create(:service, name: "Universal Exports")
What if there are no photos?
service = FactoryBot.create(:service, photos: [])
That's just fumbling around with what FactoryBot can do.
Factories are more flexible and convenient than torrent files.