ushumpei’s blog

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

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>

google app scriptで背景色の置換

google app scriptで背景色の置換スクリプトを書いたのでメモします。以下の内容が含まれています。

  • 独自(「拡張ツール」)メニューの追加
  • モーダル(背景色置換モーダル)の表示
  • 初期値の挿入(テンプレートhtmlの使用)方法

概要

 主にgoogleのガイドを参考にして作成しました。とりあえずできることと、コードを記載します。

 できること;

  • スプレッドシート内での背景色の置換、置換実行モーダルの表示
  • 選択したセルの背景色を置換元としてデフォルト値として挿入

 そんなこといいから、という方は、スプレッドシートを開いて、「ツール」>「スクリプトエディタ」を選択し、スクリプトエディタのgsファイルにjavascriptコード、新規作成でhtmlファイルを作成しテンプレートファイル(index.html)を貼り付けてください。貼り付けたあとリロードするとメニューに「拡張ツール」が現れるかと思います。現れない場合はコメント頂けると幸いです。。。

f:id:ushumpei:20170925003319p:plain

 コード;

main.gs

function onOpen() {
  SpreadsheetApp.getUi()
    .createMenu('拡張ツール')
    .addItem('背景色置換', 'showModal')
    .addToUi();
}

function showModal() {
  var background = SpreadsheetApp.getActiveSheet().getActiveCell().getBackground();

  var template = HtmlService
    .createTemplateFromFile('index.html');
  template.from = background;
  
  var html = template
    .evaluate()
    .setWidth(300)
    .setHeight(150)
    .setSandboxMode(HtmlService.SandboxMode.IFRAME);

  SpreadsheetApp.getUi().showModalDialog(html, 'Replace background color');
}

function replaceBackgroundColor(from, to) {
  var sheet = SpreadsheetApp.getActiveSheet();
  var data = sheet.getDataRange();
  var backgrounds = data.getBackgrounds();
 
  var replacedCount = 0;
  for(var row = 0; row < backgrounds.length; row++) {
    for(var col = 0; col < backgrounds[row].length; col++) {
      if (backgrounds[row][col] != from) continue;
      sheet.getRange(row + 1, col + 1).setBackground(to);
      replacedCount++;
    }
  }

  ui = SpreadsheetApp.getUi();
  ui.alert('Successfully replaced!', replacedCount + ' cell\'s background color replaced to ' + to + ' from ' + from, ui.ButtonSet.OK);
}

index.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons.css">
    <script>
      function handleClick() {
        document.getElementById('replace').setAttribute("disabled","disabled");
        var form = document.getElementById('form');
        var from = form.querySelector('input[name="from"]').value;
        var to = form.querySelector('input[name="to"]').value;
        if (!from || !to) return showError('Input #from and #to');
        google.script.run
          .withSuccessHandler(closeModal)
          .withFailureHandler(showError)
          .replaceBackgroundColor(from, to);
      }
      
      function closeModal() {
        google.script.host.close();
      }
      
      function showError(message) {
        document.getElementById('error').innerHTML = 'Error: ' + message;
        document.getElementById('replace').removeAttribute("disabled");
      }
    </script>
    <style type="text/css">
      html, body, #form { height: 100%; }
    </style>
  </head>
  <body>
    <div id="form" style="display: flex; justify-content: space-between; flex-direction: column;">
      <div style="display: inline-flex; justify-content: flex-end">
        <label for="from" style="flex: 1">From: </label>
        <input id="from" type="text" name="from" placeholder="from" value="<?= from ?>" style="flex: 3">
      </div>
      <div style="display: inline-flex; justify-content: flex-end">
        <label for="to" style="flex: 1">To: </label>
        <input id="to" type="text" name="to" placeholder="to" style="flex: 3">
      </div>
      <button id="replace" onclick="handleClick()">Replace</button>
      <p id="error" style="color: red"></p>
    </div>
  </body>
</html>

解説

下準備

 onOpenスプレッドシートファイルを開いたときに実行する関数を指定できます。ここではスプレドシートのUIのAPIを呼び出して、メニューの追加と、メニュー項目と選択時の実行関数の登録を行なっています。

 メニュー項目が選択された際に実行される関数は、選択中のセルの背景色取得、テンプレートへの値挿入、モーダルの表示などです。対応するコードを以下に記載します;

 まず現在選択中のセル(光っているセル)の背景色を取得するためにgetActiveCellを使ってセルを取得します。そのあとにgetBackgroud#?????? 形式のカラーコードを取得し格納します。

  var background = SpreadsheetApp.getActiveSheet().getActiveCell().getBackground();

