雖然想要在不做額外動作的情況下設計自訂資源,不過資源本身是無法保存狀態的。所以這篇文章會快速跑過一次用 Rails 和 Grape 這兩個套件配置簡單的伺服器的範例。
實際上使用什麼方式實作都沒有關係,這邊假設我們是開發一套遊戲,所以需要存取系統中某個版本後所有怪物的資訊。
這篇文章可以跳過,如果希望直接使用範例可以使用本次的範例專案,文章結尾也有介紹如何部署到 Heroku 上面使用。
rails new monster-api -d postgresql
透過 rails new
指令來新增一個專案(monster-api
是目錄名稱)
我們預期要儲存兩種資料:
基於上述的資訊,可以用 rails generate scaffold
快速生成樣板。
rails g scaffold version major:integer minior:integer description:text
rails g scaffold monster name:string hp:integer mp:integer version:references
這是在本機開發測試時會使用到的,如果想直接使用筆者的範例可以稍微看過即可。
bundle exec rake db:create
bundle exec rake db:migrate
因為第一次使用還沒有對應的開發用資料庫,所以要先跑 rake db:create
產生,之後用 rake db:migrate
自動產生資料表。
Rails 會自動管理資料表的變動,只需要使用 DSL 配置好 Migration 檔案即可
雖然已經可以使用的,不過為了方便起件要做一些小修改。
修改 config/router.rb
檔案,新增下面的程式碼。
root to: "versions#index"
簡單說預設首頁是 versions
的頁面,方便我們新增版本。
修改 Gemfile
在適當位置加入這一行(例如 gem "rails"
下面一行)
gem 'simple_form'
運行指令
rails generate simple_form:install
修改 app/views/monsters/_form.html.erb
作出以下調整。
把第一行的 form_for
換成 simple_form_for
然後用下面的規則替換程式碼
原始有好幾段都是這個樣子
<div class="field">
<%= f.label :name %>
<%= f.text_field :name %>
</div>
替換成
<div class="field">
<%= f.input :name %>
</div>
唯一的例外是 version_id
的設定,要替換成下面這樣。
<div class="field">
<%= f.association :version %>
</div>
修改 app/models/version.rb
增加以下內容到 class
... end
之間。
has_many :monsters
def to_s
"#{major}.#{minior}"
end
以上的步驟是為了讓新增怪物時比較好辨識,會得到類似下面的畫面。
用 Rails 預設的方式是不會有下拉選單可以選擇版本的,相對來說不好使用。
修改 Gemfile
檔案,在適當位置加入下面這行。
gem 'grape'
修改 config/application.rb
在註解(#
開頭的程式碼)加入下面這些程式碼。
config.paths.add File.join('app', 'api'), glob: File.join('**', '*.rb')
config.autoload_paths += Dir[Rails.root.join('app', 'api', '*')]
修改 config/route.rb
增加這行(像是剛剛增加的 root
下方)
mount Game::API => '/'
新增 app/api/game/api.rb
這個檔案,放入下面的程式碼。
class Game::API < Grape::API
version :v1, using: :path
format :json
get :versions do
return Version.all.map(&:to_s) if params[:major].nil? && params[:minior].nil?
Version.where("major >= ? AND minior >= ?", params[:major] || 0, params[:minior] || 0).map(&:to_s)
end
params do
requires :major, type: Fixnum
requires :minior, type: Fixnum
end
put :version do
version = Version.find_by(major: params[:major], minior: params[:minior])
version.touch unless version.nil?
version.as_json(only: [:major, :minior, :updated_at]) || {}
end
get :monsters do
return Monster.all.pluck(:name, :hp, :mp) if params[:major].nil? && params[:minior].nil?
versions = Version.where("major <= ? AND minior <= ?", params[:major] || 0, params[:minior] || 0)
Monster.where(version: versions).pluck(:name, :hp, :mp)
end
end
以上是利用 Grape 這個套件快速建構的 API 伺服器,主要支援下面這些功能。
check
動作)名稱
HP
MP
的順序存在陣列)in
動作)put
動作)有了上面幾個 API 我們就能個別測試三個動作對應的行為。
為了方便起見,所以就採用 Heroku 部署,請先裝好 Heroku 的命令列工具。
執行以下指令建構新的 APP 在 Heroku 上。
heroku create APP_NAME
用 git
的方式上傳到 Heroku (會自動偵測並且安裝環境)
git push heroku master
最後在 Heroku 上對資料庫做更新(已經自動建立好 PostgreSQL 資料庫)
heroku run "rake db:migrate"
如果對 Rails 有興趣,目前網路上已經有豐富的中文資源。可以去參考並且了解以上步驟的原理。
下一篇會介紹如何實作 check
功能。