herokuのpostgresqlのプランをアップグレードした

仕事でherokuのpostgresqlのプランをアップグレードしたので、ついでにメモ的に残しておこうと思います。

参考ページ: Upgrading Heroku Postgres Databases

本当にここに書かれたまんまにしか作業してないので、上記のページを見るだけでもいいと思います…。オフィシャルだし。

standard-0からpremium-0へのアップグレード

今回はstandard-0からpremium-0へのアップグレードを行いました。
さっきのページでいうところの、Upgrade with Follower Changeoversの箇所が作業的にも楽そうだったので、これでやりました。この方法で行うと、新規にDBを作るときにDBのバージョンを指定するのをし忘れて最新バージョンのDBができてしまってバージョンの同期が取れなくなった!とかそういうことが起きないため、オススメです。
また、ダウンタイムも少なくて済むようです。

作業概要を書くと、

  1. 新しいDB(premium-0)を既存DBのfollowで作成。データがコピーされて作成される。
  2. 新しいDB(premium-0)が出来上がるのを待つ。
  3. メンテナンスモードに変更。
  4. 新しいDB(premium-0)をunfollowする。
  5. 新しいDB(premium-0)を正規DBに指定する。
  6. メンテナンスモードを解除。
  7. 動作確認後、元のDB(standard-0)を削除する。

という感じです。

1. 新しいDB(premium-0)を既存DBのfollowで作成

元のDB(standard-0)のURLが、HEROKU_POSTGRESQL_IVORY_URLとします。
app-nameは各自のアプリ名に置き換えてください。

heroku addons:create heroku-postgresql:premium-0 --follow HEROKU_POSTGRESQL_IVORY_URL --app app-name

2. 出来上がるのを待つ

以下のコマンドを打って、待ちましょう。

heroku pg:wait --app app-name

終わったら、次のコマンドを打って、確認しましょう。

heroku pg:info --app app-name

こんなのがでるでしょう。
新しくできたDBは、HEORKU_POSTGRESQL_BLUEのようですね。

=== HEROKU_POSTGRESQL_BLUE_URL
Plan:               Premium 0
Status:             Available
(略)
Following:          HEROKU_POSTGRESQL_IVORY
Behind By:          0 commits
(略)

=== HEROKU_POSTGRESQL_IVORY_URL, DATABASE_URL
Plan:               Standard 0
Status:             Available
(略)
Followers:          HEROKU_POSTGRESQL_BLUE
(略)

3. メンテナンスモードにする

DBを差し替えるので、メンテナンスモードにします。

heroku maintenance:on --app app-name
heroku ps:scale worker=0 --app app-name

4. 新しいDB(premium-0)をunfollowする

unfollowします!したからといって、データが消失することはありません。

heroku pg:unfollow HEROKU_POSTGRESQL_BLUE_URL --app app-name

5. 新しいDB(premium-0)を正規DBに指定する

URLが最後に付かないことに注意しましょう。

heroku pg:promote HEROKU_POSTGRESQL_BLUE --app app-name

ちゃんと変更されたか確認します。

heroku pg:info --app app-name

DATABASE_URLがBLUEのほうに紐付いてるのを確認しました。OKです。

=== HEROKU_POSTGRESQL_BLUE_URL, DATABASE_URL
Plan:               Premium 0
Status:             Available
(略)
Forked From:        HEROKU_POSTGRESQL_IVORY
(略)

=== HEROKU_POSTGRESQL_IVORY_URL
Plan:               Standard 0
Status:             Available
(略)
Forks:              HEROKU_POSTGRESQL_BLUE
(略)

6. メンテナンスモードを解除

メンテナンスモードを解除して、動作確認作業しましょう。

heroku maintenance:off --app app-name
heroku ps:scale worker=1 --app app-name

7. 動作確認後、元のDB(standard-0)を削除する

元のDBは不要になるので、削除しましょう。削除し忘れると毎月50ドルかかるので。

