ushumpei’s blog

生活で気になったことを随時調べて書いていきます。

自作Vimプラグインの置き場所と置き方

先日プラグインを作成してみた記事を公開しましたが、その記事のコメントで tyruさん からご指摘いただいた(とてもありがたいです)、プラグインのインストール場所と方法について整理しておきます。

分類

そもそも自分で書いたVim スクリプトVimに読んでもらうためには次の3つが考えられると思います。

  1. プラグインとして配置する
  2. パッケージとして配置する
  3. プラグインマネージャーを使用して配置する

ここではデバッグのことを考えて自力で配置する方法「1. プラグインとして配置する」「2. パッケージとして配置する」に限定して説明していきます。なので、「3. プラグインマネージャーを使用して配置する」に関しては書きません(プラグインマネージャーのリファレンスを読んだ方がわかると思います。私は dein.vim 使っています)

前提

プラグインリポジトリpluginautoload の2つのソースファイルディレクトリを持っていることを前提とします。自分が作ったプラグイン mdtable を例にすると、次のようになっています。

.
├── autoload
│   └── mdtable.vim
└── plugin
    └── mdtable.vim

プラグインとして配置

Vimには2種類のプラグイン「グローバルプラグイン」、「ファイルタイププラグイン」があります。ここではクローバルプラグインについて説明します。

Vimが起動時に読み込むプラグインディレクトリはシステムごとに異なります。以下の表は:help add-global-pluginから見ることができます。

system plugin directory
Unix ~/.vim/plugin/
PC や OS/2 $HOME/vimfiles/plugin or $VIM/vimfiles/plugin
Amiga s:vimfiles/plugin
Macintosh $VIM:vimfiles:plugin
Mac OS X ~/.vim/plugin/
RISC-OS Choices:vimfiles.plugin

注意: PC や OS/2Windows用なのですね

参考: プラグインの追加 | :help plugin

私が作成したプラグインを配布する場合は以下のようになります。

$ cd ~/.vim
$ mkdir plugin                                                                   # pluginディレクトリがなければ実行する
$ cd plugin
$ git clone git@github.com:ushumpei/mdtable-vim.git

パッケージとして配置

Vim 8から使えるようになったパッケージ機能を使って配置します。パッケージは複数のプラグインをまとめるのにも使用できますが、ここでの例ではプラグイン1つを配置するためにパッケージを作成します。

パッケージディレクトリは~/.vim/packになります。この下にパッケージを置いていきます。パッケージ名のディレクトリを作成し、その下にstartというディレクトリを作ってください。その中にプラグインを入れていきます。パッケージのディレクトリ構成は以下のようになることが:help package-createから見ることができます。

.
├── start/foobar/plugin/foo.vim          " 常にロードされ、コマンドを定義する
├── start/foobar/plugin/bar.vim          " 常にロードされ、コマンドを定義する
├── start/foobar/autoload/foo.vim        " fooコマンドを使用した時に読み込む
├── start/foobar/doc/foo.txt             " foo.vimのヘルプ
├── start/foobar/doc/tags                " ヘルプタグ
├── opt/fooextra/plugin/extra.vim        " オプションのプラグイン、コマンド定義
├── opt/fooextra/autoload/extra.vim      " extraコマンドを使用した時に読み込む
├── opt/fooextra/doc/extra.txt           " extra.vimのヘルプ
├── opt/fooextra/doc/tags                " ヘルプタグ

参考: Vimパッケージを作る | :help package-create

プラグイン作成時に pluginautoload などのディレクトリでコードを分けて作った理由がようやく実感できました。

私が作成したプラグインを配布する場合は以下のようになります。

$ cd ~/.vim
$ mkdir pack                                                                     # packディレクトリがなければ実行する
$ mkdir -p pack/mypackage/start
$ cd pack/mypackage/start
$ git clone git@github.com:ushumpei/mdtable-vim.git

まとめ

Vimはドキュメントがとても充実しているため、大変助かります。なのであまりブログに書いても、と思ったりしましたが、自分の勉強を兼ねてこれからも書きます。

【初めてプラグイン作って見た】Markdownのテーブル雛形を作るプラグイン

この記事は Vim Advent Calendar 2017 8日目の参加記事です。

こんにちは。プログラマーをやっています ushumpei と申します。Vim歴は3年くらいです。

今回は、そこそこVimを覚えてきたけれど、 Vimの仕組みをもっと深く理解していきたいと思っている人(自分)向けVim script勉強しました的な記事を書かせていただきました。自分がプラグインを作って見た時に感じたことがつらつらと書いてあります。

プラグインVimversion 8.0 で書きました。もし試していただけた方で、動かないよ!ということがあれば、大変恐縮ですがその旨コメントしていただけると幸いです。(ごめんなさい!バージョンごとの文法の違いは追えてません)

目次

動機

基本的な操作は覚えたけどもっとVimに関して詳しくなりたいというのが動機です。聞いた話によると、 Vim知るにはVim script書くのが早い ということなので勉強し始めました(すごい人たちは書いているし)。せっかくなのでplugin作ってみようと思い、今回はMarkdownのテーブルを作るプラグインを作成することにしました。同じ機能のプラグインはもうすでにいくつかありますが、世の中ほとんどのものはすでに作られているのでしょうがない、ということで練習のつもりで作りました。

リファレンス

基本的な文法については以下のリンクにお世話になりました。

作るもののイメージ

インタフェースとしては :Mdtable コマンドというものを作っていこうと思います。 Markdown table の短縮です。引数に行数、列数を渡すとその行数と列数を持ったテーブルをカーソル位置以降の行に挿入します。

"入力
:Mdtable 2 3

"出力
|   |   |   |
|:--|:--|:--|
|   |   |   |
|   |   |   |

またこのコマンド入力を補助するキーマッピング mdt もデフォルトで設定するようにします。

なので学べることとしては、 コマンドの定義と引数の渡し方キーマッピングカレントバッファへの書き込み引数チェック です。

書いて見た

書いて見ましたushumpei/mdtable-vim。ソースは2つだけで、ディレクトリ構成は以下のようになっています。

.
├── README.md
├── autoload
│   └── mdtable.vim
└── plugin
    └── mdtable.vim

試すには ~/.vim/plugin/ ディレクトリで git clone git@github.com:ushumpei/mdtable-vim.git とかしてもらえる嬉しいです( :echo &rtp でどこに置けばいいかわかるそうです )。 dein.vim とかを使っている方は call dein#add('ushumpei/mdtable-vim') とか書いてもらえてもやっぱり嬉しいです。

追記: 2017/12/09: Vim 8の場合、上記の方法ではなく、~/.vim/pack/mdtable/startディレクトリを作成して、その中でgit clone git@github.com:ushumpei/mdtable-vim.gitしてもらえればいいです。プラグインの配置についてまとめました: 自作Vimプラグインの置き場所と置き方 - ushumpei’s blog

それぞれのファイルの説明

  • autoload/mdtable.vim: 実際の処理を記述しています。 行数、列数を引数にテーブル文字列を生成する関数 と、 テーブル文字列生成関数を呼び出してバッファに書き込む関数 の2つだけです。
  • plugin/mdtable.vim: インタフェースを記述しています。上記の関数をコマンド、キーマップとして登録しています。

f:id:ushumpei:20171203120943g:plain

学んだことの詳細

コマンドの定義と引数の渡し方

if !exists(":Mdtable")
  command -nargs=* Mdtable :call mdtable#write(<f-args>)
endif

:Mdtable を使えるようにするには command でコマンドを定義するのですが、「引数2個だから -nargs=2 だ!」とか書いて若干ハマりました。 :help command-nargs をちゃんと読んでおけばよかったです。。。

キーマッピング

mapの設定方法は複雑に感じました、 :help 41.12 のマップの箇所で説明されているものを真似して書いていきました。

if !hasmapto('<Plug>Mdtable')
  nmap mdt <Plug>Mdtable
endif
nmap <Plug>Mdtable :Mdtable 
  • hasmapto: <Plug>Mdtable に対してマッピングが行われているか確認
  • 2行目はマッピングがなければデフォルトとしてmdtでマップ
  • 4行目は <Plug>Mdtable:Mdtable にマップ

