TypeScriptで"SyntaxError: Unexpected token import"になったときの解決方法

昨日の記事を公開したところ、

blog.zuckey17.org

↓のように、

ライブラリと型定義のimportとで分けている必要はあるのか?

という指摘をいただきました。

const { createConnection } = require("mysql"); // ライブラリのimport
import { Connection, MysqlError } from "mysql"; // 型定義のimport

僕も実装をしている際に、以下のような書き方をしようとしましたが、

import * as mysql from "mysql";

トランスパイル後のjsを実行するとランタイムで以下のようなエラーが出てしまいました。

******/ts-mysql/src/index.ts:1
(function (exports, require, module, __filename, __dirname) { import * as mysql from 'mysql';
                                                              ^^^^^^

SyntaxError: Unexpected token import
    at createScript (vm.js:80:10)
    at Object.runInThisContext (vm.js:139:10)
    at Module._compile (module.js:607:28)
    at Module.m._compile (/Users/matsumura/dev/src/github.com/zuckeyM-17/ts-mysql/node_modules/ts-node/src/index.ts:400:23)
    at Module._extensions..js (module.js:654:10)
    at Object.require.extensions.(anonymous function) [as .ts] (/Users/matsumura/dev/src/github.com/zuckeyM-17/ts-mysql/node_modules/ts-node/src/index.ts:403:12)
    at Module.load (module.js:556:32)
    at tryModuleLoad (module.js:499:12)
    at Function.Module._load (module.js:491:3)
    at Function.Module.runMain (module.js:684:10)

tsconfig.json

結論としては、tsconfig.jsoncompilerOptions中のmoduleの値をes2015からcommonjsに変更すると解決しました。

module

TypeScript公式のcompilerOptionsリファレンスにおいて、
moduleの項には、

target === "ES3" or "ES5" ? "CommonJS" : "ES6"

という記載がされています

トランスパイル先がES5やそれ以下の場合、CommonJSを使いましょうということですね。
今回は、サーバサイドで動かすということで、WebpackBabelなどを利用せず、Node.jsとして動かすという実装だったので、CommonJSを指定する必要があったようです。

Node.jsとES2015のmoduleのインポートの方法の違い

moduleES2015を利用するとそれでは普通に使える、import/exportの記法は、そのままスルーされる。 型定義のimport句については、TypeScriptのトランスパイル時の静的解析に使われるだけなので、ランタイム時には影響がなかった。

ということでした。
つまり、サーバサイドで動かすとしても、WebpackBabelをかませるなどすると、問題なく利用できるようです。
そちらを検討しても良いかもしれません。

まとめ

TypeScriptのみならず、JavaScriptにおいても、モジュール解決や、import/export、が実行環境などによってややこしいです。
また、環境設定などは公式や人の記事を見て動けばそのまま使ってしまっていたり、一度固まればあまり振り返る機会がなかったりしていました。
日頃からtsconfig.jsonや、webpack.config.jsなどなどもやもやしながら使っている部分があるので、もう少ししっかり学習する必要があると感じました。

今回のブログをかけたのは、昨日のブログにたいして指摘いただけたからです。
もしこのブログでも気になる部分がありましたらコメントやTwitterで指摘いただけるとうれしいです。

おまけ

TypeScriptを簡単に実行するために、ts-nodeという実行環境を利用しています。

npx ts-node 実行ファイル

とすると、TypeScriptを実行することができます。

しかしながら、この状態では、importが利用できません。
そのため、import/exportを利用したい場合は、以下のようにprojectオプションを付ける必要があります。

# --project の引数は、tsconfig.jsonへのパス
npx ts-node --project tsconfig.json 実行ファイル

TypeScriptでMySQLを触るメモ

最近、TypeScript でサーバサイドを書く機会がありました。
DBに MySQLを利用していましたが、データの挿入や取得の簡単なものでも、少しハマったのでメモを残しておこうと思います。
TypeScriptについては、触りたてということで間違いなどがあれば、コメントやTwitterなどで指摘いただければと思います。

github.com

作成済みのデータベース

すでに作成しているデータベース、テーブルは以下のような状態になっています。

データベース名:sandbox テーブル名:users

users

id name
1 zuckey

インターフェース

interface User {
  id?: number;
  name: string;
}

事前準備

ライブラリのimportとMySQLコネクションを作成します。

const { createConnection } = require("mysql"); // ライブラリのimport
import { Connection, MysqlError } from "mysql"; // 型定義のimport

// コネクションをはる
const con: Connection = createConnection({
  host: "localhost",
  port: 13306,
  user: "root",
  password: "password",
  database: "sandbox"
});

SELECT

const selectSql = "SELECT * FROM users;";
const getUsers = (): Promise<User[] | Error> => {
  return new Promise((resolve, reject) => {
    con.query(selectSql, (err: MysqlError | null, results: any) => {
      if (err) {
        reject(err);
        return;
      }
      resolve(results);
    });
  });
}

getUsers().then((users: any) => {
  users.forEach((user: any) => console.log(user.name)); // "zuckey" と表示
  process.exit(0);
})
.catch((err: Error) => {
  console.log(err);
  process.exit(1);
});

INSERT

const insertSql = "INSERT INTO users SET ?;";
const createUser = (name: string): Promise<string | Error> => {
  return new Promise((resolve, reject) => {
    con.query(insertSql, { name: name }, (err: MysqlError | null, results: any) => {
      if (err) {
        reject(err);
        return;
      }
      resolve("success!");
    });
  });
};

new Promise((resolve, reject) => {
  con.beginTransaction((err: MysqlError) => {
    if (err) {
      reject(err);
      return;
    }
    resolve(getUsers());
  });
})
  .then((result: any) => {
    console.log(result); // "success!"と表示
    process.exit(0);
  })
  .catch((err: Error) => {
    console.log(err);
    process.exit(1);
  });

ハマった部分

@types/mysqlにおいて、Connection.query の第2、3引数のqueryCallbackの型定義は以下のようになっています。

https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/mysql/index.d.ts#L226

export type queryCallback = (err: MysqlError | null, results?: any, fields?: FieldInfo[]) => void;

そのため、上の例ではこちらにのっとっています。

しかしながら、「typescript mysql」でググると、3番目くらいに出てくる以下のリポジトリを見ると、

f:id:zuckey_17:20180317073552p:plain:w300

https://github.com/types/mysql#usage

queryCallbackの第2引数は、SELECTの場合 RowDataPacket[]、INSERTの場合 OkPacketになる、という風に読めます。
スター数などを見ると明らかに怪しいですが、使用例を出してあると、それっぽいな、と思ってしまいました。

まとめ

振り返るとそこまで難しくはないですが、ハマってしまったのは、サードパーティ性ライブラリの TypeScriptの型定義の扱いに慣れていなかったのかなと思いました。
引き続きTypeScriptを触っていき、学習しようと思いますが、MySQLの利用については、ORMの利用を考えようと思います。

雑な感想とメモ【PHPerKaigi 2018】@練馬区産業プラザ 20180310

phperkaigi.jp

前夜祭に引き続き当日も参加しました。

blog.zuckey17.org

トークの感想と聞いている最中のメモを残しておきます。

PHPの現場公開録音もありました!!

togetter.com

TODO: 資料とビデオのパスを追加

トーク

今からでも出来る!Wwbサービスモニタリング!! - @soudai1025

speakerdeck.com

soudai.hatenablog.com

RDBで有名な@soudai1025さんのWebサービスについての監視についてのお話。有名でお名前はしってたんですが、初めてトークを聞きました。
僕は普段アプリケーション層の開発をしていて、インフラをされている方ほど、監視は積極的に考えていなかったです。

そんな中で、話を聞いていたんですが、

  • 小さく初めてその後踏み込んでいくこと
  • 時系列データを取って可視化すること

などを強調して語られており、初心者にもわかりやすいトークでした。

SOLIDの原則って、どんなふうに使うの? - @hidenorigoto

ベストトーク賞おめでとうございました!!

[資料公開待ち]

とある新人と先輩のコミカルなやり取りを例にしてオープン・クローズド原則について説明してくださってました。
名前は聞いたことがありましたが、理解はしていなかったので非常にわかりやすく、他のかたもTwitterでおっしゃっていましたが、新人教育みたいな場でやってほしいトークでした。

「バリエーションからソースコードを守る」という表現が非常にキャッチーでわかりやすかったと思いました。

サーバーが完膚なきまでに死んでもMySQLのデータを失わないための表技 - @yoku0825

www.slideshare.net

PHPなしのMySQLのバックアップのお話。
監視と同様、普段あまり気にしていない分野でしたが、主に

という2つのバックアップ手法を用いてDBの復元が出来る方法を、注意点とともにまとめてくださっていました。
細やかな情報が書いてあるスライドを爆速で飛ばしていくスタイルで、資料はしっかり読み返さないとなという感じでした。

Hackで作るマイクロフレームワーク - @ytake

speakerdeck.com

事前に資料が公開されるスタイルだったので、読んでから参加しました。
Hackという言語は触ったことがなかったので、その概要と今後の大まかな流れについて知ることができました。

型定義ファイルのhhiファイルの紹介で、TypeScriptやFlowTypeのことを例に出されていて、結局そのへんはコミュニティの尽力なんだなっていう風に思いました。

また、前夜祭でのテスティングフレームワークにもありましたが、言語を勉強するときにフレームワークを作るというのが勉強になる、ということだったので、とりあえず公開されているらしいコードをよんでみるこからはじめてみたいな、と思いました。

PHPStanで始める継続的型検査 - @Hiraku

speakerdeck.com

PHPStanによる静的解析の話。
素のPHPに対してのもの、という点では違いますが、↑のトークも見ようによっては静的型解析の話でした。

7系でプリミティブ型の型注釈が入ったとはいえ、あまり強くない型チェックのPHPと静的な解析は難しく、テストで担保するべき部分があるということをおっしゃっていました。
前前夜祭のLarvel Meetup Tokyo #10、前夜祭とともにテストの話が多かったのも、静的な言語が流行りつつある中で、動的なPHPを好きな人たちが、それでも安心感を持って開発するためには、テストが必須だったということなのかな、と思いました。

まとめ

前夜祭とは違って、PHPを利用してアプリケーション開発をする上での色々な分野の話を1日で聞けたのは非常に楽しかったです。
最近参加した大きなカンファレンスの中でも、どなたの発表もすごく上手だな、とすごくハイレベルに感じたので、発表する手法としても学ぶ分が多かったカンファレンスだったと思いました。*1

僕はしがないラジオというPodcastのパーソナリティをしているんですが、そのリスナーの方が話しかけてくださって、写真を取りました!!ありがとうございました。
こういうのも、カンファレンスのいいところだなと思いました!


おまけ(以下メモ)

あくまで個人的なメモです。詳細は、公開される資料とビデオを参照ください。
LTもありましたが、ビールが入っていたので省略します笑

今からでも出来る!Wwbサービスモニタリング!! - @soudai1025

PHPerのためのWebサービスモニタリング

モニタリングしてますか?
アプリケーションエンジニアのためのモニタリング

  1. 障害に気づく
  2. 障害原因を究明
  3. 未然に障害を防ぐ

話さないこと

  • システムメトリック
  • データベースの監視はしない

過去のスライドとかブログを見てほしい

そーだいなるらくがき帳

Webサービス + PHP

Webサービスは生き物 = 常に変化
シーズンや時間帯、年によっても変わる

3層の監視

  • サーバサイド
    • モニタリングしやすい
  • インターネット
    • コントロールできない領域だからこそ、監視が必要
  • クライアント
    • コントロール可能だが、ユーザ操作など意図しないことが多い、予期しない不具合

可視化が大事

時系列のデータを取る

課金料や使っているサービスのモニタリングもする

モニタリングの勘所

ちっちゃく始める

  • リソースを"正しく"使えているか?
    • リソースは過不足なく使えているか?
  • 意図しない挙動に気づく
    • リリースによる変化の機微に気づく

踏み込んだシステムの可視化

スライドに多くの例示あり

誰も見ないグラフに意味はない => ダッシュボードの棚卸しが必要

PHPのモニタリング

PHPの仕組み?
プロセスとPHP

イベントとPHP

参考ブログ: 2015年Webサーバアーキテクチャ序論

PHPとキャッシュ

OPCache => コードキャッシュ
APCu => データキャッシュ

https://github.com/krakjoe/apcu/
https://gist.github.com/ck-on/4959032

Apache Server Status
nginx_status

まとめ

常に見続けられるわけじゃないから、時系列にデータを持つ、そして定期的に変化を確認する
推測 -> 計測 -> 観測
事実をより多く、正しく知ることで、未来を正しく予測できる

エンジニアには根拠が必要
なんとなくでは仕事はできない

  • テストコードはプログラムの品質の可視化
  • モニタリングはインフラの品質の可視化

QA

  • DBの問題は金で殴ると解決する

    • CPU
    • メモリ
    • ファイルI/O
  • サーバレスのモニタリング

    • 正しく処理ができているときにどのくらい時間がかかっているか、その結果、処理の数が増え続けていたら注意
    • AIで自動でindexをはるとかいう時代になったときにエンジニアはどう振る舞うのか?が今後の課題

SOLIDの原則って、どんなふうに使うの? - @hidenorigoto

SOLIDの原則
5つの設計原則の頭文字

  • 単一責務
  • Open Closed
  • リスコフの置換原則
  • インターフェース分離の原則
  • 依存関係逆転

オープン・クローズドの原則

目標

  • 原則の理解する

コマンドラインで使うTODOアプリ - いわゆるMVCフレームワークは使わない - TODOにバリエーションが有る - TODO - GoogleCalendar由来のTODO

常に未知の仕様を検討するわけじゃなく、検討すべき根拠のあるものを検討する

オープン = 機能を拡張できる
クローズド = 修正を行わない

同時に満たす

バリエーションを起因として、モジュールに新たな振る舞いを追加する際に、既存のコードを修正せず、単に新しいコードを
バリエーションからソースコードを守る

バリエーションによって変化する部分は?軸に沿ってまとめる

勝手に調べた資料: 何かのときにすっと出したい、プログラミングに関する法則・原則一覧 - Qiita

おすすめ書籍: レガシーコード改善ガイド

サーバーが完膚なきまでに死んでもMySQLのデータを失わないための表技 - @yoku0825

MySQLのバックアップ

リプレイする

最初から全部同じクエリを発行していれば、同じテーブルを再現できる
全て正しく

randomは難しい

リプレイ unsafe = バックアップ unsafe

手法おさらい

定期的なフルバックアップとバイナリーログバックアップをする
レプリケーションフィルターは使用しない

バイナリーログバックアップ

任意の時間まで戻れる
できるだけ多くのログを他のストレージにうつしておけるかがキモ

GTID

日々のバックアップ

バックアップスクリプトのログを残す
文字コードの設定が変わっている場合の文字化け => configもバックアップしとこう

障害の切り分け

単純なクラッシュ
データの復元

サーバー1台でやる?

結局スレーブ作ったほうが早い。。。

フルバックアップをデイリーで取って90日分のバイナリーバックアップ

binlogのリアルタイム保存の方法

--read-from-remote-saver  --stop-never --raw

スレーブだと思って文字コードを整えて、やってくれる

リストアの手順
gtid_mode

まとめ

安全なバックアップライフを

QA

binlogのバックアップの正しさはどう担保する?

  • 監視
  • 1つ1つのバックアップ量を小さくして取り直しのオーバーヘッドをできるだけ小さく

Hackで作るマイクロフレームワーク - @ytake

なぜGoじゃないの?
=> PHPの開発者が使えるものを増やしかった

開発するための知識

PHPのコードがそのまま実行可能
厳格なType Checker
Async, XHP, Generics, Collections
3.24 LTS
HHVMは、Hackを対象にしていくので、PHPとHackはどんどん別物になっていく

Type Checker

  • decl
  • parcial
  • strict

既存のPHPコードはstrictでは使えない
=> コードレビューをビジネスロジック

callableに気をつける

hhiファイル

TypeScript, Flowと同様の型定義ファイル
単体では実行不可
使いたいPHPのコードも保管したい場合は、作成してpackagistで公開しよう!

PSR For Hack

独自のものを作ろうとしている
PSR-7対応のライブラリから対応してくるもよう

強力なライブラリ

hhvm/hhvm_autoload
Composer Plugin

hhiを探して、IDEの補完が効く
PHP7依存のライブラリは、コマンドでHackで使うことができる

hack-router

ytake/hh-container

コードは資料参照

mixed使うな!
anyと一緒笑

コンパイラのために書く
invariant

hhvm/type-assert

RouterとContainer、簡単なValidationがアレば、最低限の動作が可能に

フレームワークは小さなライブラリの集合体へ

Request BodyなどのValidationにtype-assett
mixedのケア

まとめ

  • Hackだからなにか特別なもの、というのはない
  • バグが少なく堅実
  • コードレビューの負荷軽減
  • 簡単では?

QA

  • 文化的バックボーン、簡単なものになれているユーザーを取り込むのはきびしい?
    • PHPの悪いところをなくす => C++のながれや、その違いが出てきている
    • 別離の方向に進んでいるのは事実
  • フレームワークはあるの?
    • ない、作ろう

PHPStanで始める継続的型検査 - @Hiraku

dynamic と static

dynaic = 動かしてみる

  • 実行されると困る場合
  • 全部テストするのは現実的にムリな場合

static = 動かさずに読む

  • 言語によって出来ることが違う
  • 理論上、決定できない種類のものもある

工夫が必要

影響範囲が多いと漠然とした不安がある

マニュアルでは莫大な時間が
リリースに勇気が必要

staticをCIに組み込みたい!

PHPStan

phpstan/phpstan

Install

  • composer
  • phar版
  • docker hub

バージョン違いに注意

Run

vender/bin/phpstan
カスタムルールも設定可能
正規表現でエラーメッセージをマッチさせることもできる

CIとの連携

2段階でのチェック
reveiwdogが便利

autoload

PHPStanは一部コードを実行する
autoloadに酔って呼び出されたファイルは実行されてしまう

PHPは静的解析しづらい
コンパイルとランタイムの境界が曖昧

100%静的に頑張るのではなく、動的も混ぜるPHPらしいいい落とし所

PHPStanの基本動作

型注釈が丁寧であればあるほどよい
PHPの型注釈は自由度が低い
なので、PHPDocを混ぜて使う
PHPStanはどちらも解釈してくれる

スカラ型に注意

doubleじゃなくてfloat
integer、booleanはない

PHPDoc

PHP公式の機能ではないが、標準らしきものはある

  • スペース区切り
  • 型を固定した配列
  • union風記法
  • true/false

現状できないこと

リスコフの置換原則(LSP) - 型を派生した時、元の能力が減ったらだめ

クラスで頑張れなくもない
ムリに頑張る必要もない

テストすればいい

不安ならユニットテスト

まとめ

  • PHPも静的解析がしやすく
  • static方面の充実により漠然とした不安が
  • 動的と静的の美味しいところを使う

*1:実際、ベストトークはすごく僅差だったという話でした

PHPerKaigi 2018前夜祭に行ってきた(前前夜祭のLaravel Meetup Tokyo #10にも行ってきた)

phperkaigi.jp

PHPerKaigi 2018前夜祭

PHPerKaigi 2018の前夜祭に行ってきました。
最近色々な人からコミュニティへの貢献の大事さという話を聞くことがあって、ノリと勢いでサポーターとして登録しました。
普通が4000円でサポーターが8000円、前夜祭だけでもビールが出たりTシャツがついていたりと+4000円でも文句なしなんじゃないかと思いました(まだ明日の本番があるけど)。

PHPerが集まるカンファレンスとしては、秋にPHPカンファレンスが開催されています。
PHPerKaigiは初めての開催で、Kaigiという名前からも分かる通り、スピーカーの方が一方的に話すのではなく、聞く側からの反応(双方向のコミュニケーション)も大事にしていきたいというお話でした。

トーク

PHPでテスティングフレームワークを実装する前に知っておきたい勘所 - @tadsan

niconare.nicovideo.jp

PHPでテスティングフレームワークを0から実装されたお話でした。
ミニマムな要件から段階的に実装されていく流れは聞いていて面白いなと思いました。
コードも公開されているようです。

github.com

ライブラリとフレームワークの違いとして、フレームワークプログラマが書いたソースコードを呼び出すもの、とおっしゃっていたのは初めて知ったので面白かったです。
フレームワークは作れるよ、というまとめと、現実のアプリケーションのテストをするにはフレームワーク以外でもセッション管理やDB層など色々考えることが多いよね、という問題提起で締められていました。
言語を学ぶときにテスティングフレームワークがよい、という話を聞いてことがあるので今度なにか違う言語でやってみたいと思いました。

PHP と SAPI と ZendEngine3 と - @do_aki

www.slideshare.net

PHPの現場というPodcastの第17回を聞いて、PHPの中の話をされるということを聞いて、楽しみにしていたトークでした。
はじめのSAPIや、Zent Thread Safety、Zend Engineの話などをされていて、はじめのあたりはすごくわかりやすくて、なるほどーときいていたのですが 、すべてのテーマで途中から全くついていけませんでした。泣
しかしながら、他のかたもTweetされていたのですが、非常によくまとまっていた資料および発表ということだったので、あとで復習したいと思います。

大統一PHP - @uzulla

speakerdeck.com

Apache + mod_php や Nginx + php-fpmなど、PHPの環境構築がややこしいという話で、PHP単体でhttpdになれればその環境構築もよりわかりやすくなるよね、やってみた!というお話でした。
PHPの内部やその周辺のニッチ(?ぼくにはそう見えた)な技術を色々紹介しつつ、その目的を達成していく、という流れはハック感がかなりあってすごく面白かったです。

1つ前のdo_akiさんのトークと呼応している部分もあるように感じてこの辺で内部が分かればもっと面白いんだろうなという気持ちになりました。

また、トーク内容とは異なるのですが、発表者のuzullaさんは何かのイベントで司会をされたこともあるということで、話の煽り方、トーンダウンがすごくて、あまりわからないながらも引き込まれて聞けました。

まとめ

総括として、かなりレベルが高くて(特に少しレイヤーが低い話が多くて?)ついていけない感はあったものの、普段触っていても意識しない部分について詳しい方々の話を聞けるのは良い機会でした!
明日(出す頃にはもう今日ですが)の本番はDBの話も多かったり、裏トラックでPHPの現場の公開収録やLaravel、CakePHP相談会などの企画もあったりするので、楽しみです!

おまけ

Laravel Meetup Tokyo #10

また、その前夜にあったLaravel Meetup Tokyo #10にも出席しました。
LT枠が空いていたので、「Eloquentの使い方を考え直した」というタイトルで発表してきました。

speakerdeck.com

正直なところ、皆さんの発表のレベルが高く、自分のあまり練習できていないLTが気になって、緊張しすぎて良く内容が入ってこなかったです。汗

ただ、テストの話が多く、皆さんそこに共通の悩みを持たれているのだな、という風に思いました。
あと、スポンサーさんのリブセンスさんが提供の転職DRAFTビールが美味しかったです。
10回目開催、おめでとうございました!

laravel-meetup-tokyo.connpass.com

【Vue.js】リッチなUIのWEBアプリに必須なドラッグ・アンド・ドロップの実装

f:id:zuckey_17:20180304215932g:plain

WEBアプリケーションでリッチなUIを実現したい場合、内部的には複雑な操作を簡単そうに見せるためのひとつの工夫として、Drag and Dropは必須と言ってもいいと思います。
今回、Vue.jsとそのライブラリのVue.Draggableを利用して、簡単にリストのDrag and Dropを実現することができたので、紹介したいと思います。

本エントリに記載しているソースコードは以下にあります。

github.com

デモ => https://zuckeym-17.github.io/vue-dnd/

Vue.Draggable

Vue.DraggableはSortable.jsというJavaScriptでリストの Drag and Dropを実装するためのライブラリをベースに開発されています。
そのため、なにかやりたいなと思ったときは、Sortable.jsのドキュメントをあたったほうが、知りたかった情報が書かれていたりします。

Vueの基本的な説明は省略します。
画面の都合上、.vueファイルのstyleタグも省略しています。

また、コード中で利用しているListItemというコンポーネントは、単なるリストの1要素です。
liタグの中にspanタグを配置し、ドラッグハンドルとして使っています。

<template>
  <!-- spanタグをドラッグハンドルとして利用、dragHandle Propsがtrueのときのみ現れる -->
  <li><span v-if="dragHandle" class="drag-handler">:::</span>{{ item }}</li>
</template>

<script>
import Vue from "vue";

export default Vue.extend({
  props: {
    item: String,
    dragHandle: false
  }
});
</script>

1列のリスト

基本的な1列のリストを作成します。
Vue.Draggableをdraggableでインポートして、element属性を変更しているだけです。

<template>
  <div style="margin: 50px;">
    <p>1列リスト</p>
    <!-- Vue.Draggableのコンポーネントを利用。
    ListItemは li なので、レンダリング時の要素を ul にするため element 属性を設定 -->
    <draggable element="ul">
      <!-- list を v-for で 回す。
      key属性については、 https://jp.vuejs.org/v2/guide/list.html#key を参照。 -->
      <list-item
        v-for="item in list"
        :key="list.indexOf(item)"
        :item="item"></list-item>
    </draggable>
  </div>
</template>

<script>
import Vue from "vue";
import ListItem from "./ListItem";
import draggable from "vuedraggable"; // Vue.Draggableのコンポーネントをインポート

export default Vue.extend({
  components: { draggable, ListItem },
  props: ["list"]
});
</script>

与えられている listは以下のようなものです。

const list = [
  "トレイン=ハートネット",
  "スヴェン=ボルフィード",
  "イヴ",
  "リンスレット=ウォーカー"
];

複数列のリスト

複数列間でDrag and Dropを実現します。
Vue.Draggable コンポーネントは、options属性にオブジェクトを渡すことで色々な設定ができます。
ここでは、group オプションを指定し、Drag and Dropで要素を 複数列に渡って行き来させることができます。

<template>
  <div style="margin: 50px;">
    <p>複数列リスト</p>
    <div style="display: flex">
      <!-- options 属性に オブジェクトをbind
      board を v-for で 回す。 -->
      <draggable
        :options="options"
        element="ul"
        v-for="list in board"
        :key="board.indexOf(list)"
      >
        <!-- 1列リストのときと同様 -->
        <list-item
          v-for="item in list"
          :key="list.indexOf(item)"
          :item="item"></list-item>
      </draggable>
    </div>
  </div>
</template>

<script>
import Vue from "vue";
import ListItem from "./ListItem";
import draggable from "vuedraggable";

export default Vue.extend({
  components: { draggable, ListItem },
  props: ["board"],
  data() {
    return {
      options: {
        group: "board", // group オプションを指定した draggable 間はDrag and Dropで要素を移すことができる
        animation: 100 // animationオプションを指定すると ソート時に 値(ms)で アニメーションされる
      }
    };
  }
});
</script>
const board = [
  [
    "セフィリア=アークス",
    "ベルゼー=ロシュフォール",
    "エミリオ=ロウ",
    "クランツ=マドゥーク"
  ],
  [
    "ナイザー=ブラッカイマー",
    "アヌビス",
    "ジェノス=ハザード",
    "バルドリアス=S=ファンギーニ"
  ],
  [
    "デイビッド=ペッパー",
    "リン=シャオリー",
    "ベルーガ=J=ハード",
    "メイソン=オルドロッソ"
  ]
];

追加可能なリスト

リストであれば、要素を追加したかったりします。

<template>
  <div style="margin: 50px;">
    <p>追加可能なリスト</p>
    <draggable element="ul">
      <list-item
        v-for="item in list"
        :key="list.indexOf(item)"
        :item="item"
      ></list-item>
      <!-- slot="footer" をつけると、リストに並ぶ異なった要素を入れることができる。
      入力された値を、addItem メソッドによってリストに追加していく。 -->
      <div slot="footer">
        <input type="text" v-model="input"/>
        <button @click="addItem">Add</button>
      </div>
    </draggable>
  </div>
</template>

<script>
import Vue from "vue";
import ListItem from "./ListItem";
import draggable from "vuedraggable";

export default Vue.extend({
  components: { draggable, ListItem },
  props: ["list"],
  data() {
    return { input: "" };
  },
  methods: {
    // addItem メソッドを定義しておく
    addItem: function(e) {
      e.preventDefault();
      this.list = this.list.concat(this.input);
      this.input = "";
    }
  }
});
</script>

ドラッグハンドルを指定したリスト

ドラッグするときのツマミを指定して、他の要素には別のイベントを仕込みたい場合などがあります。
その時は handle オプションをつけます。

<template>
  <div style="margin: 50px;">
    <p>ドラッグハンドルありのリスト</p>
    <draggable
      element="ul"
      :options="options"
    >
      <!-- list-item に dragHandle Propsを渡して、ドラッグハンドルを表示させる -->
      <list-item
        v-for="item in list"
        :key="list.indexOf(item)"
        :item="item"
        :dragHandle="true"
        ></list-item>
    </draggable>
  </div>
</template>

<script>
import Vue from "vue";
import ListItem from "./ListItem";
import draggable from "vuedraggable";

export default Vue.extend({
  components: { draggable, ListItem },
  props: ["list"],
  data() {
    return {
      options: {
        // handle オプションによって、リストの中でつかめる要素を指定することができる
        handle: ".drag-handler"
      }
    };
  }
});
</script>

まとめ

Vue.Draggableを利用して、簡単にドラッグ・アンド・ドロップを実現することができました。 オプションや、ドラッグ開始時、ドロップ時の各種制御方法は以下のページに記載されているので参考にしてみてください。

github.com

関連

blog.zuckey17.org

Storybookをまず動かす!ReactとVueの最小環境を作った

最近Storybookを会社で導入していて、環境設定についてまとめました。
基本的には、公式サイトのとおりですが、コンポーネントの例の記載がなかったり、ReactとVueでできるものが異なったりして、困った部分が合ったので、すぐに動く環境を作りました。

github.com

本エントリのコードはNode v8.9.4の環境で動作を確認しています。

Storybook?

f:id:zuckey_17:20180222101257p:plain

https://storybook.js.org/

The UI Development Environment

ReactやVueなどのUIライブラリで作成したコンポーネントの動作やデザインをエンジニア、デザイナ間で共有、閲覧できるツールです。
Storybookという名前の通り、あるコンポーネントコンポーネントの集合に対して、storyを複数用意し、storyごとの振る舞いを閲覧することができます。

環境構築

StorybookはReact、Vueの場合それぞれ、内部的にWebpackを利用しています。
このエントリで紹介しているものを動かすためには特別な設定は必要ありませんが、必要に応じてWebpackの設定をすることもできます。

Storybook - Custom Webpack Config

f:id:zuckey_17:20180222041649g:plain

動くサンプルはこちら↓

https://zuckeym-17.github.io/storybook-sample

Reactの場合

1. プロジェクトフォルダを作成、npm init

mkdir storybook-sample
cd react-storybook-sample
npm init

2. StorybookとReactについて必要なライブラリを追加

@storybook/reactおよび、reactreact-dombabel-coreを追加する必要があります。

npm i --save-dev @storybook/react
npm i --save react react-dom
npm i --save-dev babel-core

3. Storybookの環境を立ち上げるnpmスクリプトを追加

{
  "scripts": {
    "storybook": "start-storybook -p 9001 -c .storybook"
  }
}

ここでpacage.jsonの全容は以下のようになります。
https://github.com/zuckeyM-17/storybook-sample/blob/master/react-storybook-sample/package.json

4. Storybookの設定ファイルを作成

Storybookは設定ファイルを.storybookというディレクトリに置きます。
最小環境では、Storybookにstoryの場所を伝える設定を.storybook/config.jsに書くだけです。
以下では、stories/index.jsを読み込むという設定をしています。

import { configure } from '@storybook/react';

function loadStories() {
    require('../stories/index.js'); // 好きな場所のstoryをrequireできます。
}

configure(loadStories, module);

5. Reactのコンポーネントを作成

ボタンの中身と、クリック時のメソッドをPropsとして渡せる単純なコンポーネントを作成します。
components/Button.jsxとして以下を保存します。

import React from 'react';

const Button = ({onClick, children}) => {
  return (
    <button onClick={onClick}>{children}</button>
  );
};

export default Button;

6. storyを作成

ここまでいけばあとはstoryを書くことができます。
ストーリーはいわば、そのコンポーネントの初期状態です。

import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import Button from '../components/Button';

storiesOf('Button', module)
  .add('with text', () => (
    <Button onClick={action('clicked')}>Hello Button</Button>
  ))
  .add('with some emoji', () => (
    <Button onClick={action('clicked')}>😀 😎 👍 💯</Button>
  ));

7. Storybookの起動と確認

これで準備は完了です。
以下のコマンドにてStorybookを起動し、localhost:9001にアクセスしてみましょう。

npm run storybook

Vueの場合

1. プロジェクトフォルダを作成、npm init

mkdir storybook-sample
cd vue-storybook-sample
npm init

2. StorybookとVueについて必要なライブラリを追加

@storybook/vueおよび、vuebabel-coreを追加する必要があります。

npm i --save-dev @storybook/vue
npm i --save vue
npm i --save-dev babel-core

3. Storybookの環境を立ち上げるnpmスクリプトを追加

{
  "scripts": {
    "storybook": "start-storybook -p 9001 -c .storybook"
  }
}

ここでpacage.jsonの全容は以下のようになります。
https://github.com/zuckeyM-17/storybook-sample/blob/master/vue-storybook-sample/package.json

4. Storybookの設定ファイルを作成

Storybookは設定ファイルを.storybookというディレクトリに置きます。
VueのStorybook最小構成では、

  • Storybookにstoryの場所を伝える設定: .storybook/config.js
  • Storybookに用意されているaddonを使う宣言: .storybook/addons.js

をそれぞれ用意する必要があります。

.storybook/config.js

stories/index.jsを読み込むという設定をしています。

import { configure } from "@storybook/vue";

import Vue from "vue";

function loadStories() {
  require("../stories/index.js");
}

configure(loadStories, module);

.storybook/addons.js

@storybook/addon-actionsという、イベントハンドラのコールバック関数として設定し、そのイベントが発火したことをロギングするためのaddonを利用する旨を設定します。

import '@storybook/addon-actions/register';

5. Vueのコンポーネントを作成

ボタンの中身と、クリック時のメソッドをPropsとして渡せる単純なコンポーネントを作成します。
components/Button.vueとして以下を保存します。

<template>
  <button @click="handleClick">{{ children }}</button>
</template>

<script>
import Vue from "vue";

export default Vue.extend({
  props: ["handleClick", "children"],
});
</script>

6. storyを作成

ここまでいけばあとはstoryを書くことができます。
ストーリーはいわば、そのコンポーネントの初期状態です。

import Vue from "vue";
import { storiesOf } from "@storybook/vue";
import { action } from '@storybook/addon-actions';
import MyButton from "../components/Button.vue";

storiesOf("Button", module)
  .add("with text", () => ({
    components: { MyButton },
    data() {
      return {
        handleClick: action("clicked"),
        children: "hello button"
      };
    },
    template: '<my-button :handleClick="handleClick" :children="children"></my-button>'
  }))
  .add("with some emoji", () => ({
    components: { MyButton },
    data() {
      return {
        handleClick: action("clicked"),
        children: "😀 😎 👍 💯"
      };
    },
    template: '<my-button :handleClick="handleClick" :children="children"></my-button>'
  }));

7. Storybookの起動と確認

これで準備は完了です。
以下のコマンドにてStorybookを起動し、localhost:9001にアクセスしてみましょう。

npm run storybook

まとめ

ReactとVueで同じStorybookが動くサンプル環境を作ってみました。
コードベースも基本的に記事中のものが全てですので、是非一度動かしてみていただければなと思います。

おまけ

グローバルなCSSなど

Reset CSSなど、コンポーネントに当てるCSSとは別にグローバルになにかコンテンツを読み込んでおきたいということがあると思います。
その際は、設定ファイル置き場である.storybookpreview-head.htmlを作成し、HTMLのheaderに追加したいタグを書くことができます。

例)Reset CSS、Font AwesomeのJSをCDNから利用する

<link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/3.18.1/build/cssreset/cssreset-min.css">
<script defer src="https://use.fontawesome.com/releases/v5.0.6/js/all.js"></script>

日本初!?運用のノウハウがつまったRedash Meetup #1に参加した

redash-meetup.connpass.com

@ariarijp さんと、@kakakakakku さんが運営されているRedash Meetup #1に参加してきました。
僕は年始から会社でRedashを導入していて、MySQLAWS Aurora)と Elasticsearchをデータソースとして運用しています。

日本初のRedash Meetup!?

Redashについて、以前から名前は知っていましたし、昨年12月、今年の1月と続いてハンズオン会などが開催されていたりしたものの、ユーザーなどが集まってRedash単体の話をするような勉強会は僕が調べた限り始めてだったのではないかと思います。(間違っていたらすみません)
そういう会に参加できてうれしかったです!

会自体は2人の方の20分ほどの発表、5分 * 2のLTという構成でした。
19時半開始、20時45分解散というすごく健全な勉強会でした。

発表

Redash 導入事例から考える OSS BI ツール導入チェックリスト - ariarijp

speakerdeck.com

ariarijpさんが働かれているSNS広告を運用をされている会社さんでの導入の経緯や利用され方、困った点を紹介した後に、同様のユースケースではBIツールにどういう要素を求めるのか、ということを話されていました。
こちらの会社では、広告運用の中で日々出てくるデータ抽出作業をすべての人に開放するためにRedashを使われている、ということでした。
業務上、重要な作業をRedashが担うということになったがゆえにRedashのパフォーマンスが業務効率に大きく寄与するようになり、その上でメンテナンス性をBIツールに求めるべきという話と、実際の運用のノウハウなどの紹介も面白かったです。

ジモティーでの Redash 活用事例 - kyoshidajp

speakerdeck.com

ジモティーさんでは、Redashの利用者が40人でクエリ数が2300クエリあるということでした。
比較的大規模に使われている、ということでユーザー管理のGoogle認証連携とそれに付随するOSSとしてのRedashへの貢献についての紹介も面白かったです。

また、少し早めに発表が終わったこともあって、Q&Aの時間が長くあったのですが、その中で、クエリの管理の方法についての議論になっていてそれが面白かったです。
クエリが多くなりすぎると検索しづらくなるという問題があるという話で、たしかに僕の会社でも導入して1ヶ月足らず程度にも関わらず、50近いクエリができているので棚卸しが必要だなと思いました。
参加者の方々は、すでに運用されていた方も多いようで、ディスカッションの中でこの問題への対処方法が紹介されていました。

  • Redash管理層のDBから使われていないクエリを抽出して、スプレッドシートに削除対象リストとして公開
  • クエリのDiscriptionの部分にタグのようなものを書いておいて、検索性を上げる

LT 僕と Redash と Athena - dyuju_you

S3にためたログやデータをAthena経由で抽出、分析したいというときに、無邪気にクエリを打つと

  • Redashのリソース
  • ブラウザ性能
  • Athenaの課金

の観点で色んな意味で死んでしまうという問題をRedashにおけるAthenaのデータソースのコードをカスタマイズして仕組みで防いだという話でした。

OSSだからソースコード読めば良いという話をされていましたが、僕は完成度高そうなツールのコードを読みに行って改善するのって大変そうだなぁと漠然と思っていたので、
データソースのコードに絞って手を入れて、ツールの中のコードに手をいれて改善する良い事例紹介だったのではないかなと思いました。

LT Redash ユーザーのための Metabase 入門 - manabusakai

speakerdeck.com

Redash Meetupでは挑戦的な、別のOSS BIツールであるMetabaseの紹介LTでした。

  • インターフェースの美しさ
  • SQLを書かずに分析ができること

Metabaseはより簡単に、早くデータ可視化、分析をできるようにしたツールで、
Redashのように多くのデータソースやそれらをまたいだ分析などをコードで解決できるというツールとは、対象ユーザーが違う、という話をされていました。

まとめ

Redashを会社に導入して思ったのは、ダッシュボードやグラフ化などの可視化の機能が見栄えが良いのでそこに意識が向きがちで、様々なデータソースにアクセスができてjoinできるという点をうまく活用するのは、データのありかや意味がわかっているエンジニアばかりになってしまうということでした。
可視化して美しいダッシュボードを作るのは夢のある話だな思いますが、まずは日々のデータ抽出から始めて、エンジニア以外の人も巻き込んでやっていくのがよいというのは、いろいろな方がおっしゃっており、地道にやっていくしかないのだなと思いました。
その中でパラメータによって誰でも使えるクエリをたくさん作る、など少しでもRedashを触るハードルを下げる取り組みはもう少し積極的に社内でも推し進めていこうと思いました。

また、今回、OSSとしてのRedashへの貢献であったり、ハックであったりの事例も多く話されていたように思います。
先程も書きましたが、そういう貢献はハードルが高そうに思えていました。
こういう勉強会の場でその事例が紹介されていると、アプローチの方法がぼんやり浮かんできそうで、その辺を意識してツールを触れたら良さそうだなと思いました。

最近導入して、たくさん触っているというのもあって、総じてすごく良いイベントでした。運営のお二方ありがとうございました!!

おまけ

今回の運営の一人である@kakakakakku さんが公開されているハンズオン資料を実施して、ブログで紹介していたり

blog.zuckey17.org

WEBエンジニア勉強会#05にて導入して少したったタイミングでの所感などを話しています。

speakerdeck.com

メモ

以下、勉強会中のメモです。

Redash導入事例から考えるOSS BI導入チェックリスト

@ariarijp

ユニトーン(SNS広告の運用) ariarijp.hatenablog

ユニトーンにおける導入事例

導入以前

データ抽出は社内ツール、SQL

=> ツールの魔改造EXCELによるデータ加工 => データ抽出へのリードタイム

導入の経緯

  • クエリのFork
  • APIで扱える

導入後の変化

  • パラメータ変換を使うなどして、データ抽出の制限がなくなった
  • 抽出結果の共有がスムーズ
  • 集計結果をREST APIで加工できるので集計結果を入力としたプログラムで、データを二次利用

大変になったこと

  • 複雑な集計クエリが大量に
  • 今までにないデータを集計対象にしたいという要望
  • Redashが業務の中心になり、業務効率に直結 => 棚卸しが必要

チェックリスト

前提: エンジニアは10人

  • データ抽出がしたい
  • エンジニアの工数削減
  • コストをかけたくない

誰が使うかを考える

非エンジニアも使える?
BIツール導入と併せて、データに係る知識の底上げをエンジニア主体で行う

どのDBを参照するか?本番、レプリカ?
ネットワーク上どこに置くか?
可能であれば集計用DWHに逃がすのが吉? => BigQuery、Redshift、TDなど

運用

  • Ubuntupython、nginx、Redis、PosgreSQLの知識
  • サーバーのモニタリング
  • 設定のチューニング、公式ドキュメント

まとめ

  • 誰が使うか?エンジニアが巻き込む
  • データ基盤の整理
  • 安定運用できる知識が必要

ジモティーでのRedash利用事例

kyoshidajp

40人でクエリ数が2300
PostgreSQLからメタデータも簡単に取れる

基盤

mongoDB、Aurora

↓ embulk

Redshift、BigQuery // BigQueryのほうが安くてよい?

dry runモードというのがあって、料金を抑えたりする

利用事例

アカウント管理

  • Google認証の導入
  • セキュリティグループによる制限

データ抽出

BigQueryの場合はユーザ定義関数も使える
Pythonデータソース

Redash社内勉強会

パラメータが便利

まとめ

OSSなので不満や、こうなれば便利というのがあればPRを投げる

僕と Redash と Athena

dyuju_you

Hadoopが好き

webpage tracking
nginx golang
fluentd
s3

データソースは2つ
- presto => hadoop - athena => s3

無邪気なクエリ

  • Redashの死 => * 使うな!!
  • ブラウザの死
  • お金の死 (Athena高い、1クエリ/500$)

どうする?

システム的に何とかするのが吉
OSSだからソースコード読めば良い

  • Limit制約
    • 一定のLimitを付けないとだめ
  • Pertition制約

スキャン量を減らす

Glue Data Catalog

怖くないSQLライフを

Redash ユーザーのための Metabase 入門

manabusakai

Metabase?

  • シンプル、かんたん、早くアクセス
  • セットアップが簡単
  • SQLいらず
  • 美しいダッシュボード

blog.manabusakai.com

GUIからSQLができる?

  • 異なるData Sourceのjoinが出来ない
  • Data Sourceがやや少ない

ターゲットユーザーが違う
エンジニアチームにはRedash、分析チームにはMetabase

Redashとは補完し合うツール
権限管理はMetabaseのほうが良い