Github の API を使って GraphQL を触ってみる

先日社内の勉強会で Github API を使って GraphQL を触ってみたので、ちょっとまとめてみようと思います。

参考にしたリンク

GraphQL のチュートリアルを見ながら、各文法を Github GraphQL API の Explore を使って実際にクエリを組んでレスポンスを見ていきます。

手始めに

まずは簡単なクエリから

query {
  viewer {
    name
  }
}

こういうクエリを投げてみると

{
  "data": {
    "viewer": {
      "name": "Yohei Honda"
    }
  }
}

こんな感じのレスポンスが返ってきます。 リクエストのクエリと同じような感じで返ってきてわかりやすい。

複数の field を取得してみる

query {
  viewer {
    name
    bio
    location
  }
}

レスポンスは以下の通り。

{
  "data": {
    "viewer": {
      "name": "Yohei Honda",
      "bio": "Ruby developer",
      "location": "Japan, Tokyo"
    }
  }
}

Alias

次に Alias を設定してみます。 例えば、使うときに Viewer だとちょっとわかりにくいかもしれないので myself っていう Alias を貼ってみます。

query {
  myself: viewer {
    name
    bio
    location
  }
}

レスポンスは以下。

  "data": {
    "myself": {
      "name": "Yohei Honda",
      "bio": "Ruby developer",
      "location": "Japan, Tokyo"
    }
  }
}

join みたいな感じ

次に、SQL の join みたいなことをしてみます。 ここでは Viewer(User) のリポジトリの最初の一件の name と description を取ってきます。

クエリ

query {
  viewer {
    repositories(first: 1) {
      edges {
        node {
          name
          description
        }
      }
    }
  }
}

レスポンス

{
  "data": {
    "viewer": {
      "repositories": {
        "edges": [
          {
            "node": {
              "name": "design-pattern-study",
              "description": "デザパタの勉強用リポジトリ"
            }
          }
        ]
      }
    }
  }
}

Fragment

次にフラグメント機能です。 フラグメント機能はクエリの一部を関数みたいに切り出すことが出来る機能です。

クエリ

query {
  viewer {
    ...userBasicFragments
  }
}

fragment userBasicFragments on User {
  name
  bio
  location
}

レスポンス

{
  "data": {
    "viewer": {
      "name": "Yohei Honda",
      "bio": "Ruby developer",
      "location": "Japan, Tokyo"
    }
  }
}

変数

GraphQL では変数も使えます。

クエリ

query Sample($repo_count: Int!) {
  viewer {
    repositories(first: $repo_count) {
      edges {
        node {
          name
          description
        }
      }
    }
  }
}

で変数は以下

{
  "repo_count": 1
}

レスポンス

{
  "data": {
    "viewer": {
      "repositories": {
        "edges": [
          {
            "node": {
              "name": "design-pattern-study",
              "description": "デザパタの勉強用リポジトリ"
            }
          }
        ]
      }
    }
  }
}

Directives

GraphQL ではクエリ内で if とかも使えちゃいます。

クエリ

query Sample($get_repositories: Boolean!) {
  viewer {
    name
    repositories(first: 1) @include(if: $get_repositories) {
      edges {
        node {
          name
          description
        }
      }
    }
  }
}

で引数

{
  "get_repositories": false
}

レスポンス

{
  "data": {
    "viewer": {
      "name": "Yohei Honda"
    }
  }
}

クエリ上 repositories を取ってきていますが、 get_repositories という引数が false なので 取ってこないようになっています。

感想

色々機能もあるし、API を使う側としては柔軟にいろいろできそうでいい感じですね。触ってて楽しい。 一方で API を用意する側は GraphQL のパーサーを用意したりしなきゃいけないので、結構大変なのかな。 そこら辺もライブラリやフレームワークが揃ってくればいいのかもしれませんが、用意する側の敷居はやはり高い気もしました。

CodeClimate をチームで導入してみた

この記事は Sansan Advent Calendar 2015 の 16 日目です。

CodeClimate をチームで導入してみたのでその経緯や良かった点などを書きたいと思います。

もともとの課題

ある日の会話

よくチームの KPT とかで、こんな会話がありました。

A 「最近コード汚くなってきましたねー」

B 「うーん、そうだね、言われてみれば確かに」

C 「・・・そう?」

コードの汚さは人によって価値観が違う

チームとして「コードは綺麗であるべき」という価値観はみんな持てているとは思います。 一方で、サービスのために一旦ある程度汚くても動くものをスピードを持って作らなければならないことはよくある話です。 (もちろん綺麗なコードをスピードを持って作れるようにしていく努力は惜しまないことを前提に。)