ちゃんと、動作確認してから削除しましょう!!
ちゃんと、動作確認してから削除しましょう!!

大事なことなので2回書きました。

heroku addons:destroy HEROKU_POSTGRESQL_IVORY --app app-name

感想

standard-0以上から上位のプランへの変更は、この方法がよさそうですね。
pg:copyするよりは楽かなーと思いました。DBのバージョンの差異も発生しないし。


Grails3.0.4で、Bootstrap3のレイアウトでscaffoldできるようにする。その2

前回の続きです。
今回は、scaffoldのindex.gspを編集していきます!

<!DOCTYPE html>
<html>
    <head>
        <meta name="layout" content="bootstrap" />
        <g:set var="entityName" value="\${message(code: '${propertyName}.label', default: '${className}')}" />
        <title><g:message code="default.list.label" args="[entityName]" /></title>
    </head>
    <body>
        <div id="list-${propertyName}" class="content scaffold-list" role="main">
            <div class="page-header">
                <h1><g:message code="default.list.label" args="[entityName]" /></h1>
            </div>
            <g:link class="btn btn-primary" action="create"><g:message code="default.new.label" args="[entityName]" /></g:link>
            <g:if test="\${flash.message}">
                <div class="alert alert-info" role="alert">\${flash.message}</div>
            </g:if>
            <f:table collection="\${${propertyName}List}" />

            <div class="pagination">
                <g:paginate total="\${${propertyName}Count ?: 0}" />
            </div>
        </div>
    </body>
</html>

とりあえず書き換えたのは、上記のハイライト部分です。
前回伝えた通り、meta name=”layout”のcontentをbootstrapに変更しています。また、class名をBootstrap3のものに変えたり、page-headerで囲んだりしています。

f:tableってなんぞ?

これですが、Grailsのfieldsプラグインによって作られているTagLibです。いきなり登場するので、正直わけがわかりませんでした。

参考ページ
qiita: Groovyのちょっとしたこと「GrailsのScaffoldingページのGSPに出てくる謎の<f:table />タグ」

このqiitaの記事のコメントによると、fieldsプラグインによって作られるテーブルのテンプレートも定義すれば書き換えられるようです。うーむ、罠だ。Grailsはちゃんとドキュメント作っておいてほしいですねぇ…。

f:tableで参照されるテンプレートを作る

grails-app/views/templates/_fields/_table.gspを定義して、作り直せばいいそうです。
作り直すというと大仰ですが、Bootstrap3のclassをテーブルに反映させたいので、githubからの参照コードを丸々書いて、クラス名だけ反映させておきました。

<table class="table table-striped">
    <thead>
    <tr>
        <g:each in="${domainProperties}" var="p" status="i">
            <g:set var="propTitle">${domainClass.propertyName}.${p.name}.label</g:set>
            <g:sortableColumn property="${p.name}" title="${message(code: propTitle, default: p.naturalName)}" />
        </g:each>
    </tr>
    </thead>
    <tbody>
    <g:each in="${collection}" var="bean" status="i">
        <tr>
            <g:each in="${domainProperties}" var="p" status="j">
                <g:if test="${j==0}">
                    <td><g:link method="GET" resource="${bean}"><f:display bean="${bean}" property="${p.name}" displayStyle="${displayStyle?:'table'}" /></g:link></td>
                </g:if>
                <g:else>
                    <td><f:display bean="${bean}" property="${p.name}"  displayStyle="${displayStyle?:'table'}" /></td>
                </g:else>
            </g:each>
        </tr>
    </g:each>
    </tbody>
</table>

table-stripedをあてたら、自動的に奇数行・偶数行で背景色が変わるので、tr部分でクラスをつける処理は削除しています。

ページネーションはどうするの?

