How to Build CRUD Functionality in Rails via BDD
In this guide, we are going to implement the ability to create new posts and we're going to use BDD to build out the feature.
Guide Tasks
  • Read Tutorial
  • Watch Guide Video
Video locked
This video is viewable to users with a Bottega Bootcamp license

In this guide, we are going to implement the ability to create new posts and we're going to use BDD to build out the feature.

Let's start with a new describe block in post_spec.rb:

# spec/post_spec.rb

describe 'creation' do
  it 'has a new form that can be reached' do
    visit new_post_path
    expect(page.status_code).to eq(200)
  end
end

In this code, we are simply creating a test to check if there is a page for users to create a new record. If you're wondering where I got new_post_path, go to your terminal and type

bundle exec rake routes | grep post

This should bring up all the routes that have the word post , like this:

large

The rest of the code is pretty self-explanatory as it is similar to the previous tests. Now, if you run this in the terminal, it will fail because we don't have the controller action or the view template, right now we only have the route.

large

To start integration this feature, go to posts_controller.rb, and create an action called new.

# app/controllers/posts_controller.rb

def new
end

Also, go to your views/posts directory and create a file called new.html.erb.

Now if you go back and run rspec, everything should pass.

Even though everything passed, our new action is not doing anything for us. To make it create a record, let's write another test case in post_spec.rb.

The code for this functionality is:

# spec/features/post_spec.rb

it 'can be created from new form page' do
  fill_in 'post[date]', with: Date.today
  fill_in 'post[rationale]', with: "Some rationale"
  click_on "Save"

  expect(page).to have_content("Some rationale")
end

This test is going to visit the new_post_path route and is going to check for the presence of a form with two fields in it called date and rationale. It will fill the date field with today's date and the rationale field with Some rationale. Next, it will click on the Save button, and will expect to have a show page where the value "Some rationale" will be displayed.

If you run this test, it will fail since we haven't built this feature yet. To start, we'll create a form in a new.html.erb file:

<!-- app/views/posts/new.html.erb -->

<%= form_for @post do |f| %>

  <%= f.date_field :date %>
  <%= f.text_area :rationale %>

<% end %>

Also, in posts_controller.rb, add this code to new action:

# app/controllers/posts_controller.rb

def new
  @post = Post.new
end

If you run this code, the error message is:

large

If you see, the error states that that it is unable to find the link or button called Save.

To fix this let's add the button to our form:

<!-- app/views/posts/new.html.erb -->

<%= form_for @post do |f| %>

  <%= f.date_field :date %>
  <%= f.text_area :rationale %>

  <%= f.submit 'Save' %>

<% end %>

If you run rspec, you'll get another error that says the action create cannot be found in PostsController. It essentially means the test was able to find all the fields and buttons, but was unable to create the post because there is no such action in the controller.

Now if you're new to Rails development, you may be confused between the concept of new and create. In Ruby, new simply creates a form and an instance of post, but creates nothing in the database. This is why we need another method called create.

In this method, we are going to take the values passed to it, and add a record to the database.

# app/controllers/posts_controller.rb

def create
  @post = Post.find(params.require(:post).permit(:date, :rationale))

  @post.save
end

If you look at the code, we are first looking for the parameters passed to post, and we are permitting access only to the date and rationale parameters for security reasons and to avoid problems such as SQL injection. After storing these values in the variable @post, we are asking it to save it in the database.

Let's run it in the browser to see if it's working, and there's an error.

large

This error is because I used find instead of new in the create method. We can fix this by updating our method to look like this:

# app/controllers/posts_controller.rb

def create
  @post = Post.new(params.require(:post).permit(:date, :rationale))

  @post.save
end

Now, that bug is fixed. But, we still have to create a template, which in our case, is a show page that'll display the value entered into the rationale form element.

To start, let's update our create method to include a redirect to the show page:

# app/controllers/posts_controller.rb

def create
  @post = Post.new(params.require(:post).permit(:date, :rationale))

  @post.save

  redirect_to @post
end

If you run rspec again, it's going to say we don't have a show template, so let's implement that. Add a show method in PostsController, and create a new file called show.html.erb and place it in the views/posts directory.

# app/controllers/posts_controller.rb

def show
  @post = Post.find(params[:id])
end

In this code, we are finding the post params with a particular id, and passing it to the post variable. We should also update the show.html.erb page with the following code, so it can display the values.

<!-- app/views/posts/show.html.erb -->

<%= @post.inspect %>

If you run rspec now, everything should work.

You can also check it in the browser. When you open localhost:3000/posts/new, it will display the date and rationale fields. Enter the values and click on Save button, and you'll be redirected to a page that displays the values you entered, just like this:

large

So, everything works fine. In the next video, we'll refactor all this code so it fits in with the best practices of Rails.

Resources