問題は、今どれくらい汚くなっていて、チームとしてどれくらい課題なのか、という共通認識を持つのがなかなか難しいということでした。

そこで CodeClimate

きっかけは、僕らのチームが Slack (有償)を導入し始めて、僕が個人的な興味で「もっと Slack の Integration 活用したいなぁ」と思って Integration の一覧を見て面白いサービスないかなぁと見ていて CodeClimate を見つけたことでした。

前述のような課題もあり、ちょっと高いけど入れてみようか、ということが部内で合意取れたので使ってみました。

CodeClimate とは

https://codeclimate.com/

簡単に言うと、

  • コードをファイル単位、リポジトリ単位でスコアリングしてくれる
  • コードの汚そうなところを Issue として管理してくれる

というものです。

対応している言語はざっと Document を見る限り

なんかが対応しているみたいです。

僕らのチームは Ruby で開発しているので、これ以降は Ruby における話をします。

お値段

まず、Public リポジトリは、他のサービスとかと同じように無料で利用できます。 Private リポジトリは、僕らが導入した頃はリポジトリ数でプランが分かれていて、利用していたのは 10 リポジトリまで $ 199 というプランでしたが、 最近変わったようで、今はユーザー数で課金がされるようになったようです。

https://codeclimate.com/pricing

CodeClimate の始め方

すごく簡単です。

  • CodeClimate に Sign Up する(Github アカウントでログインできます)
  • 解析したいリポジトリを選択する

これだけです。

どんなかんじか

CodeClimate の管理画面

f:id:yohei1229:20151223093013p:plain

こんな感じです。 リポジトリ全体のスコアは週次でメールでもお知らせしてくれます。

Github Pull Request Integration

CodeClimate には Github Pull Request Integration も用意されていて、 利用するとこんな感じに PR 上で新しい Issue の状況を教えてくれます。 f:id:yohei1229:20151223093636p:plain

Slack Integration

Slack Integration を利用すると、default ブランチにマージされる度に、スコアの変更を教えてくれます。

あと、CodeClimate はこの情報も含めて Feed も提供してくれていて、僕らのチームでは Slack の RSS Integration を利用して この Feed を Slack に流すようにしています。 f:id:yohei1229:20151223093511p:plain

使ってみた感想

CodeClimate のスコアをキーに、綺麗になった、汚くなったみたいな話がチームでされるようになった

こんな感じに、スコアを上げるモチベーションにもなりつつあります。

f:id:yohei1229:20151223093119p:plain

急いで作らないといけなくて汚く書いてしまっているのがわかって、逆に罪悪感が。。。

みたいな意見もありました。 やっぱりコードを綺麗にするうえで、元々の設計の良くない部分とかで、致し方なく汚くなってしまうケースなどもあって、 課題感が明確になって良くもありますが、そこをどうチームで捉えていくかというところはまだまだこれから考えていかなきゃいけないです。

やっぱり、機械にやらせられることは機械にやらせるべき

これは CodeClimate だけの話ではないですが、昨今システム開発に関する良いサービスがいろいろ出てきている中で、 適材適所で導入して、チームの課題解決や開発の効率化していくことは良いことだなぁと思いました。

余談

今回の記事で、過去の CodeClimate のスクショ撮ってて気づきましたけど、スコア悪くなってるスクショが多かったなぁ。。。

Electron で Remotty のクライントアプリ作ってみた

