mmts1007’s diary

プログラミング関連の技術系ブログです。

Ruby Enumerable#group_by を使ってみた

DB から取得したデータを Ruby 上でグルーピングしたくてドキュメントを漁っていたら見つけたので紹介。

経緯

DB に入っている

id task_type_id task_id
1 1 1
2 1 2
3 1 3
4 2 4
5 2 5
6 2 6

こんな感じでデータを

[
  {
    task_type_id: 1,
    task_id: [1, 2, 3]
  },
  {
    task_type_id: 2,
    task_id: [4, 5, 6]
  }
]

こんな感じにタスクの種別に紐付いているタスクの一覧 JSONRuby で作りたかった。 task_type_id でグルーピングされた値が取れれば と思い、繰り返し関係を提供している Enumerable モジュールのドキュメントを漁った。

結果

案の定欲しいメソッドはあった。メソッド名見た瞬間、「これ!」ってなった。

instance method Enumerable#group_by (Ruby 2.2.0)

使い方

# DB から取得したデータ
records = [
  { id: 1, task_type_id: 1, task_id: 1 },
  { id: 2, task_type_id: 1, task_id: 2 },
  { id: 3, task_type_id: 1, task_id: 3 },
  { id: 4, task_type_id: 2, task_id: 4 },
  { id: 5, task_type_id: 2, task_id: 5 },
  { id: 6, task_type_id: 2, task_id: 6 }
]

records.group_by { |record| record[:task_type_id] }
# => {1=>[{:id=>1, :task_type_id=>1, :task_id=>1}, {:id=>2, :task_type_id=>1, :task_id=>2}, {:id=>3, :task_type_id=>1, :task_id=>3}], 2=>[{:id=>4, :task_type_id=>2, :task_id=>4}, {:id=>5, :task_type_id=>2, :task_id=>5}, {:id=>6, :task_type_id=>2, :task_id=>6}]}

ということで、task_type_id でグルーピングされた値を取得することができた。
後はデータの形式を求めている形に変換すれば完了

records.group_by { |record| record[:task_type_id] }
  .map { |k, v| { task_type_id: k, task_id: v.map { |e| e[:id] } } }

(group_by の後の map が分かりづらい気がする…。また考えよう。)

JJUG CCC 2015 Fall に行ってきた

2015/11/28 (土) に開催された JJUG CCC に行ってきました!

JJUG CCC 2015 Fall(11月28日開催) | 日本Javaユーザーグループ

各セッションのスライドは下記サイトにまとめられているようです。

techstars.jp

今回は下記セッションを聞いてきました

EF-1エバンジェリスト直伝!Kotlinを既存プロダクトで使う!

JVM で動くプログラミング言語 Kotlin の紹介でした。
Kotlin の概要から実戦へ投入する場合の方法等を紹介していました。

speakerdeck.com

AB-2 Javaエンジニアに知っておいて欲しい「KDDIクラウドプラットフォームサービス mBaaS by Kii」によるアプリ開発

mBaaS の説明でした。
mBaaS が流行ったら、バックエンドエンジニアの需要って減るのかなーなんて恐怖を感じていました。

CD-3 よくある業務開発の自動化事情

「あるある!」って頷きながら聞いていたセッションでした。
スピーカーの方の話し方が面白かった! 自動化はしたい、しなければと思っているけどどこの現場も苦労しているのかなと感じました。

www.slideshare.net

CD-4 クラウドネイティブアプリケーションとSpring Framework

マイクロサービス、クラウドネイティブといった技術を Spring, Netflix OSS 等を用いて紹介する内容でした。
Spring Cloud, Service Discovery, Histerics, cirkit breaker, Config Server などのクラウドネイティブのための技術が素晴らしいなと思いました。
また、いつかは業務で実戦したいと思いました。 (趣味でやるとしたら…自分でサービス立ち上げるしか!)

AB-6 【こっそり始める】Javaプログラマコーディングマイグレーション