ページネーションはg:paginateっていうTagLibがあるのですが、これ、固定のhtmlになっているようで、Bootstrap3のページネーションを気軽に当てる方法がありません。css側をいじってGrailsのページネーションに合わせるか、新しいTagLibを作ってBootstrap3のページネーションに合わせるか、しかないと思いますが、私的には後者にしたいなぁというところですね。といってもTagLibとかまだ作ったこともないのですが…(しかも車輪の再発明感が半端ない)。

Grailsはここ直したほうがいいんじゃないですかねぇ…。デフォルトはこのままでもいいから…。

Bootstrap3化には大きな支障が出ますが、とりあえずはページネーションは今回は放っておきます。他のscaffoldを先に直すことを優先していきます!

今回のまとめ

  • src/main/templates/scaffolding/index.gspを修正しよう。
  • fieldsプラグインが突然登場するので注意しよう。
  • g:paginateはイケてない…。今回は放っておく。

次回の予定

次は新規作成画面を修正していきたいと思います。


第100回 Okayama.rbに参加してきました。

2015/08/22(土)に、ついに100回目の開催になったOkayama.rbに参加してきました。

100回目の節目なので、いつもは木曜日なのですが、土曜日にして16時より2時間が勉強会で、18時からお祝いの飲み会というゆるい感じでした。会場の都合上、飲み会の時間まではワンドリンク制だったので、この時点でアルコールが入る人たちもいます!うーん、ゆるい!このゆるさがokayama.rbのいいところですね。だから長続きしているのだとも言えます。

発表者は

の3名でした。
予定では2名でしたが、勉強会の時間があまりそうだったのと、開始1時間前に会場近くに着いてしまったので、スタバでMacBookProを開いて急いでスライドを作りました。

ActiveRecord::Enumのススメ

発表内容は、ActiveRecord::Enumのススメにしました。
Rails4.1から導入されてる機能なのですが、まだ使ってない人もいるかもしれないなぁ〜というところと、ハマりどころなどをTips的に紹介したLTレベルの発表でした。

他の発表者の資料

他の発表者の資料は各自ツイートしてますので、そちらを参照してください。

こにしさんのは、Ruby自体に新しいメソッドを追加してみたっていう話で、Rubyのコードがgithubで読めるということ、言語自体にメソッドを追加するのがカジュアルに試せる時代であることの話でした。Ruby内部のドキュメントが日本語で充実しているというのがすごいなーと思いました。こんな言語はやはりRubyだけではなかろうか…(他の言語は知らんけど)。

ハーレム(@mako_wisの通り名)の内容は、異なるDBでjoinしようとしてたっていう話で、そりゃ失敗するわ…っていうことでしたが、preloadだとjoinsしてないから結構動くという話でした。
しかし、今適当にググってみたのですが、異なるDB間でのテーブル結合は、簡単にできるっぽいですね。
http://q.hatena.ne.jp/1178781253

問題はそれがRailsのActiveRecordでできるかっていうところだろうから、それだと簡単にはできないのかも…(できても表現力的に旨みがないかもしれない、という意味で)。

その後は飲み会

飲み放題だったので雑談をしながらワイワイと過ごしました。
次回からは通常営業の毎週木曜日になります。
皆様のご参加をお待ちしております。ゆるくやっていきましょう!


Grails3.0.4で、Bootstrap3のレイアウトでscaffoldできるようにする。その1

Grailsでgenerate-allしたときに作られたscaffoldのテンプレートの変更はどうするんだろう?と思って調べようと思っていたところ、たまたまgrails helpを打ってみたら、install-templatesというコマンドを発見。早速打ってみました。

grails install-templates

すると、src/main/templates/scaffolding以下に、ControllerとSpecとViewのテンプレートが出来上がりました。これを編集してから、generate-allすれば、捗りそうです。

今回は、Bootstrap3のCRUDが出る程度までには進めてみたいと思います。

Bootstrap3用のlayoutファイルを作る