このマップが二回行われる必要性が、ユーザーが独自にマッピングする場合の考慮、だということだとなかなか分からなかったです。<Plug> を使う意味はユーザーが独自にマッピングするためだとリファレンスにも書いてあるんですけどね!

カレントバッファへの書き込み

これは本当にいい方法だったのかわかっていないのですが、以下のように executenormal を使って記述しました。

" 変数tableにはマークダウンテーブル文字列が入っています
execute "normal o" . table . "\<Esc>"

バッファをスクリプトから触っている記事を見たりするのですが、まだそのあたり勉強不足です。

引数チェック

function! s:create(rowNum, colNum) abort
...
  if !(a:rowNum > 0) || !(a:colNum > 0)
    echoerr 'Arguments must be positive numbers.'
  endif

関数の引数をチェックして 正の数字以外を弾く ということがやりたかったのですが良い方法が見つけられなかったです。はじめは type 関数を使って見たのですが、全ての引数で弾かれるようになってしまい軽く混乱、結局 引数は文字列で渡ってくる という話でした。最終的に、 数値との比較の際に文字列が数値に変換される こと、 文字列がうまく数値に変換できないときは0になる こと、の2つを使ってゆるいチェックをおこなっています。

Vim scriptは途中でエラー出ても止まらない力強いスクリプトなので、間違った入力でもなんらかの出力をしてくれちゃうみたいです。書くにあたってそのあたりのエラーハンドリングのために tryabort を使って見ました。

感想

Vim少し使える」と思っていたのですが、全然知らないことが多くて驚いています。 プラグインを書いた後に自分の .vimrc を見返してみると、今まで曖昧で済ましていた部分が違って見えてとても嬉しい です。

僕はもともと、Vim自体の使い勝手を変えないように、プラグインは使わないようにしていたのですが、今回プラグインを書いたことで、各プラグインがどのようにVimに影響を与えているのか少しわかるようになったため、これからは多少使っていってもいいという気持ちになりました。

これからのことで言えば、もう少し実際に役に立つ機能をかいて行きたい感じです。このプラグインで言えば、多分テーブルとか最初からサイズ決めうちより調節機能とかあったほうがいいとか思います。(まあでも、他のプラグインを色々触ってみることから始めます)

拙い文章、内容になりました。 読んでいただいてありがとうございます!!!

Vim Advent Calendar 2017

Apollo Client 2.0 with React関連のライブラリ整理

Apollo Client 2.0 with React関連のライブラリをざっくり整理して見ます。失敗したらごめんなさい。詳細は Apollo Docs | Apollo Docs に書いてあります。

目次

Apollo Client

サーバーとの間で、クエリ発行、データ取得、キャッシングなどしてくれます。2017/10にバージョン2.0がリリースされて、大幅にコードの分離が行われました。キャッシング、サーバーとの通信部分が分離されたため、apollo-client-presetを使用しない場合は明示的に指定する必要があります。React, Angular, Vue, その他多くのJavaScriptFrameworkで、GraphQLクライアントとして使用できるそうです。

使い方はインスタンス化したのち、呼び出したいクエリを渡して実行してもらいます。 React の場合は react-apollo から提供されているコンポーネントインスタンスを渡し、子コンポーネントとクエリをHOCに入れてクエリ発行したりします(後述)。

import { ApolloClient } from 'apollo-client';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { HttpLink } from 'apollo-link-http';

const client = new ApolloClient({
  cache: new InMemoryCache(),
  link: new HttpLink({ uri: 'http://localhost:3000' })
});


// クエリの発行
import gql from 'graphql-tag';

client.query({
  query: gql`
    query {
      channels {
        id
        name
      }
    }
  `,
})
  .then(data => console.log(data))
  .catch(error => console.error(error));

Cache関連

apollo-cache-inmemory

Apollo Clientで使用する標準的なインメモリーキャッシュのライブラリ。分離されました。パッケージとしてはApollo Clientと分かれていますが、ソースの管理はapollo-clientリポジトリで行われているのでそこに行くと読めます。

const inMemoryCache = new InMemoryCache({
  dataIdFromObject,
  cacheResolvers: {
    Query: {
      channel: (_, args) => {
        return toIdValue(dataIdFromObject({
          __typename: 'Channel',
          id: args['id'],
        }))
      },
    },
  },
});

Link関連

ネットワークを柔軟に選択することができます。HTTP通信はapollo-link-httpで行えます。

apollo-linkApollo Linkを継承すれば自分でもネットワークを作成することができます。また、httpwsを場合によって切り分けて通信したい場合に、ApolloLink.splitを使って新たなLinkを記述できます。

import { getOperationAST } from 'graphql';

import { WebSocketLink } from 'apollo-link-ws';
import { HttpLink } from "apollo-link-http";
import { ApolloLink } from 'apollo-link';

import { SubscriptionClient } from 'subscriptions-transport-ws';

const wsLink = new WebSocketLink(new SubscriptionClient('ws://localhost:4000/subscriptions', { reconnect: true }));

const httpLink = new HttpLink({ uri: 'http://localhost:4000/graphql' });

const link = ApolloLink.split(
  ({ query, operationName }) => {
    const operationAST = getOperationAST(query, operationName);
    return !!operationAST && operationAST.operation === 'subscription';
  },
  wsLink,
  httpLink,
);

subscriptions-transport-ws

WebSocketのGraphQLエンドポイントに接続するクライアント SubscriptionClient が入っています。

GraphQL関連

graphql-tag

このライブラリから、GraphQLクエリ文字列を、クエリの構文木(GraphQL.js AST format)に変換するgqlテンプレートタグが使用できます。

import gql from 'graphql-tag';

...

export const channelsListQuery = gql`
  query ChannelsListQuery {
    channels {
      id
      name
    }
  }
`;

GraphQL.js

Facebook製のライブラリで、Queryに対してより詳細な処理をしたい時に使ったりします。上の方のLink関連に出てきている、Query内容を調べて処理を分けるといった時に、このライブラリのgetOperationASTを使ってASTから値を読んでいます。

graphql-tools

サーバー側でschemaとresolverを結びつける関数を提供してくれます(schemaは型やクエリ、ミューテーションの仕様を書くもの、resolverは各仕様の実装を書くものです)。

import { makeExecutableSchema } from 'graphql-tools';

const executableSchema = makeExecutableSchema({
  typeDefs,
  resolvers,
});

React関連

react-apollo

Apollo Client向けのReactライブラリです。ApolloProviderコンポーネントgraphql高階コンポーネントを提供してくれます。ApolloProviderはrootコンポーネントをラップして使用し、Apollo Clientのインスタンスをpropに流してくれます。graphql高階コンポーネントはクエリとオプションとコンポーネントを引数に、新しいコンポーネントを生成します。ラップされたコンポーネントpropsdataというプロパティが追加されます。(クエリがMutationのときはmutate?)

graphql(gql`
  {  query... }
`, { options... }
)(Component)

const Component = ({ data }) => (...);

まとめ

書いているうちに未整理なことに気がついてしまいました。GraphQLの話をしているときは、schema、resolverって使っていいのか心配です。Apolloだけだったりする?Relayは触っていないです。。。

読み物

人にオススメ読み物を勧められるように、読み物のリストをまとめておきます。知人アフィリエイトです。地獄のような人間関係です。

不完全にしておよそ正しくないプログラミング言語小史

不完全にしておよそ正しくないプログラミング言語小史

とても好きです

Fine Software Writings

Fine Software Writings

モチベーションを上げたい時に読みます

Joel on Software

Joel on Software -

他の開発者の頭の中が知れていいです

リーダブルコード

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)

綺麗なコードってなんだっけ、という時に読みます

情熱プログラマー

情熱プログラマー ソフトウェア開発者の幸せな生き方

情熱プログラマー ソフトウェア開発者の幸せな生き方

人生について悩んだ時に読みます

SOFT SKILLS

SOFT SKILLS ソフトウェア開発者の人生マニュアル

SOFT SKILLS ソフトウェア開発者の人生マニュアル

人生について悩んだ時に読みます

ハッカーと画家

Hackers and Painters

やる気出したい時に読みます

リーン・スタートアップ

リーン・スタートアップ

リーン・スタートアップ

サービス作りたい時に読みます

ノンデザイナーズ・デザインブック

自分が作ったページがダサい時に読みます