このセッションも「あるある!」って感じでした。開場もよく笑いが起きていましたw
現場に新しい技術を入れていきたい私としてはとても参考になるセッションでした。
ピープルウェア読んでみたいなーと思いました。

www.slideshare.net

GH-7 てらだよしおの赤裸々タイム

JavaWindows 主に Azure 関連のお話だったり、てらださんへの質問だったりというお時間でした。
てらださんにとっては魔の時間だったのかと思いますw

まとめ

JJUG CCC などの外部のセミナーは最新技術などを学べるのですごく刺激になります。
特に今回は Kotlin, クラウドネイティブアプリケーション、Javaプログラマコーディングマイグレーションに興味を持ちました。
あと、今回初めて懇親会に参加させて頂きました。
LT がとても楽しかったです。そして、じゃんけん大会勝ちたかった…。

次回は今回興味を持った Kotlin について記事を書ければと思います。

Node EasyMock で API モックサーバを作る

Node EasyMock を使って、API モックを作ってみた。

github.com

フロントエンド:Angular JS
バックエンド:Spring Boot
という感じでアプリを作っているのですが、フロントエンドから作りたくなったので使ってみました。

インストール

基本的には README の通り

$ npm install -g easymock
$ easymock

で動作しました。

ただし、node のバージョンの指定があるようで v5.0.0 で実行したときに下記のメッセージが表示されました。

wanted: {"node":">= 0.10.0 < 0.11.0 || >= 0.11.13 < 0.13.0 || >= 1.0.0 < 2.0.0"} (current: {"node":"5.0.0","npm":"3.3.6"})

ということで、今は v0.12.7 を使用しています。

API モック

Node EasyMock は URL、HTTP Method と対応する JSON ファイルの内容を返却します。
例えば、GET /users をリクエストした場合 users_get.json ファイルの内容を返却します。 [URL]_[HTTP Method].json の形式でファイルを用意するだけで、API モックを作ることができます。

また、/users/1/ などのネストした URL の場合は users ディレクトリ配下に 1_get.json を配置します。

ログとドキュメント

Node EasyMock はログとドキュメントを表示する機能もあります。 /_documentation/ にアクセスすると API ドキュメントを表示することができます。

ドキュメントでは API の一覧を確認することができます。

f:id:mmts1007:20151121235050p:plain

エンドポイントをクリックすると、対象のエンドポイントのレスポンスを確認することができます。

f:id:mmts1007:20151121235250p:plain

同様に /_logs/ にアクセスすると、アクセスログを確認することができます。

f:id:mmts1007:20151122000101p:plain

まとめ

フロントエンドの開発とバックエンドの開発が並行して進む場合など、バックエンドがまだ出来ていないときに利用してみてはいかがでしょうか? JSON ファイルのみ用意できれば使用できるので、学習コストも低くて良いと思います。

GitHub を使ったプロジェクト管理ツール Waffle を使ってみた

先日面白い記事を見つけました。

tech-startup.hatenablog.com

GitHub の issue、Pull Request を使ってプロジェクト管理を行うためのツールです。

この中でも、Waffle が使いやすいというコメントをいただいたので、早速使ってみました。

使い方

画面はこんな感じです。

f:id:mmts1007:20151022232713p:plain

Backlog, Ready, In Progress, Done にカラムが分かれていて、 ドラッグ&ドロップで issue を移動させることができます。

また Waffle はコミットコメント、Pull Request のコメントを使って issue を移動させることができます。
close #xx、fix #xx のようにキーワードと issue 番号を記述した Pull Request がマージされると、対象の issue をクローズ してくれます。

サンプルとして「ワッフル テスト issue」という issue を作成しました。

f:id:mmts1007:20151022232718p:plain

この issue を自動的にクローズさせます。

自動的に移動させるには前述のとおり キーワードと issue 番号 をコミットコメントに含める必要があります。

キーワードは下記のとおりです。

  • close, closes, closed
  • fix, fixes, fixed
  • resolve, resolves, resolved

