Sprint 5: Creating Sample Data
Published: 2024-03-11 16:43:13

When creating an application, it is helpful to generate a sample data that populates our tables. In doing so, we can visualize how we want our users to interact with our application. 

Taking an example from our Industrial Photogram, here's an example of a "sample_data" rake task:

```ruby
desc "Fill the database tables with some sample data"
task example_sample_data: :environment do
  starting = Time.now

  if Rails.env.development?
    FollowRequest.delete_all
    Comment.delete_all
    Like.delete_all
    Photo.delete_all
    User.delete_all
  end

  12.times do
    name = Faker::Name.first_name
    User.create(
      email: "#{name}@example.com",
      password: "password",
      username: name.downcase,
      private: [true, false].sample,
    )
  end

  users = User.all

  users.each do |first_user|
    users.each do |second_user|
      next if first_user == second_user
      if rand < 0.75
        first_user.sent_follow_requests.create(
          recipient: second_user,
          status: FollowRequest.statuses.values.sample
        )
      end

      if rand < 0.75
        second_user.sent_follow_requests.create(
          recipient: first_user,
          status: FollowRequest.statuses.values.sample
        )
      end
    end
  end

  users.each do |user|
    rand(15).times do
      photo = user.own_photos.create(
        caption: Faker::Quote.jack_handey,
        image: "https://robohash.org/#{rand(9999)}"
      )

      user.followers.each do |follower|
        if rand < 0.5 && !photo.fans.include?(follower)
          photo.fans << follower
        end

        if rand < 0.25
          photo.comments.create(
            body: Faker::Quote.jack_handey,
            author: follower
          )
        end
      end
    end
  end

  ending = Time.now
  p "It took #{(ending - starting).to_i} seconds to create sample data."
  p "There are now #{User.count} users."
  p "There are now #{FollowRequest.count} follow requests."
  p "There are now #{Photo.count} photos."
  p "There are now #{Like.count} likes."
  p "There are now #{Comment.count} comments."
end
```

Let's break this down.

1. 
```ruby
desc "Fill the database tables with some sample data"
task example_sample_data: :environment do
  starting = Time.now
```
   -Line 1: "desc" stands for description. Essentially, in this line we are giving a proper description of what the task would do when called.

   -Line 2: we use the "task" method which takes in a Hash argument. The key is a Symbol of the name of our task "sample_data" and the value for this should be ":environment". This makes the Rails environment have access to all models, gems, and initializers.

   -Line 3: Using the Time class to create a timestamp. (We will later call another Time.now so we can determine how long it took to create the sample data. 

2. 
```ruby
if Rails.env.development?
  FollowRequest.delete_all
  Comment.delete_all
  Like.delete_all
  Photo.delete_all
  User.delete_all
end
```
   -This enables us to destroy previous iterations of the sample data every time we call on this task in the terminal. Use it with caution since we do NOT want to destroy records associated with the user = wrap the whole delete_all logic inside an if statement. 

3. 
```ruby
  12.times do
    name = Faker::Name.first_name
    User.create(
      email: "#{name}@example.com",
      password: "password",
      username: name.downcase,
      private: [true, false].sample,
    )
  end
```

   -This block of code utilizes the gem "Faker" to generate randomized data. In this case, we are trying to create randomized "first_name". Before we decide what properties we want to define, it is a good idea to reference the corresponding model to know which columns need to filled. 

   -Afterwards, it is just up to you to define the Ruby logic needed to fill up each column of an Instance. 

4. 
```ruby
users = User.all

  users.each do |first_user|
    users.each do |second_user|
      next if first_user == second_user
      if rand < 0.75
        first_user.sent_follow_requests.create(
          recipient: second_user,
          status: FollowRequest.statuses.values.sample
        )
      end

      if rand < 0.75
        second_user.sent_follow_requests.create(
          recipient: first_user,
          status: FollowRequest.statuses.values.sample
        )
      end
    end
  end
```

   -The idea behind this code block is to iterate through the 12 randomly generated Users to have the chance (75%) to send follow requests to other users + randomly define the status of that follow request (pending, accepted, or rejected).  

5. 
```ruby
users.each do |user|
    rand(15).times do
      photo = user.own_photos.create(
        caption: Faker::Quote.jack_handey,
        image: "https://robohash.org/#{rand(9999)}"
      )

      user.followers.each do |follower|
        if rand < 0.5 && !photo.fans.include?(follower)
          photo.fans << follower
        end

        if rand < 0.25
          photo.comments.create(
            body: Faker::Quote.jack_handey,
            author: follower
          )
        end
      end
    end
  end
```
There are 3 different things happening within this code block:

   -Part 1: randomly determine how many photos the block variable "user" is going to post. In it, we use the faker gem + robohash to randomly generate values for our caption and image column, respectively.

   -Part 2: if a follower of the user does NOT like the photo and the rand() method returns true, add the follower to the fans record, which is a list of User Instances that liked the photo. 

   -Part 3: give the follower a 25% chance to leave a randomly generated comment (via Faker) in the photo. Also populate the author_id column with the user_id.

5. Finally, the hardest part.
```ruby
  ending = Time.now
  p "It took #{(ending - starting).to_i} seconds to create sample data."
  p "There are now #{User.count} users."
  p "There are now #{FollowRequest.count} follow requests."
  p "There are now #{Photo.count} photos."
  p "There are now #{Like.count} likes."
  p "There are now #{Comment.count} comments."
end
```

   -As said before, we create another Time.now to take a timestamp of when the whole sample_data process ends. The remaining "print" lines is to show us that we created the sample data we wanted for our application. 

It is always useful to create a sample_data task in order to aid in our development process. Make sure you are aware of the columns you want to fill out by referencing the models you created previously. 

CY@!