ノンデザイナーズ・デザインブック [第4版](リフロー版) で読みましたが、リフローだと画面崩れて見難かったです。(リフローの意味を履き違えていなければ、通常版だと画面崩れないはず)

徳丸本

セキュリティ関連で忘れていることがあった時に読みます

徳丸浩のWebセキュリティ教室(日経BP Next ICT選書) も気になりますが読んでないです

サーバー/インフラを支える技術

[24時間365日] サーバ/インフラを支える技術 ?スケーラビリティ、ハイパフォーマンス、省力運用 (WEB+DB PRESS plusシリーズ)

[24時間365日] サーバ/インフラを支える技術 ?スケーラビリティ、ハイパフォーマンス、省力運用 (WEB+DB PRESS plusシリーズ)

なんかサーバーってたくさんあって大変そうだよねという時に読みます

まとめ

随時更新していきます。

CLIの豆知識

友人に教える必要があったのでメモします。

概念

  • CLI(Command Line Interface) プログラマーとかのラップトップをのぞくと画面に表示されている黒い背景のウィンドウがそれです。ユーザーがコンピューターに何かさせたい時に、コマンドを実行できるインターフェースのことです
  • GUI(Graphical User Interface)CLIじゃないやつ。コマンドを使用しないで視覚的にマウスだったり指だったりで操作するインターフェースです。ブラウザとかエクセルとかGUIアプリケーションです。
  • Terminal.appMacに標準でインストールされているCLIアプリケーション。Applications > Utilities > Terminal.app から開けます。

コマンド

主要なコマンドは以下の通りです。コマンドの実行はコマンド入力後 Enter キーを押してください。入力途中で tab キーを押すと文字列を保管してくれて便利です。

  • pwd: 現在のディレクトリの位置を確認
  • ls: 現在のディレクトリに含まれているファイルの一覧を表示
  • cd ディレクトリ名: ディレクトリの移動
  • exit: ターミナルを閉じます
  • open ファイル名: ファイルを開きます。GUIで操作したくなったらこれを使う感覚です。

lsに関するTips

  • ls -l: ファイル一覧の詳細表示
  • ls -la: 隠しファイルも含めたファイル一覧の詳細表示

cdに関するTips

openに関するTips

カーソル

ターミナルではマウスによる操作が制限されているため、カーソルの移動ショートカットを覚えると便利です。ターミナルだけでなく色々な場面でこのショートカットは使えるので生産性が向上します。(emacsキーバインドと言います。ほとんどのアプリケーションの入力箇所でこのショートカットは有効です)

  • Ctrl + f: 行末方向へ移動
  • Ctrl + b: 行頭方向へ移動
  • Ctrl + e: 行末へ移動
  • Ctrl + a: 行頭へ移動
  • Ctrl + d: カーソル位置の文字を削除
  • Ctrl + h: カーソルのひとつ前の文字を削除
  • Ctrl + k: カーソル位置の文字から行末までの文字を全て削除
  • Ctrl + u: カーソル位置の文字から行頭までの文字を全て削除

入力ミスして一旦全てのコマンドを消したい時には、Ctrl + u または Ctrl + a + Ctrl + kを使ったりします。(Ctrl + cでも同じことができますが、これはコマンドキャンセルのショートカットなので、手癖にならないようにしてます)

履歴

実行したコマンドは全てではないですが履歴に残ります。履歴を使うことで打ち間違えなどのミスを減らすことができます。保持されている全ての履歴を表示するコマンドはhistoryです。

  • Ctrl + p: ひとつ前に入力したコマンドを表示します(連続して入力することでどんどん遡っていくことができます)
  • Ctrl + n: ひとつ後に入力したコマンドを表示します(遡りすぎた時に戻るために使います)
  • Ctrl + r: 履歴検索対話プログラムが起動し、入力した文字に部分マッチする直近の履歴を表示します。結果が正しくて実行したい場合はEnter、正しいけど実行したくはない場合はCtrl + aなど何か他のショートカット、正しくない場合はCtrl + rでさらに遡る、もうやめたい場合はCtrl + cでキャンセルします。

まとめ

快適に使うための豆知識をまとめて見ました。多分知ってるよとか、これ足りないとかあると思いますが、とにかく言いたいことはemacsキーバインドがものすごく便利だということです。僕はプログラミング始める時にvimemacsで迷って結局vimにしたのですが、その時に軽くemacs触っていたおかげで、たくさんのアプリケーションで快適な入力体験ができています。

あとCLIのショートカットとか便利な使い方は友達とか先輩から教えてもらったことが多いので、自分も誰かに教えたくなったというのも動機です。

GraphQLに関するメモ

GraphQLについて調べたことのメモです。

GraphQLはFacebookによって考案・開発されたクライアントアプリ向けのクエリ記述言語です(仕様)。モバイルアプリ向けの使いやすいAPIを実現するために考えられていて、2015年にオープンソースになって騒がれ始めました。Facebook, GitHub, Pinterest…など大規模な方々が使っているそうです。

思想としてはRESTにかけている柔軟性をAPIに持たせるために作られていて、 単一のエンドポイントから必要なデータを必要な分、明示的に取ってくることができるようになっています。うまくやればデータ通信量を減らせる上にいろいろメリットがあったりします。

概要

仕様によるとGraphQL以下のコンセプトでデザインされているそうです。私の意訳、ひどいので感じを掴むくらいで読んでください。また何かいいものがあれば教えていただきたいです。

  • 階層型データ構造(Hierarchical):
    • クエリ自体が階層を持っているので、レスポンスと構造が一致して理解しやすいし、検索条件なども記述しやすい。
  • 製品中心(Product‐centric):
    • フロントエンドとフロントエンドエンジニアファースト。
  • 強い型付け(Strong-typing)
    • クエリが定義された型によって安全に実行されることが保障されます。
  • クライアント主導の問い合わせ(Client‐specified queries)
    • 従来のAPIはサーバー側が何を送るか決めていましたが、クライアント側がフィールド単位で何が欲しいかで問い合わせることができます。
  • 内省的(Introspective)
    • GraphQLサーバーはGraphQLで問い合わせできるようになっている(サーバーに型定義を問い合わせることができるので、その意味かと思います?)

GraphQLのクエリとレスポンスのイメージ

例えばブログサイトのAPIとかでユーザーの記事のタイトルを一覧表示しようと思った場合、RESTだとユーザーに紐付く記事一覧をGETするエンドポイント(/users/:id/postsとか、または/posts?user=id)を作って、そこを叩いて返ってきたレスポンスをクライアント側でフィルタリングするかと思いますが、GraphQLだと単一のエンドポイントで記事名の一覧のみを取得できるクエリがかけます。

query {
  user(id: "1") {
    name
    posts {
      title
    }
  }
}

上のクエリをクライアントから発行すると、クエリとそっくりな構造の以下のレスポンスが返ってきます。

{
  "data": {
    "user": {
      "name": "ushumpei",
      "posts": [
        { title: "GraphQLの概要" },
        { title: "Relayで遊ぶ" }
      ]
    }
  }
}

キーワードの説明

サーバーにスキーマと呼ばれる形で、API定義を行います。以下のキーワードを用いて定義していきます。

  • 型定義
    • Type
    • Interface
  • 操作定義
    • Query
    • Mutation
      • Fragment (補助)
      • Input (補助)

Type

型の定義を行います。

type User {
  id: String
  name: String
  posts: [Post]
  ...
}

Interface

型から継承できるインタフェースも定義できます。フィールドとしてインタフェースの型を指定するような、以下のような使い方ができます。

interface Media {
  url: String
}

type Image impliments Media {
  url: String
  width: String
  ...
}

type Audio impliments Media {
  url: String
  playTime: Int
  ...
}

type Post {
  title: String
  media: Media // Image, Audio両方をフィールドに持てる。この場合インタフェースで定義されているurlのみ取得可能。
  ...
}

Query

受け付けるクエリを定義します。

type Query {
  user(id: String): User
  users: [User]
}

この定義に対しては以下のようなクエリをクライアントから発行できるイメージです。Userのどのフィールドを取ってくるとかは自由です。

query {
  user(id: "1") {
    id
    name
    profile
  }
}

Mutation

更新を受け付けるためのインタフェースを定義します。

type Mutation {
 createUserPost(userId: String!, title: String!, body: String!): Post
}