次に、作成済みのhtmlテンプレートを読み込み、プレースホルダfromに対し背景色を割り当てます。プレースホルダーの指定は、

<input id="from" type="text" name="from" placeholder="from" value="<?= from ?>" style="flex: 3">

のようにvalue=<?= form ?>となっていて、この仕組みを使って置換前の背景色のデフォルト値、として現在選択中のセルの背景色を割り当てます。参考

  var template = HtmlService
    .createTemplateFromFile('index.html');
  template.from = background;

最後にモーダルの表示処理を行います。

  var html = template
    .evaluate()
    .setWidth(300)
    .setHeight(150)
    .setSandboxMode(HtmlService.SandboxMode.IFRAME);

  SpreadsheetApp.getUi().showModalDialog(html, 'Replace background color');

 evaluateメソッドを呼び出すことで、テンプレートからhtmlオブジェクトを生成します、モーダルとして表示するためにサイズを設定し、実行方式をiframeに指定しました。実行方法に関しては他に選択肢があるようで、調べきれていないです。なので特に深い意味はありません、チュートリアルに沿って、iframeを指定しただけです。

モーダル

 モーダルの記載方法に関しては1ページのhtmlを書いて行く感じです。heightwidthが決まっている以外はwebと同じ感覚だと言えます。ただし、少し違うのは、スクリプトエディタで定義した関数を呼び出す方法があるということです。方法としては提供されているAPIを使用します;

      function handleClick() {
        document.getElementById('replace').setAttribute("disabled","disabled");
        var form = document.getElementById('form');
        var from = form.querySelector('input[name="from"]').value;
        var to = form.querySelector('input[name="to"]').value;
        if (!from || !to) return showError('Input #from and #to');
        google.script.run
          .withSuccessHandler(closeModal)
          .withFailureHandler(showError)
          .replaceBackgroundColor(from, to);
      }

 handleChlick関数はユーザーが置換実行する際に押下するボタンに紐づいている関数です。概要としては、表示されたモーダルに対象の背景色、変換後の背景色を入力し、「置換」を押下したら、シート内の全てのセルに対して、背景色の置換を行うためのものです。処理の流れとしては二回連続のクリック禁止、入力した値の取得、軽いバリデーション、google.script.runAPIを使用した背景色置換スクリプトの実行です。

 詳細が必要なのはgoogle.script.runかと思います。このAPIgoogle.script.run.hogehoge()と記載することで、スクリプトエディタで定義したhogehoge関数を呼び出すことができます。またここでは、実行の成功時(withSuccessHandler)、失敗時(withFailureHandler)にコールバックを指定しています。コールバック関数は index.htmlで定義したものが指定可能です。

 以下はモーダルに値を入力して実行した際に呼び出されるスクリプトです。

function replaceBackgroundColor(from, to) {
  var sheet = SpreadsheetApp.getActiveSheet();
  var data = sheet.getDataRange();
  var backgrounds = data.getBackgrounds();
 
  var replacedCount = 0;
  for(var row = 0; row < backgrounds.length; row++) {
    for(var col = 0; col < backgrounds[row].length; col++) {
      if (backgrounds[row][col] != from) continue;
      sheet.getRange(row + 1, col + 1).setBackground(to);
      replacedCount++;
    }
  }

  ui = SpreadsheetApp.getUi();
  ui.alert('Successfully replaced!', replacedCount + ' cell\'s background color replaced to ' + to + ' from ' + from, ui.ButtonSet.OK);
}

 特に目新しいことはないかと思います。alertgetUiでUIから使用可能です。

感想

 まず不安に思ったこととしては、「それ標準機能であるよ」と言われることです。その辺どうなのでしょうか、、、たまにコード書きたい欲がまさってしまい、ちゃんとした検索を行わないという傾向があります。

 すごく感心したのが、google app scriptのモーダルでhtmlを表示できること、テンプレートを使用できることです。テンプレートを使用できるということは、webアプリケーションに近い何かを作れることだと思います。例えばSQLドライバーを作ってみたりすると楽しいかもです、スプレッドシートをデータベース、シートをテーブルと見立てて、とか思ったりします。

 javascriptでユーザーインプットを受け取る方法は、alertとかpromptとか柔軟性がなくて困ることがありました。webだと画面側をリッチにすればいいのですが、spreadsheetだとそうもいかないので、それを解消するためにガイドが役に立つのだと思います。

