公開Slackのリンク
今更だけど調べたのでメモ。
公開Slackの作り方は以下のリンクです。Slackin、SlackinをHerokuで(簡単)、Google Apps Script、 Arukas.ioとかが使えそうです。
- slackin: rauchg/slackin: Public Slack organizations made easy
- Heroku + slackin: SlackinでSlackに誰でも参加できるチームをつくる
- Google Apps Script: 公開Slack用 自動招待フォーム by GAS
- Arukas:
Arukas.io で Slack 自動招待フォームβサービス終了で今使えないみたいです(確認: 2017/11/27 14:02)
Google Apps Scriptでやります ちょっとやり方考えます。
追記
Gitで大雑把にConflict解消
こんにちは。
Gitのコンフリクトの解消方法毎回忘れるのでメモしておきます。エディタで修正するのは手間でしたので・・・
$ git checkout --ours filepath # 変更しない $ git checkout --theirs filepath # マージ先のファイルで上書き $ git checkout -m filepath # よくわからなくなったので、コンフリクトが発生した直後の状態に戻す
まとめ
コンフリクトのdiff
が読みにくすぎてやばいです。<<<<<<< HEAD
にHEAD
とか書いてありますがだいたい文字とか見るの面倒なので、diffの囲み(<<<<<<< ... ======= ... >>>>>>>)の中にあるやつは上にあるのが僕らの、下にあるのが奴らのと覚えてます。
<<<<<<< ours(僕らの) ======= theirs(奴らの) >>>>>>>
よくわからなくなったらgit checkout -m .
でやり直しちゃえばいいかなと思います。
あと、個別のファイルに対して変更するかしないかを判断するのはかなりきついな。。。という感想を持ちました。
願わくばコンフリクトが起きない方がいいですが、コンフリクト恐れすぎるのも良くないので、mergetool
とか駆使していきたいです。(Gitはいくらでもやり直しがきくのでやってみてから考えるでいいかと思います)
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メニューからタスクが実行されているのが確認できるかと思います。
一旦流れを作っておけば、後からテストを追加とかもできるはずなので、一歩前進というところです。
内容に関して何かございましたらお知らせいただきたいです。こっちの方がいい、ここ間違っている、これめっちゃ危ない、根本的に勘違いしている、ちゃんとリファレンス読みなさい、など指摘いただけると大変助かります。
ハマった
- リポジトリの権限
- Permission denied
ssh-agent
が止まっているとか~/.ssh
周りの権限とか所有者が適切ではなかったり- どの時点での権限不足なのか、サーバー3つ(Pipelines, Deploy, Bitbucket)なのでわかりにくかった
- Pipelinesを使い始める順番
React NativeでARを体感する、Introducing Expo AR
9月末にExpo
がアップデートされてiOS
のAR Kit
対応が行われました。チュートリアルが公開されたのでちょっと手を出してみました。使用する要素としては、
- expo
- three
- expo-three
のみです。3Dオブジェクトの生成はThree.js
、レンダリングはExpo
、Expo Three
という構成になっています。
Introducing Expo AR: Three.js on ARKit
デモ
感想
Expoの制限はあるもののARアプリをJavaScript
でかけるというのは前提となる知識量のハードルが下がるかと思います。
チュートリアルやると満足感が得られてしまうのでやや危険ですね。色々いじってみるのは大切な一歩だと思いますが、一歩踏み出した後に自分の発想が広がっていかないのがなかなか辛いところです。
サービスがSSL化するとき
ものすごく短い話ですが、サービスがSSL化するときの対応を想定してみました。近々知り合いのサイトにSSL入れられることになりそうなのが理由です。そういえばはてなブログもSSL化するそうですね!
概要
http
をhttps
に書き換えるの面倒ですよね。省略できた気がします。(確かにできるみたいです参考: リンクのhttp:
やhttps:
を省略して現在のプロトコルでリンク先にアクセスさせる)。ただ参考リンクのタイトル通り、通信は現在のスキームになるのでほおっておくと 本来SSLが必要なページでもhttp
で接続されてしまいます。というのはかなり問題だと思うので、移行するときは以下の切り分けをするかもしれないです。
切り分け方法: 追記(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として、そこからどれくらい要素が離れているかをピクセル値で返します。例えば要素をスクロールして通り過ぎていたら、負の値が返ってきます。
つまり、ページの一番上から現在表示している画面上端までの距離
と現在表示している画面上端から要素までの距離
を足すことでページの一番上から要素までの距離
を計算しているということになります。(多分絵で描くとわかりやすい???)
ページの一番上から現在表示している画面上端までの距離
現在表示している画面上端から要素までの距離
まとめ
多分覚えたのでもう忘れないはずです。ぶっちゃけていうとすでにここに書いてありました。。。
おまけ
デモです。
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)を貼り付けてください。貼り付けたあとリロードするとメニューに「拡張ツール」が現れるかと思います。現れない場合はコメント頂けると幸いです。。。
コード;
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
を書いて行く感じです。height
とwidth
が決まっている以外は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.run
APIを使用した背景色置換スクリプトの実行です。
詳細が必要なのはgoogle.script.run
かと思います。このAPIはgoogle.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); }
特に目新しいことはないかと思います。alert
はgetUi
で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とモニターがずらりと並んでいます。それぞれ漫画とか本とかも乗っていました。その他、バランスボールあり、サッカーボールあり、あとサッカーのアレ(二人でバーを回してボールを蹴り合うゲーム)もありました。テック系は世界共通なんだな、と少し安心しました。
受付を済ませてプレゼンしているキッチンスペースに入ると、ビール手渡され、真ん中のダイニングテーブルにはピザが置いてあり、ご自由にどーぞといった感じでした。
イベント
最初の発表は、会場に到着したときは既に始まっていて、ちょっと詳細がわからなかったです…。発表者はエンジニアの一日、のようなスライドを表示していました。とりあえず自分はタガログ語が一切できないので、英語でプレゼンしていてホッとしました。ただ英語がそこまでできるわけでもないし、トピック不明だったのでなんとなく頷いたりして過ごしました。
2番目の発表はrails
のパフォーマンス改善やテストなどの発表でした。bootsnap
, PhantomJS
, rack-mini-profiler
, New Relic
, その他諸々、をそれぞれいい感じに使って、計測、改善したよ!という感じです。そうですよね。納得な感じです。
3番目の発表はjupyter/notebook
を使って見た系の発表。マークダウン + コードでドキュメントかけるやつです。これ自体は先輩に教えてもらっていて知っていたのですが、バグレポートとかに使って、楽に可視化していこうよ!といった、やっぱりそう考えるよね的な共感を持ちました。
4番目の発表は、開発Tipsの紹介でした。git
のprecommit-hook
でコミット前にrubocop
してます、とか。rails
のサーバープロセスの優先順位あげたらなおった、とか。やっぱりコミット前にちゃんとしておきたいと思うよね!という再度、強い共感を持ったりしました。
感想
会が終わった後にエンジニアの方にフィリピンのプログラミング言語事情について尋ねて見たところ、大企業とか、求人の多さで言えばjava
, php
。ベンチャーはruby
, python
といった感じです。scala
はとてもいい言語だと思うけど学習コストが高いのと、まだ事例をそこまで聞かない、とのことでした。日本では既に事例があるので、このあたりの情報共有とか何かできるかも(逆もあるだろうし。そういえばtwitterは?scalaの事例としてはどう捉えていたのだろう?国内事例がない、という感じなのだろうか?)。
フロントエンドのjavascriptフレームワークに関しては、angular2
, react
。javascriptフレームワークはリッチすぎて、本当に欲しいものがその中の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
と言うように記述すると、値が取り出せる時のみ使用される switch
はcase
とdefault
で構成していきます。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?.hoge
でmethod
の戻り値があれば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-C
のwiki
を少し読んで感じをつかみます。とりあえず読むために必要そうなこと3つです。
書いて見る
環境: Xcode: Version 8.3.3 (8E3004b)
ではとりあえずhello world
します。別にアプリケーションが書きたいわけではないので、コマンドラインツールプロジェクトとして書きます(もっと言えばコマンドラインツールが書きたいわけではないですが、実行の構成方法が不明だったため一旦これでいかせていただきます)
まずXcode
を起動してFile > New > Project
を選択します。モーダルが表示されるので、macOS
タブのCommand Line Tool
を選択しNext
を押します。名前などは自由に設定します、ただし使用言語はObjective-C
にして、初期設定を終えます。
次にコードを書いていきます。すでに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.h
、Hello.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アイコン)を押してコードを実行します。
感想
コンパイラディレクティブをちゃんと覚えていけばある程度読めるようになると思いました。@autoreleasepool
はGCということでいいのかな?いやちゃんと覚えないといけないですね。とりあえず公式ドキュメントをこの辺から探して見ます。
書けるようになる必要があるか?という問いに関しては一瞬、「まあでもSwift
あるし、、、」とか思いましたが、まだまだ使われているため、やっておいて損はなさそうな気がします。
React Nativeの画像遅延読み込み(ライブラリのソースを読んで見る)
この記事を要約すると、「画像遅延読み込みの方法が知りたくて、ライブラリのソースコードを読んで、Objective-Cのコードにたどり着いて、次に進めなくなってしまい一旦諦めたけれど、これを糧にもっと勉強しようという気になった」という自己満話です。
概要
少し前までWebサービスのユーザーのインターネット回線速度をそこまで強く意識したことがありませんでした。
もちろんパフォーマンス計測はしていましたが、サーバー側の処理を改善することで対処していて、あくまで一般的なユーザーは回線がある程度の速度であることを仮定していましたし、回線が弱すぎるのはユーザー側の問題としている部分がありました。
この回線の強弱に関する考え方が私が感じた、モバイルアプリとWebの大きな違いでした。
モバイルでは回線が弱くなることが容易に起こりうるし、そのせいでアプリが操作不能になってしまうことは、ユーザーにとってストレスだと思います(操作不能にする方がいいケースもあると思います)。いや、といよりモバイルから閲覧するWebサービスとアプリとの違いでいうと、アプリだとオフライン状態でも使えるように作れる部分があるので、極力そうしていった方が親切だし、ユーザーも安心だよね、という話です。
多分モバイルアプリ開発者の方々からすると当然すぎることだと思うのですが、私がこのことに気がつくためには、いつまでたっても画像が読み込まれないまま動かない、という経験が必要でした。(頭が悪いです)
読んでみる
そんなこともあり遅延読み込みのライブライreact-native-image-progress
とかに触れたのですが、そもそもどうやって遅延読み込みを実現しているか、ソースを読んでみようというのが今回の内容です。
とりあえず一番はじめのリリース0.1.0
を読む;
- 基本的には
react-native
のImage
コンポーネント Image
の読み込み開始、進捗、終了イベントハンドラにコールバックを登録しているImage
のprogressイベントについて苦労したんだろうなと感じられる- 遅延読み込み方法は
Image
のコードを読まなければわからない
ということなのでreact-native
のImage
コンポーネントのソースを読む;
RCTImageView
を読むべきとわかる
ここからObjective-Cです、RCTImageView.m
を読む;
RCTDirectEventBlock
で詰まる…
すみません、詰まりました。
感想
知らないことがたくさん見つかりましたし、いかに色々なことを知らないままで使っていたかわかりました。予想として遅延読み込みに関してはjavascriptのfetch
メソッドとかで実装されてて、簡単に読めないかな、とか思って軽く記事を書き始めたのですが、普通にネイティブでした。
結局わからずじまいになってしまい、かなりダメダメなので、しばらくいろんなソースを読んでみることにすると思います。Objective-Cの基本的な構文も抑えて今回の詰まったところを解消したいです。あとReact Nativeの仕組みもちゃんと知りたくなりました。
余談ですがFacebookのライセンスの話、どう捉えるか考えなければ。。。
ハノイの塔
なんだか久しぶりにjavascriptでハノイの塔を解いて見ました。以下の関数は、number
枚のハノイの塔をtime
回操作した時の状態を計算するものです。特に目新しいものではないと思います。
const hanoiSnapshot = (number, time) => { // 円盤の移動が完了した以降の操作はエラーを表示(良いやり方かどうかは悩む) if (time > Math.pow(2, number) - 1) throw new Error(`${Math.pow(2, number) - 1}回目の操作時点ですでに塔は完成しています`); // 三本柱の配列データを定義 hanoi = [[], [], []]; // 1~number番目の円盤それぞれの場所を計算して柱に格納して行く for (let i = 1; i <= number; i++) { // 移動する方向 const direction = Math.pow(-1, number + i + 1); // 移動した回数 const moveCount = Math.floor((Math.pow(2, i - 1) + time) / Math.pow(2, i)); // 1番目の柱を位置0とした時の円盤の絶対位置 const absolutePosition = moveCount % 3; // 方向を加味して場所を0~2に正規化して決定 const position = (direction * absolutePosition < 0) ? (3 + direction * absolutePosition) % 3 : absolutePosition; // 柱に円盤を追加する hanoi[position].unshift(i); } return hanoi; }
感想
イメージとしては円盤は方向と速度を持っていて、3つの柱を移動しつつ、くるくる回って行る感じです。数学で三乗根の乗算を考えるときに書くような図をイメージするとわかりやすい気がします。
wgetでサイズ確認
ものすごく小さな話かつググったら速攻出ますが、自分にとっては切実な問題なのでメモします。
結論から言うと--spider
オプションです。
$ wget --spider ***URL.zip*** Spider mode enabled. Check if remote file exists. --2017-08-31 09:49:43-- ***URL.zip*** ... Length: 11121426 (11M) [application/zip] Remote file exists.
補足(curl)
たまにwget
入ってないよ、となる時があるのでcurl
の場合も追記します。普通にヘッダ取得しているだけです。
$ curl --head ***URL.zip*** HTTP/1.1 200 OK ... Content-Length: 11121426 ...
(単位byte
)
ちなみにファイルのダウンロード自体はcurl -O ***URL.zip***
なのですね。勉強不足です。
感想
一日の通信量上限のあるネット環境で生活しているため、迂闊に大きいファイルをダウンロードすると終わってしまう、と言う背景です。
URL
の拡張子がzip
な理由は特にないです。
辞書.appのデータ構造の補足
注意: 結論としては補足になっていません。。。RELAX NG
XMLスキーマをここで勉強した際のメモ書き程度になっています
先日書いた辞書.app
の記事ではあまり辞書を作ることに関して調べきれていなかったところがあるので追記します。先日の記事でも単語と意味を登録することはできましたが、MyDictionary.xml
の構造(辞書のデータ構造)について補足していきたいと思います。
MyDictionary.xml
の構造(辞書のデータ構造)
MyDictionary.xml
はRELAX NG
というxml schema言語で記述されています。xml
に明るくないためやや中途半端な説明になりますが、RELAX NG
は外部のデータセットを使用する前提で使われることが大半のようです(参考)
xml
なのでデータを定義するために、簡単な方法を提供してくれているそうです。例えば子要素はelement
、属性はattribute
などがあり、それらに対し1つ以上持つ<oneOrMore>
、つけてもつけなくても<optional>
、どちらか一方だけ<choice>
、などの制約をツリーとして書いていくことができます。私のイメージではタグを定義して作っていく感じです。
まず最初の方の行を見ていきます。
<?xml version="1.0" encoding="UTF-8"?> <d:dictionary xmlns="http://www.w3.org/1999/xhtml" xmlns:d="http://www.apple.com/DTDs/DictionaryService-1.0.rng"> ... </d:dictionary>
ここでは、2つのネームスペース、xml
とhttp://www.apple.com/DTDs/DictionaryService-1.0.rng
のを宣言しています。またxmlns:d
の部分で、d:
というプレフィックスを宣言しているため、データセットを使用する際は語頭にd:
をつけて使用していくことになります。(ようやくd:entry
などd:
をつけて記述していた意味がわかりました)
続いてデータセットを見てどんな定義があるのかを見ていきます。内容は/Dictionary Development Kit/documents/DictionarySchema/modules/dict-struct.rng
に記載されております。(http://www.apple.com/DTDs/DictionaryService-1.0.rng
はアクセスしても404
でちょっと分かっていません。ネームスペースとしてだけ使っているのでしょうか?)
webで公開されているものもあるので、適宜そちらも参照ください。
まず初めに<grammer>
が全体を囲っていることから、Named patterns
と呼ばれるタグを分割して定義できるようにするパターンで記述していることがわかります(リンク先の セクション4. Named patterns)。
<grammer>
は、1つの<start>
と複数の<define>
を持ちます。<define name="hoge">
と定義したものは、<ref name="hoge">
により参照することができるため、定義を組み合わせて最終的な<start>
を組み立てていく形になります。
ここで定義されている主なものを見てみましょう;
dictionary
<define name="dictionary"> <element name="dictionary" ns="http://www.apple.com/DTDs/DictionaryService-1.0.rng"> <zeroOrMore> <ref name="style"/> </zeroOrMore> <ref name="dictionary.attlist"/> <oneOrMore> <ref name="entry"/> </oneOrMore> </element> </define>
dictionary
の定義は、
- 実態は
dictionary
タグ。アップルのネームスペースを属性として指定している。(ネームスペースの必要性の理解が曖昧です。名前が一般的なので、重複しないようにという慣習的なことでしょうか?それかプレフィックスを共通化するためとか関係している?)- タグは0個以上の
style
を使用できる - タグは1個以上の
entry
を使用できる - タグは
dictionary.attlist
属性リストを使用できるdictionary.attlist
はXTHMLのバージョンと国際化指定を属性として持っているようです
- タグは0個以上の
ref
では参照先がelement
なのかattribute
なのかわからないため、どう表現したら適切か悩みます(おそらく言語で表現しにくいからこそ、この記法が使われているのでしょうが)。
entry
<define name="entry"> <element name="entry" ns="http://www.apple.com/DTDs/DictionaryService-1.0.rng"> <ref name="entry.attlist"/> <zeroOrMore> <ref name="index"/> </zeroOrMore> <ref name="Flow.model"/> </element> </define>
entry
の定義は、
- 実態は
entry
タグ。- 0個以上の
index
が使用可 Flow.model
(なんだこれ。内部にhtml
書けることと関係している?)が使用可entry.attlist
が使用可entry.attlist
はid.attrib
属性が使用可能(<index id="hoge"
とか書ける)parental-control
属性が使用可能(任意)title
属性が使用可能(任意) <- (なぜなくてもいいのだろう?)
- 0個以上の
index
<define name="index"> <element name="index" ns="http://www.apple.com/DTDs/DictionaryService-1.0.rng"> <ref name="index.attlist"/> </element> </define> <define name="index.attlist"> <attribute name="value" ns="http://www.apple.com/DTDs/DictionaryService-1.0.rng"> <text/> </attribute> <optional> <attribute name="title" ns="http://www.apple.com/DTDs/DictionaryService-1.0.rng"> <text/> </attribute> </optional> <optional> <ref name="parental-control.attrib"/> </optional> <optional> <ref name="priority.attrib"/> </optional> <optional> <attribute name="anchor" ns="http://www.apple.com/DTDs/DictionaryService-1.0.rng"> <text/> </attribute> </optional> <optional> <attribute name="yomi" ns="http://www.apple.com/DTDs/DictionaryService-1.0.rng"> <text/> </attribute> </optional> </define>
index
の定義は
- 実態は
index
タグで、属性としてvalue
、オプションの属性としてtitle
、parental-control
、priority
、anchor
、yomi
などが使用可能
といった形になっています。
つまり?
つまり今のところ、そういうデータ構造になっているということがここを読むとわかるようになったというだけのことでした。。。
これらのデータ構造が辞書データとして取り込まれた時に実際どのように扱われるかをみていく必要があり、補足し切れていないです。
データ構造の読み方はわかった、ならばタグとしてどのような形をしているか、その子要素、属性はどのように扱われるか、それらのことを次にまとめていきたいと思います。