僕らのチームではこの前の神山.rb(https://kamiyamarb.doorkeeper.jp/) をきっかけに知った Remotty を使ってみています。 リモートワーカーがいるチームとしてはすごく良いツールです。

ただ、Slack のように常時立ちあげて利用するほど重宝しているツールであるため、 僕個人の使い方的に、ブラウザの 1 タブとして立ち上げるのではなく、クライアントアプリみたいな感じに利用したいと思いました。

そこで、最近興味を持っていた Electron で簡単なクライアントアプリを作ってみました。 作ってみたアプリは https://github.com/yonda/electron-remotty で公開しています。

前提

node は npm は入っていること。

導入

以下のコマンドで Electron をインストールします。

$ npm -g install electron-prebuilt

で、プロジェクト用のディレクトリをきって、npm init

$ mkdir electron-remotty
$ cd electron-remotty
$ npm init -y

package.json の main は main.js としておきます。

main.js の実装

まずは https://github.com/atom/electron/blob/master/docs/tutorial/quick-start.md 通りに main.js を作っていきます。

'use strict';

var app = require('app');
var BrowserWindow = require('browser-window');

require('crash-reporter').start();

var mainWindow = null;

app.on('window-all-closed', function () {
  if (process.platform != 'darwin') {
    app.quit();
  }
});

app.on('ready', function () {
  mainWindow = new BrowserWindow({width: 1280, height: 800});

  mainWindow.loadUrl('file://' + __dirname + '/index.html');
  mainWindow.on('closed', function () {
    mainWindow = null;
  });
});

僕は MacBookPro の 13 inch をつかっていて、ウィンドウサイズはフルサイズで起動したかったので width と height だけ少し変えています。

index.html の実装

ここは Quick Start とは少し変えていて、webview で remotty を表示するようにしています。 また、style タグで webview のサイズをウィンドウサイズに合わせるようにしています。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>remotty</title>
  </head>
  <body>
    <script>console.log("Hello World");</script>
    <webview id="mainWebview" 
      src="https://www.remotty.net" autosize="on"></webview>
    <style>
       webview {
         position: absolute;
         top: 0;
         right: 0;
         bottom: 0;
         left: 0;
       }
     </style>
  </body>
</html>

<script>console.log("Hello World");</script> という変な記述がありますが、これは後ほど説明します。

起動

あとは起動するだけで、一通り完成です。

$ electron .

すごく簡単。デスクトップ通知もこれだけで出てくれます。

ハマった点

ここはちょっと良くわかっていないのですが、何故か webview を追加しただけだと何も表示されませんでした。 調べてみると、何かしら script タグがないと動いてくれないらしい。 ので、何かしら script が必要になるまでは <script>console.log("Hello World");</script> みたいに適当な script を突っ込んでおきました。

参考

http://qiita.com/yoching/items/b1eba88f75320a61bcc6 https://github.com/atom/electron/issues/1117

いろいろ便利にしたこと

コピペやリロードができるようにした

このままではコピペやリロードは出来ないので、https://github.com/atom/electron/blob/master/docs/api/menu.md を参考に、コンテキストメニューを追加しました。 (追加した、というより会社の後輩に PR もらった。感謝。)

main.js

app.on('ready', function () {
  // 省略
  setApplicationMenu();
}

function setApplicationMenu (){
  var Menu = require('menu');
  var menu = Menu.buildFromTemplate([
    {
      label: "Application",
      submenu: [
        {label: "About Application", selector: "orderFrontStandardAboutPanel:"},
        {type: "separator"},
        {label: "Quit", accelerator: "Command+Q", click: function() { app.quit(); }}
      ]
    },
    {
      label: "View",
      submenu: [
        {
          label: 'Reload',
          accelerator: 'CmdOrCtrl+R',
          click: function(item, focusedWindow) {
            if (focusedWindow)
              focusedWindow.reload();
          }
        },
      ]
    },
    {
      label: "Edit",
      submenu: [
        { label: "Undo", accelerator: "Command+Z", selector: "undo:" },
        { label: "Redo", accelerator: "Shift+Command+Z", selector: "redo:" },
        { type: "separator" },
        { label: "Cut", accelerator: "Command+X", selector: "cut:" },
        { label: "Copy", accelerator: "Command+C", selector: "copy:" },
        { label: "Paste", accelerator: "Command+V", selector: "paste:" }
      ]
    }
  ]);
  Menu.setApplicationMenu(menu);
}

これでコピペやリロードができるようになりました。

リンクをクリックしたらデフォルトブラウザで開くようにした

このままでは外部リンクが開けなかったので、https://github.com/atom/electron/blob/master/docs/api/web-view-tag.md を見つつ対応してみました。

index.html

<script>
onload = function() {
  var webview = document.getElementById("mainWebview");

  webview.addEventListener('new-window', function(e) {
    require('shell').openExternal(e.url);
  });
}
</script>

使ってみての感想

Electron すごく便利。今回みたいな WebView 貼るだけだったらほんと 30 分あればできるし、ここからクライアントアプリっぽくいろいろカスタマイズも出来そう。いろいろ試してみたい。

神山.rb に参加してきました

神山.rb

徳島県神山町で開催された勉強会に縁あって参加してきました。

kamiyamarb.doorkeeper.jp

話したこと

rails-api について

www.slideshare.net

リモートワークについて

訳あって非公開

所感

リモートワークをテーマにしたセッションもあることから、今回はリモート参加も OK ということで、skype + remotty でリモート参加者とつなげながら勉強会を実施しました。 僕はどちらかというと主催者側にいたのですが、リモート周りのセッティングでトラブルが続き(会場の wifi が使えない、skype で大規模障害が発生する etc...)結構大変でしたが、結果的にリモートの大変さを実感して頂けたり、remotty や appear.in などのリモートツールを参加者の方に使って頂くいい機会になったり、最後はリモート参加者も含めての集合写真を撮って一体感を感じられたり、非常にいい経験ができました。次回あればまた参加したい。

remotty www.remotty.net

あと、二次会の Café on y va さんは本当にご飯が美味しかったです。

Café on y va(カフェ オニヴァ)

virtus-dirty_attribute について

最近、仕事上の Ruby プロジェクトで、"モデルに変更があった時だけそのモデルに対して update したい" みたいな要望があって、いろいろ調べたのですが、最終的に virtus-dirty_attribute というのを使うことになった。

virtus-dirty_attribute の使い方

https://github.com/ahawkins/virtus-dirty_attribute

README にも書いてあるけど、

class Hoge
  include Virtus.model
  include Virtus::DirtyAttribute

的な感じに include すると、hoge.dirty? とか hoge.clean? とかで変更があったかどうかが見れるという代物。

へー、便利だなー、でも Rails さんそこらへんやってくれないのかしら?と思っていたら、やっぱり一応あった。それが ActiveModel::Dirty。

ActiveModel::Dirty

http://api.rubyonrails.org/classes/ActiveModel/Dirty.html

なにやら define_attribute_methods とか定義したり、各 attribute のメソッドを定義して hogehoge_will_change とか呼んであげたりと、結構色々やってあげなきゃいけなそう。。。

ActiveRecord 使っているとそこら辺勝手にやってくれるらしく、ActiveRecord のモデルでやる文には便利そう。

というわけで

今回 "モデルに変更があった時だけそのモデルに対して update したい" ということがしたかったのは、たまたま ActiveRecord のモデルじゃなかったので、virtus-dirty_attribute で行くのが良さそう。

rbenv 環境下で Atom(Github) で Rspec を動かしてみた

ちょっと前に Atom のβ版の招待申請を出していて、 「Welcome to Atom」というメールが届いて、使ってみたらなかなかいい感じ。

atomRspec のPackage があったので、ちょっと欲が出て install して atomRspec 実行できるようにしたところ、

$ command not found 'rspec'

的なエラーが出て Rspec が実行できなかった。

むーなんで?と思って調べていたところ、 https://github.com/fcoury/atom-rspec/issues/10 の Issue があって、読んでみたら、

Atom uses bash. so, you have to setup .bash_profile/.bashrc as https://github.com/sstephenson/rbenv

と書いてあるではありませんか!

たしかに、この Issue の人同様、私は zsh ユーザーだったので、 .bashrc に rbenv の記述をしたところ、Rspec 実行できた! 勝手にペインを分割してテスト結果も出してくれていい感じ。

sublime text でもできていたことだけど、エディタ内でテストが実行できるとか、 ちょっと感動しますね。

Timecopを使ってみた

概要

ReadMeに、

A gem providing "time travel" and "time freezing" capabilities, making it dead simple to test time-dependent code. 
It provides a unified method to mock Time.now, Date.today, and DateTime.now in a single call.

とあるように、時間を止めたり、時間を進めたりすることができるライブラリ。 中の動きとしてはTIme.now とか、Date.today とかを指定した日時でモックしたり、時間のを進まないようにしてくれる。

Github

使い方

時間を止める

now = Time.Now  # => 2014-01-26 14:18:46 +0900

# 時間を止める
Timecop.freeze(now)

p Time.now # => 2014-01-26 14:18:46 +0900

時間を特定に日時に変更する

t = Time.local(2012, 1, 1, 0, 0, 0)

# 現在時刻をtに変更する
Timecop.travel(t)

sleep(1)
p Time.now # => 2012-01-01 00:00:01 +0900

1秒で進む秒数を変更する

# scaleを10secに変更する
Timecop.scale(10)

5.times.each do
  p Time.now
  sleep(1)
end

# => 2014-01-26 14:13:10 +0900
# => 2014-01-26 14:13:20 +0900
# => 2014-01-26 14:13:30 +0900
# => 2014-01-26 14:13:40 +0900
# => 2014-01-26 14:13:50 +0900

do ~ end で括るか、Timecop.returnを呼ぶとこれらは解除される模様。