PhRUG(Philippine Ruby Users Group) September 2017 Meetupに行ってきた

 最近フィリピンに滞在しています。2017/09/21にフィリピンのRubyユーザーグループのイベントが行われるということで、海外勉強会ってどんな感じだろう?日本と違いがあったりするのかな?とか思い参加してきました。たくさん写真撮ってくるの忘れました。

 とりあえず結論、強く思ったのは、フィリピンでも日本でも、技術系の勉強会は大体同じ雰囲気、ということです。

概要

 イベントはmeetupから発見しました。PhRUG September 2017 Meetup  会場のホストはShield Foundryというベンチャー企業で、HPも綺麗な感じです、ただHPを見ても何を作っているかはよくわかりませんでした。場所はShield Foundryのオフィス、マニラのBGC(Bonifacio Global City)にあります。BGCにはいったことなかったですが、未来っぽい町でした。外資系企業がたくさんあるみたいで、金持ちが多いみたいな話です。

 余裕を持って会場へ向かったのですが、交通渋滞、ビルを間違える、などのアクシデントで、会場に着いたのは開始30分後くらいになってしまいました…

 会場のShield FoundryはいかにもITベンチャーといった感じでした。広さは3~40畳くらいで、オフィススペースとキッチンスペースに分かれています。オフィススペースには長机に沢山の椅子(アーロンチェア?)、各椅子の前の机にiMacとモニターがずらりと並んでいます。それぞれ漫画とか本とかも乗っていました。その他、バランスボールあり、サッカーボールあり、あとサッカーのアレ(二人でバーを回してボールを蹴り合うゲーム)もありました。テック系は世界共通なんだな、と少し安心しました。

 受付を済ませてプレゼンしているキッチンスペースに入ると、ビール手渡され、真ん中のダイニングテーブルにはピザが置いてあり、ご自由にどーぞといった感じでした。  

イベント

f:id:ushumpei:20170923211805j:plain

 最初の発表は、会場に到着したときは既に始まっていて、ちょっと詳細がわからなかったです…。発表者はエンジニアの一日、のようなスライドを表示していました。とりあえず自分はタガログ語が一切できないので、英語でプレゼンしていてホッとしました。ただ英語がそこまでできるわけでもないし、トピック不明だったのでなんとなく頷いたりして過ごしました。

 2番目の発表はrailsのパフォーマンス改善やテストなどの発表でした。bootsnap, PhantomJS, rack-mini-profiler, New Relic, その他諸々、をそれぞれいい感じに使って、計測、改善したよ!という感じです。そうですよね。納得な感じです。

 3番目の発表はjupyter/notebookを使って見た系の発表。マークダウン + コードでドキュメントかけるやつです。これ自体は先輩に教えてもらっていて知っていたのですが、バグレポートとかに使って、楽に可視化していこうよ!といった、やっぱりそう考えるよね的な共感を持ちました。

 4番目の発表は、開発Tipsの紹介でした。gitprecommit-hookでコミット前にrubocopしてます、とか。railsのサーバープロセスの優先順位あげたらなおった、とか。やっぱりコミット前にちゃんとしておきたいと思うよね!という再度、強い共感を持ったりしました。

感想

 会が終わった後にエンジニアの方にフィリピンのプログラミング言語事情について尋ねて見たところ、大企業とか、求人の多さで言えばjava, phpベンチャーruby, pythonといった感じです。scalaはとてもいい言語だと思うけど学習コストが高いのと、まだ事例をそこまで聞かない、とのことでした。日本では既に事例があるので、このあたりの情報共有とか何かできるかも(逆もあるだろうし。そういえばtwitterは?scalaの事例としてはどう捉えていたのだろう?国内事例がない、という感じなのだろうか?)。

 フロントエンドのjavascriptフレームワークに関しては、angular2, reactjavascriptフレームワークはリッチすぎて、本当に欲しいものがその中の1つだったりする、という日本でも割と聞く話を伺うことができました。ionicでネイティブを、とかいう話題もちらほら聞くようです。

 ここまで書いて、強いて挙げるならフィリピンと日本のIT界隈の相違点はなんなのだろうか?という方向に思考が向かいました。フィリピンの方が学歴に関して厳しいのかな?という予想を最近持っていますが、まだちゃんと議論したことがないので結論としては不十分と思います。さらに交流を重ねなければと思います。

 参加する前はブログを書くつもりがなかったため、少々微妙なレポートになってしまいました。来月もグループがあるそうなので、もし行くとなればもっといい感じにレポートしたいと思います。meetup便利です。

