WordPress のテーマで、依存している Plugin をインストールしてもらう
必要な plugin がある場合に、通知を表示してインストールしてもらう
コード
required_plugins.php
<?php class RequiredPlugins { public static function setup($plugins) { add_action('init', function () use ($plugins) { if (!current_user_can('activate_plugins')) return; foreach ($plugins as $plugin) { if (self::is_plugin_active($plugin['key'])) continue; add_action('admin_notices', function () use ($plugin) { $name = $plugin['name']; $url = network_admin_url( "plugin-install.php?tab=search&type=term&s=${name}&plugin-search-input=Search+Plugins" ); echo "<div class=\"error\"><p>The <a href=\"${url}\">${name}</a> is required.</p></div>"; }); } }); } public static function is_plugin_active($key) { include_once(ABSPATH . 'wp-admin/includes/plugin.php'); return is_plugin_active("${key}/${key}.php"); } }
呼び出す時
functions.php
<?php ... require_once 'required_plugins.php'; ... RequiredPlugins::setup([ ['key' => 'import-users-from-csv', 'name' => 'Import Users From CSV'] ]);
注意
['key' => '...', 'name' => '...']
の形の要素の配列で、複数の通知を表示させるkey
はプラグインのディレクトリ名、wp-content/plugins
以下から確認してくるname
は何でもいいが、ユーザーがわかるようにするis_plugin_active
で一応プラグインが有効化されているかどうかの判定ができる
参考
感想
Vue.js の slot と props
内容としては これ (Vue.js のガイドの「スロット」の「スコープ付きスロット」) を読んで理解できなかった自分向けのメモです。
Vue.js で slot を使ったコンポーネントの持つ値を、slot に差し込まれるコンポーネントに props として渡す方法を調べました。
slot
を使ったコンポーネントを Hoge
コンポーネント、差し込むコンポーネントを Fuga
コンポーネントとすると、以下の感じでいけます。
Hoge コンポーネントで slot
の部分に <slot :hoge="hoge" />
みたいに渡したい値書いて、実際このコンポーネントを使うときに
<Hoge v-slot="{ hoge }"> <Fuga :fuga="hoge" /> </Hoge>
のように v-slot
という構文で hoge
を取り出して子のコンポーネントに props として渡してあげればいけます。
要するに slot タグに露出させたい属性値書いておけば v-slot
で取り出せる感じ。
サンプル
登場するコンポーネントは 3 つで、
- blur コンポーネント (slot タグを含むコンポーネント): クリックされるまで子コンポーネントをボヤかす
- spark コンポーネント (差し込まれるコンポーネント): props の
isSparking
が true になると点滅するコンポーネント - app コンポーネント (ルートコンポーネント。上記二つを実際に組み合わせる)
になります。
blur コンポーネントがクリックされたことを、spark コンポーネントに伝え、点滅を開始してもらうという動作になります。
See the Pen Vue Slot Props by ushumpei (@ushumpei) on CodePen.
実用的では無い例です。自分が実際にこれを必要としたのは、タブのコンポーネントで、タブがアクティブになったら子コンポーネントに伝えてデータフェッチし直す、的なやつです。
感想
Node.js の stream.pipe のエラーハンドリング
stream.pipe
の代わりにバージョン 10 で追加された stream.pipeline
を使うと良さそうです。
stream.pipe
では ここ に書かれているように、エラーハンドリングとしてストリームの後始末を書かなければいけないためです。
One important caveat is that if the Readable stream emits an error during processing, the Writable destination is not closed automatically. If an error occurs, it will be necessary to manually close each stream in order to prevent memory leaks.
あと pipe(...).on('error', ...).pipe(...).on('error', ...)...
みたいに書かなきゃいけない気がするし。
確認コード
stream.pipeline
でどのようなエラーハンドリングが行われるかをコードで確認しました。ファイルを読んで、変換をかまして、ファイルへ書き込むサンプルです。変換部分でエラーを任意に発生させられるようにし、
- エラーが発生しない時の挙動
- エラーが発生した時の挙動
の二つを確認します。
(動かすには入力用のファイル from.txt
が必要になります)
const { Transform, pipeline } = require("stream"); const { createReadStream, createWriteStream } = require("fs"); // Readable ストリーム作成 const readable = createReadStream("from.txt", "utf8"); // Writable ストリーム作成 const writable = createWriteStream("to.txt", "utf8"); // Transform ストリーム作成 (何もしないけどエラー投げられるようにしたやつ) const getTransform = (fail) => new Transform({ transform(data, _encoding, callback) { if (fail) { callback(new Error("fail")); } this.push(data); callback(); }, }); // [NOTE] ここでエラーの発生を任意に切り替える // const transform = getTransform(true); const transform = getTransform(); // イベントの発生をロギング。`i-th` は各ストリームに対応 (0-th: readable, 1-th: transform, 2-th: writable) [readable, transform, writable].forEach((s, i) => { s.on("end", () => { console.log(`${i}-th stream:`, "end"); }); s.on("finish", () => { console.log(`${i}-th stream:`, "finish"); }); s.on("close", () => { console.log(`${i}-th stream:`, "close"); }); }); // 入力 -> 変換 -> 出力 pipeline(readable, transform, writable, (e) => { if (e) { console.log(e); console.log( "エラーが起きた時は全てのストリームに close イベントが発行される" ); } else { console.log( "エラーなく終了した時は全てのストリームにそれぞれの終了イベント、加えて入出力ストリームに close イベントが発行される" ); } });
1. エラーが発生しない時の挙動
0-th stream: end 1-th stream: finish 1-th stream: end 2-th stream: finish エラーなく終了した時は全てのストリームにそれぞれの終了イベント、加えて入出力ストリームに close イベントが発行される 0-th stream: close 2-th stream: close
2. エラーが発生した時の挙動 ([NOTE]
と書いてあるコメント行近くの transform
変数定義をコメントアウト/インして実行)
1-th stream: close 2-th stream: close Error: fail at Transform.transform [as _transform] (/Users/ushumpei/node-stream/index.js:15:18) at Transform._read (_stream_transform.js:189:10) at Transform._write (_stream_transform.js:177:12) at doWrite (_stream_writable.js:428:12) at writeOrBuffer (_stream_writable.js:412:5) at Transform.Writable.write (_stream_writable.js:302:11) at ReadStream.ondata (_stream_readable.js:722:22) at ReadStream.emit (events.js:209:13) at addChunk (_stream_readable.js:305:12) at readableAddChunk (_stream_readable.js:282:13) エラーが起きた時は全てのストリームに close イベントが発行される 0-th stream: close
各イベントは、
end
: Readable ストリームが読まれ終わった時に発火finish
: Writable ストリームが書かれ終わった時に発火close
: ストリームとその使用しているリソースが閉じた時に発火 (でも発火しないストリームもあるらしい)
と言う感じか?
まとめ
確認できたこととしては以下
- どちらのケースでも
close
が呼ばれて使用しているリソースを閉じている。 - transform は
Duplex
でReadable
かつWritable
なのでend
とfinish
が呼ばれている。
感想
Transform
が close
されるのはどう言うことだ?と悩む。
あと、根本的な問題として、ストリーム閉じなくて良いケースを理解できていない。文字列から単なるメモリに乗っている Readable なストリーム作った場合は、閉じなくて良いのか?GC されるか?
公式ドキュメントに promisify
で pipeline
を Promise 化した時の例も書いてあったので、async/await
で書きたい時はそれを使う。
Node.js でオブジェクトの配列からストリームを作成する
多分知っておくべきこととして、ストリームはバージョン 12.3.0 でかなり変化があった。この記事は 11.15.0 で書いている。
Node でオブジェクトの配列からストリームを作る方法。
const { Readable } = require("stream"); const readable = Readable.from({ objectMode: true, read() {} }) オブジェクトの配列.map((o) => readable.push(o)) readable.push(null)
12.3.0 以降 は以下で良い。
const { Readable } = require("stream"); const readable = Readable.from(オブジェクトの配列)
ストリーム自体の使い方があまりわかっていない。
とりあえず動かす
エラー時、ストリームを頑張って閉じなければいけないと思う、 destroy()
とか使う。
const { Readable, Transform } = require("stream"); // テストデータ作成 const testData = new Array(1000) .fill("") .map((_, i) => ({ id: i, val: Math.random() })); testData.push(null) // Readable ストリーム作成 const readable = new Readable({ objectMode: true, read() {}, }); // Transform ストリーム作成 const stringifyTransform = new Transform({ transform(data, _encoding, callback) { this.push(JSON.stringify(data)); callback(); }, objectMode: true, }); // この辺微妙 stringifyTransform.on("error", () => readable.destroy()); process.stdout.on("error", () => { readable.destroy(); stringifyTransform.destroy(); }); // 入力 -> 変換 -> 出力 readable.pipe(stringifyTransform).pipe(process.stdout); // 入力へデータを入れる testData.forEach((o) => readable.push(o));
標準出力にオブジェクトの文字列が表示される。
ストリームの閉じる閉じないの話
ストリームが閉じているかどうか調べる方法がわからない。イベントを監視していればできるがもっと良い方法はないだろうか? (12.3.0 以降とかだとプロパティがある)
以下はストリームの挙動を確認するための、ストリームを標準出力へパイプするコード。失敗させたりしてイベント拾う。
const { Readable } = require("stream"); // Readable ストリーム作成 const readable = new Readable({ objectMode: true, read() {}, }); console.log(0, readable.readableFlowing); // => null const pipe = readable.pipe(process.stdout); console.log(1, readable.readableFlowing); // => true console.log(2, readable.push("ok\n")); // => true // 出力あり console.log(3, readable.push({})); // => true // pipe 先の process.stdout がエラー起こして失敗 pipe.on("error", (e) => { console.log(4, readable.readableFlowing); // => false console.log(5, readable.push("ok\n")); // => true // ただ pipe が切れているので出力はない readable.destroy(); // readable を切る }); readable.on("close", (e) => { console.log(6, "close"); // destroy() で close が発生する。pipe 先のエラーでは切れたりしないようだ });
出力
0 null 1 true 2 true 3 true ok 4 false 5 true 6 'close'
readableFlowing
: パイプされているかどうか。最初 (0) では繋がっていないのでnull
。繋がると (1)true
、エラー発生後 (4) は切れててfalse
。pipe
のerror
を拾うようにして、その中で各ストリームを切っていけば良いっぽい。
感想
API ドキュメントのストリーム部分 ちゃんと読んだほうがいいのだろうな、と思いました。英語読むのちょっと気合がいる。
疑問たくさんある
- エラー時に
transform
も切る必要があるだろうか? - 各種イベントの正しい使い方。
error
起きた時に閉じるとかちゃんとしなきゃダメだがあまり整理できてない。finish
?end
? - ストリーム作成時の
read() {}
。pipe
だと不要?実装してみる? Transform
はReadable
とWritable
を継承しているらしいがWritable
っぽさが見えてない。gzip
のTransform
の実装どんなだ
fetch
の body
でストリーム取得できるから画像を canvas に書き込むとか楽しそう。何に使えるかはわからない。加工とかしてみる?RGB から一色なくすとかはできそう。ただそれは何のために?
ストリーム Java でしか使ったことがなかった
GZIPOutputStream と ByteArrayOutputStream と try with resources
Java の try with resources
は途中で return
してもリソースの close
をしてくれるけど、なんか間違えた。
GZIPOutputStream
と ByteArrayOutputStream
を使って以下のような、データを圧縮してバイト列にして返す処理を書いていました。( InputStream
の部分は実際は外部のファイルのストリームとかになると思います)
String input = "hogehogehoge"; InputStream in = new ByteArrayInputStream(input.getBytes()); try ( BufferedReader reader = new BufferedReader(new InputStreamReader(in)); ByteArrayOutputStream out = new ByteArrayOutputStream(); BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new GZIPOutputStream(out))) ) { String line; while ((line = reader.readLine()) != null) { writer.write(line); writer.newLine(); } return out.toByteArray(); } catch (IOException e) { e.printStackTrace(); throw new RuntimeException(e); }
すると次の例外が発生。
java.io.EOFException: Unexpected end of ZLIB input stream
これは GZIPOutputStream を閉じていないときに出る例外で、「 try with resources
だから閉じられるのでは?」と思いましたが、 toByteArray
しているタイミングはまだ処理が try
を抜けていないので閉じられていなかったようです。
以下のように変更して解決しました。バイト列を取得するのは try
を抜けてから行うようにしました。
String input = "hogehogehoge"; InputStream in = new ByteArrayInputStream(input.getBytes()); ByteArrayOutputStream out = new ByteArrayOutputStream(); try ( BufferedReader reader = new BufferedReader(new InputStreamReader(in)); BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new GZIPOutputStream(out))) ) { String line; while ((line = reader.readLine()) != null) { writer.write(line); writer.newLine(); } } catch (IOException e) { e.printStackTrace(); throw new RuntimeException(e); } return out.toByteArray();
感想
すぐ終わると思って書いたコードが動かなくて夜が明けてしまった。
最近景気が悪いので、仕事がなくなったら各地のお祭りを巡る旅とかしたい。
修了証明書
Certificate of Completion
スライドとかでよかった気がするけど、なんとなく html で修了証明書を書いてみました。css は無限に時間を溶かすと思います。
See the Pen Certificate of Completion by ushumpei (@ushumpei) on CodePen.
Mac の Chrome でしか確認していない。もしかしたら他の環境でフォントとかダメかもしれないです。
COURSE DETAILS (リボンの下の細かい文字) が短すぎると良い感じの形にならないです。三文くらいあると印刷した時に A4 サイズにうまくおさまる。
参考
ありがとうございました。
リボン
リボンのコード、border で三角形を作るやつの応用で白い三角形を使って切り口を表現する。
枠
画像使わないと縁取りかっこよくならないかと思ったけど、radial-gradient
と linear-gradient
を使って丸みを出している。ただコードちゃんと追えていない。
感想
効力のない、お気持ちのやつ。というか正式なフォーマットがどこかにあるんだろうな。人に教えるほど何かを極めているわけでもないのにどこへ向かっているんだろうか私は。
スマホで見るとビールのラベルっぽい。
VSCode で Ruby 書くための設定
PC 変えた時に vim & ruby のいい感じの設定が失われてしまいしばらく書かないから放置していたらまた書くことになり VSCode でなんとなく書いてたらいい加減不便さを感じてきたので本当にとりあえずの設定を調べました。(早口)
この辺ができるように設定。
- 構文チェック
- 補完と定義ジャンプ
あとこれは
でやってます。
やること
VSCode に Ruby の拡張があるので入れる (名前そのまんま Ruby
のやつ)
GitHub - rubyide/vscode-ruby: Provides Ruby language and debugging support for Visual Studio Code
拡張を動かすために rubocop
があればいいそうなので入れる
gem install rubocop
VSCode の設定ファイルに以下を追加
"ruby.lint": { "rubocop": true }, "ruby.useLanguageServer": true, "ruby.intellisense": "rubyLocate",
説明
"ruby.lint": { "rubocop": true }
: Linter はrubocop
"ruby.useLanguageServer": true
: Language Server 使う (構文チェックしてくれるようになる)"ruby.intellisense": "rubyLocate"
: インテリセンスの設定 (ある程度、補完、定義ジャンプができるようになる)
終わり
rubyLocate
のオプションには定義検索するパスが設定できるようで、うまくやれば結構便利になるかもです?脳死で初期設定できないと割と放置しがちな癖があり、よく無いですね。
open-uri の close とか
ruby の open-uri
の open
で、サイトをスクレイピングするプログラムを見かけたのだけれど、「close
呼ばなくていいの?」と思って色々調べたのでメモしておきます。結論としては、ブロック渡した時は呼ばなくていいけど基本的には呼ぶ。
調べたのは ruby 2.3
なので古くなるかもです、がそんなに変わらなそうな部分?
ブロック渡した時は呼ばなくていい
open
メソッドの実装を見てみます。
> require 'open-uri' => true > method(:open).source_location => ["/System/Library/Frameworks/Ruby.framework/Versions/2.3/usr/lib/ruby/2.3.0/open-uri.rb", 29]
https://github.com/ruby/ruby/blob/ruby_2_3/lib/open-uri.rb#L29-L39
def open(name, *rest, &block) # :doc: if name.respond_to?(:open) name.open(*rest, &block) elsif name.respond_to?(:to_str) && %r{\A[A-Za-z][A-Za-z0-9+\-\.]*://} =~ name && (uri = URI.parse(name)).respond_to?(:open) uri.open(*rest, &block) else open_uri_original_open(name, *rest, &block) end end
今回は引数として url 文字列の見渡すので、引数 name
は URL として解釈され、URI#open
が呼ばれるみたいです。そこだけ切り出して実行してみます。
> (uri = URI.parse('https://ushumpei.hatenablog.com')).respond_to?(:open) => true > uri => #<URI::HTTPS https://ushumpei.hatenablog.com> > uri.method(:open).source_location => ["/System/Library/Frameworks/Ruby.framework/Versions/2.3/usr/lib/ruby/2.3.0/open-uri.rb", 716]
https://github.com/ruby/ruby/blob/ruby_2_3/lib/open-uri.rb#L716-L718
OpenURI::OpenRead#open
が呼ばれるみたい。その中から OpenURI.open_uri
が呼ばれている。
https://github.com/ruby/ruby/blob/ruby_2_3/lib/open-uri.rb#L132-L166 (抜粋)
if block_given? begin yield io ensure if io.respond_to? :close! io.close! # Tempfile else io.close if !io.closed? end end else io end
153 行目で block_given?
によりブロックが与えられていた時は close
を呼ぶように実装されている (ローンパターンか)
なのでブロック渡せば close
は明示的に呼ばなくてもいいことがわかりました。
気になったコード
html = open('https://ushumpei.hatenablog.com').read
え、これ絶対 close
されないのでは?と思ったので lsof
で開きっぱなしなことを確認しました。確認コマンドとその意味は以下の感じになります。
$ lsof -c ruby | grep open-uri | wc -l
ruby
が開いているファイルを見たいので-c ruby
でコマンド名を指定しました。- 一時ファイル名に
open-uri
が付いていたのでそれで絞り込み - 行数取得
該当コード実行前の lsof -c ruby | grep open-uri | wc -l
は 0
該当コード実行 (irb
は開きっぱなし)
> require 'open-uri' => true > 10.times { |n| open('https://ushumpei.hatenablog.com').read } => 10
該当コード実行後の lsof -c ruby | grep open-uri | wc -l
は 10
。irb
を終了するとファイル数は 0
に戻りました。
ブロック渡した時は irb
開きっぱなしの状態でもファイル数は 0
のままです。
(注意: open-uri
で open
した時に、lsof
で open-uri
がひっついていている行が出力される、という事象は、ソースコードレベルでは確認しきれていません。ちょっとお粗末になり申し訳ないですが、「多分 open
時に作成された開かれているファイルの行だ」という予想に基づいている、と断っておかなければいけません)
まとめ
open(XXX).read
はopen(XXX) { |f| f.read }
とかに変えようlsof
ってlist open files
なんですね。ポート使っているプロセス調べるコマンドかと思っていた。- というか linux が全てをファイルという形で抽象化しているアレのアレなのか。(よく知らない)
- そういえば
open
って|ls
とかでパイプつけたコマンド渡すと実行できたりしてアレらしいので、OpenURI.open_uri
呼んじゃった方がいいっぽい。
追記 2019/04/07
- 可読性微妙かもだけど、
open(XXX).read
の書き換えopen(XXX, &:read)
もいけるのか、 ruby すごい
静的 html を BASIC 認証付きで雑に公開する方法
雑なメモです (なんかやばかったら教えていただきたいです)
1. 適当なリポジトリを作成する
$ mkdir private $ cd private $ git init
2. html ファイルとかを配置する
$ echo '<!DOCTYPE html><html><head><title>private</title></head><body><h1>I am private!</h1></body></html>' > index.html
3. heroku アプリを作成して、 php のビルドパックを追加する。php プロジェクトだと認識させるために composer.json
を作成する。
これ、 php じゃないのに php 使う気持ち悪さがあります、多分違う方法ありそう。
$ heroku create $ heroku buildpacks:set heroku/php $ composer init
4. .htaccess
、.htpasswd
を作成する
デフォルトの設定で apache を使用するようになっているけど、Procfile
に web: heroku-php-apache2
とか書くほうが確実かも
$ echo 'AuthUserFile /app/.htpasswd AuthType Basic AuthName "Restricted Access" Require valid-user' > .htaccess $ htpasswd -c ./.htpasswd なんか好きなユーザー名
(名前が .ht*
のファイルにはアクセス制限かけてるみたいだけど、リポジトリに .htpasswd
含めるのってどうなのだろう)
5. 必要なものステージしてコミットして heroku に push する。
$ git add . $ git commit -m 'Initial commit.' $ git push heroku master
6. 終わり
$ heroku open
PostgreSQL 10 でパーティションをまるっと切り替えてみる
PostgreSQL の 10 では宣言的パーティショニングが使えるようになったそうですね (しかし僕は 10 以前でパーティショニングしたことがなかったので感想がない)
これを使ってデータの一括削除、一括追加を実現するために、パーティションの切り替えについて練習します。
準備
Docker で PostgreSQL 10 を用意します
$ docker run --name postgres -p 5432:5432 -d postgres:10
テーブルの作成
こんな感じのテーブルを作ります。ユーザーがいつどこに居たかをひたすら集める謎のテーブルです。
user_id | latitude | longitude | created_at |
---|---|---|---|
integer | decimal(11, 8) | decimal(11, 8) | timestamp |
パーティションは created_at
の range
で切って見ます。
create database testgres; \c testgres create table locations ( user_id integer not null, latitude decimal(11, 8) not null, longitude decimal(11, 8) not null, created_at timestamp not null ) partition by range (created_at);
パーティションの作成
パーティションは partitions スキーマ以下に作っていきます。一秒刻みで適当なデータを作成します。 20181010 と 20181011 のパーティションを作成しました。
create schema partitions; create table partitions.locations_20181011 partition of locations for values from ('2018-10-11') to ('2018-10-12'); insert into partitions.locations_20181011 select (random() * 10000)::int, 130 + (20 * random())::decimal(11, 9), 20 + (20 * random())::decimal(11, 9), generate_series('2018-10-12'::timestamp - '1 sec'::interval, '2018-10-11', -'1 sec'::interval) ; create table partitions.locations_20181010 partition of locations for values from ('2018-10-10') to ('2018-10-11'); insert into partitions.locations_20181010 select (random() * 10000)::int, 130 + (20 * random())::decimal(11, 9), 20 + (20 * random())::decimal(11, 9), generate_series('2018-10-11'::timestamp - '1 sec'::interval, '2018-10-10', -'1 sec'::interval) ;
クエリしてみると、ちゃんとパーティションを使った検索が行われているのがわかります。
explain select * from locations where created_at = '20181010 12:00:00'; QUERY PLAN ----------------------------------------------------------------------------------- Append (cost=0.00..1716.00 rows=1 width=28) -> Seq Scan on locations_20181010 (cost=0.00..1716.00 rows=1 width=28) Filter: (created_at = '2018-10-10 12:00:00'::timestamp without time zone) (3 rows)
パーティションの切り替え
以下が概要になります。
- 普通のテーブルとして
locations_20181011
を作成します。 - 現在のパーティション
partitions.locations_20181011
をlocations
から Detach します - 新しいパーティションとして
locations_20181011
をlocations
に Attach します - 必要なくなった
partitions.locations_20181011
を削除します - (跡片付け) 新しいパーティションとして Attach した
locations_20181011
をpartitions.locations_20181011
にリネームします
手順 3, 4 と、なるべく alter table locations
をまとめて行うことで、テーブルロック時間を少なくしていこうと思います。
新しく locations_20181010
を普通のテーブルとして public スキーマに作成します。(すでにあるものと被らなければ他の方法でもいい)
ここでは check 制約を追加して、Attach される際のパーティションの制約チェックを先立って行っています。これがないとテーブルロック時間が attach の際に増えてしまうそうです。
create table locations_20181011 ( user_id integer not null, latitude decimal(11, 8) not null, longitude decimal(11, 8) not null, created_at timestamp not null, check (created_at >= '2018-10-11' and created_at < '2018-10-12') ); insert into locations_20181011 select (random() * 10000)::int, 130 + (20 * random())::decimal(11, 9), 20 + (20 * random())::decimal(11, 9), generate_series('2018-10-12'::timestamp - '1 sec'::interval, '2018-10-11', -'1 sec'::interval) ;
パーティションの切り替えを行います。現在使用されている partitions.locations_20181011
を切り離し、新しく作った locations_20181011
をくっつけます。
begin; alter table locations detach partition partitions.locations_20181011; alter table locations attach partition locations_20181011 for values from ('2018-10-11') to ('2018-10-12'); commit;
いらなくなったパーティションの削除、跡片付けとして作成したパーティションのリネームを行います。
drop table partitions.locations_20181011; alter table locations_20181011 set schema partitions;
以上です。
感想
- テーブルロック怖い
- トリガー怖い
- インデックスも適切に
iOS 版 Chrome の <input type="date">
解決策とかわかったら書きますが、なんなんでしょうかこれ?
iOS 版 Chrome の input type="date" 入力時、何かの条件を満たすと Picker から「消去」が消える
ScreenRecording 07 03 2018 01 51 23
- iOS 11.4
- Google Chrome: 67.0.3396.87 (Official Build) stable (64 ビット)
WebAssembly って?
ブラウザからアセンブリ言語を実行できる仕組みが WebAssembly という理解です (雑魚)。
とりあえず、動かしてみます。
Emscripten
C/C++ から WebAssembly で実行可能なアセンブリにコンパイルするツールだそうです。C/C++ に特に思い入れはなく、仕事で使ったことはないですが、例えば C/C++ で書かれたライブラリを JavaScript ライブラリに変換するとかできるのかなーと思います。
C/C++からWebAssemblyにコンパイルする を参考に emscripten をインストールします。(すっごい時間かかりますね)
使う
適当な cpp ファイル (main.cpp) を作成します (これ c といってもいいのでは)
#include <stdio.h> #include <emscripten/emscripten.h> extern "C" { int main() { puts("Hello, World"); } int myFunction(int x) { return ++x; } }
em++ main.cpp -s EXTRA_EXPORTED_RUNTIME_METHODS="['ccall']" -s EXPORTED_FUNCTIONS="['_main', '_myFunction']"
を実行します。ここでは
- クライアント側から関数を呼び出す
Module.ccall
を使用するためにEXTRA_EXPORTED_RUNTIME_METHODS
でccall
を指定します。 - 呼び出せる関数を
EXPORTED_FUNCTIONS
で指定します。関数名に_
プレフィックスをつけなければいけないそうです。
以下のファイルが生成されました。
a.out.js
a.out.wasm
main.cpp
index.html
を作成してそこから a.out.js
を読み込みます。
<html> <head> <script src="a.out.js"></script> <script> function callMyFunction() { var count = document.getElementById('count') var nextCount = Module.ccall('myFunction', 'number', ['number'], [count.innerText]) count.innerText = nextCount } </script> </head> <body> <p>Count: <span id="count">0</span></p> <button onclick="callMyFunction()">Call C++ Function</button> </body> </html>
しかしこれでは動きません。http 経由で配信しなければいけないそうです。express
で配信するようにします。yarn init && yarn add express
でサーバーを準備します。index.js
を以下のように記述しました。
var express = require('express'); var app = express(); app.use(express.static('public')); app.get('/', function(req, res) { res.sendFile(__dirname + '/public/hello.html'); }); app.listen(3000);
またディレクトリ構造を少し変えます。コンパイルしたものは public
以下に放置しています。
. ├── index.js ├── node_modules ├── package.json ├── public │ ├── a.out.js │ ├── a.out.wasm │ ├── index.html │ └── main.cpp └── yarn.lock
サーバーを node index.js
で起動すると localhost:3000 でアクセスできるようになます。
感想
また何かに入門だけしているやつです
Pro Git 2nd Edition 読んでる
最近手が痛くてプログラミング時間を少々減してて、久しぶりに本でも読もうかという気分になっています。
git ちゃんとわかっていなかったので、 Pro Git 2nd Edition を読み始めました。ちょっと面白いことがあったのでメモします。
前提: git は差分ではなくファイルのスナップショットを保持している
git が既存の VCS (Version Control System) と大きく異なった点として、ファイル変更履歴の管理方法が 変更されたファイルの差分ではなく 、 変更されたファイル全体のスナップショット であることだと書かれています。自分の理解だと、ファイル容量などを考えると変更差分を保持していた方がいいと思いますが、ぶっちゃけテキストファイルが主だしまあスナップショットでも大丈夫なんだろうなー、くらいの軽い感じでした。しかしこのことが結構重要で、 git がめちゃくちゃ速い理由につながっているようです。
リポジトリ内のファイル全体のスナップショットが作成されるのは commit 時で、次の要素が .git/objects 以下にファイルとして作成されます。ファイル形式は blob でファイル名はそのデータのハッシュ値です。
- blob オブジェクト: 変更後のファイル
- tree オブジェクト: ディレクトリツリー。変更対象のファイルを持っていたディレクトリに対して、ツリーのノードの参照先 (tree, blob オブジェクトのハッシュ値) を書き換えた tree オブジェクトが作成される。(毎回リポジトリルートの tree は更新される)
- commit オブジェクト: 新しく作成されたリポジトリルートの tree への参照と、parent commit オブジェクトへの参照と作成者情報やコミットメッセージ。
言葉だとややこしいですが実物は以下のようになります(ハッシュ値は適当です)。
blob オブジェクト: .git/objects/f5/83c304ea36b6fa554eb01381e781b04e45477f
# Pro Git URL: [https://git-scm.com/book/ja/v2](https://git-scm.com/book/ja/v2)
tree オブジェクト: .git/objects/20/4bfe00b89a265e7c16e8688a90dfb86e52c5eb
100644 blob f583c304ea36b6fa554eb01381e781b04e45477f README.md
commit オブジェクト: .git/objects/31/ba323616cc8cbc543882c5a4eef6aa95eef803
tree 204bfe00b89a265e7c16e8688a90dfb86e52c5eb author ushumpei <mail@example.com> 1528220889 +0900 committer ushumpei <mail@example.com> 1528220889 +0900 Initial commit.
(.git/objects
以下のファイルは git cat-file
コマンドに -p
オプションをつけてハッシュ値の頭から 6 文字を引数にして実行すると、中身を閲覧することができます: .git/objects/f5/83c304ea36b6fa554eb01381e781b04e45477f
なら git cat-file -p f583c3
)
要するに?
要するに私が面白いと感じたのは、特定のコミットに入っているファイルを取り出したいときは、commit オブジェクトのハッシュを使って、
commit オブジェクト -> tree オブジェクト -> (ツリーの探索) -> blob オブジェクト
という風に取り出してくれるので、めっちゃ速い、すげー、ということです。これは過去のコミットでも、分岐してある程度進んだブランチのコミットでも同じように行われます。(多分)
感想
「git 速い」って何と比較して、というと svn なのですが、ベンチマークとか取っていないので (どう比較するか謎ですが) 怒られそうですね。「考え方の変化で処理が変わった」ということにテンションが上がっただけです。
React 360 で vnc クライアントを作って Oculus Go のブラウザから自分のPCを見る (未完成)
注意: チラシの裏です
こんにちは
Oculus Go に Mac 用のリモートデスクトップがなかったので (現在: 2018/05/31) なんとかできないかと思って色々やっています。理想的には HMD つけながら無線キーボードでお布団に入りながらコーディングしたいという思いがあります。
でも全然完成までの道のりが見えないので、現状の整理のためのメモを書きます。
とりあえず Oculus Go のブラウザから Mac の画面に ws + vnc で繋がった!目標は布団に入ったままプログラミングしたい。(VR 時の写真も取れるようにしたい) pic.twitter.com/RqVFhqEkmo
— ushumpei (@ushumpei_) 2018年5月25日
(これ以降進展なし)
なんで作ってるんですか?
Oculus Go で使える Mac 用のリモートデスクトップアプリが見つからなかったためです (有料のやつある?)。ブラウザで画面共有できる Web サービスもあるのですが、仕事のコードとか書くことを考えるとローカルネットワークで完結するのが気分的に一番良いんじゃないか?と思ったため作り始めました。(自作すること自体はそれはそれでリスクですが)
Mac にはデフォルトで「vnc」と呼ばれる (?) リモートデスクトップのサーバーが入っているため、それ用にクライアントのコード書いたら良いんじゃないか、ということで vnc 周りを調べつつ作っています。
なんで React 360 なんですか?
- Android studio で Android Mobile SDK: C++ 混じってて読めなかった
- Unity: 入ってるけど使ったことない
- React 360: 既存の知識使ってできる
という消極的な理由からです。 1 週間くらいで終わらせたかったのでがっつり学習する必要があるものは避けました(終わってないけど)。今考えると Unity は空間すでにあるし知見も多いため一番良い気がしてます。
どんな感じで作りますか?
という構成を元に、
- 同じローカルネットワーク内のブラウザから WebSocket で Mac の vnc サーバーと接続
- vnc サーバーから送られてくる画面データを React 360 内の canvas に描画
- ブラウザのキー入力やポインター移動イベントをサーバーに通知する
していくように作っています。
できたこと
- React 360 の平面オブジェクト (Plane) に canvas を貼り付ける
- RFB 3.8 プロトコル (vnc のプロトコル) を実装してサーバーとの接続を確立する
- ブラウザでサーバーから画面データを受信する
- 受信したデータを canvas に描画する
できてないこと
- ブラウザのイベントをサーバーに通知する
- ブラウザ -> サーバー への通信は、初めはいらないと思っていたんですがユーザーのサインインが必要なので、ないとログイン画面を延々とみていなければならなくなります
- 画面が全更新になっているので差分更新で済むようにしたい
どうします?
- RFB プロトコルのコードを整理する
- React 360 を一旦やめる
一応リポジトリ: GitHub - ushumpei/VncClient: Vnc client for browser
感想
遊んでないで仕事探す
既存の Laravel プロジェクトに自動構文チェックを軽く入れる
Laravel 始めました。今回はコードの自動構文チェックをすごく軽く入れる話。ゆくゆくは CI に含める予定だけどまだその時間と根回しがないので簡単にやります。
どんな効果を狙っているか?
- レビューにおいて構文に関する指摘が大半を占めているのでその時間を削減する
- 構文に関する規約が暗黙知化しているので (レビューで指摘が多く上がる原因のひとつ)、規約を管理できる構成にする
- 書くのが面白そう!
どんなものを作るか?
- git のプレコミットフック (
git/hooks/pre-commit
) で追加/変更したコードの構文チェックをして、違反していたらコミットできなくする (コミットできない、はやりすぎか?) - 違反している箇所については構文チェックライブラリのエラーを出す (変更行だけチェックできないか? -> 今回はできなかった...)
- 緊急時のためにフックを無視する手段も作っておく(すでにプレコミットフックとして仕組みがある? ->
--no-verify
option) - プレコミットフックがない環境に影響を与えない (まあ多分 pre-commit 配置しなければ良いだけと思う)
- 構文チェックのルールはファイルで管理できるようにする (構文チェックライブラリが標準で備えているはず)
- とりあえずチェック対象は、
js
とphp
(当面は拡張子によって判定)
準備
使うものとしては以下を考えています
- phpcs (PHPCodeSniffer)
- eslint
Laravel と言っていますが、git 使ってれば応用効くと思います。それぞれ composer
, npm
(or yarn
) で開発環境の依存ライブラリとして入れます。
(これらがそもそもグローバルインストールされている状態だとどうなるんだろう…?それも忘れずに確認しなきゃ…)
pre-commit 作ってみる
まずは何も構文チェックライブラリ入れずにフックだけ作成してみます。処理の流れとしては、 git commit
を実行した直後に、pre-commit
が呼ばれ、スクリプトの戻り値(?) が 0
以外の時に commit
が停止されるというものです。
.git/hooks/pre-commit
#!/bin/sh echo hoge exit 1
また、スクリプトの実行権限を 755 に変更しておきましょう。
$ touch fuga
$ git add .
$ git commit
hoge
とりあえず commit 時にフックが実行されるのがわかりました。あとは処理を書いていきます。
対象ファイルの抽出
git のコマンドを使用して HEAD
との比較で、追加、更新があったファイルを取得します。コードは以下のようになります。
#!/bin/sh # Get against commit hash to compare. if git rev-parse --verify HEAD >/dev/null 2>&1 then against=HEAD else # Initial commit: diff against an empty tree object against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 fi # Redirect output to stderr. exec 1>&2 # Get added and modified files. files=`git diff-index --cached --name-only --diff-filter=AM ${against}` echo ${files}
比較結果は files
に、スペース区切りの文字列で入ってきます (配列として取り扱うかどうかは悩みましたが、特に必要ないので git diff-index ...
の戻り値をそのまま使います。)
ここでは --diff-filter=AM
によって追加 (A)
、更新 (M)
のあったファイルのみを抽出しています。削除やリネームしただけのファイルはチェックしません。 (削除ファイルやリネームファイルのチェックは、自分が書いたわけでも無いファイルの構文を直せということになり、プロジェクトの整理作業に負のモチベーションを与える気がします。)
exec 1>&2
は標準出力をエラー出力にリダイレクトしています。このスクリプトで出力が行われることは、即ちエラーだからです。 (と偉そうにいってますが、この行までは全て .git/hooks/pre-commit.sample
から盗んできたものです。)
対象ファイルを拡張子で分類
ここまでくると特別なことはありません。シェルスクリプトを書くだけです。
... # ↓チェック対象のファイル取得後の処理 # Check syntax. is_error=0 output="" php_files="" js_files="" for f in ${files} do extension=${f##*.} case ${extension} in php) php_files+="${f} " ;; js) js_files+="${f} " ;; esac done if [ -n "${php_files}" ] then output+=`phpcs ${php_files}` is_error+=$? fi if [ -n "${js_files}" ] then output+=`eslint ${js_files}` is_error+=$? fi if [ ${is_error} -gt 0 ] then is_error=1 echo "${output}" | less else is_error=0 fi exit ${is_error}
これで一応、複数ファイルに対してまとめて構文チェックを行えるようになりました ( 上のスクリプトは構文チェックコマンド入っていないと動かないですが )。出力が流れてしまうので、構文エラーに関しては less
に渡して表示するようにしています。色々思うところは以下かなと思います。
- 文字列連結の半角スペースがダサいので配列使うべきでは?
- 拡張子ごとにほぼ同じ処理を書いているので、新しい構文チェック追加時に変更大きいよ?
本当は、構文チェックライブラリに変更対象ファイル全部渡して、そっちでフィルタリングしてほしいという気持ちなので今の所シンプルすぎるくらいがちょうどいいのかなと思います。
phpcs
, eslint
の設定
phpcs
開発環境の依存パッケージに PHPCodeSniffer
を追加します。
composer require --dev 'squizlabs/php_codesniffer'
phpcs
は ./vendor/bin/phpcs
に入っているのでスクリプトからはそれを読むことにします。
phpcs
は実行時に --standard=
オプションで使用するルールや設定ファイルを指定することができます。そのほかにも設定ファイルを phpcs.xml
という名前で作成しておけば自動的にそのファイルを読んでくれるようです。(自分ディレクトリから親のディレクトリを順に登って行って phpcs.xml
を見つけたら使用してくれるみたいです)
PSR2 を継承しつつ色々調整できるように雛形 phpcs.xml
を作成してみました。
<?xml version="1.0"?> <ruleset name="MyRule"> <description>Coding standard based on PSR2 with some additions for my project.</description> <!-- You can add your rules below. For example, you can include new standard, like that; <rule ref="PEAR" /> If you want to know more about phpcs, See: https://github.com/squizlabs/PHP_CodeSniffer/wiki See also to know the notation of this file: https://github.com/squizlabs/PHP_CodeSniffer/wiki/Annotated-ruleset.xml --> <!-- Include the whole PSR2 standard --> <rule ref="PSR2"> <!-- You can exclude specific standard by adding exclude_tag and name_attribute here; <exclude name="PSR1.Classes.ClassDeclaration.MissingNamespace" /> --> </rule> </ruleset>
次は .git/hooks/pre-commit
で、ローカルのコマンドと、設定ファイルのパスを使うようにスクリプトを修正します。(設定ファイルは存在チェックだけ必要としています)
app_root_path=`git rev-parse --git-dir`/.. phpcs=${app_root_path}/vendor/bin/phpcs phpcs_config=${app_root_path}/phpcs.xml ## Execute check. ### PHP if [ -n "${php_files}" ] then # Check installation of phpcs. if [ -e "${phpcs}" ] then # Check existence of cording standard file. if [ -e "${phpcs_config}" ] then # For upgrading rule file, add -s option to display rule's name. output+=`${phpcs} -s ${php_files}` is_error+=$? else output+='\nNOTE: phpcs configuration file is not found. going to check based on psr-2' output+='\n' output+=`${phpcs} --standard=PSR2 ${php_files}` is_error+=$? fi else output+='\nNOTE: phpcs is not installed. php syntax checking is skipped.' fi fi
うん、すごく読みにくい気がします。他の構文チェックする前にシェルスクリプトの構文チェックしろよ的なブーメランですね。
phpcs.xml
が見つかった時だけ -s
オプションを渡してルール名を表示しておいてあげます。ルールファイルの修正の障壁にならないためにも。
ファイルがないときは何もしない、コマンドがないときはお知らせ、設定ファイルあるときはそれで実行、ないときはPSR2で実行という流れです。
eslint
急に疲れてきてしまったのですが、だいたい phpcs
と同じになります。こちらのインストールは yarn add --dev eslint
や npm install --dev eslint
で行います。
開発環境の依存性でインストールされた後は、初期の雛形ファイルを作成します。 Use popular style guide などで適当に js
ファイルとして作っておきました。(ファイル形式も pre-commit でみなきゃいけないかもしれないけど今は js 決め打ちで行きます)
$ yarn add --dev eslint $ ./node_modules/.bin/eslint --init $ ls .eslint.js .eslint.js $ yarn # init 後にもう一回パッケージのダウンロードが必要...
先ほど同様に .git/hooks/pre-commit
を修正します。変更内容みたい方いましたら github でご確認ください。 pre-commit (すみません疲れてしまいました)
まとめ
shell じゃなくて php か js で書けばよかったのではと今更ながら思います。特に制御構文の省略記法とかどれを使っていいか悩んでしまい、ものすごくベタに書きました。それこそ構文チェックが必要な気がします。
まあ、ないよりはマシか、、、という気持ちで書きました。最低限動いたら後は使いながら修正していけばいい、という言い訳をしておきます。