White Box技術部

WEB開発のあれこれ(と何か)

MacPortsでMySQL5.7をインストールする

homebrewが猛威を振っていますが、MacPortsおじさんの私は元気です。

MySQL 5.7をインストールする

仮想環境にはMySQL 5.7を入れたりしていましたが、そういえばMacには入れてなかったのでインストール作業をしてみました。

環境情報

とりあえずport install

まずはsudo port selfupdateして、portを最新にしつつ(これで2.3.4になりました)、 mysqlの5.7がリポジトリに存在することをport search mysql | grep ^mysqlで探して、 おもむろに以下のインストールコマンドを叩きます。

$ sudo port install mysql57

すると以下のようなエラーが発生しました。

--->  Configuring mysql57
Error: The file /opt/local/lib/libwrap.dylib does not exist, though it was
Error: expected to have been provided by one of mysql57's dependencies. Try
Error: rebuilding the port that should have provided that file by running
Error: 
Error:     sudo port -n upgrade --force <portname>
Error: 
Error: org.macports.configure for port mysql57 returned: missing required file
To report a bug, follow the instructions in the guide:
    http://guide.macports.org/#project.tickets
Error: Processing of port mysql57 failed

depsチェック

mysql57をインストールするときに期待する依存ライブラリないというようなことを言われているので、 おもむろに以下のコマンドを叩いて依存ライブラリを確認します。

$ port deps mysql57 

すると以下のような結果になりました。

Full Name: mysql57 @5.7.11_1
Build Dependencies:   cmake
Library Dependencies: zlib
Runtime Dependencies: mysql_select

port installedで確認すると、どれもインストールされているので少し調べ、 その調べた感じだとcmakeがちゃんと入ってるか?みたいなことが書かれていたのでチェックしましたが、 ちゃんと現時点の最新のcmake @3.5.1_0が入っていました。

libwrap.dylibとは?

次にエラーになっているこのファイルが何なのか調べたら、 TCP Wrapperだったみたいなので、以下を実行してインストールし、 その後mysql57のインストールを実行したところ、いい感じに進みました。

$ sudo port install tcp_wrappers

mysql57-serverのインストール

とはいえ、以下のようなメッセージが出ており、mysql57-serverのインストールが別途必要なことをここで知ります。

--->  Computing dependencies for mysql57
--->  Configuring mysql57
--->  Building mysql57
--->  Staging mysql57 into destroot
--->  Installing mysql57 @5.7.11_1
The mysql57 client has been installed.
To install the mysql57 server, install the mysql57-server port.
--->  Activating mysql57 @5.7.11_1

On activation if no /opt/local/etc/mysql57/my.cnf file exists one
will be created which loads
/opt/local/etc/mysql57/macports-default.cnf.

If a /opt/local/etc/mysql57/my.cnf file exists MacPorts does not
touch it and any changes you make to /opt/local/etc/mysql57/my.cnf
will be preserved (e.g., during port upgrades, deactivations or
activations). /opt/local/etc/mysql57/my.cnf is a good place to
customize your mysql57 installation.

Any changes made to /opt/local/etc/mysql57/macports-default.cnf
will be lost during port upgrades, deactivations or activations so you
are advised to not make changes here. Currently
/opt/local/etc/mysql57/macports-default.cnf contains only one
directive; to disable networking. With disabled networking it is
possible to install and have running all the MacPorts mysql ports
simultaneously.

ちなみにこの時点のmy.cnfはmacports-default.cnfを見るという設定しか無いですし、 macports-default.cnfはコメント除くと以下だけです。

[mysqld]
skip-networking

実行結果

インストールコマンドを実行すると以下のようになります。

$ sudo port install mysql57-server
--->  Computing dependencies for mysql57-server
--->  Fetching archive for mysql57-server
--->  Attempting to fetch mysql57-server-5.7.11_0.darwin_14.noarch.tbz2 from https://packages.macports.org/mysql57-server
--->  Attempting to fetch mysql57-server-5.7.11_0.darwin_14.noarch.tbz2.rmd160 from https://packages.macports.org/mysql57-server
--->  Installing mysql57-server @5.7.11_0
--->  Activating mysql57-server @5.7.11_0

If this is a new install you might want to run:

$ sudo /opt/local/lib/mysql57/bin/mysqld --initialize --user=_mysql
$ /opt/local/lib/mysql57/bin/mysql_secure_installation

