[Ruby on Rails] AWS::Record::HashModel を使って Amazon DynamoDB のモデルを作る

eye_catch_aws_dynamo_db

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

AWS::Record::HashModel

AWS SDK for Ruby で DynamoDB を使うと思い、ググってみると AWS::DynamoDB クラスを使っているサンプルがほとんどでした。しかし SDK の中に AWS::Record::HashModel という DynamoDB のための便利な Model クラスを発見し、これはと思い使ってみたところとっても簡単に実装できました。

class Task < AWS::Record::HashModel
  string_attr :name
end

task = Task.new(:name => "My Sample Task")
task.name #=> 'My Sample Task'

シンプルなタスク管理アプリを作ってみる

Rails アプリのひな形をつくる

ということで Model に AWS::Record::HashModel クラスを用いた、シンプルなタスク管理アプリを作ってみたいと思います。まずはこちらの記事を参考に、Rails アプリのひな形を作ります。

mkdir my-simple-todos && cd my-simple-todos
bundle init
vim Gemfile # gem 'rails' をコメントアウトする
bundle install
bundle exec rails new . --skip-bundle

AWS サービスを使えるようにする

次に AWS SDK を Gemfile に追加します。また Bootstrap で実装したいので bootstrap-sass も追加しました。

vim Gemfile # gem 'aws-sdk' と gem 'bootstrap-sass' を追加する
bundle install
vim app/config/aws.yml

aws.yml は AWS の設定ファイルです。以下のように記述します。

app/config/aws.yml

development:
  access_key_id: 'YOUR_ACCESS_KEY_ID'
  secret_access_key: 'YOUR_SECRET_ACCESS_KEY'
  dynamo_db_endpoint: 'dynamodb.ap-northeast-1.amazonaws.com'

test:
  access_key_id: 'YOUR_ACCESS_KEY_ID'
  secret_access_key: 'YOUR_SECRET_ACCESS_KEY'
  dynamo_db_endpoint: 'dynamodb.ap-northeast-1.amazonaws.com'

production:
  access_key_id: 'YOUR_ACCESS_KEY_ID'
  secret_access_key: 'YOUR_SECRET_ACCESS_KEY'
  dynamo_db_endpoint: 'dynamodb.ap-northeast-1.amazonaws.com'

ここまでで AWS を扱えるようになりました。

実装する

あとはモデル・ビュー・コントローラーを実装していきましょう。ちなみにモデルクラスを生成するときですが migration は不要なので無効にしています。

# model の作成
bundle exec rails g model task \
id:integer name:string end_time:timestamp --migration=false
# view, controller の作成
bundle exec rails g controller tasks index

モデルをつくる

モデルのクラスは AWS::Record::HashModel を継承します。あとは string_attr で属性を作っていきます。他の型を使いたい場合は boolean_attr や date_attr などがあるのでそちらを使いましょう。また timestamps メソッドを入れると created_at と updated_at という属性を付けてくれます。楽ちんですね!

app/model/task.rb

class Task < AWS::Record::HashModel
  string_attr :name
  timestamps
end

コントローラーをつくる

コントローラーは index, create, destroy を実装しました。
index ではテーブル内の全てのレコードを取得しています。そのままではデータがバラバラに入ってきてしまうので created_at でソートしています。また create では input に入力された前提でパラメータを Task モデルに渡して保存しています。そのときテーブルが存在しない場合は rescue でキャッチしてテーブルを作るような処理も書きました。

app/controller/tasks_controller.rb

# coding: utf-8
class TasksController < ApplicationController
  
  def index
    # created_at でソート
    @tasks = Task.all.sort{ |a, b| a.created_at <=> b.created_at }
  end
  
  def create
    task = Task.new(params[:task])
    task.save
    redirect_to :back, notice: 'Created New Task.'
  rescue AWS::DynamoDB::Errors::ResourceNotFoundException
    # テーブルが存在しない場合は作成してリダイレクト
    Task.create_table 1, 1
    redirect_to :back, alert: 'Sorry, Try again.'
  end

  def destroy
    task = Task.find_by_id(params[:id])
    task.delete
    redirect_to :back, notice: 'Deleted Task.'
  end

end

routes も忘れずに設定しましょう。

config/routes.rb

MySimpleTodos::Application.routes.draw do
  resources :tasks, only: [:index, :create, :destroy]
end

ビューをつくる

最後にビューを作って終わりです。特に特別なことはしていません。。 注意点としては Task モデルは ActiveRecord ではないので form_for メソッドは使えません。今回はその代わりに form_tag メソッドで実装しています。 created_at は見やすいようにフォーマットかけてみました。

app/view/tasks/index.html.erb

<div class="container">

  <h1>MyTasks</h1>

  <!-- 操作が成功したときに表示する -->
  <% if notice %>
    <p class="alert alert-success">
      <a class="close" data-dismiss="alert">&times;</a>
      <%= notice %>
    </p>
  <% end %>

  <!-- 操作が失敗したときに表示する -->
  <% if alert %>
    <p class="alert alert-error">
      <a class="close" data-dismiss="alert">&times;</a>
      <%= alert %>
    </p>
  <% end %>

  <!-- 入力フォーム -->
  <div>
    <%= form_tag({:controller => :tasks, :action => :create}, {:class => "input-append"}) do %>
      <%= text_field_tag "task[name]", "", :id => "task_name" %>
      <%= submit_tag "ADD", :class => "btn" %>
    <% end %>
  </div>

  <!-- アイテム一覧 -->
  <div>
    <table class="table table-striped table-bordered">
      <tbody>
        <tr>
          <th>Name</th>
          <th>CreatedAt</th>
          <th>UpdatedAt</th>
          <th>Done</th>
        </tr>
        <% @tasks.each do |task| %>
          <tr>
            <td><%= task.name %></td>
            <td class="span3">
              <%= task.created_at.strftime('%Y/%m/%d %H:%M:%S') %>
            </td>
            <td class="span3">
              <%= task.updated_at.strftime('%Y/%m/%d %H:%M:%S') %>
            </td>
            <td class="span1">
              <%= button_to 'Done', { :action => "destroy", :id => task.id }, method: :delete, class: 'btn btn-small btn-primary' %>
            </td>
          </tr>
        <% end %>
      </tbody>
    </table>
  </div>
</div>

alert を使ったので bootstrap-alert も読み込みます。

app/assets/javascripts/application.js

//= require jquery
//= require jquery_ujs
//= require bootstrap-alert
//= require_tree .

動かしてみる

これでひと通り完成しました。起動してみましょう!

bundle exec rails s

hash_model_dynamo_db01

アイテムの追加・削除・一覧ができます。追加・削除の動作がかなり軽快に動くと思います!
ManagementConsole から DynamoDB の中身を直接見てみても、もちろんちゃんと格納されています。

hash_model_dynamo_db02

ソースコード

今回つくったアプリを GitHub で公開しました。ご自由にお使いください〜。

suwa-yuki/my_simple_todos

まとめ

AWS::Record::HashModel を使うと Dynamo DB のモデルが簡単にできるよ!というお話でした。ちなみに Simple DB は AWS::Record::Model を使うと楽に実装できそうです!