As said in the book "where is the genius", many so-called geniuses are obtained through repeated deliberate exercises. When your exercise time reaches 10000 hours, you will become an expert in this field.
I recently learned how to implement restful Web Service in rails. I want to design an exercise template for myself and perform the exercises repeatedly. The development process adopts TDD for development.
Exercise Background:
We have three domain objects: Products, orders, and payment.
1. Create a new project rails-rest-Practice
Rails new rails-rest-Practice
CD! $
Bundle install
2. Install rspec-rails
Add in gemfile
Gem "rspec-rails",: Group => [: development,: Test]
Then
Bundle install
Rails g rspec: Install
Remove -- warnings from. rspec
3. Get/products => User get List of Products
Step 1: Create a controller and return HTTP status 200
The user products API is as follows:
GET/products user get List of Products
Create a file: spec/products/products_controller_spec.rb
Require 'rails _ helper'
Describe productscontroller,: TYPE =>: controller do
Describe 'products controller' do
It 'get index of products' do
Get: Index
Response CT (response).Have_http_status(200)
End
End
End
Have_http_status: Http://rubydoc.info/gems/rspec-rails/RSpec/Rails/Matchers#have_http_status-instance_method
Create a file: APP/controllers/products_controller.rb
Class productscontroller <applicationcontroller
Def Index
End
End
Run rake spec and get an error:
Actioncontroller: urlgenerationerror:
No route matches {: Action => "Index",: controller => "Products "}
Configure config/routes. Rb
Resources: products do
Collection do
Get: Index
End
End
Run rake spec and get an error:
Failure/error: Get: Index
Actionview: missingtemplate:
Modify APP/controllers/products_controller.rb
Class productscontroller <applicationcontroller
Def Index
Render: Nothing => true
End
End
This completes our first step. Although it seems that this step has not been tested, it is actually not. In this step, we have set up routes, at the same time, a necessary controller class and its corresponding methods are created.
Step 2: Return JSON
Install rabl
Add rabl to gemfile
Gem 'rabl'
Bundle install
Refer to rails + rabl
Modify and test: spec/products/products_controller_spec.rb
Render_views
Describe 'products controller' do
Before (: All) Do
@ Products = [
Product. New ({: Id => 1,: Name => 'apple juice ',: Description => 'good '}),
Product. New ({: Id => 2,: Name => 'banana juice ',: Description => 'Just so so '})
]
End
It 'get index of products' do
Keep CT (product). To receive (: All). and_return (@ products). Once
Get: Index,{: Format =>: JSON}
CT (response). To have_http_status (200)
Products_json = JSON. parse (response. Body)
Reverse CT (products_json.size). To eq (2)
End
End
Run the test rake spec
Error:
Nameerror:
Uninitialized constant Product
Create model product:
Rails G model Product Name: String Description: Text
Rake DB: migrate
Run the test rake spec
Error:
Failure/error: products_json = JSON. parse (response. Body)
JSON: parsererror:
A json text must at least contain two octets!
This is because our response is incorrect and we have not configured how to obtain the output in JSON format.
Create a file: APP/views/products/index. JSON. rabl
Collection @ products,: object_root => false
Attributes: Name
Run rake spec again and pass the test
Step 3: add more fields
In spec/products/products_controller_spec.rb
Products_json = JSON. parse (response. Body)
Reverse CT (products_json.size). To eq (2)
CT (products_json [0] ['id']). To eq (1)
CT (products_json [1] ['id']). To eq (2)
Round CT (products_json [0] ['name']). To eq ('apple juice ')
Reverse CT (products_json [1] ['name']). To eq ('banana juice ')
CT (products_json [0] ['description']). To eq ('good ')
CT (products_json [1] ['description']). To eq ('Just so so ')
CT (products_json [0] ['uri ']). To end_with ('/products/1 ')
CT (products_json [1] ['uri ']). To end_with ('/products/2 ')
In app/views/products/index. JSON. rabl
Collection @ products,: object_root => false
Attributes: ID,: name,: Description
Node: URI do | product |
Product_url Product
End
4. Get/products => User get a product of specified ID
Step 1: Create the corresponding Controller method and return HTTP 200
Add test: spec/products/products_controller_spec.rb
It 'get product by product id' do
Get: Show, {: Id => 1}
CT (response). To have_http_status (200)
End
Corresponding modification: APP/controllers/products_controller.rb
Def show
Render: Nothing => true
End
Corresponding modification: config/routes. Rb
Resources: products do
Collection do
Get: Index
End
Member do
Get: Show
End
End
Rake spec passed the test.
Step 2: Create the corresponding JSON display
Add test: spec/products/products_controller_spec.rb
Before (: All) Do
#......
@ Product = product. New ({: Id => 1,: Name => 'apple juice ',: Description => 'good '})
End
It 'get product by product id' do
Keep CT (product). To receive (: Find). With (1). and_return (@ Product). Once
Get: Show, {: Id => 1,: format =>: JSON}
CT (response). To have_http_status (200)
Product_json = JSON. parse (response. Body)
CT (product_json ['id']). To eq (1)
Round CT (product_json ['name']). To eq ('apple juice ')
CT (product_json ['description']). To eq ('good ')
CT (product_json ['uri ']). To end_with ('/products/1 ')
End
Corresponding modification: APP/controllers/products_controller.rb
Def show
@ Product = product. Find (Params [: Id]. to_ I)
End
Q: Params [: Id]. to_ I, why does Params [: Id] from the test code make a string type?
Add JSON display: APP/views/products/show. JSON. rabl
Object false
Node (: ID) {| product | @ Product. ID}
Node (: Name) {| product | @ Product. name}
Node (: Description) {| product | @ Product. Description}
Node (: URI) {| product | product_url @ Product}
Run the test and pass
Step 3: reconstruct rabl
Modify APP/views/products/show. JSON. rabl
Object @ Product
Attributes: ID,: name,: Description
Node (: URI) {| product | product_url product}
Modify APP/views/products/index. JSON. rabl
Collection @ Products
Extends 'products/show'
Configure rabl: Create the file config/initializers/rabl_config.rb.
Rabl. Configure do | config |
Config. include_json_root = false
End
Run the test and pass. This reduces the repeated code between rabl.
Step 4: HTTP 404
Add test: spec/products/products_controller_spec.rb
It 'get 404 when product not found 'do
Wrong CT (product). To receive (: Find). With (100). and_raise (activerecord: recordnotfound)
Get: Show, {: Id => 100,: format =>: JSON}
CT (response). To have_http_status (404)
End
Corresponding modification:
Class productscontroller <applicationcontroller
Rescue_from activerecord: recordnotfound, with: product_not_found
#......
Def show
@ Product = product. Find (Params [: Id])
End
Protected
Def product_not_found
Response. Status =: Not_found
End
End
Refer to rescue_from
(Updating. Please advise)
The part that will be modified is how to write rspec, see: http://betterspecs.org/