- Read Tutorial
- Watch Guide Video
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:
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.
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:
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.
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:
So, everything works fine. In the next video, we'll refactor all this code so it fits in with the best practices of Rails.