クライアントからは以下のような問い合わせが想定できます。

mutation {
  createUserPost(userId: "1", title: "GraphQLの概要", body: "GraphQLについて...") {
    id
    title
  }
}

(補助)Fragment

クライアント側からレスポンスを記述しやすくするために、複数のフィールドをひとかたまりに断片化してQuery、Mutationに記述できます。

fragment basicInfo on User {
  id
  name
  profile
}

query {
  user: {
    ...basicInfo
  }
}

(補助)Input

Query、Mutationに対して引数のセットを定義できます。先ほどのMutationは次のように書き直せます。(作成、更新とかで引数をまとめられるのと、type Mutationが読みやすくなると思います)

input PostInput {
  userId: String!
  title: String!
  body: String!
}

type Mutation {
 createUserPost(post: PostInput!): Post
}

とりあえず試すには?

チュートリアルを公開してくださっている記事があるのでやって見たら少し解りました。 ただしApolloClientのバージョンが2になりnetworkInterfacecustomResolversなど消えた機能があるのでバージョン気をつけてください、バージョン

参考: All you need to know about Apollo Client 2

問題

あくまでクエリ記述言語なので、どのようにデータを取得するかは自分で書く必要があります。データストレージとして何を採用するかなども考える余地しかない感じです。

とりあえずは、graphql-server-express(apollo-server-expressに変わった?)とかで練習できますが、データ保持はオンメモリしかやっていないです。

外部ホスティングサービスとしてはざっと検索すると、scaphold.io, graph.cool, meldioとかありますが、まだ使って見ていないのでなんとも言えません。

感想

GraphQL旨味としては クライアントがなんのデータを取得しているか楽に明示できることだと思います。クエリの形を見れば何を取ってきているかわかります。

RESTでも取得するフィールドをクエリパラメータで指定して取ってこれるようにすれば取得しているデータを明示できますが、検索条件とかも色々絡んでくるとクエリ自体がかなり長くなって脳に負担になってきます。

またその場合ドキュメンテーション必須です。それに比べるとGraphQLは共通認識化しやすそうですし、定義したスキーマがドキュメントの役割を果たせるので(実際、型定義の際にdescriptionフィールドにmarkdownドキュメンテーションできる)そこも楽になるかと思います。

雑記

  • ライブラリの名前変わったりいろいろモジュール化が盛んなのでもうちょっと頭整理したい
  • GraphQL運用で使って見たスライドとか調べるとある
  • データ永続化はPostGraphQLとかどうなんでしょうか
  • NoSQLでのうまい使い方とか誰か考え済みなんでしょうか。NoSQLのデータ設計方法が全然わかっていないので一度どこかでちゃんと体験しないといけない。
  • 「フィリピン -> ネットインフラ弱い -> でもネット高い -> でもFacebookアクセス無料 -> みんなめっちゃ使う」の裏でGraphQLが活躍しているのかもしれない

Gitについて自分なりに説明してみた

友達に説明する必要があったのでGitの使い方を誤解を恐れずメモしておきます。自分の知識の整理も兼ねています。

この記事ではローカルマシン(一台のPC)での操作で完結しています。リポジトリの作成、コミットの作成、ブランチの作成、マージまでを範囲としています(これらの単語は順を追って説明していけたら、と思います)(ブランチの扱いは、最終的なリポジトリの形としてはgithub-flowです。)

Gitの概要

Gitは 分散型バージョン管理システム です。というとやばく聞こえますが、Gitを使う目的は 「大勢で1つのアプリケーションを一緒に作ること」 です。なのでそのために必要そうなバージョン管理機能がちゃんと備わっています。これあるべきでしょうとか予想しながら読んでいっていただけるといいかと思います。以下チュートリアルを通して概念と機能を説明していきます。

この記事を読み進めるために

  • 環境: Mac (他の環境の場合、概念と機能の説明は問題ありませんが、手順に関して読み替えが必要です)
  • 知識: ターミナルの基本的な操作 (自信がなくなった場合、こちらこちらを適宜参考にしながら読んでいただければ幸いです)

チュートリアルを始める前にちょっとした下準備を行います。

(下準備)gitコマンドがインストールされているか確認

今回は CLI での使い方を説明していきます。他の使い方としては、GUIアプリケーションなど沢山開発されているのでそれ経由で操作するのもありです。(その場合GUIアプリの解説ページに色々書いてあると思うので、この記事は閉じてしまって大丈夫です!)

Macを使っている場合、すでにGitがインストールされているかと思います。アプリケーションフォルダのユーティリティの中にある ターミナル.appを開いて、Gitがインストールされていることを確認してみましょう。ターミナルを開いたらgit --versionと入力して、実行してください。

$ git --version
git version 2.13.6 (Apple Git-96)

git version ...のような表示が出たらインストールされています。

$ git --version
-bash: git: command not found

と出たらインストールされていないので、ググってインストールしてください。

(下準備)Gitの初期設定

以下のコマンドを実行して、Gitのユーザー名、アドレスが登録されていることを確認してください。

$ git config user.name
ushumpei
$ git config user.email
mail@ushumpei.com

登録されていない(実行しても何も表示されない)場合は次のコマンドを実行してユーザー情報を登録しておいてください。

$ git config --global user.name "あなたの名前"
$ git config --global user.email あなたのアドレス

チュートリアル

リポジトリ作成

Gitによるバージョン管理はファイル単位で行われます。と言ってもコンピューターすべてのファイルを管理するわけではなく、管理する範囲は限定することができます。限定できる範囲はディレクトリ(フォルダ)単位で、Gitによって管理されるのはそのディレクトリに含まれているすべてのファイルになります。この限定された範囲を リポジトリ(repository) と呼びます。

実際にリポジトリを作ってみましょう。デスクトップにgit_tutorialリポジトリを作ります。

ターミナルで 1.デスクトップに移動 2.git_tutorialディレクトリの作成 3.新規ディレクトリ内へ移動 4.ディレクトリのgitリポジトリ化 を行います。TABキーでファイル名を補完したりして頑張ってください。

~$ cd ~/Desktop/
~/Desktop$ mkdir git_tutorial
~/Desktop$ cd git_tutorial
~/Desktop/git_tutorial$ git init
Initialized empty Git repository in /Users/ushumpei/Desktop/git_tutorial/.git/

Initialized empty Git repository in ...が出たら成功です。リポジトリが完成しました。

注意: もしかすると日本語環境の場合~/Desktopがないかもしれないです(この辺りは自信ないです)。~/デスクトップなど試してみてください。

git statusというコマンドを実行して見てください。このコマンドは現在のリポジトリの状態を表示してくれるものです。 とてもよく使います 、他のコマンドを打つ際もこのコマンドで実行前、実行後の変化の確認をしながらやると何をしているかわかってくると思います。

~/Desktop/git_tutorial$ git status
On branch master

Initial commit

nothing to commit (create/copy files and use "git add" to track)

各行の意味は以下です。不明な単語については後ほど説明いたします。

  • 現在はmasterという ブランチ(branch)にいる
  • まだ コミット(commit) が存在しない
  • コミットの対象に含まれているものはない

Initial commit

管理下にあるファイルに対して、「誰が、いつ、なぜ、どのファイルの、どこの部分を、どのように変更したか(変更に関する5W1H)」という変更情報を管理するのがGitの大きな役割です。この変更情報は コミット(commit) と呼ばれます。コミットは開発者が好きな粒度で作成することができます、複数ファイルの変更を1つのコミットに含めるのが一般的です。

(コミットのイメージ)
変更理由: モバイルブラウザでトップページの挨拶文の表示が崩れてしまっていたので修正
日付: 2017/11/27
変更者: ushumpei
index.htmlというファイルの12行目を`<p>こんにちは</p>`から`<h1>こんにちは</h1>`に変更
stylesheet/mobile.cssというファイルを追加

コミットを作成する手順は次のようになります;

  • 変更: リポジトリ内でファイルに関する変更を行う(作成、更新、削除のどれか)
  • ステージ: git addgit rmなどのコマンドで、どの変更内容を履歴に残したいかGitに知らせる
  • コミット: git commitコマンドで変更履歴を作成する

超個人的なイメージではGit管理下のファイルを変更したら「変更内容の紙が出てくる(変更) -> 変更に含めたい紙を束ねる(ステージング) -> キリのいいところで束を箱に入れる(コミット)」ということをやっている感じです。