まずは、Bootstrap3用のlayoutファイルを作ります。ひとまず、grails-app/views/layouts/main.gspをコピーして、bootstrap3.gspを作りました。これに手を入れていきます。scaffoldっぽいBootstrap3の例ですが、Dashboradかなと思ったので、これに似せていきます。まずはDashboardのソースを見て、コピーしていきましょう。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="description" content="">
    <meta name="author" content="">
    <title><g:layoutTitle default="Dashboard" /></title>
    <asset:stylesheet src="application.css"/>
    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
    <!--[if lt IE 9]>
      <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
      <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
    <![endif]-->
    <g:layoutHead/>
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top">
    <div class="container-fluid">
        <div class="navbar-header">
            <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="#">Project name</a>
        </div>
        <div id="navbar" class="navbar-collapse collapse">
            <ul class="nav navbar-nav navbar-right">
                <li><a href="#">Dashboard</a></li>
                <li><a href="#">Settings</a></li>
                <li><a href="#">Profile</a></li>
                <li><a href="#">Help</a></li>
            </ul>
            <form class="navbar-form navbar-right">
                <input type="text" class="form-control" placeholder="Search...">
            </form>
        </div>
    </div>
</nav>

<div class="container-fluid">
    <div class="row">
        <div class="col-sm-3 col-md-2 sidebar">
            <ul class="nav nav-sidebar">
                <li class="active"><a href="#">Overview <span class="sr-only">(current)</span></a></li>
                <li><a href="#">Reports</a></li>
                <li><a href="#">Analytics</a></li>
                <li><a href="#">Export</a></li>
            </ul>
            <ul class="nav nav-sidebar">
                <li><a href="">Nav item</a></li>
                <li><a href="">Nav item again</a></li>
                <li><a href="">One more nav</a></li>
                <li><a href="">Another nav item</a></li>
                <li><a href="">More navigation</a></li>
            </ul>
            <ul class="nav nav-sidebar">
                <li><a href="">Nav item again</a></li>
                <li><a href="">One more nav</a></li>
                <li><a href="">Another nav item</a></li>
            </ul>
        </div>
        <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
            <g:layoutBody/>
        </div>
    </div>
</div>
<asset:javascript src="application.js"/>
</body>
</html>

ハイライトしている部分が、主に重要な部分です。
ナビゲーションバーと、サイドバーは今のところ丸々コピーしておいて、メイン部分にgspのlayoutBodyを設定しています。assets系も読み込んでいます。assets系はJSはCoffeeScript、cssはSassを使っています。そちらの設定が気になる人はまずそちらをやってみるといいでしょう。

次に、Dashboard用のcssもコピーしておきました。
cssをコピーしただけですが、拡張子はscssにしてます。

/*
 * Base structure
 */

/* Move down content because we have a fixed navbar that is 50px tall */
body {
  padding-top: 50px;
}


/*
 * Global add-ons
 */

.sub-header {
  padding-bottom: 10px;
  border-bottom: 1px solid #eee;
}

/*
 * Top navigation
 * Hide default border to remove 1px line.
 */
.navbar-fixed-top {
  border: 0;
}

/*
 * Sidebar
 */

/* Hide for mobile, show later */
.sidebar {
  display: none;
}
@media (min-width: 768px) {
  .sidebar {
    position: fixed;
    top: 51px;
    bottom: 0;
    left: 0;
    z-index: 1000;
    display: block;
    padding: 20px;
    overflow-x: hidden;
    overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */
    background-color: #f5f5f5;
    border-right: 1px solid #eee;
  }
}

/* Sidebar navigation */
.nav-sidebar {
  margin-right: -21px; /* 20px padding + 1px border */
  margin-bottom: 20px;
  margin-left: -20px;
}
.nav-sidebar > li > a {
  padding-right: 20px;
  padding-left: 20px;
}
.nav-sidebar > .active > a,
.nav-sidebar > .active > a:hover,
.nav-sidebar > .active > a:focus {
  color: #fff;
  background-color: #428bca;
}


/*
 * Main content
 */