--->  Cleaning mysql57-server
--->  Updating database of binaries
--->  Scanning binaries for linking errors
--->  No broken files found.

これでインストールはおしまいですね。
あとは上のログにもあるようにmysqldを立ち上げて、Initializeコマンドを叩けばOKです。

でも今日はもうやりません。

後始末

これらの流れでmysqlコマンドにはPATHが通らなかったので、適当に/opt/local/lib/mysql57/binをPATHに通しておきましょう。

bashなら.bashrcに以下を追記してsource ~/.bashrcですね。

  • ~/.bashrc
export PATH=$PATH:/opt/local/lib/mysql57/bin

ついでにMacPortsも綺麗にする

MySQLのインストール前にport outdatedで更新が必要なパッケージがどのくらいが見て、 sudo port upgrade outdatedをしてからのほうが、依存性で怒られる可能性が減っていいと思いますが、 upgradeの際に-uオプションを付けていないと、過去バージョンのパッケージも残っている場合があるので、 port installedで確認して特に問題なければ(過去バージョンが必要なければ)、 以下のコマンドでささっと使ってないパッケージを削除してしまいましょう。

$ sudo port uninstall inactive

お疲れ様でした。

結論

MySQLなんてそうそう簡単にバージョン上げたりしないので、MySQLの公式サイトからdmgファイルを落としてきて入れるのが一番いいのではないでしょうか。

MySQL :: Download MySQL Community Server

追記

試しにdmgでインストールしてみたところ、 PATHは通ってないし、Initializeコマンドの説明も出てこなかったので、思っていたのとは違いました。 もう少しサポートしてくれるのかなと期待していたので。

ちなみにインストールが終了するとダイアログが表示され、デフォルトパスワードが設定されたことがわかります。

2016-04-01T05:55:51.394996Z 1 [Note] A temporary password is generated for root@localhost: XXXXXXXXX(←パスワード)

If you lose this password, please consult the section How to Reset the Root Password in the MySQL reference manual.