では実際にファイルを作り、Gitの管理下に追加してみます。1.ファイルの作成 2.ファイルを追加対象に含める 3.追加の流れになります。git statusを使いながら1つ1つ変化を追って確認していきましょう。

~/Desktop/git_tutorial$ git status
On branch master

Initial commit

nothing to commit (create/copy files and use "git add" to track)

先ほどと同じ状態です。

~/Desktop/git_tutorial$ touch index.html
# index.htmlが作成されたので、好きなエディタで開いて以下の内容を記述して保存してください(# は不要です);
# <html>
# <head>
#   <title>git_tutorial</title>
# </head>
# <body>
#   <p>Git Tutorial</p>
# </body>
# </html>
~/Desktop/git_tutorial$ git status
On branch master

Initial commit

Untracked files:
  (use "git add <file>..." to include in what will be committed)

    index.html

nothing added to commit but untracked files present (use "git add" to track)

git statusによると、依然としてコミットの対象に含まれているものはないですが、新しくindex.htmlというファイルが作成されていて、まだGit管理下に入っていないという情報が表示されています。

Git管理下に含めるにはgit addコマンドを実行してくださいという指示が括弧中に記載されているので実行します。

~/Desktop/git_tutorial$ git add index.html
~/Desktop/git_tutorial$ git status
On branch master

Initial commit

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)

    new file:   index.html

git statusによると、コミットの対象に「index.htmlを新規作成した」という変更を追加する準備ができたことが表示されています。この状態をindex.htmlの変更が ステージ(stage) されていると言い、この変更のステージを取りやめることを アンステージ(unstage) すると言い、git rm --cached index.html(場合に応じてgit reset HEAD index.html)でGitに知らせることができます。

最後にステージされている変更からコミットを作成してみましょう。

~/Desktop/git_tutorial$ git commit -m "Initial commit"
[master (root-commit) c04dc8f] Initial commit
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 index.html
~/Desktop/git_tutorial$ git status
On branch master
nothing to commit, working tree clean

上記のように、追加に関する情報が表示されたら成功です。git statusの結果は変更を全く行なっていない状態に戻っているかと思います。

これで 変更->ステージ->コミット の1サイクルが終わりました。Initial commitの部分は変更理由を記述します。慣習としてリポジトリを作成した直後のコミットはInitial commitと書くことが多いです。

作成したコミットを見る: git log

作成したコミットを見て見ましょう。git loggit log -pを実行してください。

~/Desktop/git_tutorial$ git log
commit c04dc8ffd000dd7ba23b20177c208e9095901d19 (HEAD -> master)
Author: ushumpei <shumpei.uzawa@gmail.com>
Date:   Tue Nov 28 00:59:56 2017 +0800

    Initial commit

~/Desktop/git_tutorial (master)$ git log -p
commit c04dc8ffd000dd7ba23b20177c208e9095901d19 (HEAD -> master)
Author: ushumpei <shumpei.uzawa@gmail.com>
Date:   Tue Nov 28 00:59:56 2017 +0800

    Initial commit

diff --git a/index.html b/index.html
new file mode 100644
index 0000000..e69de29

上記のコマンドで過去に行なったコミット内容が全て確認できます。変更の詳細がいらない場合はgit log、詳細を見たい場合はgit log -p変更履歴が確認できます。

更新

続いてindex.htmlを更新してコミットして見ましょう。index.htmlをエディタで開いて以下のように修正してください。(6行目のpタグをh1タグに変更します)

...
  <h1>Git Tutorial</h1>
...

git statusを実行して変化を見てみてください。

~/Desktop/git_tutorial$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   index.html

no changes added to commit (use "git add" and/or "git commit -a")

index.htmlが変更されたことが表示されています。もしstageしたいなら先ほどと同様にgit add index.html、変更を破棄して最後のコミットの状態に戻したいならgit checkout index.htmlを実行してくださいという指示が書かれています。

ここでどんな変更が行われたかを確認するコマンドgit diffを使ってみましょう。

~/Desktop/git_tutorial$ git diff
diff --git a/index.html b/index.html
index 135d8e3..243ca88 100644
--- a/index.html
+++ b/index.html
@@ -3,6 +3,6 @@
   <title>git_tutorial</title>
 </head>
 <body>
-  <p>Git Tutorial</p>
+  <h1>Git Tutorial</h1>
 </body>
 </html>

index.htmlに対する変更が表示されています。先ほど行なった通り、pタグの行が削除され(- <p>Git Tutorial</p>)、h1タグの行が追加され(+ <h1>Git Tutorial</h1>)ていることがわかります。そのまんまですが、行頭の-は削除、+は追加の意味です。(差分は文字単位ではなく、行単位での表示となります)

この変更で問題ないのでステージしてコミットしましょう。

~/Desktop/git_tutorial$ git add index.html
~/Desktop/git_tutorial$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    modified:   index.html

~/Desktop/git_tutorial$ git commit -m "ページタイトルなのでh1にタグを変更"
[master 081f264] ページタイトルなのでh1にタグを変更
 1 file changed, 1 insertion(+), 1 deletion(-)
~/Desktop/git_tutorial$ git status
On branch master
nothing to commit, working tree clean

1サイクルが終わりました。これまで行なった作業を確認するために、git log --graphを実行してみてください。2つのコミットが追加されていることが確認できるかと思います。

~/Desktop/git_tutorial$ git log --graph
* commit 081f264cc85e5074a93f7304ba2febf9efb90bd9 (HEAD -> master)
| Author: ushumpei <shumpei.uzawa@gmail.com>
| Date:   Tue Nov 28 10:17:57 2017 +0800
| 
|     ページタイトルなのでh1にタグを変更
| 
* commit b80d4812cbde6655bee82bfa20127519e8826b81
  Author: ushumpei <shumpei.uzawa@gmail.com>
  Date:   Tue Nov 28 10:02:38 2017 +0800
  
      Initial commit

--graphオプションをつけて実行したので、2つのコミットが線で繋がって表示されています。これは ブランチ(branch) と呼ばれる概念に関係しています。

ブランチ(branch)

ブランチ(branch) について説明します。ブランチは連続したコミットの集まりです。先ほど作った2つのコミットはmasterブランチの先頭に追加されています、どんどんコミットを重ねてブランチ(枝)を伸ばしていくイメージです。

ブランチと呼ばれるからには 枝分かれ させることができます、ブランチの途中から別の ブランチを生やして そちらを伸ばしていくことができます。この機能により、ブランチを分けることによって複数の開発者が互いに影響を受けずに並列に開発を行っていくことができます。(あとで出てきますが、枝分かれしたブランチは好きなタイミングで他のブランチに合流させることができます)

リポジトリにどんなブランチがあるのかはgit branchコマンドで確認できます。

~/Desktop/git_tutorial$ git branch
* master

Gitリポジトリを作った直後、ブランチはmasterしかありません。先ほどのindex.htmlに関する変更はmasterに追加していました。masterから違うブランチを生やしてコミットを追加してみましょう。ブランチの作成はgit branch ブランチ名です。

# 一行目より現在masterブランチにいることがわかります
~/Desktop/git_tutorial$ git status
On branch master
nothing to commit, working tree clean

# stylingブランチを作成
~/Desktop/git_tutorial$ git branch styling

# ブランチ一覧にstylingが表示されています、「*」は現在いるブランチを表しています
~/Desktop/git_tutorial$ git branch
* master
  styling

# git checkout ブランチ名でstylingブランチに移動します
~/Desktop/git_tutorial$ git checkout styling
Switched to branch 'styling'

# 一行目より現在stylingブランチにいることがわかります
~/Desktop/git_tutorial$ git status
On branch styling
nothing to commit, working tree clean

# ブランチ一覧でも同様に確認できます
~/Desktop/git_tutorial$ git branch
  master
* styling

それではstylingブランチの先頭にコミットを追加します。スタイルシートファイルstyle.cssを作成して、index.htmlでそれを読み込んでスタイルを適用しましょう。