iOS11アップデートについていくためのSwift入門(主観)

 2017/09/20に、iOS11がリリースされました。今回のアップデートでは機械学習やSiriアプリ、ARなど様々な新機能が使えるようになりました。リンクを見ていて、「ちょっとSwift読めるようになっておかないと、おそらく半年くらいつまらなくなってしまう…上がってくるニュースを見ているだけになってしまう…」と思ったのがこの記事を書こうと思ったきっかけです。

 リファレンスとしてはAppleのサイト、The Swift Programming Language (Swift 4): A Swift Tourを使います。学習の目的はSwiftのコード(主に今後発表されるであろう「iOS11で追加された〇〇を使って見た系の記事のコード」)を読めるようになることです。

 Hello, world!を目標、と思ったのですが、ベタ書きで終わってしまいました。

print("Hello, world!")

概要

 リファレンスの内容は、Swiftを書き始めるのに十分な知識を得るためのツアーです。記事がSwiftのプロジェクトとしてまとめられていて、ダウンロードできるのでそれを使って学習していきます。

 セクションは次のようになっています;

  • Simple Values
  • Control Flow
  • Functions and Closures
  • Objects and Classes
  • Enumerations and Structures
  • Protocols and Extensions
  • Error Handling
  • Generics

 本記事ではリファレンス内に記載されている情報で、主に書き方に関することをメモしていきます。すでに序文の時点で以下の二点が記載されています;

  • グローバルスコープの処理は勝手に実行されるので、エントリーポイントとしてのmain関数が必要ない
  • 文末にセミコロンは必要ない