インストール先などの情報は以下の通りで、rootのファイルになっています。

  • インストール先:/usr/local/mysql(/usr/local/mysql-5.7.11-osx10.9-x86_64のエイリアス
  • my.cnfサンプル:/usr/local/mysql/support-files/my-default.cnf

my-default.cnfはMacPorts版より詳しい説明がある気がします。

# For advice on how to change settings please see
# http://dev.mysql.com/doc/refman/5.7/en/server-configuration-defaults.html
# *** DO NOT EDIT THIS FILE. It's a template which will be copied to the
# *** default location during install, and will be replaced if you
# *** upgrade to a newer version of MySQL.

[mysqld]

# Remove leading # and set to the amount of RAM for the most important data
# cache in MySQL. Start at 70% of total RAM for dedicated server, else 10%.
# innodb_buffer_pool_size = 128M

# Remove leading # to turn on a very important data integrity option: logging
# changes to the binary log between backups.
# log_bin

# These are commonly set, remove the # and set as required.
# basedir = .....
# datadir = .....
# port = .....
# server_id = .....
# socket = .....

# Remove leading # to set options mainly useful for reporting servers.
# The server defaults are faster for transactions and fast SELECTs.
# Adjust sizes as needed, experiment to find the optimal values.
# join_buffer_size = 128M
# sort_buffer_size = 2M
# read_rnd_buffer_size = 2M

sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES

Phoenix Framework Documentの翻訳:Overview

www.phoenixframework.org

概要

Phoenixは、サーバサイドMVCパターンを実装するためにElixirで書かれたWebフレームワークです。 その構成要素と概念の多くは、Ruby on RailsPythonDjangoのような、他のWebフレームワークの経験を持つ私たちに馴染みのあるものだと思います。

Phoenixは、開発者に高い生産性を与えつつ、アプリケーションがハイパフォーマンスとなるように設計されています。 また、リアルタイム機能を実現するためのチャネル(Channels)や、燃え盛る炎のようなスピードをだすために事前コンパイルされるテンプレート(Templates)のような、興味深くて新しい、ひねりのある仕組みをいくつか持っています。

もしあなたがすでにElixirに精通しているのであれば、最高です! そうでなくても、Elixirを学ぶための場所はいくつかあります。 Elixirガイドは学び始める場所として最適です。 また、私たちもElixirとErlangの学習ガイドに役立つ情報をまとめています。

この入門ガイドの目的は、Phoenixを構成するパーツとPhoenixがサポートするレイヤーについて、簡潔かつ高レベルな概要をお伝えすることにあります。

vimeo.com

Phoenix

Phoenixは、モジュール構造で、かつ柔軟であるようにデザインされた多重レイヤーシステムの最上位レイヤーになります。 他のレイヤーには、このPhoenixの概要のすぐ後で言及する予定の、PlugとEctoが含まれます。 ErlangのHTTPサーバであるCowboyは、PlugとPhoenixの基盤として機能しますが、私たちがこのガイドで直接Cowboyの説明をすることはありません。

Phoenixは、Webアプリケーションを構築するために独自の目的や役割を持った、いくつかの個別パーツで構成されています。 これらすべてについて、ガイド全体を通して深く解説していきますが、以下に簡単な内容を記載します。

  • エンドポイント
    • ルータに処理を引き継ぐまで、リクエストのすべての側面を処理する
    • すべてのリクエストに適用するPlugの主要なセットを提供する
    • 指定されたルータにリクエストを割り当てる
  • ルータ
    • 受け取ったリクエストを解析し、適切なコントローラ/アクションに、必要に応じてパラメータと共に割り当てる
    • リソースへのルートパスとURLを生成するヘルパーを提供する
    • パイプラインという名前で定義された機能を使って、リクエストを渡すことができる
    • パイプライン
      • ルートのセットで、Plugグループが簡単に利用できるようにする
  • コントローラ
    • リクエストを処理する、アクションと呼ばれる機能を提供する
    • アクション
      • データを準備し、ビューに渡す
      • ビュー経由でレンダリングを実行する
      • リダイレクトを行う
  • ビュー
    • テンプレートをレンダリングする
    • プレゼンテーション層として振る舞う
    • テンプレートで利用可能な、プレゼンテーション用のデータを装飾するヘルパー関数を定義する
  • テンプレート
    • テンプレートは顔文字表現のようなものだ
    • テンプレートは事前コンパイルされ、高速に動作する
  • チャネル
    • 簡単にリアルタイム通信用のソケットを管理する
    • チャネルは、チャネルが永続コネクションとの双方向通信を許可していることを除いて、コントローラに類似している
  • PubSub
    • チャネルレイヤーの下にあり、クライアントはトピックを購読(サブスクライブ)することができる
    • サードパーティのpubsubインテグレーションのために、基底となるpubsubアダプタを抽象化する

プラグ(Plug)

Plugは、Webアプリケーションを作るために利用する、組み立て可能なモジュールを構築するための仕様です。 プラグは、その仕様によって作られた、再利用可能なモジュール、または関数です。 それらは、リクエストヘッダの解析やロギングのように、別個の動作を提供します。 Plug APIは小さく、一貫しているため、複数のプラグを定義し、パイプラインのようにセットした順で実行することができます。 それらは、プロジェクト内、または別のプロジェクトで再利用することもできます。

プラグには、認証からパラメータの事前処理まで、ほぼ全ての処理を記述することができます。レンダリングでさえ記述可能です。

Phoenixは全般的に、プラグによって大きなアドバンテージを得ています。ルータとコントローラが特にそうです。

プラグで最も重要なことの一つは、それが最終的に私たちのユーザーにアプリケーションコンテンツを配信するHTTPサーバへのアダプタを提供しているということです。 現在のところプラグは、99sのLoïc HoguinによってErlangで書かれたWebサーバであるCowboyのアダプタのみ提供しています。

より詳しい内容については、Plugガイドをご覧ください。

Ecto

Ectoは、クエリ組み立てツールと、データベースラッパーを統合したElixirのための言語です。 Ectoを使うことで、異なるデータベースへのRead/Write、ドメインデータの作成、型安全な方法での複雑なクエリ記述、SQLインジェクションを含む攻撃ベクトルからの保護など、多くのことができます。

Ectoは、4つの主要な抽象概念を中心に作られています:

  • Repo - リポジトリは個々のデータベースへの接続を表します。全てのデータベース操作は、リポジトリを介して行われます。

  • Model - モデルはデータ定義です。テーブル名やフィールドだけでなく、各フィールドの型を定義します。またモデルは、モデル間の関係も定義します。

  • Query - クエリは、モデルとリポジトリの両方を一緒に結びつけます。これにより私たちは、リポジトリからデータをエレガントに取得し、モデルにデータをキャストすることができます。

  • Changeset - チェンジセットは、アプリケーションがモデルデータを使用する前に、モデルデータに実行する必要のある変換内容を宣言します。これらは、型変換、検証など多数の要素を含みます。

新しく作成したPhoenixのアプリケーションは、デフォルトでは、PostgreSQLのストレージに対するEctoを使用します。

勉強会おじさんになって思うこと

チーム内交流のために勉強会を開催していたら、なんか勉強会おじさんっぽくなってしまったので 今後の実施方法も考えて、少し現状を振り返ってみました。

なぜ勉強会を始めたのか?

そもそも勉強会を始めるきっかけだったのは、もちろん単純にやりたかっただけというのもありますが、 急激なチームメンバー増と、チーム内でのチーム分けにより、チームメンバーの交流機会が減り、 メンバー間で信頼関係の構築がなかなかできていないように感じたからです。

そこで、開発機能や案件内容にかかわらず、メンバーが集って交流できる場の一つとして、 勉強会を企画することにしました。

勉強会が信頼関係の構築になるのか?

勉強会のスタイルはいくつかありますが、現在実施している勉強会は、

『発表者がいて、他の人はそれを聞いて、場合によって質問する』

というスタイルです。

一般募集で行われる勉強会やカンファレンスでよくあるスタイルのあれです。
これだと発表者以外とは交流できないように感じますが、私にはちょっとした期待がありました。

同じ経験を共有することによる効果

映画館で好きな映画を観たとき、周りの人に不思議な親しみを感じることがありませんか?
別に誰かと会話をしたわけでもないのに、です。
同じ場に集って一つのことを経験・共有すること、ただそれだけで見知らぬ他人と共感できるなら 同じチームのメンバー間であれば会話せずにはいられなくなるのではないでしょうか?

結果的には

思惑通り、発表者への質問から会話が盛り上がったり、終わってから雑談が行われたりと、交流のネタになってくれました。 KPTのKeepでも、複数人から勉強会に関することが度々上がり、受け入れられているように感じました。

またgitの勉強会後では、業務中gitの操作で詰まった場合は、発表していた人に相談しているところも見受けられ、 発表者には仕事が増えて申し訳ないですが、聞かないことによって問題が発生する機会はなくなっていくため、 いい方向に向かっているように感じます。

ふと思いましたが、勉強会の間に今集まっているメンバーがどういう人なのか、 軽く紹介しているとより一層効果があるかもしれませんね。

勉強会の運営方法について

ここからは勉強会の運営自体に関する内容になります。

1. テーマ決め

まずは勉強会のテーマを決めます。

メンバーが興味を持ちやすいように、単純に技術の話と、現在の開発内容に関連した話で、 交互にテーマを変えるようにしています。(勉強会は週1で定時後1時間に実施しています)

テーマは事前募集したり、KPT内容からストックしておいたりしています。

2. 発表依頼

次に、テーマに即した内容を発表して貰えそうな人に、こんなこと話してもらえませんかとお願いに行きます。

これ大事!本当に大事!!発表者がいないと始まらないですし、一生懸命お願いしています。
ちなみに、このときお願いする発表者は、なるべく違う人になるように心掛けています。

なかなか発表者が確保できなそうであれば、自分が一人目の発表者になり、一緒にやりましょうという感じで誘っています。

3. 場所決め

そして、集まりそうな人数を想定して、開催場所を押さえます。

個人的な理想としては、普段の開発作業場の近くにオープンスペースがあって、 ちらっと立ち寄っていけるような感じでやれたらよいのですが、 なかなかそう上手いようにオフィスができているわけではないため、適当な広さの会議室を押さえます。

最近は参加人数に合った会議室が押さえられず、ここが頭を悩ませています。。

4. 参加者募集

最後に参加の募集と、発表・LTの募集をチャットと全体定例でします。

勉強会への参加は強制ではないので、参加して貰えるように勉強会の内容をまとめた周知ページを作成しています。
また、1回の勉強会は定時後1時間を目安にしているので、発表人数が不足していればLTネタがありそうな人に声をかけたりします。

5. 開催

開催の挨拶と今日のテーマについて軽く話して、順次発表に入ります。

メンバー交流という目的があるため、発表中のツッコミもOKとしているので、 そのことを予め話しておくのと、発表中に合いの手を入れたりして、 会話できる雰囲気作りを忘れないようにしています。

あと、写真も撮るようにしています。

6. まとめ

勉強会の周知ページに、発表資料と撮った写真を上げて、チャットで周知します。

これは参加できなかったメンバーにも好評のようなので、当日中にやってしまうといい感じでした。 感想等あればチャットのネタになりますし、話題になってる間ほど、次の発表をお願いしやすい気がします。

以上、繰り返し

勉強会のテーマ決めやスケジューリングは、大体2回先まで行うようにしています。

メイン発表者が1名だけだと、当日急遽都合がつかなくなった場合に開催できないので、 できれば2名以上に発表をお願いできているといい感じでした。

課題として感じていること

今のところ上手くいっているように見える勉強会ですが、個人的には課題も感じています。

人数について

最初は7人位だったのですが、すぐさま20人超えで集まるようになり、 いつの間にか部署をまたいでの参加者も出てきて、単純に場所の調整が難しくなってきました。

また、人数が増えるたことによって、参加を控える人が出てくることを懸念していたりします。

参加者が増えることは、交流の輪が広がり発表内容も膨らむため喜ばしいのですが、 チーム内交流という目的もあるため、チームメンバーが参加しやすいように、 テーマと発表者は偏らないように調整しないとダメですね。

主催者が自分だけ

今一番課題として感じているのは、主催者が私だけなところです。

私がいないから開催できない、ではよろしくないので、 勉強会の流れを定期的に周知しておき、私がいなくても参加者主導で進行できるように種を蒔いておきたいです。

そして勉強会おじさん問題

あんまり勉強会のことばかりを周知していると、「セリは勉強会のことしかやってないのではないか?」と思われてしまい、 私がチームの利益にならない人として認識され、そもそも勉強会に参加する人が減るのではないかという想いがあったりします。

これを防ぐには、業務で成果を出すしかないと思います。
そのため勉強会を主催する人は、業務+勉強会調整で単純に忙しくなります。

これはもうしょうがないですね。バランスを取りながらやっていくしかないでしょう。

それでも勉強会は良いです

色々ありますが、勉強会が開催できてよかったと思っています。

新しいことを知れたり、知識・考えを共有できたり、話のネタができたり、エンジニアならではの喜びが享受できました。 まだ始めて間もないので、定着するように頑張っていきたいと思います。

まあ、でもあれですね、あえて頑張りどころを言うなら・・・

「この人こんなにすごいんだ。あれ?なんかセリさんはリードエンジニアなのに感心してばっかじゃね?発表内容もしょぼくね?変わったほうがよくね?」

みたいなプレッシャーとの戦いですね!

【Groovy/ShangriLa】JsonSlurperとHTTPBuilderの使い方サンプル

引き続きShangriLaを例にしていますが、今度はGroovyでGETリクエストの実行と返却されたJSON文字列を処理してみました。

おや、Gradleサイトの様子が・・・!

README書くためにGradleのサイトに行ったところ、サイトがリニューアルしていました。

どうもgradle.comというコミュニティサイトっぽいものを作るから先行登録してね、 みたいなことを通知したいためのようです。 今のところサイトには簡単なGradleの使い方が載ってるぐらいで、 ほとんど情報はなく、このサイトがどうなっていくのかは不明です。

自分としては、ただGradleのインストール手順までの遷移がわかり辛くなったな、と感じただけでした。

なので、インストール手順が書いてあるページを以下にリンクしておきます。

HTTPBuilderでのGET方法

GroovyでHTTP通信をするのであれば、HTTPBuilderを使うのが良いと思います。

github.com

HttpBuilderの設定

build.gradleのdependenciesにhttp-builderを追加すると、対象のGradleプロジェクトから利用可能になります。

  • build.gradle
repositories {
    mavenCentral()
}

dependencies {
    compile ('org.codehaus.groovy:groovy-all:2.4.5') {transitive = false}

    compile 'org.codehaus.groovy.modules.http-builder:http-builder:0.7.1'
}

以前はbuild.gradleのrepositoriesに、HTTPBuilderが置いてあるMavenリポジトリのパスを2つ追記し、 以下のようにする必要がありましたが、現在はこの指定は不要になっているようです。

  • 昔の書き方
repositories {
    mavenCentral()
    maven {
        // for HTTP Builder
        url "http://repository.codehaus.org"
        url "http://snapshots.repository.codehaus.org"
    }
}

GETリクエストの送信方法

HTTPBuilderクラスのgetメソッドを呼ぶ方法と、requestメソッドで実行する方法があります。

記載の通りに実行すればGETリクエストを送信できますが、せっかくなので色々試してみました。

requestメソッドの利用方法

公式の記述のように、HTTPBuilderクラスの引数を指定せずに、requestメソッドの第1引数にホスト名を渡す方法でもできますが、省略して以下のように書くこともできます。

def http = new HTTPBuilder("http://api.moemoe.tokyo")
def resJsonText = http.request(GET, TEXT) {
    uri.path = '/anime/v1/master/cours'
    headers.Accept = 'application/json'
  
    response.success = { resp, json ->
        assert resp.status == 200
        def jsonString = json.text
        println("GET /anime/v1/master/cours json response: $jsonString")
        jsonString
    }

    response.'404' = { resp -> println "Access denied" }
    response.failure = { resp -> println "Unexpected failure: ${resp.statusLine}" }
}

ヘッダー情報も付ける必要がなければ付けなくても動作します。もちろん付けたければ公式のように色々付けれます。

ちなみにresponse.'404'response.failureのときのresJsonTextの戻り値はnullになります。何も返してないですからね。

はまりポイント

HTTPBuilderのパラメータや、requestメソッドの第1引数にホスト名以降を書くと、404になります。

また、uriの指定で先頭に/がないとエラーになります。

getメソッドの利用方法

これは公式にある通りに書けば大丈夫なので、上記のサンプルを書き直すと以下のような感じになります。

def http = new HTTPBuilder("http://api.moemoe.tokyo")
def resJsonText = http.get(
    path : '/anime/v1/master/cours',
    contentType : TEXT) { resp, json ->
        if (resp.status < 400) {
            def jsonString = json.text
            println("GET /anime/v1/master/cours json response: $jsonString")
            jsonString
        }
        else if (resp.status == 404) {
            println "Access denied"
        }
        else {
            println "Unexpected failure: ${resp.statusLine}"
        }
    }

結果を受け取ることだけを考えると記述量が減って簡単ですが、リクエストの成功と失敗の判断を自前でやることになるので、状況によって使い分けるべきですね。

switchで書いた方が綺麗かも。

GroovyでJSONを扱う

次に返却値のJSONを処理するために、groovy.jsonのJsonSlurperクラスを使います。
Scalaを書いた後だったからなのか、これ、すごく便利に感じました。Groovyの最強感がすごい。

JSON文字列をGroovyで扱えるJSONオブジェクトに変換する

JsonSlurperを使ってJSONオブジェクトを作るのはとても簡単です。
JsonSlurper#parseTextメソッドJSON文字列を渡すだけで完了します。

def jsonSlurper = new JsonSlurper()
def resJsonObj = jsonSlurper.parseText('{"3":{"id":3,"year":2014,"cours":3}}')

こうして作成したJSONオブジェクトは、JSONデータをMapで表現するため、直感的に扱うことができます。
Mapなので、キーを得たい場合はresJsonObj.key、値を得たい場合はresJsonObj.valueで取得できますし、 キーを指定して値を得たい場合はassert resJsonObj.value.year == 2014のように、 キーの要素で指定するだけです。ポイントはvalue値がダブルクォート無しの数値であれば、ちゃんと数値型で取り出せるというところでしょうか。なので文字列比較にしていると失敗します。

今回のサンプルコード

今回の解説で利用したサンプルコードのベースとなるGroovyプロジェクトは、以下になります。こっちではもうちょっと色々やっているので、ぜひこちらもご覧ください。

github.com

正月早々

帰省中にプログラミングしようと思っていたのですが、このサンプルを書いたところで風邪でダウンしてしまいました。 今年は家に引きこもるのもほどほどにして、少し体力をつけて病気に負けないようにしたいです。

そして、久しぶりにGroovyでコード書きましたが、びっくりするぐらいに書きやすかったです、Groovy。
前回、ScalaJSON操作するのに四苦八苦したことを考えると、JsonSlurperの直感性は感動的でした。
なかなかプロダクトで使うのは難しい言語ですが、個人的にはこれからも使っていくので、都度トピックを紹介させて頂こうと思っています。

もし興味を持っていただけたのであれば、試しに今まで書いたJavaのコードをGroovyに変換してみてください。そうすると魅力に気付きやすいかと思います。

そして慣れてきたら、是非Spockを使ってGroovyでテストコードを書きましょう!