~/Desktop/git_tutorial$ touch style.css
# style.cssを好きなエディタで開いて以下の内容を記述して保存してください;
# h1 {
#   color: #727272;
#   font-family: monospace;
# }
#
# index.htmlは次のようにheadタグ内にlinkタグを追加してください;
# <html>
# <head>
#   <title>git_tutorial</title>
# + <link rel="stylesheet" href="./style.css" />
# </head>
# ...
~/Desktop/git_tutorial$ open index.html # ブラウザで表示してみてもいいかもです

# 変更の確認をしましょう。index.htmlが変更され、style.cssが追加されています
~/Desktop/git_tutorial$ git status
On branch styling
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   index.html

Untracked files:
  (use "git add <file>..." to include in what will be committed)

    style.css

no changes added to commit (use "git add" and/or "git commit -a")

# index.html、style.cssをステージします
~/Desktop/git_tutorial$ git add .

# 2つのファイル変更がステージされています
~/Desktop/git_tutorial$ git status
On branch styling
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    modified:   index.html
    new file:   style.css

# コミットします。現在stylingブランチにいるのでコミットはstylingブランチの先頭に追加されます(stylingブランチが1つ伸びます)
~/Desktop/git_tutorial$ git commit -m "スタイルシートを追加"
[styling 59df46b] スタイルシートを追加
 2 files changed, 5 insertions(+)
 create mode 100644 style.css

コミットが追加されました。ブランチの形を確認してみましょう。

~/Desktop/git_tutorial$ git log --graph
* commit 59df46ba1df6e9a29584dd2ad2d95b79248f0409 (HEAD -> styling)
| Author: ushumpei <mail@ushumpei.com>
| Date:   Tue Nov 28 11:10:48 2017 +0800
| 
|     スタイルシートを追加
| 
* commit 081f264cc85e5074a93f7304ba2febf9efb90bd9 (master)
| Author: ushumpei <mail@ushumpei.com>
| Date:   Tue Nov 28 10:17:57 2017 +0800
| 
|     ページタイトルなのでh1にタグを変更
| 
* commit b80d4812cbde6655bee82bfa20127519e8826b81
  Author: ushumpei <mail@ushumpei.com>
  Date:   Tue Nov 28 10:02:38 2017 +0800
  
      Initial commit

3つめのコミットが今追加したものです。今は直線で表示されているので枝分かれ感が薄いですが、2つめのコミットからstylingブランチがmasterブランチから分かれて、1コミット分伸びたという情報が表示されています。このコミットの変更はmasterブランチには影響しません。そのことをmasterブランチに移動して確認してみましょう。ブランチの移動はgit checkout ブランチ名です。

# masterブランチへ移動
~/Desktop/git_tutorial$ git checkout master
Switched to branch 'master'

# 状態の確認
~/Desktop/git_tutorial$ git status
On branch master
nothing to commit, working tree clean

# ファイルの確認
~/Desktop/git_tutorial$ ls
index.html

先ほど追加したファイルstyle.cssが存在しないことがわかります。またmasterブランチ上でgit logを行うとまだコミットが2つしかないことが確認できます。

~/Desktop/git_tutorial$ git log
commit 081f264cc85e5074a93f7304ba2febf9efb90bd9 (HEAD -> master)
Author: ushumpei <mail@ushumpei.com>
Date:   Tue Nov 28 10:17:57 2017 +0800

    ページタイトルなのでh1にタグを変更

commit b80d4812cbde6655bee82bfa20127519e8826b81
Author: ushumpei <mail@ushumpei.com>
Date:   Tue Nov 28 10:02:38 2017 +0800

    Initial commit

(補足) なぜブランチを分けるか?全てmasterにコミットしないのはなぜ?

それは多くの場合、masterブランチは実際にユーザーが使っているアプリケーションのソースコードになることが多いからです。レビュー、テストを経て開発者みんなで合意したソースコードのみがmasterに存在します。なのでmasterブランチへの変更は完全な状態で行われなければいけません。作業途中のコードを追加するといったことは避けるべきです。しかし開発作業では区切りのいいところでコミットしていく方が、全てのコードを書き終わってからコミットする(ゲームをセーブしないでクリアするようなものです)よりも断然楽です。なので開発作業を行う場所として新たにブランチを作成し、完成したらmasterにマージするというのがより優れた方法です。

マージ(merge)

現在このリポジトリには2つのブランチが存在しています。masterstylingです。流儀にもよりますが、基本的にGitではmasterブランチに全ての作業が集約していきます。スタイル設定作業が一旦落ち着いたのでstylingで行なった仕事をmasterブランチに適応しましょう。この別のブランチの変更を別のブランチに適応する作業を マージ(merge) と呼びます。分かれていた枝をくっつけるイメージです。超個人的なイメージでは、masterブランチからstylingブランチの先っぽを掴んで、masterブランチにくっつけてぎゅっとコブを作る感じです。

stylingmasterにマージします。コマンドはgit merge --no-ff stylingになります(--no-ffはマージした時にコブを作るおまじないです。マージには3つくらい種類がありますが、この方法が複数人の開発では重宝されています)。この時どちらがどちらにマージされるかというのは重要で、自分がmasterブランチにいることを確認した上でマージを行なってください。

~/Desktop/git_tutorial$ git status
On branch master
nothing to commit, working tree clean

~/Desktop/git_tutorial$ git merge --no-ff styling # 実行後適当なエディタが開かれます。使い方が不明なら、vimなら`ZZ`、nanoなら`Ctrl + x`を入力してください
Merge made by the 'recursive' strategy.
 index.html | 1 +
 style.css  | 4 ++++
 2 files changed, 5 insertions(+)
 create mode 100644 style.css

マージが実行されました。git log --graphで履歴を見ると、4つのコミットが表示されています。このことはstylingブランチにあった「スタイルシートを追加」のコミットがmasterブランチに適応されたこと、マージしたという事実を示す新しいマージコミット(merge commit)が作成されたこと、を示しています。

~/Desktop/git_tutorial$ git log --graph
*   commit c45179e33ebf642db092e04130bbd981078cb6f9 (HEAD -> master)
|\  Merge: 081f264 59df46b
| | Author: ushumpei <mail@ushumpei.com>
| | Date:   Tue Nov 28 11:46:57 2017 +0800
| | 
| |     Merge branch 'styling'
| | 
| * commit 59df46ba1df6e9a29584dd2ad2d95b79248f0409 (styling)
|/  Author: ushumpei <mail@ushumpei.com>
|   Date:   Tue Nov 28 11:10:48 2017 +0800
|   
|       スタイルシートを追加
| 
* commit 081f264cc85e5074a93f7304ba2febf9efb90bd9
| Author: ushumpei <mail@ushumpei.com>
| Date:   Tue Nov 28 10:17:57 2017 +0800
| 
|     ページタイトルなのでh1にタグを変更
| 
* commit b80d4812cbde6655bee82bfa20127519e8826b81
  Author: ushumpei <mail@ushumpei.com>
  Date:   Tue Nov 28 10:02:38 2017 +0800
  
      Initial commit

まとめ