.main {
  padding: 20px;
}
@media (min-width: 768px) {
  .main {
    padding-right: 40px;
    padding-left: 40px;
  }
}
.main .page-header {
  margin-top: 0;
}


/*
 * Placeholder dashboard ideas
 */

.placeholders {
  margin-bottom: 30px;
  text-align: center;
}
.placeholders h4 {
  margin-bottom: 0;
}
.placeholder {
  margin-bottom: 20px;
}
.placeholder img {
  display: inline-block;
  border-radius: 50%;
}

これを、application.scssで読み込みます。

@import 'variables';
@import 'webjars/bootstrap-sass/3.3.4/assets/stylesheets/bootstrap';
@import 'dashboard';

ここまでで、ひとまずレイアウトファイルの準備は完了かなと思います。

templatesの編集(layout指定の変更)

では、テンプレートのlayout指定を変えていきましょう。
src/main/templates/scaffolding/の以下のファイルたちです。

  • create.gsp
  • edit.gsp
  • index.gsp
  • show.gsp

これらのファイルの該当箇所を変更します。

<meta name="layout" content="bootstrap3" />

これで、レイアウトはとりあえず先ほど作ったbootstrap3を使ってくれるようになりました。

次回について

長丁場になりそうなので、連続物にすることにしました。
次回は、index画面に手を入れていきます。


Grails3.0.4でPostgresqlに接続する

今回は接続先データベースをH2からpostgresqlにしてみようと思います。
おそらく、デプロイ先はHerokuにすると思うので、先にそうしておこうということです。

PosgresqlのJDBCドライバを入れる

まず、PostgresqlのJDBCドライバを使うようにbuild.gradleに記述します。
2015-08-12時点で最新ぽいのを入れました。

dependencies {
  // 略
  runtime 'org.postgresql:postgresql:9.4-1201-jdbc41'
  // 略
}

application.ymlの修正

次にapplication.ymlの修正です。

dataSourceの箇所と、environments以下の環境ごとの箇所の修正になります。
ユーザー名、パスワードは適当に設定してください。

dataSource:
    pooled: true
    jmxExport: true
    driverClassName: org.postgresql.Driver
    username: example
    password: example

environments
    development:
        dataSource:
            dbCreate: update
            url: jdbc:postgresql://localhost/example_development
            logSql: true
    test:
        dataSource:
            dbCreate: update
            url: jdbc:postgresql://localhost/example_test
    production:
        # 略(とりあえず変更せず)

developmentのdbCreateのところを、create-dropからupdateにしておきました。
というのも、create-dropだとアプリの終了・起動時に毎回削除されてしまうためです。これが正しい設定なのかはまだよくわかっていませんが、とりあえずこうしています。一旦アプリを再起動させてもデータが残っていることは確認できたので、目的は果たせていると思います。

詳しい人は是非とも教えてください!

データベースユーザーとデータベースの作成

postgresqlはインストールされているものとします。
ない場合はインストールしましょう。Macならhomebrewで入れられます。

プロジェクト用のデータベースユーザーとデータベースを作ってみます。
まずは、データベースユーザーの作成。
ユーザーはexample、パスワードもexampleとします(適当に変えてください)

createuser -h localhost -d example -W
password: (表示されないけれどexampleと入力してEnter)

次に、データベースを作ります。
開発用のexample_developmentとテスト用のexample_testを作っておきます。
-Oオプションで、データベースの所有者(Owner)をexampleユーザーにしています。

createdb -E UTF-8 --locale=C -T template0 -O example example_development
createdb -E UTF-8 --locale=C -T template0 -O example example_test

Grailsの起動

とりあえずこれで起動してみました。

grails run-app

無事に起動すれば成功です。前に作成したBookのScaffoldからBookのデータの登録ができるところまで確認できました。

まとめ

Grailsでの対象データベースの変更はJDBCドライバを入れ替えるくらいでそこまで大変ではないが、データベースの準備はやっぱり大変である…。