Simple Values

  • 変数宣言について、定数はlet、通常の変数はvarで宣言する: var hoge = 1とか。
  • 明示的な型の宣言はlet piyo: Double = 70 (後置型宣言)
  • \()で文字列に変数を埋め込める。"template \()"
  • 3ダブルクォート"""で複数行の文字列を囲むことができる
  • 配列リテラル["hoge", "piyo",]が使用可能
  • [“hoge”: 1, “piyo”: 2]辞書も可能
  • 空の辞書は[:]

Control Flow

  • if, switch, for-in, whileとかありますがこの辺は不思議なさそう
  • 型?でOptionalな変数を宣言できる(例var hoge: String? = "hoge")
  • if let hoge = { ... }でOptional変数から値を取り出すことができる、取り出すことができたらブロック内の処理を実行するという記法
  • if var piyo = { ... }ともかけるが、多分そんなに使わない
  • 取り出した変数のスコープはブロック内
  • その他にOptional変数を扱う方法としては、??を使って、defaultValue ?? optionalValueと言うように記述すると、値が取り出せる時のみ使用される
  • switchcasedefaultで構成していきます。breakは不要、defaultは必須です。
  • 0..<4で、0,1,2,3の範囲のレンジを作成できる

Functions and Closures

  • 関数定義はfunc、引数の定義はhoge: String(後置型宣言)、アロー->で戻り値の型を記述: func hoge(hoge: String) -> String { ... }
  • 引数を名前付きで渡すことができるhoge('fuga', piyo: 'giyo')これは定義時にfunc hoge(_ hoge: String, piyo fuga: String)と言うようにする。引数のラベルと言うもので、_はラベルなしを意味する。
  • 高階関数も作れる: (Int) -> Intとかで型を宣言
  • クロージャ使える:
{ (hoge: Int) -> Int in // クロージャの型と変数名
  return hoge * 2
}
  • 場合によっては型も省略できるらしいので、{ hg in ... }とかブラケットとinが出てきたらクロージャとして読む。引数も$0, $1と順番で記載されることがあるので、{ $0 < $1 }とか書かれていても混乱しないように。

Objects and Classes

  • クラスのインスタンス化はClassName()
  • フィールドはベタ書きでいいが、外部からアクセスするにはgetterメソッドが必要
  • イニシャライザー(コンストラクター)はinit、クラス内のメソッドでインスタンス自身を参照するにはself、デイニシャライザーはdeinit
  • 継承はclass SubClass: SuperClass { ... }
  • setter定義時のnewValueパラメーターは予約語
  • method?.hogemethodの戻り値があればhogeを呼び出すと言うことができる

Enumerations and Structures

  • enumはcaseで値を振っていく。ラベルから数値を取り出すのはrawValue、数値からラベルを作成するにはイニシャライザを使う(この時の値はOptional)。
  • structで構造体を定義。だいたいの機能はクラスと同じだが、構造体は常に値渡しされる。
  • class, struct, enumは似ている

Protocols and Extensions

  • protocolでインタフェースのようなものが定義できる
  • メソッドにmutating宣言することで、インスタンスプロパティを変更するメソッドを定義できる(クラスの時は不要、struct,enumの時だけ)
  • extensionで既存の型に機能拡張を行える、Protocolの機能を追加するのにも使える

Error Handling

  • do...catch構文でかく、怪しいところにtryをかく
  • try?でOptional値を取り出す

Generics

  • ジェネリクス<Item>で、Itemを変数のようにしようできる(数学でいう変数的な、x的な): var hoge = [Item]()Item型の配列を宣言とか。
  • where句もジェネリクス関連、親、子などの型の検証を記述できる

感想

 以上、自分がSwiftを読む際に詰まりそうなところをまとめ終えました。逆に言えば詰まらなそうなところは、私の主観の範囲で無視しています。

 Optional型の存在がちょっと気になりました。だいぶ面白そうです。またクラス、構造体、列挙型に関する扱いも違った見方を得られて興味深いです。自分としては列挙型はそこまで重要視していなかったのですが、Swiftをかくことでもっと本質をつかむことができるかもと、少し前のめりになりました。

 iOS`界隈の盛り上がりに少しでもついていきたいと思った次第です。

文法の勉強のため簡単なObjective-CのコードをXcodeで実行してみた

 前回、React Nativeのソースコードを読もうとして挫折したので、基礎文法を勉強するためにObjective-Cで何か書いて見ます。

 その前にObjective-Cwikiを少し読んで感じをつかみます。とりあえず読むために必要そうなこと3つです。

  1. C言語としてもいける、コンパイラディレクティブを駆使する
  2. メッセージ式 [Object method:arg1:arg2]でメソッド呼び出し
  3. クラス定義は定義部(.h)と実装部(.m)に分かれている

書いて見る

環境: Xcode: Version 8.3.3 (8E3004b)

 ではとりあえずhello worldします。別にアプリケーションが書きたいわけではないので、コマンドラインツールプロジェクトとして書きます(もっと言えばコマンドラインツールが書きたいわけではないですが、実行の構成方法が不明だったため一旦これでいかせていただきます)

 まずXcodeを起動してFile > New > Projectを選択します。モーダルが表示されるので、macOSタブのCommand Line Toolを選択しNextを押します。名前などは自由に設定します、ただし使用言語はObjective-Cにして、初期設定を終えます。

f:id:ushumpei:20170919225414p:plain

 次にコードを書いていきます。すでにmain.m(エントリーポイントとなるファイル)が生成済みだったので、そこに処理を記述していきます。クラスの定義の練習をするためにHelloクラスを作成しましたのでそれを呼び出します。(@コンパイラディレクティブの他にもリテラルを使用するときの接頭辞として使う、いやおそらくコンパイラディレクティブでリテラルを変換している?)

  • main.m
#import <Foundation/Foundation.h>
#import "Hello.h"

int main(int argc, const char* argv[]) {
    @autoreleasepool {
        Hello* object = [[Hello alloc] init];
        NSString* message = @"Hello world!";
        [object setMessage:message];

        [object say];
        NSLog(@"[object message]: %@", [object message]);
    }
    return 0;
}

 次にHelloクラスをNew > FileからmacOSタブのCocoa Classを選択します。こうするとHello.hHello.mファイルが生成されます。内容を記述していきます。以下を定義しています、

  • 挨拶文を保持するmessage変数
  • 挨拶文を取得するmessage関数
  • 挨拶文を格納するsetMessage関数
  • 挨拶文をコンソールに出力するsay関数

です。

  • Hello.h
#import <Foundation/Foundation.h>

@interface Hello : NSObject {
    NSString* message;
}

-(NSString*) message;
-(void) setMessage: (NSString*) s;
-(void) say;

@end
  • Hello.m
#import "Hello.h"

@implementation Hello

-(NSString*) message {
    return message;
}

-(void) setMessage: (NSString*) s {
    message = s;
}

-(void) say {
    NSLog(@"say: %@", message);
}

@end

動かして見る

 Xcodeのウィンドウ上部メニュー左辺りにある▶︎(Runアイコン)を押してコードを実行します。

f:id:ushumpei:20170919230326p:plain

感想

 コンパイラディレクティブをちゃんと覚えていけばある程度読めるようになると思いました。@autoreleasepoolGCということでいいのかな?いやちゃんと覚えないといけないですね。とりあえず公式ドキュメントをこの辺から探して見ます。

 書けるようになる必要があるか?という問いに関しては一瞬、「まあでもSwiftあるし、、、」とか思いましたが、まだまだ使われているため、やっておいて損はなさそうな気がします。