長い文章を読んでくださってありがとうございました。またはまとめを見てくださってありがとうございます。とりあえず基本的な概念の説明を自分なりにまとめて見ただけです。わかりにくい部分、間違っている部分、お気づきになりましたらお知らせ下さい。(コメント、メール、twitter#など最大限対応します)

おまけ

  • リポジトリ(repository)は、枝分かれしたブランチ達を持っている(木みたいなもの)
  • ブランチ(branch)は、連続したコミット達から成り立っている(枝みたいなもの)
  • コミット(commit)は、複数ファイル達に対する変更履歴で出来ている(コブみたいなもの)

といった形です。Gitはこれら3つの対象に対して、様々な操作を行える機能を提供してくれるツールです。Gitは 分散型バージョン管理システム でその目的は 「大勢で1つのアプリケーションを一緒に作ること」 です。今回は バージョン管理システム の側面をクローズアップしてましたので、次回は 分散型 と言われる所以となる機能について書きたいと思います。

最低限必要なGitコマンド

友人に説明する必要性が出てきたので頭の整理を兼ねてメモしておきます。どんなものがあるでしょうか?自分的には最低限以下のやつらかと思います。(mergeとかはプルリクエストで行うので直接打たない想定)

status, log, diff, pull, push, checkout, add, commit, reset

めちゃくちゃ主観的なワークフロー的なものを描いて見ました。基本的には、新しいブランチ作るコミットいくつか作るリモートにブランチをプッシュするの一連の流れができればいいという話かと思います。

# 現状把握系(随時、癖のように打つ)
$ git status # 現在のワーキングディレクトリ、ステージエリアの状態みる
$ git log --graph --oneline # ブランチの状態確認
$ git log --graph --oneline --all # リポジトリの状態確認
$ git log -p # 直近何やってたか確認


# ローカル最新化
$ git checkout <branchname> # 最新化の準備(大体master)
$ git pull # 最新化


# ブランチ作成とチェックアウト
$ git checkout -b <newbranchname> # 作成してチェックアウト
$ git branch -d <newbbbbranchname> # タイポしたので削除


# コード書いてコミットを繰り返す
$ git diff -w <filename> # 差分確認
$ git add <filename> # ステージ
$ git reset HEAD <filename> # ステージやめる
$ git checkout <filename> # 変更戻す
$ git diff HEAD # コミット前最終確認
$ git commit # コミット


# プルリクエスト準備のため、ローカルブランチをリモートにプッシュ
$ git push -u origin newbranchname

感想

本当に最低限なので、cloneとか一度やったらあとはやらない系はのぞいています。ファイルの変更取り消し、ステージの取り消し、ときたのでコミット取り消し(reflogreset --hard)もいるかと思ったのですが、余裕ができたらでいい気がします。

参考リンク

あとGitに関する大まかなイメージを持っていると楽かもしれないです。

公開Slackのリンク

今更だけど調べたのでメモ。

公開Slackの作り方は以下のリンクです。Slackin、SlackinをHerokuで(簡単)、Google Apps Script、 Arukas.ioとかが使えそうです。

Google Apps Scriptでやります ちょっとやり方考えます。

追記

なんだかSlackのレガシートークン使う記事が多いので、新しいトークンに対応した記事を書こうかと思います。

Gitで大雑把にConflict解消

こんにちは。

Gitのコンフリクトの解消方法毎回忘れるのでメモしておきます。エディタで修正するのは手間でしたので・・・

$ git checkout --ours filepath   # 変更しない
$ git checkout --theirs filepath # マージ先のファイルで上書き
$ git checkout -m filepath       # よくわからなくなったので、コンフリクトが発生した直後の状態に戻す

まとめ

コンフリクトのdiffが読みにくすぎてやばいです。<<<<<<< HEADHEADとか書いてありますがだいたい文字とか見るの面倒なので、diffの囲み(<<<<<<< ... ======= ... >>>>>>>)の中にあるやつは上にあるのが僕らの、下にあるのが奴らのと覚えてます。

<<<<<<<
  ours(僕らの)
=======
  theirs(奴らの)
>>>>>>>

よくわからなくなったらgit checkout -m .でやり直しちゃえばいいかなと思います。

あと、個別のファイルに対して変更するかしないかを判断するのはかなりきついな。。。という感想を持ちました。

願わくばコンフリクトが起きない方がいいですが、コンフリクト恐れすぎるのも良くないので、mergetoolとか駆使していきたいです。(Gitはいくらでもやり直しがきくのでやってみてから考えるでいいかと思います)

参考: Gitコンフリクト解消ガイド(git mergetoolの使い方)

BitbucketのPipelinesでmasterブランチにPull Request後、自動デプロイ

いい加減デプロイ自動化しないといけない。

プライオリティ低くなってしまっていて放置気味でしたが、毎回サーバーに入ってgit pullすることに飽きてきました。

BitbucketのPipelinesについて調べたことを書いていきます。

無料で使用できますが月50分までの使用制限があるそうなので、そんなにがっつりは使えない気もします。(2017/10/31 時点)

また、この記事ではPipelinesを使いますが、他の選択肢としてはWebhookとかあります。 その場合サーバー側にエントリポイント作って、POST受け取ってあれこれするという風になるかと思います。 状況に応じてWebhookも検討してみると良いかもしれないです。

概要

BitbucketのPipelinesを使えばリポジトリごとに 特定のブランチへのコミットを検知したタイミング で行いたい処理、テスト実行とか通知とかデプロイスクリプト起動とか、を設定できます。

Pipelinesはコミット検知したら裏側でDocker立ち上げているので、私たちはコンテナ内で起動するコマンドを書いていく感じです。立ち上げるDockerイメージも指定できます。

Pipelinesの使い方は簡単で、 bitbucket-pipelines.ymlというYAMLファイルに記述してレポジトリルートにおいておくと勝手に読んでくれます。

プルリクエストのマージじゃなくて 特定のブランチへのコミットを検知したタイミング でいいの?

とか思ったのですがよく考えたら、デフォルトだとプルリクエストをマージするとマージコミットが生成されるため(fast-fowardでもコミット追加されますし)、 masterブランチの設定を記述しておけば、masterへのプルリクエストが閉じられたら本番環境にデプロイ、とか出来る様になります。なるはずです!

やること

  • Pipelinesの有効化とbitbucket-pipelines.ymlの記述
  • デプロイ用ユーザーを作成
  • deploy.shの記述

今回は最小限、デプロイだけです。テストとかロールバックとかについては考えてないので、お気をつけください。

Pipelinesの有効化とbitbucket-pipelines.ymlの記述

Bitbucketへ行き、対象のリポジトリ管理画面の「Settings > Pipelines > Settings」からPipelinesを有効化します。

(リファレンスは次です。参考ページ)

YAMLを書きます。私は次のように記述しました。Dockerイメージは現在使っているサーバーに合わせてcentosにしています。sshできるようにしてデプロイスクリプトを叩くだけです。スクリプトは後で書きます。

image: centos:latest

pipelines:
  branches:
    master:
      - step:
          name: Deploy
          script:
            - yum -y install openssh-clients
            - ssh -p $SERVER_PORT $DEPLOY_USER@$SERVER_IP bash < deploy.sh

$SERVER_PORT, $DEPLOY_USER, $SERVER_IPなどの環境変数を、 「Settings > Pipelines > Environment variables」 から設定します。参考

作成したYAMLファイルをどうにかmasterに取り込んでください。好きな方法でいいです。add, commit, pushとか。

デプロイ用ユーザーを作成

デプロイサーバー -> Bitbucket

デプロイサーバーからBitbucketのリポジトリにアクセスするユーザーを作成します。サーバーに接続してbitbucketユーザーを作成します。($DEPLOY_USERと同じもの)

次に新しいユーザーのSSHキーペアを作成してください、これはBitbucketからgit pullするときに使用します。参考: Creating SSH keys

Bitbucketのサイトから作成した公開鍵を、リポジトリのデプロイキーに設定します。

このユーザーでリモートリポジトリにアクセス(fetchとか)できたら成功です。

Pipelines -> デプロイサーバー

PipelinesのDockerコンテナからデプロイサーバーにssh接続するために、BitbucketからSSHキーペアを作成します。参考: Use SSH keys in Bitbucket Pipelines

「Settings > Pipelines > SSH keys」 からキーペアを作成してください。

サーバーに戻り、先ほど作ったユーザーの~/.ssh/authorized_keysに公開鍵を貼り付けます。

またsshが困らないように、この画面の下部にある Known hosts の追加も行っておきましょう。(ポート付きの場合XXX.XXX.XXX.XXX:PORT)

deploy.shの記述

#!/bin/bash

REPOSITORY_PATH=/repository/path # 適当に変えてください

cd $REPOSITORY_PATH
git pull

リポジトリパスに移動してgit pullするだけのスクリプトです。注意としては、リポジトリのブランチが常にmasterになっていないと予期せぬ挙動が起こると思います。

完了

適当にブランチ切って修正してコミットしてプッシュしてプルリクエストしてマージして、Pipelinesメニューからタスクが実行されているのが確認できるかと思います。

一旦流れを作っておけば、後からテストを追加とかもできるはずなので、一歩前進というところです。

内容に関して何かございましたらお知らせいただきたいです。こっちの方がいい、ここ間違っている、これめっちゃ危ない、根本的に勘違いしている、ちゃんとリファレンス読みなさい、など指摘いただけると大変助かります。

ハマった

  • リポジトリの権限
    • 私のケースではサーバーに、すでに別ユーザーでクローンしたリポジトリがあったため、新しいユーザーでは更新系のgit操作ができない状態になってました。今回はオーナーを再帰的に変更して誤魔化しました。
  • Permission denied
    • ssh-agentが止まっているとか
    • ~/.ssh周りの権限とか所有者が適切ではなかったり
    • どの時点での権限不足なのか、サーバー3つ(Pipelines, Deploy, Bitbucket)なのでわかりにくかった
  • Pipelinesを使い始める順番
    • Bitbucketサイトからアクティベートしないと使えない
    • メニュー直下のPipelinesはチュートリアル形式で混乱したので、設定以下のPipelinesで普通にアクティベートした
    • 環境変数などもアクティベート後から設定可能になる

React NativeでARを体感する、Introducing Expo AR

 9月末にExpoがアップデートされてiOSAR Kit対応が行われました。チュートリアルが公開されたのでちょっと手を出してみました。使用する要素としては、

  • expo
  • three
  • expo-three

のみです。3Dオブジェクトの生成はThree.jsレンダリングExpoExpo Threeという構成になっています。

Introducing Expo AR: Three.js on ARKit

デモ


Introducing Expo AR #1

感想

 Expoの制限はあるもののARアプリをJavaScriptでかけるというのは前提となる知識量のハードルが下がるかと思います。

 チュートリアルやると満足感が得られてしまうのでやや危険ですね。色々いじってみるのは大切な一歩だと思いますが、一歩踏み出した後に自分の発想が広がっていかないのがなかなか辛いところです。

サービスがSSL化するとき

 ものすごく短い話ですが、サービスがSSL化するときの対応を想定してみました。近々知り合いのサイトにSSL入れられることになりそうなのが理由です。そういえばはてなブログSSL化するそうですね!

概要

 httphttpsに書き換えるの面倒ですよね。省略できた気がします。(確かにできるみたいです参考: リンクのhttp:https:を省略して現在のプロトコルでリンク先にアクセスさせる)。ただ参考リンクのタイトル通り、通信は現在のスキームになるのでほおっておくと 本来SSLが必要なページでもhttpで接続されてしまいます。というのはかなり問題だと思うので、移行するときは以下の切り分けをするかもしれないです。

  • リソースとかユーザーの情報を扱わないリンクに関しては、スキーム省略
  • 明示的にssl化したページはしっかりhttps書いておく
    • またはsslページにリダイレクトさせる

切り分け方法: 追記(2017年10月3日)

  • 内部
    • リソースとかユーザーの情報を扱わないリンクに関しては、スキーム省略
    • 明示的にssl化したページはしっかりhttps書いておく
  • 外部
    • リンク先に合わせたスキームを書く

感想

 でも切り分けるくらいならhttpsに置換しちゃったほうが早くない?というと、それもそうだと思います。前に、「画面動かない!直して!->ssl切れちゃってました」、というhttps -> http現象があったので対応減らせたらいいなと思った次第です。

 混在コンテンツ(Mixed Content)に対しても効果があるそうです。

配列でsplitもどき(JavaScriptメモ)

 JavaScriptに関するメモです。配列(Array)を適当な部分で分割したかったのでsplitもどきの関数を書きました。文字列のsplitをちゃんと配列版にしたわけではないので「もどき」と言っています。

function split(array, separator) {
  return array.reduce(function(p, n, i) {
    if (n !== separator) {
      p[p.length - 1].push(n);
    } else {
      if (i !== 0 && i !== array.length - 1 && array[i - 1] !== separator) p.push([]);
    }
    return p;
  }, [[]])
}
// 's'で分割する
split(['a', 'b', 's', 'c', 'd', 's', 'e'], 's')
=>
[
  ["a", "b"],
  ["c", "d"],
  ["e"]
]

// 配列の最初と最後に区切り文字があるときは、空配列を作らずに無視する
// 連続している区切り文字も1つとみなし、空配列は作らない
split([1,2,3,3,3,3,3,4,5,4,3,2,2,1,1,2,3,3,4,1], 1);
=>
[
  [2, 3, 3, 3, 3, 3, 4, 5, 4, 3, 2, 2],
  [2, 3, 3, 4]
]

// 区切り文字が配列に含まれていないときは、配列そのものを要素に持った配列を返す
split([1,2,3,3,3,3,3,4,5,4,3,2,2,1,1,2,3,3,4,1], 0)
=>
[[1, 2, 3, 3, 3, 3, 3, 4, 5, 4, 3, 2, 2, 1, 1, 2, 3, 3, 4, 1]]

感想

 必要に駆られて書いてみたので、文字列のsplitの配列版にはなっていないです。スプレッドシートExcel方眼紙状態になっていて、そこからデータを取得しなきゃいけない、というときに使用しました。

 配列版splitを作る場合、配列を区切り文字にして配列を分割できるようにするのが自然だと思います。文字列のsplitをうまく使って配列版をかけないか?と思ったのですが、空文字とかの扱いが微妙です。

 なんか圏論とか使うと整理できそうな気がしました。文字列対象から配列対象への射を、配列関手で写した先みたいな。全然よくわかっていないです。圏論ということはHaskellでの実装を見ると面白いかもしれないです。(多分split的なものがある?)

ページに目次をつけて、項目を押したらスクロールする

 ものすごい小さい話ですが、毎回忘れてしまうのでメモします。

$('html,body').animate({
  scrollTop: window.pageYOffset + target.getBoundingClientRect().top - 8 // 要素がブラウザ上部ぴったりになるので少し隙間をあけたり
}, 500, 'swing');

 まずwindow.pageYOffsetについて、これはwindow.scrollYエイリアスだそうです。ページの一番上を0として、どのくらい下にスクロールしているかのピクセル値を返します。

 次にelement.getBoundingClientRect().topですが、現在クライアントが見ている画面の上端を0として、そこからどれくらい要素が離れているかをピクセル値で返します。例えば要素をスクロールして通り過ぎていたら、負の値が返ってきます。

  つまり、ページの一番上から現在表示している画面上端までの距離現在表示している画面上端から要素までの距離を足すことでページの一番上から要素までの距離を計算しているということになります。(多分絵で描くとわかりやすい???)

ページの一番上から現在表示している画面上端までの距離

f:id:ushumpei:20170925210302p:plain

現在表示している画面上端から要素までの距離

f:id:ushumpei:20170925210314p:plain

まとめ

 多分覚えたのでもう忘れないはずです。ぶっちゃけていうとすでにここに書いてありました。。。

おまけ

 デモです。

See the Pen MEbNmj by ushumpei(@ushumpei) on CodePen.

一枚にまとめたやつ

<html>
  <head>
    <style type="text/css">
      .container {
        display: flex;
        flex-direction: row;
        justify-content: space-between;
      }
      .menu {
        background-color: #eee;
        display: flex;
        flex-direction: column;
        flex: 1;
        height: 120px;
        justify-content: space-around;
        padding: 8px;
        position: sticky;
        top: 8px;
      }
      .content {
        display: flex;
        flex-direction: column;
        flex: 3;
        justify-content: space-between;
        margin-left: 8px;
      }
      .section {
        height: 100vh;
      }
      .section > h1 {
        background-color: #eee;
        padding: 8px;
        margin-top: 0;
      }
    </style>
    <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
    <script>
      window.addEventListener('load', function() {
        var menuItems = Array.from(document.getElementsByClassName('menu-item'));
        var sections =  Array.from(document.getElementsByClassName('section'));
        menuItems.forEach(function(item, i) {
          var target = sections[i];
          item.addEventListener('click', function() {
            $('html,body').animate({
              scrollTop: window.pageYOffset + target.getBoundingClientRect().top - 8 // 要素がブラウザ上部ぴったりになるので少し隙間をあけました
            }, 500, 'swing');
          });
        });
      });
    </script>
  </head>
  <body>
    <div class="container">
      <div class="menu">
        <a href="#" class="menu-item">section1</a>
        <a href="#" class="menu-item">section2</a>
        <a href="#" class="menu-item">section3</a>
        <a href="#" class="menu-item">section4</a>
        <a href="#" class="menu-item">section5</a>
      </div>
      <div class="content">
        <div class="section"><h1>section1</h1></div>
        <div class="section"><h1>section2</h1></div>
        <div class="section"><h1>section3</h1></div>
        <div class="section"><h1>section4</h1></div>
        <div class="section"><h1>section5</h1></div>
      </div>
    </div>
  </body>
</html>