「ワッフル テスト issue」は #17 のため

$ git commit -m "fix #17"

とコミットコメントに記載することで自動的に issue をクローズします。

コミットしたブランチを Push し、Pull Request を作成します。

f:id:mmts1007:20151022232731p:plain

すると、issue と Pull Request が紐づけられ、自動的に issue が In Progress に移動します。

f:id:mmts1007:20151022232727p:plain

最後に Pull Request をマージすると issue, Pull Request が Done に移動します。

f:id:mmts1007:20151022232735p:plain

これで issue を自動的にクローズさせることができました。

issue と Pull Request の紐付き

この記事を書くために Waffle を触っていたのですが、
前述のとおり issue と Pull Request が 1つに合わさったような見た目になる場合と、ならない場合がありました。

結果として Pull Request のタイトルもしくはコメントにキーワードと issue 番号を含めた場合 1つに合わさったような見た目になることがわかりました。

コミットコメントに issue 番号を入れれば、クローズすることはできるので、
最低限コミットコメントにキーワードと issue 番号を含め、
余力があれば Pull Request のコメントに書くことで
どの Pull Request でどの issue が対応されたのか さらに分かりやすくなるのではと思いました。

まとめ

コミットコメントにキーワードと issue 番号を書くだけで、ステータス管理をしてくれるのはとても楽だと感じました。
SCM、チケット管理 etc... 様々なツールを使うとあちこち見なければいけないので、1つにまとまってるのは良いですね。

Lombok でコード量を削減!

前回の記事でも書きましたが、Spring Boot のサンプルではあえて Lombok を使用しませんでした。
理由はまだ Lombok の解説をしていなかったので。

ということで、Lombok の解説をしようと思います。

What's Lombok

公式:Project Lombok

簡単にいうと、Java のお決まりのコードを排除するためのライブラリです。 Getter/Setter とかコンストラクタとかお決まりで書いているコードをアノテーションを付けることで排除できるようになります。(すごい!)

導入

Lombok は依存関係に追加する他に、IDE にも設定する必要があります。

  1. 依存関係の解決 公式にも記載されていますが、 Gradle を使う場合は下記の通り
provided "org.projectlombok:lombok:1.16.6"

ちなみに providedapply plugin: 'war' を指定しないと使えないので、指定していない場合は compile に変更してください。

  1. IDE への追加 Eclipse の場合は公式から jar をダウンロード、ダブルクリックし、Lombok をインストールしたい IDE を選択すれば完了です。

サンプル

サンプルに Spring Boot の時に使用したエンティティをリファクタします。

元ソース

package hello.domain;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "tasks")
public class Task {

    @Id
    @GeneratedValue
    private Integer id;

    @Column(nullable = false)
    private String context;

    public Task() {
    }

    public Task(Integer id, String context) {
        this.id = id;
        this.context = context;
    }

    public Integer getId() {
        return id;
    }

    public String getContext() {
        return context;
    }
}

まずはコンストラクタを削除
今回、引数なしのコンストラクタと、すべてのフィールドを持つコンストラクタが定義されているので、この 2 つを Lombok で生成します。 引数なしのコンストラクタ@NoArgsConstructor、すべてのフィールドを持つコンストラクタ@AllArgsConstructor で生成できます。

package hello.domain;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;

@Entity
@Table(name = "tasks")
@NoArgsConstructor
@AllArgsConstructor
public class Task {

    @Id
    @GeneratedValue
    private Integer id;

    @Column(nullable = false)
    private String context;

    public Integer getId() {
        return id;
    }

    public String getContext() {
        return context;
    }
}

同様に Getter/Setter も削除します。
@Getter@Setter アノテーションもあるのですが、データを管理するクラスのためのアノテーション @Data を付与します。
@Data@Getter@Setter アノテーションを含んでいるので、付与されます。

package hello.domain;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Entity
@Table(name = "tasks")
@NoArgsConstructor
@AllArgsConstructor
public class Task {

    @Id
    @GeneratedValue
    private Integer id;

