您的位置:首页 > 大数据 > 人工智能

有意练习--Rails RESTful(一)

2015-09-17 20:45 447 查看
书要反复提及《哪里有天才》在说,大多数所谓的天才是通过反复刻意练习获得。

当你的练习时间达到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/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: