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/

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

  • 相关阅读:
    ICONS-图标库
    图形资源
    vue项目中,如果修改了组件名称,vscode编辑器会在引入修改组件的名字处提示红色波浪线 The file is in the program because:Imported via xxx Root file specified for compilation .
    接口在dev环境报跨域问题(has been blocked by CORS policy:Response to preflight request doesn't pass access control check:No 'Access-Control-Allow-Origin' header ispresent on the requested resource.),qa环境正常
    阿里云occ的图片文件URL用浏览器直接打开无法访问,提示This XML file does noe appear to have any style information associated with it. The document tree is shown below.
    vue 项目使用element ui 中tree组件 check-strictly 用法(父子不互相关联的反显情况)
    高德地图进行线路规划绘制标记点操作(vue)
    vue中实现拖拽调整顺序功能
    2021-01-22 浏览器相关知识
    2021-01-22 js 相关知识点
  • 原文地址:https://www.cnblogs.com/lcchuguo/p/4817617.html
Copyright © 2011-2022 走看看