zoukankan      html  css  js  c++  java
  • 有意练习--Rails RESTful(一)

    书要反复提及《哪里有天才》在说,大多数所谓的天才是通过反复刻意练习获得。

    当你的练习时间达到10000几个小时后,。你将成为该领域的专家。


    近期在学习rails怎样实现RESTful Web Service。

    自己想给自己设计一个练习的模板,进行重复练习。

    开发过程採用的是TDD的方式进行开发。


    练习背景:

    我们涉及三个Domain对象,Products、Orders、Payment


    1.新建projectrails-rest-practice

    rails new rails-rest-practice

    cd !$

    bundle install


    2.安装rspec-rails

    在Gemfile中加入

    gem "rspec-rails", :group => [:development, :test]

    然后

    bundle install

    rails g rspec:install

    在.rspec 中去掉 --warnings


    3.GET /products => user get list of products

    step 1:创建controller,返回HTTP Status 200

    user products api例如以下:

    GET   /products       user get list of products


    创建文件: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
             expect(response).to have_http_status(200)
          end
       end
    end


    have_http_status:http://rubydoc.info/gems/rspec-rails/RSpec/Rails/Matchers#have_http_status-instance_method


    创建文件:app/controllers/products_controller.rb

    class ProductsController < ApplicationController
       def index
       end
    end


    执行rake spec。得到错误:

         ActionController::UrlGenerationError:
           No route matches {:action=>"index", :controller=>"products"}


    配置相关config/routes.rb

    resources :products do
       collection do
          get :index
       end
    end


    执行rake spec,得到错误:

    Failure/Error: get :index
         ActionView::MissingTemplate:

    改动app/controllers/products_controller.rb

    class ProductsController < ApplicationController
       def index
          render :nothing => true
       end
    end


    这样就完毕我们的第一个步骤。尽管看似这个步骤什么都没測,事实上不然,在这一步中。我们搭建好了routes。同一时候创建了必要的controller类和其相应的方法。


    step 2:返回JSON

    安装rabl

    在Gemfile中加入rabl

    gem 'rabl'

    bundle install

    參考Rails + rabl


    改动測试: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
          expect(Product).to receive(:all).and_return(@products).once

          get :index, {:format => :json}
          expect(response).to have_http_status(200)

          products_json = JSON.parse(response.body)
          expect(products_json.size).to eq(2)
       end
    end


    执行測试rake spec

    得到错误:

    NameError:
           uninitialized constant Product


    创建model Product:

    rails g model Product name:string description:text

    rake db:migrate


    执行測试rake spec

    得到错误:

        Failure/Error: products_json = JSON.parse(response.body)
         JSON::ParserError:
           A JSON text must at least contain two octets!

    这是由于我们的response不正确,而且我们没有配置怎么获取json格式的输出。


    创建文件: app/views/products/index.json.rabl

    collection @products, :object_root => false
    attributes :name


    再次执行測试rake spec,測试通过


    step3: 加入很多其它的字段

    在 spec/products/products_controller_spec.rb中

    products_json = JSON.parse(response.body)
    expect(products_json.size).to eq(2)

    expect(products_json[0]['id']).to eq(1)
    expect(products_json[1]['id']).to eq(2)

    expect(products_json[0]['name']).to eq('apple juice')
    expect(products_json[1]['name']).to eq('banana juice')

    expect(products_json[0]['description']).to eq('good')
    expect(products_json[1]['description']).to eq('just so so')

    expect(products_json[0]['uri']).to end_with('/products/1')
    expect(products_json[1]['uri']).to end_with('/products/2')


    在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: 创建相应的controller方法。返回HTTP 200

    加入測试:spec/products/products_controller_spec.rb

    it 'get product by product id' do
       get :show, {:id => 1}
       expect(response).to have_http_status(200)
    end


    相应改动:app/controllers/products_controller.rb

    def show
       render :nothing => true
    end


    相应改动:config/routes.rb

    resources :products do
       collection do
          get :index
       end

       member do
          get :show
       end
    end

    rake spec測试通过


    step 2:创建相应的JSON显示

    加入測试: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
       expect(Product).to receive(:find).with(1).and_return(@product).once

       get :show, {:id => 1, :format => :json}
       expect(response).to have_http_status(200)

       product_json = JSON.parse(response.body)
       expect(product_json['id']).to eq(1)
       expect(product_json['name']).to eq('apple juice')
       expect(product_json['description']).to eq('good')
       expect(product_json['uri']).to end_with('/products/1')
    end


    相应改动:app/controllers/products_controller.rb

    def show
       @product = Product.find(params[:id].to_i)
    end

    Q:params[:id].to_i,为什么这里从測试代码过来的params[:id]它使一个string类型呢


    加入JSON显示: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 }


    执行測试,通过


    step 3:重构rabl

    改动app/views/products/show.json.rabl

    object @product

    attributes :id, :name, :description

    node(:uri) { |product| product_url product }


    改动app/views/products/index.json.rabl

    collection @products

    extends 'products/show'


    配置rabl:创建文件config/initializers/rabl_config.rb

    Rabl.configure do |config|
       config.include_json_root = false
    end


    执行測试,通过,这样降低了rabl间的反复代码


    step 4:HTTP 404

    加入測试:spec/products/products_controller_spec.rb

    it 'get 404 when product not found' do
       expect(Product).to receive(:find).with(100).and_raise(ActiveRecord::RecordNotFound)

       get :show, {:id => 100, :format => :json}
       expect(response).to have_http_status(404)
    end


    相应改动:

    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

    參考rescue_from


    (更新中,欢迎不吝赐教)

    将会要改动的部分是怎样笔试rspec,参考:http://betterspecs.org/

    版权声明:本文博主原创文章,博客,未经同意不得转载。

  • 相关阅读:
    Mongoose开启调试模式格式化输出
    MongoDB 区分大小写吗
    Potree
    mongo存储引擎
    获取mongo 数据大小及collection大小
    Nosql的发展
    spring 事务 完全注解开发
    spring 事务 声明式 基于XML
    spring 事务 声明式 基于注解 参数配置 isolation 隔离级别
    spring 事务 声明式 基于注解 参数配置 propagation 事务传播行为
  • 原文地址:https://www.cnblogs.com/lcchuguo/p/4817617.html
Copyright © 2011-2022 走看看