1、grape
rails启动最先读取的是config/目录下的配置文件;
输入culr命令构建的url (-d:参数, -H:header ):
curl -X POST -d '{"author_name": "aaaaaa","article":{"title": "bbbbbbb", "content": "ccccccccccccc"}}' 'http://localhost:3000/v1/articles' -H Content-Type:application/json
rails会去读取application.rb中的路径,得知API文件都在app/api/目录下:
require_relative 'boot'
require 'rails/all'
# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)
module GrapeTest
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 5.2
# Settings in config/environments/* take precedence over those specified here.
# Application configuration can go into files in config/initializers
# -- all .rb files in that directory are automatically loaded after loading
# the framework and any gems in your application.
config.paths.add File.join('app', 'api'), glob: File.join('**', '*.rb')
config.autoload_paths += Dir[Rails.root.join('app', 'api', '*')]
end
end
读取routes.rb文件中的路由设置:
require 'sidekiq/web'
require "#{Rails.root}/app/api/base"
#require "#{Rails.root}/app/api/twi/twitter"
Rails.application.routes.draw do
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
resources :articles
#mount Twitter::API => '/'
mount API::Base => '/' # http://localhost:3000/
root :to => 'home#index'
#mount Sidekiq::Web => '/sidekiq'
end
去app/api/目录下寻找mudule为API,class为Base的API文件,即base.rb文件:
require 'v1/v1_api'
#require 'twi/twitter'
module API
class Base < Grape::API
mount API::V1
#mount Twitter::API
end
end
Base又路由到了v1_api.rb文件中的V1 class:
# v1-api.rb
require "#{Rails.root}/app/api/v1/helpers"
module API
class V1 < Grape::API
helpers BlogApi::HelpersV1
format :json
version 'v1', :using => :path
default_error_status 400
default_error_formatter :json
#HardWorker.perform_async('bob', 5)
resource :articles do
desc "get all articles information"
get do
paginate_params = { :page => params[:page], :per_page => params[:per_page] || 20 }
@articles = Article.includes(:author).paginate(paginate_params)
present @articles
end
desc "return an artice"
params do
requires :id, :type => Integer, :desc => "artice id"
end
route_param :id do
get do
find_by_id params[:id]
present @article
end
end
desc "create an article"
params do
requires :author_name, type: String, desc: "author1's name"
requires :article, type: Hash, desc: "article's attributes"
end
post do
authenticate!
@author = Author.find_or_create_by(:name => params[:author_name])
@article = Article.create(params[:article].merge(:author_id => @author.id))
valid_with_present
end
desc "update an exist article"
params do
optional :author_name, type: String, desc: "a newer name"
requires :article, type: Hash, desc: "updated attributes"
end
put ':id' do
authenticate!
find_by_id params[:id]
if params[:author_name]
@author = Author.find_or_create_by(:name => params[:author_name])
@article.update_attributes(params[:article].merge(:author_id => @author.id))
else
@article.update_attributes(params[:article])
end
valid_with_present
end
desc "delete an article"
params do
requires :id, type: Integer, desc: "article id"
end
delete ':id' do
authenticate!
find_by_id params[:id]
@article.destroy
if @article.persisted?
error!({ :error => 'delete article failed' }, 400)
else
true
end
end
end
end
end
版本号v1和resource :articles加入到url中:http://localhost:3000/v1/articles
然后在该目录下寻找post动作:
desc "create an article"
params do
requires :author_name, type: String, desc: "author1's name"
requires :article, type: Hash, desc: "article's attributes"
end
post do
authenticate!
@author = Author.find_or_create_by(:name => params[:author_name])
@article = Article.create(params[:article].merge(:author_id => @author.id))
valid_with_present
end
2、active record
读post动作的源码可以看出,需要的参数分别是author_name和hash类型的article,在curl中已经给出参数了,然后需要在Author模型中获取author_name=params(author_name)的author的信息,然后根据 author_name 和 article 创建一份新的article,其中都有对数据库表的查询和创建操作;
2.1、创建数据库
数据库配置文件config/database.yml:
#
default: &default
adapter: mysql2
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
timeout: 5000
username: root
password: 'ur password'
development:
<<: *default
database: twi
# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
<<: *default
database: twi_test
production:
<<: *default
database: twi_pro
rails读取database.yml中的数据库配置,连接指定的数据库并且指定不同环境下的数据库名称,运行以下两个命令创建数据库:
> rails db:create
> rails db:migrate
或者
> rake db:create
> rake db:migrate
运行结果是: 数据库中多了两个数据库twi和twi_test;
2.2、用active reaord迁移创建数据库表
运行migrate命令:
> rails generate migration CreateArticles title:string content:text author_id:integer created_at:datetime updated_at:datetime
> rails generate migration CreateAuthors name:string created_at:datetime uodated_at:datetime
Running via Spring preloader in process 20708
invoke active_record
create db/migrate/20190319061333_create_articles.rb
结果是:在db/migrate目录下多出来两个迁移文件,在db/下多出schema.rb文件:
# 20190319061303_create_authors.rb
class CreateAuthors < ActiveRecord::Migration[5.2]
def change
create_table :authors do |t|
t.string :name
t.datetime :created_at
t.datetime :uodated_at
end
end
end
# 20190319061333_create_articles.rb
class CreateArticles < ActiveRecord::Migration[5.2]
def change
create_table :articles do |t|
t.string :title
t.text :content
t.integer :author_id
t.datetime :created_at
t.datetime :updated_at
end
end
end
# schema.rb
# This file is auto-generated from the current state of the database. Instead
# of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition.
#
# Note that this schema.rb definition is the authoritative source for your
# database schema. If you need to create the application database on another
# system, you should be using db:schema:load, not running all the migrations
# from scratch. The latter is a flawed and unsustainable approach (the more migrations
# you'll amass, the slower it'll run and the greater likelihood for issues).
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2019_03_19_061333) do
create_table "articles", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
t.string "title"
t.text "content"
t.integer "author_id"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "authors", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
t.string "name"
t.datetime "created_at"
t.datetime "uodated_at"
end
create_table "status", id: false, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
t.string "user", limit: 30
t.string "text"
end
create_table "statuses", id: false, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
t.string "user", limit: 30
t.string "text"
end
create_table "users", id: false, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
t.string "first_name"
t.string "last_name"
end
end
迁移文件用来创建和change数据库和数据库表,chema.rb文件用来描述数据库表的chema:
之后运行rails提供的两个命令来运行迁移:
> rails db:migrate
结果就是在数据库twi中创建了数据库表authors和articles;
2.3、创建Orm model 用来对数据库表进行CRUD操作
ActiveRecord根据'多约定少配置'的原则(Active Record 基础),使得model和数据库表进行映射(ORM),model文件:
# author.rb
class Author < ActiveRecord::Base
has_many :articles, :dependent => :destroy
validates :name, :presence => true, :uniqueness => true
end
# article.rb
class Article < ActiveRecord::Base
belongs_to :author
validates :title, :presence => true
# validates :author_id, :presence => true
def to_hash
{
:title => title,
:content => content,
:author_id => author.try(:id)
}
end
def author_name
author.try(:name)
end
def author_name=(name)
unless name.blank?
author = Author.find_or_create_by(:name => name)
self.author = author
author.name
end
end
end
在API中,不直接对数据库表进行操作,而是操作表的映射模型,使用Author和Article对数据库进行CRUD,回到v1_api.rb。
Active Record 基础中介绍了用model对表进行CRUD操作的方法。
3、test
> curl -X POST -d '{"author_name": "aaaaaa","article":{"title": "bbbbbbb", "content": "ccccccccccccc"}}' 'http://localhost:3000/v1/articles' -H Content-Type:application/json
{"id":4,"title":"bbbbbbb","content":"ccccccccccccc","author_id":3,"created_at":"2019-03-19T08:01:52.000Z","updated_at":"2019-03-19T08:01:52.000Z"}%
> curl -X GET 'http://localhost:3000/v1/articles/1'
{"id":1,"title":"haha","content":"88 characters","author_id":1,"created_at":"2019-03-19T06:16:47.000Z","updated_at":"2019-03-19T06:16:47.000Z"}%