    @Column(nullable = false)
    private String context;
}

コード量がかなり少なくなりました。

f:id:mmts1007:20151012172542p:plain

Outline を見ると、ちゃんとコンストラクタ、Getter/Setter が定義されていますね。

その他のアノテーション

Lombok には他にも様々なアノテーションがあります。

Lombok feature overview

@Data, @Value, @Builder 辺りはすぐに導入できると思います。

@Data

上でも解説しましたが、POJO や Beans に使用するアノテーションです。 また、POJO や Beans に使用するアノテーション @ToString, @EqualsAndHashCode, @Getter, @Setter, @RequiredArgsConstructor も自動で付与されます。

@ToString

デバッグがしやすいように、クラスのフィールド名、フィールドの値を出力するように toString メソッドをオーバーライドします。

@EqualsAndHashCode

オブジェクトに定義されているフィールドの値で equails, hashCode を算出できるように equails, hashCode メソッドをオーバーライドします。
プレーンな Java だと Bean クラスのフィールドの値が一緒でも、equals は false になってしまうので、開発者がいちいち再定義するのは面倒なのでありがたいです。

@Value

イミュータブル(不変の値)を持つクラスを簡単に生成するアノテーションです。
イミュータブルなので値の変更ができないため、フィールドはデフォルトで private, final になります。setter は生成されません。

@Builder

ビルダーパターンを簡単に生成するアノテーションです。

以上です。
Lombok はデファクトスタンダードになると思います。すでにデファクトスタンダードかもしれません。
メリットの方が大きいと思うので、積極的に導入しはいかがでしょうか。

Java Spring Boot その8

7回に渡って Spring Boot について記事を書いてきたので、Spring Boot を触ってみた感想を書いてみようと思います。

良かった点

設定ファイル(XML) が少ない

今までのサンプルを通して、設定ファイルは 1 回も書きませんでした。
Spring というと XML というイメージがあったのですが、全く書かなくてここまで出来るとは思っていませんでした。

実装が揃っている

リポジトリクラスを作成した時に驚いたのですが、よく使う実装を用意してくれているのがすごく有り難いですね。
DRY に繋がると思います。

starter が良い

使いたい機能の starter を設定すれば、必要な依存が自動的に追加される。依存関係の解決がとても楽でした。
Java というとライブラリ依存が激しいので、この機能は開発者にとって楽だと思います。

アノテーションが理解しやすい

こういう意味のアノテーションなんだろうな。というのが名前から推測しやすかったです。

記述量が少ない

実装が揃っている、starterが良い と繋がると思うのですが、
これらのお陰で記述量がかなり削減できていると思います。

組み込み Tomcat が良い

Java の Web アプリケーションというと Tomcat を用意して、EclipseTomcat プラグイン入れて…と色々面倒ですが、
Tomcat が組み込まれていることで、プレーンの Java を実行すればよいのはとても楽でした。
リリース時等も楽なのだろうなと思いながら、実装していました。

Spring Loaded が良い

Spring Boot の利点とは少し違いますが、Spring Loaded が結構効いてくれて、アプリケーションサーバの再起動回数が大幅に削減できました。 再起動自体も組み込み Tomcat のお陰で楽ですし。

気になった点

記述量が多い

上記の記述量が少ないと全く反対のことを書いていますが、Spring Boot として記述量が多いのではなく、Java として。
やっぱり Ruby と比べてると記述量が多いなって感じます。
今回あえて lombok を使わなかったのですが、lombok 使わないとこんなに書かなきゃいけないのかと思いました。
静的型付け言語なので、安心感は大きく得られるのですけどね。

以上です。
次回からは Spring Boot から少し離れようと思います。

Java Spring Boot その7

今回は DB 実装部分のソースを紹介します。

エンティティ

package hello.domain;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "tasks")
public class Task {

    @Id
    @GeneratedValue
    private Integer id;

    @Column(nullable = false)
    private String context;

    public Task() {
    }

