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は各自のアプリ名に置き換えてください。

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

2. 出来上がるのを待つ

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

1
heroku pg:wait --app app-name

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

1
heroku pg:info --app app-name

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
=== 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を差し替えるので、メンテナンスモードにします。

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

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

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

1
heroku pg:unfollow HEROKU_POSTGRESQL_BLUE_URL --app app-name

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

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

1
heroku pg:promote HEROKU_POSTGRESQL_BLUE --app app-name

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

1
heroku pg:info --app app-name

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

01
02
03
04
05
06
07
08
09
10
11
12
13
=== 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. メンテナンスモードを解除

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

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

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

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

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

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

1
heroku addons:destroy HEROKU_POSTGRESQL_IVORY --app app-name

感想

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


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

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

src/main/templates/scaffolding/index.gsp
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!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からの参照コードを丸々書いて、クラス名だけ反映させておきました。

grails-app/views/templates/_fields/_table.gsp
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<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というコマンドを発見。早速打ってみました。

bash
1
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のソースを見て、コピーしていきましょう。

grails-app/views/layouts/bootstrap3.gsp
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
<!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/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にしてます。

grails-app/assets/stylesheets/_dashboard.scss
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
/*
 * 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で読み込みます。

grails-app/assets/stylesheets/application.scss
1
2
3
@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

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

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

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

次回について

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


Grails3.0.4でPostgresqlに接続する

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

PosgresqlのJDBCドライバを入れる

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

build.gradle
1
2
3
4
5
dependencies {
  // 略
  runtime 'org.postgresql:postgresql:9.4-1201-jdbc41'
  // 略
}

application.ymlの修正

次にapplication.ymlの修正です。

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

grails-app/conf/application.yml
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
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とします(適当に変えてください)

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

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

bash
1
2
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の起動

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

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

まとめ

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