[Ruby on Rails]Dynamoidを使ってAmazon DynamoDBと接続する

Dynamoidとは

DynamoidはRailsの中でActiveRecordのようにDynamoDBと連携できるORM(Object-Relational Mapper)ODM(Object Document Mapper)です(追記: 公式にはORMと書かれているため、本記事もORMに訂正いたします。)もともとRubyのaws-sdkにはAWS::Record::HashModelというORMがあり、弊社ブログでも紹介されていますが、aws-sdkのv2では削除されてしまった(?)ようなので、代わりに見つけたDynamoidというgemを試してみました。(ただ、後で分かった事ですが、Dynamoidもaws-sdkのv1に依存していました。)

The Ruby Toolboxを見る限りでは、 現時点で他に有力なDynamoDBのORMは無さそうです。(知っていたらコッソリ教えてください。)。ちなみに2位のFakeDynamoはローカルで動作するDynamoDBのエミュレータだそうです。

スクリーンショット 2015-08-11 12.16.18

サンプルアプリ

今回作成するサンプルアプリは、以下の記事を参考にします。

Railsのセットアップ

ActiveRecordは使わないので-Oオプションでインストールしないようにします。

インストールしてしまうと、ActiveRecordがデータベースのアダプタを探そうとしてエラーになります。

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

次に必要なgemをインストールします。

冒頭でも書きましたが、dynamoidはaws-sdkはv1にしか対応していないので'~> 1.0'を指定します。

Gemfile

gem 'aws-sdk', '~> 1.0'
gem 'dynamoid'
gem 'bootstrap-sass'
bundle install

設定

AWSのアクセスキーとDynamoDBのエンドポイントを設定します。

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"

dynamoidの設定を書きます。

  • namespaceがテーブル名の接頭語になります。Rails.env + namespaceとすることで環境ごとにテーブルを分けられます。
  • partitioningはfalseにしておきます。

config/initializer/dynamoid.rb

Dynamoid.configure do |config|
config.adapter = 'aws_sdk' # This adapter establishes a connection to the DynamoDB servers using Amazon's own AWS gem.
config.namespace = "dynamoid_app_development" # To namespace tables created by Dynamoid from other tables you might have.
config.warn_on_scan = true # Output a warning to the logger when you perform a scan rather than a query on a table.
config.partitioning = false # Spread writes randomly across the database. See "partitioning" below for more.
config.partition_size = 1 # Determine the key space size that writes are randomly spread across.
config.read_capacity = 1 # Read capacity for your tables
config.write_capacity = 1 # Write capacity for your tables
end

ちなみにですが、DynamoDB Localはaws-sdkのv1に対応していないため、Dynamoidと合わせて使うことはできませんでした。

DynamoDB Local does not support v1 API. (AWS::DynamoDB::Errors::InvalidAction)

Model

Dynamoid::Documentをincludeします。ActiveRecordは継承しませんのでご注意ください。

keyにはDynamoDBで設定したハッシュキー名を指定します。

app/model/task.rb

class Task
include Dynamoid::Document

table name: :tasks, key: :id // テーブル名とkeyを指定

field :name, :string // Stringのフィールド
field :timestamps, :datetime // datetimeのフィールド
end

Controller

サンプルをそのまま使わせてもらいます。

app/controller/tasks.rb

class TasksController < ApplicationController
def index
@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.'
end

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

View

サンプルをそのまま使わせてもらいます。

app/views/tasks/index.html.erb

<div class="container">
<h1>MyTasks</h1>
<!-- 操作が成功したときに表示する -->
<% if notice %>
<p class="alert alert-success"><%= notice %></p>
<% end %>

<!-- 操作が失敗したときに表示する -->
<% if alert %>
<p class="alert alert-error"><%= 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><% @tasks.each do |task| %><% end %>
<table class="table table-striped table-bordered">
<tbody>
<tr>
<th>Name</th>
<th>CreatedAt</th>
<th>UpdatedAt</th>
<th>Done</th>
</tr>
<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>
</tbody>
</table>
</div>
</div>

Routing

サンプルをそのまま使わせてもらいます。

config/routes.rb

resources :tasks, only: [:index, :create, :destroy]

Asset

Bootstrapを有効にするために以下を追加します。

app/assets/stylesheets/application.scss ※.cssしかない場合は、.scssに変更します

@import "bootstrap-sprockets";
@import "bootstrap";

app/assets/javascripts/application.js

//= require bootstrap-sprockets

DynamoDB

テーブルは自動で作成されたりはしませんので、事前にテーブルを作っておきます。

  • テーブル名: dynamoid_app_development_tasks
  • プライマリインデックス(ハッシュキー): id

動作確認

Railsを起動します。

bundle exec rails s

localhost:3000/tasks

スクリーンショット 2015-08-12 16.44.08

AWSコンソールでDynamoDBにデータが書き込み/削除ができることが確認できました。

スクリーンショット 2015-08-12 16.44.29

まとめと所感

本エントリーではDynamoidのインストールと基本的な動作まで確認しました。Dynamoidのドキュメントを見ると他にもAssociationsやValidations、Callbacksなど基本的なことはできるようですが、あまり開発が活発ではないことと、aws-sdkのv1に依存するということで、本格的に使うには不安要素が大きいと感じました。