    public Task(Integer id, String context) {
        this.id = id;
        this.context = context;
    }

    public Integer getId() {
        return id;
    }

    public String getContext() {
        return context;
    }
}

@Entity

エンティティクラスを表すためのアノテーション

@Table(name = "tasks")

エンティティに紐づくテーブルを指定するアノテーション。デフォルトではエンティティ名がクラス名となります。

@Id

主キーのフィールドを表すアノテーション

@GeneratedValue

主キーが自動採番されることを表す。生成方法を指定することもできる。

@Column(nullable = false)

カラムに対する設定を行うアノテーション
nullable = false と設定し、Not Null であることを設定。

リポジトリ

package hello.repository;

import org.springframework.data.jpa.repository.JpaRepository;

import hello.domain.Task;

public interface TaskRepository extends JpaRepository<Task, Integer> {

}

JpaRepository を継承したインターフェースを作成するだけで、取得、登録、更新、削除ができるようになります。(すごい!)
JpaRepository<Task, Integer> のようにジェネリクスに対象のエンティティ、IDカラムのデータ型を宣言し、終了です。

サービス

package hello.service;

import java.util.List;

import javax.transaction.Transactional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import hello.domain.Task;
import hello.repository.TaskRepository;

@Service
@Transactional
public class TaskService {
    @Autowired
    TaskRepository taskRepository;

    public List<Task> findAll() {
        return taskRepository.findAll();
    }

    public Task findOne(Integer id) {
        return taskRepository.findOne(id);
    }

    public Task create(Task task) {
        return taskRepository.save(task);
    }

    public Task update(Task task) {
        return taskRepository.save(task);
    }

    public void delete(Integer id) {
        taskRepository.delete(id);
    }
}

@Service

サービスクラスを表すアノテーション

@Transactional

トランザクション境界を設定するアノテーション
サービスに設定したため、トランザクション境界はサービスとなります。

@Autowired

DI (依存性注入) を行うためのアノテーション
Autowired アノテーションを記述すると、自動的に DI を解決してくれます。

サービスクラス自体はリポジトリクラスの内容を呼び出す形なので特に説明はいらないと思います。

コントローラ

package hello;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import hello.domain.Task;
import hello.service.TaskService;

@RestController
@RequestMapping("api/tasks")
@EnableAutoConfiguration
@ComponentScan
public class RestSampleController {

    @Autowired
    TaskService taskService;

    @RequestMapping(method = RequestMethod.GET)
    List<Task> getTasks() {
        // GET api/tasks で実行されるメソッド
        return taskService.findAll();
    }

    @RequestMapping(value = "{id}", method = RequestMethod.GET)
    Task getTask(@PathVariable Integer id) {
        // GET api/tasks/{id} で実行されるメソッド
        return taskService.findOne(id);
    }

    @RequestMapping(method = RequestMethod.POST)
    @ResponseStatus(HttpStatus.CREATED)
    Task createTask(@RequestBody Task task) {
        // POST api/tasks で実行されるメソッド
        return taskService.create(task);
    }

    @RequestMapping(value = "{id}", method = RequestMethod.PUT)
    Task updateTask(@PathVariable Integer id, @RequestBody Task task) {
        // PUT api/tasks/{id} で実行されるメソッド
        return taskService.update(task);
    }

    @RequestMapping(value = "{id}", method = RequestMethod.DELETE)
    @ResponseStatus(HttpStatus.NO_CONTENT)
    void deleteTask(@PathVariable Integer id) {
        // DELETE api/tasks で実行されるメソッド
        taskService.delete(id);
    }

    public static void main(String[] args) {
        SpringApplication.run(RestSampleController.class, args);
    }
}

大きな変更はなく、モックデータを削除して、サービスクラスからデータを取得するようにした位です。

@ComponentScan

必要なコンポーネントを自動で読み込んでくれるアノテーションです。
これによりサービス、リポジトリ、エンティティクラスが読み込まれます。

以上で DB 実装部分は終了です。