TECH PLAY

株式会社mediba

株式会社mediba の技術ブログ

167

こんにちは。制作部の西野です。 このmediba Creator × Engineer ブログの立ち上げ担当をしておりました。 9月でブログの開設から1年になりますので、少し早いですが振り返りをしようと思います。 ブログ立ち上げの背景 このブログは、medibaのエンジニア、デザイナーが最近経験したこと、興味があって調べたこと、学んだことを投稿しているブログです。 medibaの開発のことをもっと知ってもらいたい サービスを開発、運用する上で貯めたナレッジを社内外に共有したい 技術を広めることで社会貢献に繋げたい という思いから作られました。 数値で振り返る 社内のエンジニア、デザイナーが週に1度記事を投稿しており、初回投稿から約1年が経過した今では記事数は45件超となりました。 記事数が増えるにつれ、順調にPV数も増加しています。1年前と比較するとPVは約10倍になりました。 プロジェクトの進め方や、新しい技術に関する共有、便利なツールのtipsなど記事の種類も様々ですが、 その中でもブログ開設から1年間で特にアクセス数が多い記事はこちらです。 Cloud Vision APIの画像認識精度を試してみた AWS の DNS フェイルオーバーで、サーバレス&高可用性 Sorry ページを構築する CSS backdrop-filterを使って曇りガラスなどの背景効果を出す(iOS9.0新機能) re:dashでデータ集計を簡易化 UIデザインアプリSketchの便利機能6選 ほとんどの記事は投稿直後のアクセス数がピークで、日数経過につれて下降傾向となります。 これはトップページのファーストビューに掲載されなくなったことや、取り上げた題材の目新しさがなくなるためと考えられます。 しかし上記の記事は投稿から日にちが経ちトップページのファーストビューに掲載されなくなったにもかかわらず、 全体の中でも高いアクセス数となっています。新しい技術に関する共有や、ツールの使い方に関する記事は特にアクセスが多いです。 ブログをはじめて良かったこと ブログを始めて良かったことがいくつかあります。 記事を書くことによって知識が深まった いざ、ブログを書き始めると知識の曖昧さに気がつくことがあります。そのときに、曖昧だと認識できること、そして曖昧なままでは文章を作れないので、知識の深堀をする必要が生まれるので自分の中でも理解が深まります。 また、ブログの記事を投稿する前に私たちは、「コードレビュー」ならぬ「ブログレビュー」をおこなっております。 その際に出た質問などから自分だけでは疑問に思わなかった、思わぬ気づきを発見できます。 技術のアウトプットの場が増えた 社内でも勉強会なども行われていますが、社内だけではなく社外の方にも技術共有をする場が生まれました。 発信の場が増えました。 違う部署の人がどんな知識・技術を持っている人なのか知ることができた 社内の中であっても、部署が違ったり同じプロジェクトに所属していないと、どんな仕事をしているのかや個人の知識や技術力を知る場はあまりありません。 しかしブログの記事を通して知るきっかけになります。また、同じエンジニア同士ではなく、社内の非エンジニアの方にも記事を読んでもらうことで、興味を持つきっかけになり、互いの業務の理解に役立ちます。 会社を知ってもらえる機会が増えた 採用面接を受けに来た方から「ブログを読みました」と言われることが増えたことからも、会社のことを知ってもらえる機会が増えて良かったと思っています。 ブログのこれから ブログを通し、medibaの技術をもっと日本に広める このブログを読んでくださっている方は、キーワード検索からたまたまこのサイトに辿りついた人の方が多いです。 その新規アクセスユーザーに、再度訪れたいと思ってもらえるページになるようにブログを成長させたいと思っています。 このブログのコンセプトは「ブログを通し、medibaの技術をもっと日本に広める。」です。 バックエンド、フロントエンド、デザインの技術などを日本中に届けたいです。 更新頻度を上げることや、UIの改善などやりたいことがたくさんあります。 もっと、このサイトのファンになってくださる方ができるように改善を行っていくつもりです。 また、ブログに書いていない、素晴らしい技術を持つメンバーがまだまだいます。 毎週1本づつ新しい記事を投稿していきますので、毎週チェックしに来ていただけると嬉しいです。
アバター
こんにちは。何でも屋をやっています山田です。 ここ数年、脆弱性に関する注意喚起が多く、セキュリティ対策を重点施策の一つとしているKDDIグループであるmedibaでは、日々対応に追われています。 medibaが管理するシステムはかなり多く、管理が煩雑になっているためVulsを使って品質管理グループの管理業務が効率化出来るかトライアルを始めましたのでその紹介を致します。 なお、トライアル中のため、開発したものはプロトタイプであることをご了承下さい。 Vulsとは NVD(National Vulnerability Database) に登録されている脆弱性情報とサーバにインストールされているソフトウェアのバージョンを照合し、各サーバが該当している脆弱性を検知するツールです。 概要、インストール手順や使い方は、 Readme に詳しく書かれているため割愛します。 Qiita にも詳しく書かれている記事がありますので合わせてご確認下さい。 Vulsは運用に乗るか? medibaが持つサービスは大小含めてそれなりの数になりますので、トライアルでは15システム程ピックアップして進めます。 なお、scanは各システムのテスト環境に対して行っています。 サーバの状態を一元管理出来るのは便利で少数チームだとかなり有効なツールですが、システムが多いとtuiだけでは運用が面倒なので、拡張をしつつ運用トライアルを進めることにしました。 実現したいこと slack経由でいつでも誰でも各サーバの状態を確認できる 把握したい情報は以下の4つです。 対象となっているサーバがわかる 指定したサーバが抱えている脆弱性情報がわかる 指定した脆弱性情報を抱えているサーバがわかる 指定した脆弱性情報の詳細がわかる 上記を実現するため、機能を拡張してみました。 vulca vuls.sqlite3の情報を取得しjsonで返すAPI GO言語でVulsの機能を使いつつ実装 詳細は こちら ruboty-vulca slack経由でvulcaを実行 詳細は こちら トライアル状況 拡張機能を適用するとこんな感じでslack経由でサーバの状態を確認出来るようになります。このイメージで運用に乗るかを試すためのプロトタイプなのでjsonをそのまま表示しているだけに留めています。 トライアルでの課題 もう少し検証する必要がありますが、今見えている課題は以下です。 トライアルは引き続き行ってブラッシュアップしていきたいと思います。 機能としてはとりあえず大丈夫そうだが、slackへjsonをそのままレスポンスしているだけなので見にくい(Slack APIを使って作り込む) 対象となるパッケージが多かったためどこからアップデートしていくか悩む 対象システムが今後も増えていく前提だと、vuls scan結果をカテゴリ毎に分けて保存した方がよさそう 補足:デーモン管理について vulcaとrubotyはデーモンプロセスとして常駐させる必要があり、今回は Supervisord を利用しました。 設定すればブラウザからもプロセスの状態が確認出来るため便利です。 追記 今回実装したVulcaはscan結果が保存されているvuls.sqlite3を参照してましたが、vuls.sqlite3はrevokeする計画のようで、本トライアルの内容はVulsの変更に合わせて見直していきます。 また、 vulsrepo というビューワーがあり、これでよさそうな気もしてきましたが、対象サーバ、検知された数が多いと工夫が必要そうです。 引き続きトライアルしていきます。
アバター
こんにちは。制作部の苅部です。 今回は最近実施したGoogle Analyticsを使ったA/Bテストについて書こうと思います。 仕組み自体はすごくシンプルですので、ツールの導入が難しい場合にも簡易的なA/Bテストが実施できると思います。 Google Analyticsを使う機会が少ないエンジニア/デザイナーの方や、普段フロントエンドの実装をされてないディレクターの方に向けて、今回の事例を簡単に共有できればと思います。 実施の経緯 運用中のサービスで採用しているアイコンについて、「どうもわかりづらい」「テキストラベルを加えた方が伝わりそう」といった意見が上がっておりデザイン変更を検討していました。 ただ変更するにあたっては「分かりやすくなった(効果があった)」という状態を数値で把握できるようにしたいので、[オリジナルデザイン]と[改善後デザイン]それぞれで、一定期間A/Bテストを実施することになりました。 [改善後デザイン]のクリック数が継続して高ければ、"UIの認知度が向上した"という判断ができると思ったのです。 普段のA/BテストはSaaSが提供している多機能なプラットフォームで実施しているのですが、今回のUIはサードパーティーとしてのJavaScriptを使い広範囲(複数サブドメイン)に展開しているため、自前で実装する流れとなりました。 要件と実装概要 今回の要件は以下のようになっています。 [JavaScriptファイルで展開しているUI]にてA/Bテストを実施してクリック数/CTRを計測 サービスを横断したUIのため、サービスを超えてもデザインパターンが変わらないようにする。 Google TagManager/ユニバーサルアナリティクスを利用(すでに利用していたため) そして全体の処理の流れは次のようになります。 JavaScriptが実行され、Cookieにセグメント用の乱数をセット。(一定期間保持する。) その値を使い、任意の確率で各デザインパターンを構築 デザインパターンごとにdataLayer変数(カスタムディメンション)やイベントラベルをセット dataLayer変数を拾った上でgtm.jsが構築される gtm.jsからanalytics.jsが構築される analytics.jsがカスタムディメンションを送信 HTMLの構造 Google TagManagerのスニペットはbody開始タグ直後への設置が推奨されているため、HTMLとしては以下のような形になります。 <html> <head> <!-- 共通UIを構築し、dataLayer変数を定義する --> <script src="ui_elements.js"></script> </head> <body> <!-- gtm.jsが構築される。そのあとにanalytics.jsが構築される --> <noscript><iframe src="//www.Google TagManager.com/ns.html?id=GTM-MP7TC7" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript> <script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start': new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0], j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src= '//www.Google TagManager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f); })(window,document,'script','dataLayer','profileIdが入ります');</script> <!-- 以降にコンテンツが入る --> </body> </html> Google Analytics / Google TagManagerの準備 Google Analyticsのカスタムディメンションとイベントトラッキングという機能を使います。 カスタムディメンションとはユーザーに紐づく属性を追加で設定するための仕組みで、イベントトラッキングとはユーザーのインタラクションで発生するイベントを送信するための仕組みです。 今回はクリック数とCTR計測が目的ですので [表示されたデザインパターン]をカスタムディメンション設定 [クリックされたデザインパターン]をイベント設定 として、それぞれ分母/分子としてCTRを計算しています。 ※以降の画面キャプチャは2016年8月の状態のものです。 Google Analyticsの設定 カスタムディメンションを新規に作成します。 ※カスタムディメンションを作成・編集するにはプロパティに対する編集権限が必要です。 [管理] > [任意のプロパティ] > [カスタム定義] > [カスタムディメンション]にて [新しいカスタムディメンション]を選択します。 わかりやすい任意の名前でカスタムディメンションを設定して[作成]を選択します。 今回は btn-menu としました。 ※作成後に[作成したカスタム ディメンション]という画面でサンプルコードが表示されますが、今回は気にしなくて大丈夫です。 元のページに戻ると新しいディメンションが追加されたことがわかると思います。 Google TagManagerの設定 カスタムディメンションをGoogle TagManagerで利用するため、ユーザー定義変数としてデータレイヤー変数の設定をします。 ※ データレイヤー変数でなくとも、"JavaScript変数"を使う事でグローバル変数を取得する事もできます。 まずは任意のコンテナの中で[変数]を選び[ユーザー定義変数]で[新規]を選択します。 変数の種類はデータレイヤー変数とします。 変数名とデータレイヤーの変数名を設定し変数を作成します。 次に、タグに対してカスタムディメンションとデータレイヤー変数を紐付けます。 任意のコンテナの中で[タグ]を選び、今回利用するタグを選択します。 (タグを発行していない場合は発行します。) タグの設定でカスタムディメンションの設定をします。 ここではデータレイヤー変数とGAのカスタムディメンションのインデックス番号を紐付けます。 以上の内容でGoogle Analytics/Google TagManagerの準備が整いました。 Cookieでのセグメント値の設定と読み込み デザインパターンを任意の確率でコントロールできるようにします。 4桁程度の乱数(セグメント値)をCookieに保存する仕組みと、それを取得できるような仕組みを作ります。 こんな感じの関数で使ってみます。 function initSegment() { var COOKIE_NAME = '_segment'; var COOKIE_DOMAIN = 'example.com'; var SEGMENT_LIMIT = 9999; var segmentCookie = $.cookie(COOKIE_NAME); var segmentValue = parseInt(Math.random() * SEGMENT_LIMIT, 10); if (segmentCookie) { window._segment = segmentCookie; } else { $.cookie(COOKIE_NAME, segmentValue, {expires:100, path:'/', domain: COOKIE_DOMAIN}) window._segment = segmentValue; } }; ※見通しを良くするためjQueryCookieを使っている想定で書いています。 サブドメインまでセグメント値を共有するためCookieを使っていますが、 SameOriginの範囲であればLocalStorageで良いと思います。 さて、セグメント値が設定できたので以下のような形で分岐処理ができるようになります。 if(window._segment >= 5000){ html = '<a href="#">ボタン1 : 1/2の確率で表示</a>' }else{ html = '<a href="#">ボタン2 : 1/2の確率で表示</a>' } クリックイベントも取得できるようにしたいので、AタグにGoogleのイベント用関数を設定します。 これによってボタンがクリックされた数をレポートで集計できるようになります。 if(window._segment >= 5000){ html = '<a href="#" onclick="ga(\'send\',\'event\',\'header\',\'menu\',\'button_01\');">ボタン1 : 1/2の確率で表示</a>' }else{ html = '<a href="#" onclick="ga(\'send\',\'event\',\'header\',\'menu\',\'button_02\');">ボタン2 : 1/2の確率で表示</a>' } 以上で、確率のコントロールとイベント送信ができるようになりました。 dataLayer変数の用意(JavaScript) グローバル空間にdataLayer変数を配列で用意しておきます。 ※この変数名はGoogle TagManagerのコンソールで変更可能ですが、dataLayerのままの方が明示的で誰が見ても分かりやすいと思います。 今回の場合こんな感じです。 デザインパターンごとに、配列へpushするオブジェクトを分けています。 window.dataLayer = dataLayer || [] if(_segment >= 5000){ dataLayer.push({'btn-menu': 'button_01'}) }else{ dataLayer.push({'btn-menu': 'button_02'}) } Google TagManagerで用意したデータレイヤー変数 btn-menu に button_NN という値を設定しています。 btn-menu はGoogle Analyticsのカスタムディメンション1番目に紐づけているので、Google Analytics APIコール時には cd1 として送信される事になります。 ※Google TagManagerのスニペットの実行より手前のタイミングで宣言する必要があります。 検証方法について フロントの実装が完了したら、ログと疎通の確認を行います。 クライアントからGoogle AnalyticsAPIへのコール内容は、以下URLへのリクエストパラメータにて確認することができます。 https://www.google-analytics.com/r/collect WEBブラウザのDeveloperToolを使ってみると、カスタムディメンションの1番に割り当てている内容が cd1 に button_01 として設定されている事が分かります。 コールを正しく受け取れとれているかどうかは、Google Analyticsのリアルタイムレポートのイベントタブで確認する事ができます。 直近30秒ごとのイベントが見れますので疎通確認として使えます。便利ですね。 A/Bテストの実施結果 さてさて、気になるテストの結果ですが、2箇所のUIで実施したところいずれも1週間以上継続して[改善後デザイン]でプラスの効果が確認できました。 ※ 2パターンをそれぞれ1/2の確率で表示させています。 ※ 実際に使用しているデザインとは異なるものの、構成としてはほぼ同じです。 ※ サンプル数や結果の数値差によって誤差の範囲と言えるケースもありますので、有意差の検定も実施しています。母数としては十分な数があります。 テスト1. テキストラベルの追加 アイコンの中にテキストラベルを入れてみました。 テキスト情報を含める事で、UIパーツの役割が理解しやすくなると思います。 ・UIイメージ ※ 実際のデザインとは若干異なります。 ・集計結果 ※ イベント数と日付を表示していませんが、Google Analytics上での実際のレポートです。 デイリーでのクリック数が 20% 近く向上して、CTR比較でも有意差が出ました。 ここまで明らかな違いが出ると思っていなかったので驚きです。 テスト2. テキストのサイズとコントラスト比の調整 アイコンのシンボルとテキストのサイズやコントラストを調整して視認性を向上させました。 改善パターンのコントラスト比については、WCAG 2.0およびJIS X 8341-3:2010で定められている指針の達成基準(コントラスト比4.5:1と3:1)を満たせたと思います。 最低限のコントラスト: テキスト及び画像化された文字の視覚的な表現には、少なくとも 4.5:1 のコントラスト比をもたせる。 ただし、次の場合は除く: (レベルAA) 大きな文字: サイズの大きなテキスト及びサイズの大きな画像化された文字には、少なくとも 3:1 のコントラスト比がある。 達成基準 1.4.3 を理解する | WCAG 2.0解説書 ・UIイメージ ※ 実際のデザインとは若干異なります。 オリジナル 改善パターン 背景色 #f8f8f8 #ffffff シンボルのサイズ - -20% シンボルの色 #999999 #888888 シンボルのコントラスト比 2.7:1 3.5:1 テキストのサイズ - +2px テキストの色 #777777 #666666 テキストのコントラスト比 4.2:1 5.7:1 ・集計結果 ※ イベント数と日付を表示していませんが、Google Analytics上での実際のレポートです。 デイリーでのクリック数が 15% 近く向上して、CTR比較でも有意差が出ました。 テキストラベルを見やすい形で配置しコントラスト比も高めたことで、UIが認知されるようになったと思います。 以上2つのA/Bテストは1~2週間実施して、いずれもプラスの効果があったと判断できたため新しいデザインで反映する事となりました。 注意点 今回の場合A/Bテストの情報(デザイン)を非同期で取得するような形にはせず、すべてJavaScriptファイルの中で保持しています。 つまりA/Bテストの内容が反映されるのはJavaScriptファイルのキャッシュに左右されますので実際に反映されるまでの誤差も考慮する必要があります。 (キャッシュの保持はレスポンスヘッダやブラウザの仕様に則る形となります。場合によってはCDNキャッシュも考慮する必要があります) Google Analyticsを使うことのメリット 実際にGoogle Analyticsを使ってみて、以下2点がメリットとして感じられました。 アクセス解析として現場で広く利用されているため導入障壁が低い カスタムディメンションやイベント起点でのレポーティングが可能 導入障壁の低さ やはりGoogle Analyticsの経験者は多く、ディレクターやフロントエンジニアとの意思疎通が楽だなと実感しています。 Google Analyticsに慣れておけば、将来的にFirebaseAnalyticsやOptimize360を使う事になったとしてもスムーズに使いこなす事ができるかもしれません。 レポーティング機能 バナーのクリエイティブ比較であればクリック数だけを追えばいいのかもしれませんが、UI変更の場合にはページ遷移後の行動把握のため、カスタムディメンションが必要になる事が多いと思います。 例えばそういったケースではカスタムレポートの目標到達プロセスが役に立ちます。 クリック数だけでなく、その後の行動とコンバージョンをセグメントごとに比較することができるため、より本質的なA/Bテストができると思います。 また、コホート分析を使えば[チュートリアルUIの掲出/非掲出]それぞれでセグメントを設定して比較する みたいな使い方もできたりします。7日間継続率をKPIとして設定している場合に活用できそうです。 コホート分析 このようにレポート機能が豊富なため、Google AnalyticsはA/Bテストとの相性がとても良いと思います。 テストパターンの切り替えとセグメント送信をA/Bテストプラットフォームで、レポーティングをGoogle Analyticsで、といった具合に使い分けるのもアリかもしれません。 まとめ Google Analyticsの高機能さのおかげでCookieだけでも、実践的なA/Bテストが簡単に実施できることがわかりました。 今回のA/Bテストでは、"普段見落としがちな小さなUI"へのアクセシビリティ配慮の必要性が学習でき、成果にも結びつける事ができたと思います。 A/Bテスト実施によってカンや経験に頼る事なくUIを組み立てる事ができますし、レポーティング手法を学ぶことで提案の幅も広がると思います。 今後もこのような形で検証を繰り返して、KPIとUX改善の両方を追い続けていきたいと思います。 参考URL その文字、薄すぎない? デザインにおける灰色の明度は何%が最適か 1のアイコンは1000の言葉に匹敵するか? イベント トラッキング | ウェブ向けアナリティクス(analytics.js) カスタム ディメンション / 指標 - アナリティクス ヘルプ データレイヤーと Google タグマネージャの管理画面の使用方法 - タグマネージャ ヘルプ A/Bテストの教科書
アバター
みなさん、機械学習やってますか? お久しぶりです。mediba広告システム開発部の原です。 前回はChrome Extensionを活用して広告のプレビューとかに活用しよう!って記事を書きました。 広告のプレビューをchrome-extensionで解決する その後、部署が広告システム開発部に移りましての再登板。今回は機械学習について記事を書いていきます。 アドテク分野でも活用できますしね。 今回の記事では機械学習でどんなことが出来るのか、という紹介ができればな、贅沢を言えば、読んだ皆さんに「よし、じゃあやってみようかな」と思っていただくことに主眼をおいているので、技術的な目新しさとかは考慮していません。 こんなことが出来るんだ、見ているみなさんに気づいていただければ成功かな、と思っています。 開発環境 最初に環境の話です。 本記事の作成・検証環境は以下のとおりです。 Mac OS X 10.10.5 Python 2.7.10 OpenCv 2.4.12 tensorflow 0.9.0 Network Kanji Filter Version 2.1.4 機械学習とは? つづいて機械学習について、ですが。 これが何なのか、というのはそれだけで記事が何本も書けてしまうので、今回は簡単な説明にとどめます。 すごく大雑把に言うと、 データの持っている傾向、パターンをコンピュータによるデータ解析でつかもうぜ という取り組みのことだと思ってください。 例えばスパムメールかどうかを本文などから判断できるか、アクセスの傾向からユーザの性別や年代を特定することが出来るか、といった課題が、よく機械学習の例として挙げられます。 その中から、今回はGoogleが自社開発したオープンソースの人工知能ライブラリ「TensorFlow」 を使った画像分類に取り組んでみたいな、と。 芸能人の顔を画像から分類する さて、今回の取り組みは、タイトルの通り「機械学習で芸能人の顔を分類する」という内容です。 よく人の顔を例えるのにしょうゆ顔とかソース顔っていいますが、あれってデータ的な傾向はあるのかな、あるとしたら画像解析で分類できないかな、というチャレンジになります。 具体的には以下に分類できないかな、と。 しょうゆ顔 しお顔 ソース顔 みそ顔 豚骨がないのが残念ですが、 果たしてコンピュータは画像から顔の特徴を数値化、分類できるでしょうか。 データ作り まずは学習させるデータを作ります。 今回は顔のデータを分類するので、画像データそのものとどんな顔なのかというラベルが必要になります。 つまり、コンピュータに「こういう顔をしょうゆ顔と言うんですよ」と教えていくわけです。 どういう課題に対してどんなデータが必要なのかというのは機械学習のキモなのですが、それを書くと文量がとてもすごいことになるので今回は割愛させてください。 リストアップ まずはしょうゆ顔、ソース顔、しお顔を代表する人たちを調べます。 こちら からリストを拝借しました。 できあがったリストはこんな感じ(person.txt)。 soy Mukai 向井理 soy Higashi 東山紀之 sio Oikawa 及川光博 sio Eita 瑛太 sauce Abe 阿部寛 sauce Hirai 平井堅 miso Matsuken 松平健 miso Watanabe 渡辺謙 本来であればそれぞれ10人位欲しいところですが、今回は機械学習ってこんなことできるよ、という例を示すのが目的なのでこのくらいにしておきます。 画像の収集 リストに上がった芸能人の画像を収集します。 どうやってもいいですが、wgetを再帰的に利用するのが便利なので今回はシェルスクリプトでBingのURL叩く方法でやってみました。 APIでもいいんですが、Googleはオフィシャルにはクローズしています(利用はできるようですが)し、アカウント認証なども必要になりますので、ここは簡単にすませましょう。 勿論、データボリューム拡充等の理由で必要であれば、そうした手続きを踏んだ上で、APIを使うのが適切だと思います。 #!/bin/bash # 人物データファイル定義 PERSON="./person.txt" # 中間ディレクトリ定義 tmp="../tmp" # アウトプットディレクトリ定義 out='../data/img_origin/' # アウトプットディレクトリを絶対パスで取得 SCRIPT_DIR=`dirname $0` pushd $out aout=`pwd` popd # URLエンコード関数定義 urlencode () { echo "$1" | nkf -WwMQ | tr = % } # 画像収集関数定義 imageGather () { if [ $# -ne 3 ]; then return false fi class=$1 enName=$2 jaName=$3 # 人物名をエンコード encodedName=`urlencode $3` echo $encodedName # URLを作成 url="https://www.bing.com/images/search?&q=" url=$url$encodedName # テンポラリディレクトリ作成・移動 mkdir -p $tmp/${class}/${enName} pushd $tmp/${class}/${enName} # wgetでJPEG画像のみ収集 wget -r -l 1 -A jpg,JPG,jpeg,JPEG -H \ -erobots=off \ --exclude-domains=bing.com,bing.net \ $url find . -type f \( -name "*.jpg" -o -name "*.JPG" -o -name "*.jpeg" -o -name "*.JPEG" \) | \ awk \ -v "out=$aout" \ -v "class=$class" \ -v "enName=$enName" \ '{ command = sprintf("cp %s %s/%s_%s_%05d.jpg", $0, out, class, enName, NR) # コマンドを実行して結果を取得 buf = system(command); # stream をclose close(command); }' popd } # 画像収集実行 while read line; do imageGather $line done 1: 実行した結果、202枚(執筆時現在)の画像が集まりました。 ……学習にかけるには、いかにも不安なボリュームです。本来ならば元リストを拡充するところですね。 まあ重ねて書きますが、「こんなことできるよ」が今回の記事の目的なので気にしません。 顔だけを切り出す 続いて、あつめた画像について、pythonとOpenCVで顔部分だけを切り出します。 # -*- coding: utf-8 -*- import sys import cv2 # 引数格納 params = sys.argv argc = len(params) if(argc != 2): print '引数を指定して実行してください。' quit() # 画像ディレクトリ定義 inDir = "../data/img_origin/" outDir = "../data/face_only/" errDir = "../data/error/" #カスケード分類器ロード ※必要に応じて変更してください。 cascade_path = "/usr/local/Cellar/opencv/2.4.12_2/share/OpenCV/haarcascades/haarcascade_frontalface_alt.xml" # 執筆時デフォルトで含まれている顔認識データセット # haarcascade_frontalface_default.xml # haarcascade_frontalface_alt.xml # haarcascade_frontalface_alt2.xml # haarcascade_frontalface_alt_tree.xml # haarcascade_profileface.xml image_path = inDir + params[1] print image_path #ファイル読み込み image = cv2.imread(image_path) if(image is None): print '画像を開けません。' quit() #グレースケール変換 image_gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY) #カスケード分類器の特徴量を取得する cascade = cv2.CascadeClassifier(cascade_path) #物体認識(顔認識)の実行 facerect = cascade.detectMultiScale(image_gray, scaleFactor=1.1, minNeighbors=1, minSize=(1, 1)) if len(facerect) == 1: print "顔認識に成功しました。" print facerect #検出した顔の処理 for rect in facerect: #顔だけ切り出して保存 x = rect[0] y = rect[1] width = rect[2] height = rect[3] dst = image[y:y+height, x:x+width] new_image_path = outDir + params[1] cv2.imwrite(new_image_path, dst) elif len(facerect) > 1: # 複数顔が検出された場合はスキップ print "顔が複数認識されました" print facerect if len(facerect) > 0: color = (255, 255, 255) #白 for rect in facerect: #検出した顔を囲む矩形の作成 cv2.rectangle(image, tuple(rect[0:2]),tuple(rect[0:2] + rect[2:4]), color, thickness=2) #認識結果の保存 new_image_path = errDir + params[1] cv2.imwrite(new_image_path, image) quit() else: # 顔検出に失敗した場合もスキップ print "顔が認識できません。" quit() 引数として渡されたファイル名の画像ファイルを、収集した画像ディレクトリをデータソースから取り出して、「顔」を探しています。 もし「顔」を見つけることが出来たら、その範囲を切り出して別ディレクトリに書き出します。 本来であればこのファイルが正しく存在しているか、正常な画像ファイルであるか等のバリデーションチェックも必要でしょうが、ここでは割愛。 途中で読み込んでいるカスケード分類器ですが、本項執筆環境で一番効率よく動いたものを採用しています。 検出したい顔の特徴などの理由で、別のxmlを使うほうが良いかもしれませんので、色々ためしてみてください。。 なお、この仕組だと1つの画像から複数の顔を検出することがあります。集合写真は勿論、影などによる誤検知とかが理由です。 同様の理由で、顔が見つからない、なんて場合も。 そこで、顔が認識できなかった場合、複数の顔が検出された場合はエラー処理しました。 この辺の処理はどのようなデータがどれだけ欲しいか次第ですね。 続いてこのpythonスクリプトを実行するためのシェルスクリプトです。 #!/bin/bash # 画像ディレクトリ定義 out='../data/img_origin/' # 画像処理スクリプト名定義 script='recognize.py' for file in `ls ${out}`; do python ${script} ${file} done シンプルです。 作ったシェルスクリプトを実行したところ、158枚の「顔」と認識される画像が抽出されました。 これらをさらに直接チェックして、イラストや誤検知ファイルを取り除いていき、残った146枚の「顔」画像を元に学習を行うことにします。 学習データ準備 データが揃ったら、それをTensorflowに読ませる準備です。具体的には以下の作業を行います。 画像をランダムに並び替え train用データとtest用データに機械的に分ける データとラベルをセットにしたCSVを作成する ラベル付けのためのデータ分類用の値は以下とします。 1:しょうゆ顔 2:しお顔 3:ソース顔 4:みそ顔 train用データとtest用データの比率については、とりあえず7:3くらいで試してみます。 #!/bin/bash # 画像ディレクトリ定義 sourceDir='../data/face_only' trainDir='../data/train' testDir='../data/test' # train用データ格納閾値 threshold=70 # カウンタ変数定義 cTrain=0 cTest=0 # 結果出力用CSV作成 trFile='trainDat.csv' teFile='testDat.csv' touch ${trFile} touch ${teFile} # 顔タイプ返却関数定義 function getFaceType () { case "$1" in "soy" ) echo 1;; "sio" ) echo 2;; "sauce" ) echo 3;; "miso" ) echo 4;; esac } # lsとsortを組み合わせてランダムに処理 for file in `ls ${sourceDir}/*.jpg | while read x; do echo -e "$RANDOM\t$x"; done | sort -k1,1n | cut -f 2-`; do # ファイル名成形 bn=${file##*/} kw=${bn%%_*} key=`getFaceType ${kw}` if [ `expr $RANDOM % 100` -lt ${threshold} ] ; then #train用のデータ cp ${file} ${trainDir}/${bn} echo "${file},${key}" >> ${trFile} cTrain=$((cTrain+1)) else #test用のデータ cp ${file} ${testDir}/${bn} echo "${file},${key}" >> ${teFile} cTest=$((cTest+1)) fi done echo "Train:${cTrain}" echo "Test:${cTest}" これを実行して、104枚のトレーニング用画像、42枚のテスト用画像に分け、それぞれCSVに分類コードと一緒に格納しました。 学習の実行 ながなが書いてきたのですが、これまではすべて準備。 Tensorflowで学習していきます。 用意するCNNは 入力28x28x3 第1畳み込み層とプーリング 第2畳み込み層とプーリング 高密度結合層1 高密度結合層2 ソフトマックス層 となります。 この構造については、後述の参考ブログから拝借しました。 なんちゃら層とか言葉の意味がわからないよ、という方向けに、参考URLを幾つか上げておきます。 TensorFlowチュートリアル - 熟練者のためのディープMNIST(翻訳) TensorFlowコトハジメ 手書き文字認識(MNIST)による多クラス識別問題 Theanoによる畳み込みニューラルネットワークの実装 (1) さて、この学習部分は下記ブログがほとんどそのまま利用できました。先達は偉大ですね。 TensorFlowでアニメゆるゆりの制作会社を識別する したがって、ここではコードの全文掲載はしません。 また、後述のgithubにも含める予定はありません。 変更点だけ記載すると、 コード値が0〜1ではなく、1〜4なので、NUM_CLASSES = 4にし、tmp配列の添字も1ずらす CSVのロードなのでline.splitのデリミタをカンマにする ホント、これだけで動いちゃうんです。すげぇ。 step 195, training accuracy 0.980769 step 196, training accuracy 0.980769 step 197, training accuracy 0.980769 step 198, training accuracy 0.980769 step 199, training accuracy 0.980769 test accuracy 0.785714 実行してみたところ、testデータの正答率は78.6%… 多少不満ですが、データ数が少ないこともあり仕方ないかと。 本項の最後に、 TensorBoard によるCNNと学習精度グラフを掲載しておきます。 モデルの活用 実行した結果は学習モデルとして、チェックポイントファイルに保存されます。 このファイルを使うことで、別の画像についても同じモデルで分類することが出来ます。 で、ここも 先達 がやっているわけで。。。 テストコードをつくって、 弊社CTOの写真 を解析してみます。 $ python testFaceType.py cto.jpg 2 2。 つまり しお顔 。 ……コンピュータ的にはそうなのか。。。 ミソとかを想定していたんだけど、最初に集めるデータのボリュームとラベル付けが足りなかったかなぁ… 機械学習精度向上にむけての考察 今回は少ないテストデータで行ったの結果、ちょっと想定と違う結果が得られたわけですが、色々な改善の余地があると思います。 例をあげますと、 データの拡充、ラベル付け時点での精度向上 →正しいデータ集め。仮に少ないデータでも、CVに画像の反転や回転機能がありますので、それでかさ増しをするテクニックなんかもあります。 CNNの構造改良 →今回は前例に倣う形で畳み込み層とプーリングを2重にする構造を採用しましたが、これが最適かどうか、トライ・アンド・エラーで検証していく必要があります。同時に各層で利用している学習パラメータもまだまだチューニングできます。 そもそもの学習テーマと収集データの方向性が正しいのかの検証 →そして、何よりこの項目が大切です。 機械学習は「データさえ渡せば適用に予測してくれるもの」ではありません! どういう課題を解決したいのか? そのために適切な学習アルゴリズムはどういうものか? そのアルゴリズムを実現するのに必要なデータはどういうものか? しっかり考えて、適切な方法を試す。その実現のためにこういう汎用的な例から慣れていきましょう! github 本項で書いたコードについて、下記レポジトリに公開しました。 試してみよう、というかたは是非ご活用ください。 https://github.com/medi-hara/faceClassify 最後に 機械学習について学ぼうとすると、数式とか観念的な記述に多くぶち当たって一見ハードルが高そうに見えます。 が、実際には今回の記事のように多くの先達がトライして、そのログを公開してくださっている分野で、思った以上に入門しやすいジャンルです(熟練はやはり難しいですが)。 だから、コード丸写しでいいからとにかく一度やってみようよ、やってみると意外と楽しいよ、ということを伝えたくてこの記事を書きました。 うまく意図が伝わるといいのですが…… ともあれ皆さん、機械学習、はじめてみませんか? その他注意など 本記事ではリストが小さいため特に配慮していませんが、検索ボリュームによっては検索エンジン側に負荷をかけないようにwaitをかけるなど各自工夫してください。 本記事のスクリプトを利用してのトラブル等については、一切の責任を負いかねます。
アバター
こんにちは。メディアシステム開発部の森竹です。 新規メディアやauスマートパスのサーバーサイド開発を担当しています。 今回は先日Stable版がリリースされた Docker for Mac を使い、Ruby on Railsアプリケーション開発環境を構築してみます。 Dockerとは? Docker 社が提供するコンテナ化プラットフォームのソフトウェアです。 以前は Docker Toolbox を使用していましたが、VirtualBoxが必要、環境設定が必要、Docker Machineを使う必要があるなど、使うまでの道のりが長い印象がありました。 それに比べDocker for MacではVirtualBoxが不要、環境設定が不要、インストールすればすぐ使えるのが魅力です。 前提 OS X El Capitan(10.11.6)に Docker for Mac をインストールします。 バージョンは下記の通りです。 $ docker -v Docker version 1.12.0, build 8eab29e $ docker-compose -v docker-compose version 1.8.0, build f3628c7 Docker Compose Docker ComposeとはマルチコンテナのDockerアプリケーションを定義し、実行するツールです。docker-compose.ymlファイルを定義して、各コンテナのDockerfileやイメージを読み込み、Dockerアプリケーションを実行します。 コンテナ構成 今回は下記のコンテナ構成としました。 Ruby 2.3.1(Ruby on Rails 5.0) Nginx 1.10.1 MySQL 5.6.32 docker-compose.yml version: '2' services: rails: container_name: rails build: . command: bundle exec unicorn -p 3000 -c config/unicorn.conf.rb ports: - '3000:3000' environment: RAILS_ENV: development MYSQL_ROOT_PASSWORD: 'root' DATABASE_URL: 'mysql2://root:root@mysql:3306' depends_on: - mysql volumes: - ./:/myapp links: - mysql nginx: container_name: nginx build: containers/nginx ports: - '80:80' depends_on: - rails volumes: - ./containers_data/nginx:/var/log/nginx links: - rails mysql: container_name: mysql image: mysql:5.6.32 environment: MYSQL_ROOT_PASSWORD: 'root' ports: - '3306:3306' volumes: - ./containers_data/mysql:/var/lib/mysql Railsコンテナ こちら を参考に定義しました。 Dockerfile FROM ruby:2.3.1 RUN apt-get update && apt-get install -y mysql-client --no-install-recommends && rm -rf /var/lib/apt/lists/* RUN mkdir /var/lib/mysql && touch /var/lib/mysql/mysql.sock ADD ./containers/mysql/my.cnf /etc/ RUN mkdir /myapp WORKDIR /myapp COPY Gemfile /myapp/Gemfile COPY Gemfile.lock /myapp/Gemfile.lock RUN bundle install COPY . /myapp EXPOSE 3000 Nginxコンテナ nginx.conf をコピーします。 Dockerfile FROM nginx:1.10.1 ADD nginx.conf /etc/nginx/nginx.conf MySQLコンテナ mysql:5.6.32 イメージをそのまま使います。 使い方 $ git clone git@github.com:mediba-moritake/docker-rails5.git $ cd docker-rails5 $ docker-compose build $ docker-compose run rails rails db:create $ docker-compose up -d $ open http://127.0.0.1 「Yay!You’re on Rails!」画面が表示されれば構築完了です。 Welcome画面がRails 5.0になって可愛くなってますね! 開発 早速開発して行きましょう。 今回はscaffoldを実行し、ユーザーCRUDを自動生成します。 $ docker-compose run rails rails generate scaffold user name:string $ docker-compose run rails rails db:migrate $ open http://127.0.0.1/users/ 表示された画面からユーザー登録を行い、データの永続化を確認します。 Serverspec さらにマルチコンテナ構成のテストを実行します。 今回はホスト側(Mac)からDockerコンテナに対して、 Serverspec を実行します。 $ bundle install --path vendor/bundle $ bundle exec rspec 各コンテナのテスト概要は下記の通りです。 コンテナが存在すること。 コンテナが起動していること。 コンテナイメージが正しいこと。 バージョンが正しいこと。 ポートを受け付けること。 アーキテクチャ、OSが正しいこと。 rails_spec.rb require 'spec_helper' describe docker_container('rails') do it { should exist } it { should be_running } end describe docker_image('dockerrails5_rails:latest') do it { should exist } its(['ContainerConfig.Env']) { should include 'RUBY_VERSION=2.3.1' } its(['ContainerConfig.ExposedPorts']) { include eq '3000/tcp' } its(['Architecture']) { should eq 'amd64' } its(['Os']) { should eq 'linux' } end 同様に、Nginx、MySQLも作成します。 おわりに Docker for Macを使用したRuby on Railsアプリケーションの開発環境を構築しました。 課題として、現状のコンテナベースイメージがDebianとなっているので、Alpine Linuxを採用して軽量化を図りたいと思っています。 またデータの永続化について、今回はデバッグや開発のしやすさを考慮してホスト側に永続化していますが、Production環境ではデータVOLUMEコンテナの採用を検討すべきです。 またマルチコンテナ構成のテストではServerspecだけでなく、 Infrataster を併用するようなテストパターンが良いのかもしれません。 今回は開発環境だけとなりますが、今後はProduction環境を意識して改善して行ければと思います。 ソースコード 今回使用したソースコードはこちらにあります。 https://github.com/mediba-moritake/docker-rails5
アバター
お久しぶりです、インフラストラクチャー部の沼沢です。 今回は S3 のオブジェクトに対するアクセス制御についてです。 User Agent で S3 オブジェクトへのアクセス制御したいという要望があり、ググっても意外と出てこなかったので書きました。 どうやって S3 オブジェクトへのアクセスを制御するか 結論から言うと、バケットポリシーで簡単に設定できます。 まずはポリシーの例を挙げます。 { "Version": "2012-10-17", "Id": "PolicyXXXXXXXXXXXXX", "Statement": [ { "Sid": "StmtXXXXXXXXXXXXX", "Effect": "Allow", "Principal": "*", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::numatest/numachi/index.html", "Condition": { "StringLike": { "aws:UserAgent": "* numatest *" } } } ] } 上記の例では以下の要件を満たしています。 「User Agent に “ numatest ” (前後に半角スペースあり)が含まれている場合のみ、numatest バケット内の numachi/index.html への GetObject を許可する」 このようにすることで、 オブジェクトを公開状態に設定すること無く、特定のオブジェクトへのアクセスを許可することが可能 です。 S3 オブジェクトを ◯◯ でアクセス制御したいという要件があった場合に重要になるのは、"Effect"、"Resource"、"Condition" の3つだと考えています。 IAM ポリシーと同じ書き方をするため、見慣れている方からすれば特に難しいことは無いと思いますが、少し解説します。 Effect 条件に一致した場合に許可(Allow)するのか拒否(Deny)するのかを指定します。 指定できるのは “Allow” または “Deny” のみです。 余談ですが、ここで “Deny” を定義することを 明示的な拒否 と言ったりしますが、これはまた別の機会があればお話したいと思います。 Resource アクセス制御をしたいオブジェクトの ARN を指定します。 これは、以下のフォーマットで指定します。 ... "Resource": "arn:aws:s3:::<BucketName>/<ObjectKey>", ... 例ではそれぞれ、BucketName=numatest、ObjectKey=numachi/index.html ということになります。 また、配列で定義することで複数オブジェクトを指定できます。 ... "Resource": [ "arn:aws:s3:::numatest/numachi/index.html", "arn:aws:s3:::numatest/numachi/help.html", ... ], ... Condition このポリシーを適用する条件を指定します。 これは、以下のフォーマットで指定します。 ... "Condition": { "<条件演算子>": { "<条件キー>": "<評価値>" }, ... }, ... 例では、"StringLike" を使って User Agent の Like 検索を実施しています。 “StringLike” では、ワイルドカード(任意の複数文字一致 “*"、または任意の 1 文字一致の ”?“)を利用することが可能です。 仮に、「アクセス元の IP アドレス(xxx.xxx.xxx.xxx/24)での制御も入れてほしい」となった場合には、以下のように指定することで実現できます。 ... "Condition": { "StringLike": { "aws:UserAgent": "* numatest *" }, "IpAddress": { "aws:SourceIp": "xxx.xxx.xxx.xxx/24" } }, ... 上記のように、Condition に複数条件を指定すると、AND 条件で判定されるため、どちらも一致した場合のみ、条件に一致したことになります。 まとめ 今回はメインの User Agent でのアクセス制御方法(+α)をご紹介しましたが、 Condition の条件演算子や条件キーには他にもいろいろなものが指定可能です。 詳しくは IAM ポリシーエレメントの参照 を確認していろいろ試してみてください。
アバター
こんにちは。制作部の平尾です。 私は2〜3年前くらいまでFlashがメインでお仕事していたのですが、最近はスマホのお仕事が多いので、新しく生まれ変わった Animate はまったく触っていませんでした。久しぶりにAnimateを使って、HTML5+JacvaScriptのアニメーションを作ってみたらいい感じだったので、簡単にサンプルを使ってご紹介したいと思います。 サンプルの完成イメージはこんな感じです。このアニメーションをAnimateで作成して、HTML5 Canvasにポチっとしてみようと思います。 ※注:この画像↑はgifです。 1. Animateでアニメーションをつくる シンボル内でON/OFFの顔を切り替えていて、さらにステージ上でもアニメーションさせる二重構造になっています。今回はサンプルなので、簡単にモーションプリセットでアニメーションさせてみました。 2. パブリッシュ → はい、完成! ポチっただけで出来上がりです。パブリッシュすると同時にローカルサーバーを立ち上げて、パブリッシュされたhtmlをブラウザで表示させてくれるのもありがたいです。 ※今回やってみたデータは こちら に置いてあります。 生成されたファイルの中身を確認してみる HTML htmlの方に初期化の処理が書いてあります。 CreateJSをnewしたりCanvasにステージをセットしたりします。 画像(スプライト)とjson 使っている画像はスプライト化してくれて、画像の位置情報はjsonに書いてくれます。 {"images": ["images/index_atlas_.png?1463389483401"], "frames": [[0,0,61,56],[0,58,61,56]]} スプライト化する際の画像の圧縮率とかもパブリッシュ設定で変更可能です。 JavaScript いろいろやってくれています。 ステージの設定 lib.properties = { width: 480, height: 240, fps: 30, color: "#FFFFFF", manifest: [] }; シンボルはこんな感じでスプライトを使ってオブジェクトに。 (lib.off = function() { this.spriteSheet = ss["index_atlas_"]; this.gotoAndStop(0); }).prototype = p = new cjs.Sprite(); シンボル内のアニメーションはこんな感じ。 (lib.symbol = function(mode,startPosition,loop) { this.initialize(mode,startPosition,loop,{}); // parts_off this.instance = new lib.off(); this.instance.setTransform(-30.5,-28); this.instance._off = true; this.timeline.addTween(cjs.Tween.get(this.instance).wait(8).to({_off:false},0).wait(7)); ~~~~~~ 省略 ~~~~~~ }).prototype = p = new cjs.MovieClip(); p.nominalBounds = new cjs.Rectangle(-30.5,-28,61,56); ステージのアニメーション(自分では書けないほど細かく書いてくれます…) (lib.index = function(mode,startPosition,loop) { this.initialize(mode,startPosition,loop,{}); // layer this.instance = new lib.symbol(); this.instance.setTransform(43.4,68.4,1,1,0,0,0,-0.7,26); this.timeline.addTween(cjs.Tween.get(this.instance).wait(1).to({regX:0,regY:0,x:49.3,y:42.5},0).wait(1).to({x:54.6,y:42.9},0).wait(1).to({x:59.8,y:43.4},0).wait(1).to({x:65,y:44.2},0).wait(1).to({x:70.3,y:45.2},0).wait(1).to({x:75.5,y:46.5},0).wait(1).to({x:80.7,y:47.9},0).wait(1).to({x:85.9,y:49.6},0).wait(1).to({x:91.2,y:51.5},0).wait(1).to({x:96.4,y:53.7},0).wait(1).to({x:101.6,y:56},0).wait(1).to({x:106.9,y:58.6},0).wait(1).to({x:112.1,y:61.5},0).wait(1).to({x:117.3,y:64.5},0).wait(1).to({x:122.6,y:67.8},0).wait(1).to({x:127.8,y:71.3},0).wait(1).to({x:133,y:75},0).wait(1).to({x:138.2,y:78.9},0).wait(1).to({x:143.5,y:83.1},0).wait(1).to({x:148.7,y:87.5},0).wait(1).to({x:153.9,y:92.1},0).wait(1).to({x:159.2,y:97},0).wait(1).to({x:164.4,y:102.1},0).wait(1).to({x:169.6,y:107.4},0).wait(1).to({x:174.9,y:112.9},0).wait(1).to({x:180.1,y:118.7},0).wait(1).to({x:185.3,y:124.6},0).wait(1).to({x:190.6,y:130.8},0).wait(1).to({x:195.8,y:137.3},0).wait(1).to({x:201,y:143.9},0).wait(1).to({x:206.2,y:150.8},0).wait(1).to({x:211.5,y:157.9},0).wait(1).to({x:216.7,y:165.2},0).wait(1).to({x:221.9,y:172.8},0).wait(1).to({x:227.2,y:180.6},0).wait(1).to({scaleX:1.44,scaleY:0.73,x:232.7,y:195.5},0).wait(1).to({scaleX:1.5,scaleY:0.7,x:237.9,y:185.1},0).wait(1).to({scaleX:1.44,scaleY:0.73,x:243.1,y:174.3},0).wait(1).to({scaleX:1,scaleY:1,x:248.1,y:158.7},0).wait(1).to({x:253.3,y:151.4},0).wait(1).to({x:258.5,y:145.4},0).wait(1).to({x:263.8,y:140.8},0).wait(1).to({x:269,y:137.5},0).wait(1).to({x:274.2,y:135.5},0).wait(1).to({x:279.5,y:134.8},0).wait(1).to({x:284.7,y:135.5},0).wait(1).to({x:289.9,y:137.5},0).wait(1).to({x:295.2,y:140.8},0).wait(1).to({x:300.4,y:145.4},0).wait(1).to({x:305.6,y:151.4},0).wait(1).to({x:310.9,y:158.7},0).wait(1).to({x:316.1,y:167.4},0).wait(1).to({x:321.3,y:177.3},0).wait(1).to({scaleX:1.3,scaleY:0.8,x:326.7,y:193.8},0).wait(1).to({scaleX:1,scaleY:1,x:331.8,y:182.6},0).wait(1).to({x:337,y:177.6},0).wait(1).to({x:342.2,y:173.8},0).wait(1).to({x:347.5,y:171},0).wait(1).to({x:352.7,y:169.4},0).wait(1).to({x:357.9,y:168.8},0).wait(1).to({x:363.2,y:169.4},0).wait(1).to({x:368.4,y:171},0).wait(1).to({x:373.6,y:173.8},0).wait(1).to({x:378.8,y:177.6},0).wait(1).to({x:384.1,y:182.6},0).wait(1).to({x:389.3,y:188.6},0).wait(1).to({scaleX:1.2,scaleY:0.9,x:394.7,y:188},0).wait(1).to({scaleX:1,scaleY:1,x:399.8,y:183.1},0).wait(1).to({x:405,y:181.8},0).wait(1).to({x:410.2,y:181.3},0).wait(1).to({x:415.5,y:181.8},0).wait(1).to({x:420.7,y:183.1},0).wait(1).to({x:425.9,y:185.4},0).wait(1).to({x:431.2,y:188.6},0).wait(1)); }).prototype = p = new cjs.MovieClip(); p.nominalBounds = new cjs.Rectangle(253.6,134.4,61,56); (番外編)シェイプもちゃんとJavaScriptで書いてくれます。 ちなみに上記のサンプルは画像で作りましたが、シェイプでつくった場合は、シェイプもちゃんとJavaScriptでCanvasに描画してくれます。こんな感じでひとつずつシェイプを再現してくれます。 this.shape_1 = new cjs.Shape(); this.shape_1.graphics.f("#5C5B63").s().p("AgJAMQgIgBgGgFQgGgFACgFQADgIARAAQAfAAAFAQQgPAJgOAAIgJgBg"); this.shape_1.setTransform(-8.6,-3.2); まとめ 細かなアニメーションもちゃんとJavaScriptで再現してくれるのは、とても楽チンだなと思いました。このシンボルが.jsファイル内のこの記述で表現されているのね!なんて見ていると、ワクワクします。 それから、JavaScriptでの絶妙なアニメーション調整が、Animate上でマウスでちょちょいと調整→ポチっと変換でできるのは、視覚的に操作できて良いんじゃないかと思いました。たとえば、デザイナーが、デザインしたパーツにこんなアニメーションつけたい!というイメージを持っていたとして、エンジニアに言葉で伝えるのはなかなか難しかったりしますが、Animateで作ってポチっと変換したファイルを渡せば、コピペするだけで済みます。これは デザイナー→エンジニアの連携にとっても便利なのかも! と思いました。 実際にやってみて、いろいろとメリットがあることがわかったのですが、もちろんデメリットもあります。 メリット 視覚的にアニメーション調整ができる 今までアニメーションする時は何かしらのライブラリを使って、記法とかメソッドとかを調べながら書いていましたが、この方法なら視覚的にアニメーションをつけたあとにポチっとするだけなので、とても楽チン JavaScriptでアニメーションを書くことができる人がいなくても、HTML5+JavaScriptでアニメーション表現を盛り込んだページを作ることができる レイヤーごとにアニメーションをつけることができるので、複雑なアニメーションがつくりやすい デメリット Animateの操作に不慣れな場合は、操作方法を知る必要がある CanvasとCreateJSで出来ていることなのでAndroid2系など、再現が難しい場合がある HTML5+JavaSriptでアニメーションをつくる方法はたくさんあるので、案件によって何が最適かはそれぞれだと思いますが、ひとつの方法としてAnimateという選択肢もアリだなと思いました。個人的には、Animateの操作も慣れているし、メリットの方に魅力を感じているので、今後、何かの案件で取り入れてみたいなと思います!
アバター
メディアシステム開発部の野崎です。 auスマートパスなどのサーバサイド開発を担当しております。 担当システムの特徴としては、連携システムが多いことですが、より効率的に開発運用したい感じることが多いため、 mastermind というモックサービスを試してみます。 はじめに mastermindとは mitmproxy を利用したmockサービスである URLごとやURL patternごとに定義する 定義ごとにHTTP(S)リクエストを遮断し、レスポンスを改ざんする ustwoという会社でつくられているらしい MIT License で提供 2016/07現在のところ、v0.9.0が最新版 インストール 以下の環境で試しています。 Mac OS X: 10.11.5 Webブラウザ:Google Chrome インストールは、 こちら を参考に進めていきます。 mastermindのインストール homebrewを使いインストールします。 $ brew tap ustwo/tools $ brew install mastermind 証明書をインストール 次にhttps通信も扱うため、mitmproxyの証明書をインストールします。 まず下記のコマンドで、mastermindを起動します。 $ sudo mastermind --with-driver --source-dir . ブラウザで、 http://mitm.it/ を開きます。 このようなページが閲覧できるので、Appleのアイコンを押下します。 すると mitmproxy-ca-cert.pem という証明書をDLできるのでインストールしましょう。キーチェーンアクセス.appでインストールの確認が出来ます。 サンプルコード サンプルコードの説明 さて、 こちら のサンプルコードを使って、機能を試してみます。 $ git clone https://github.com/nozaki-mediba/mastermind-sample.git $ cd mastermind-sample サンプルの中は以下の構造になっています。 ├── rulesets │ ├── 168x40.png │ ├── 544x184.png │ ├── sample1.yaml ⇛ ruleset │ └── sample2.yaml ⇛ ruleset └── sample.toml ⇛ config mastermindの使い方概要 ここで使い方を簡単にまとめておきます。 大きく設定する箇所は2点あります。 config:mastermindのhost,portの設定やrulesetの指定を行う。 ruleset:どのURLをmockにするかを指定する。 mastermindの起動停止 sample.toml をconfigとし、起動する場合以下のコマンドです。 $ sudo mastermind --config sample.toml 停止は ctrl+c らしいですが上手くいかない場合もありました。その場合は下記のコマンドで停止出来ました。 $ sudo killall -9 mastermind && sudo proxyswitch --disable rulesetの状態切り替え rulesetの状態を変更したり確認したりします。 ruleset xxxの有効化 http://proxapp:5000/xxx/start/ rulesetの状態確認 http://proxapp:5000/state/ rulesetの無効化 http://proxapp:5000/stop/ 1. PCのブラウザで試してみる では、 https://www.google.co.jp/ を使って試してみます。 このページのGoogleのロゴをローカルのものに差し替えてみます。 まずmastermindを起動します。 $ sudo mastermind --config sample.toml この状態で、sample1の設定を有効化をしてみましょう。 http://proxapp:5000/sample1/start/ をブラウザで叩きます。 httpsで配信されているGoogleのロゴですが、ローカルのファイルに差し替えることができました。 2.モバイル端末で試してみる 次にモバイル端末でも同様のことを試してみます。 iPhone6/iOS9.3.2のSafariで確認しました。 http://www.mediba.jp/ のコーポレートロゴをローカルのものに差し替えます。 mastermindを起動しているPCと同じWifiに接続し、ローカルIPとポートを指定します。 そして、sample2を有効化します。 http://proxapp:5000/sample2/start/ Safariで http://www.mediba.jp/ を閲覧すると、 ロゴが差し替わっています。 ここでは記載しませんが、端末にPCの場合と同様にmitmproxyの証明書をインストールすれば、httpsの差し替えも行えます。 最後に ここまでフロント側での使い方を見てきましたが、 vagrantでのアプリケーションで対向先が多い場合でも 例えば、libcurlを使っているアプリケーションの場合 .curlrc に mastermindのプロキシURL http://localhost:8080 を指定すると 対向先のmockが簡単に作れそうです。 このmockはyamlで定義できるので、開発チーム内で共有しやすく vagrantでスタブサーバを立ち上げなくてもよく、非力なPCでも開発が進められそうです。 以上です。
アバター
こんにちは。広告システム開発部の小林です。 私が参加しているプロジェクトでは、AWSのS3を使用しているのですが、phpunitなどのテスト実行時にS3へのアクセスが走ってしまうことがあります。 S3はアクセスとストレージ内のファイルの容量によって課金が発生するため、テスト実行時に費用が発生することを避けたいと思いました。 そこで、ローカルの閉じた環境下でS3を使用できないかと調べていたらs3rverというモジュールを見つけましたので紹介させていただきます。 s3rverとは ローカル環境に擬似的にS3の機能を再現できる、Node.jsのnpmでインストール可能なモジュールです。 https://www.npmjs.com/package/s3rver s3rverで再現可能なS3の機能 バケット 作成 リスト表示(バケット、ファイル) 削除 ファイル 保存 取得 削除 では、実際に使ってみます。 今回はテストを実施する環境としてローカル環境のvagrant上に仮想環境を用意しました。 環境 CentOS : 6.7(仮想環境) PHP : 5.6.19 AWS SDK for PHP : 2.0 Node.js : v0.11.16 npm : 2.3.0 s3rver : 1.0.2 s3rverのインストール インストールはnpmコマンドで行います。 Node.jsとnpmのインストールについては割愛させていただきます。 $ npm install s3rver -g $ s3rver --version 1.0.2 これでインストールは完了です。 s3rverの使い方 まずはオプションについてです。 $ s3rver --help Usage: s3rver [options] Options: -h, --help output usage information --version output the version number -h, --hostname [value] Set the host name or ip for the server -p, --port <n> Set the port of the http server -s, --silent Suppress log messages -i, --indexDocument [path] Index Document for Static Web Hosting -e, --errorDocument [path] Custom Error Document for Static Web Hosting -d, --directory [path] Data directory -c, --cors Enable CORS 主に使用するオプションは、以下になると思います。 -h, –hostname 受け付けるホストの名前かIPアドレスを指定します。 -p, –port 受け付けるポート番号の設定を行います。 デフォルトは 4568 となっています。 -d, –directory ファイルを格納するためのディレクトリを指定します。 192.168.33.125 (仮想環境へアクセス可能なIPアドレス)の /tmp/s3rver をs3rverのData directoryに指定するときのコマンド $ s3rver -h 192.168.33.125 -d /tmp/s3rver now listening on host 192.168.33.125 and port 4568 これでs3rverが起動したので擬似的にS3として使用することが可能です。 起動した状態で、 http://192.168.33.125:4568 にブラウザでアクセスするとXMLが表示されます。 XMLの内容は、バケットの一覧となっています。 注意点 s3rverが起動して使用できる状態にはなりましたが、注意点があります。 ファイルを指定したディレクトリに直接置いてもファイルとして認識されない /tmp/s3rver/txt/test.txt としてファイルを設置してもs3rverでは認識されません。 各言語のAWS SDKなどを使用してs3rverへファイルをputするとs3rver上でファイルとして認識されます。 AWS SDKを使用する際、endpointは起動時に指定したURLに設定する必要があります。 phpでのファイルアップロードのサンプル (AWS SDKを使用します) <?php // オートローダを登録 require('/var/www/vendor/autoload.php'); use Aws\Credentials\Credentials; use Aws\S3\S3Client; // S3へ接続の設定を行う // ACCESS_KEY_ID, SECRET_ACCESS_KEYはs3rverを使用する場合はAWSのアカウントのACCESS_KEY_IDとSECRET_ACCESS_KEYを指定する必要はありません。 $credentials = new Credentials('ACCESS_KEY_ID', 'SECRET_ACCESS_KEY'); $s3 = S3Client::factory( [ 'region' => 'ap-northeast-1', 'version' => '2006-03-01', 'credentials' => $credentials, 'endpoint' => 'http://192.168.33.125:4568', ] ); // ファイルをアップロードする $result = $s3->putObject( [ 'Bucket' => 'txt', 'Key' => 'test.txt', 'Body' => 'Hello, world!' ] ); ?> 上記のサンプルコードを実行後にディレクトリを確認すると以下のようになります。 $ ls -l /tmp/s3rver/txt/ 合計 4 drwxrwxr-x 2 vagrant vagrant 4096 7月 11 07:26 2016 test.txt アップロード時にkeyに指定したtest.txtがディレクトリで作成されています。 test.txtの中身は以下のようになっています。 $ ls -al /tmp/s3rver/txt/test.txt/ 合計 16 drwxrwxr-x 2 vagrant vagrant 4096 7月 11 07:26 2016 . drwxrwxr-x 3 vagrant vagrant 4096 7月 11 07:26 2016 .. -rw-rw-r-- 1 vagrant vagrant 13 7月 11 07:26 2016 .dummys3_content -rw-rw-r-- 1 vagrant vagrant 197 7月 11 07:26 2016 .dummys3_metadata .dummys3_contentがファイルの実体、.dummys3_metadataがファイルのメタデータとなっています。 この命名規則でファイルをディレクトリに設置すれば、aws sdkを通してputしなくてもs3rverにファイルとして認識させることは可能です。 ファイルについての注意点を記載しましたが、バケットは -d で指定したディレクトリに新規でディレクトリを作成することでバケットとして認識されます。 まとめ 簡単にですが、s3rverの使い方と注意点について紹介させていただきました。 s3rver起動後は、AWS SDK側への設定はendpointを指定するだけなので、特に意識することなく使うことができて非常に簡単だと思います。 テストや実際のAWS環境にアクセスすることなく実験したいときなどに使えると思いますので、機会があれば是非使ってみてください。
アバター
こんにちは。mediba制作部の大村です。 現場でAdobe Creative Cloudを使っているデザイナーさんに朗報です! 既に現場でも使い始めたのではないかと思いますが Adobe Experience Design (以下XD)を使うと普段の作業がサクサクスムーズなんです。2016年6月にリリースされたバージョン4で、メニューが日本語対応となりとても使いやすくなりました。 ちょっと触ってみましたので、レビューします。 参考記事 : Adobe XD Preview 4 リリース!日本語UI、オブジェクト間の距離計測、ぼかし機能など 1. Adobeユーザーなら学習コストほとんど無し 少しづつだけど確実に時短できる PhotoshopやIllustratorを使ってUIデザインしている方には、 作業時間の短縮 が見込めますので、是非使用して欲しいツールです。 ソフト自体のUIは洗練されましたが、アートボードやオブジェクトの操作方法など基本的な使い方がAdobe製品は統一されている為、Adobeユーザーであれば使い方を調べたりせずとも直感的に使えます。 また、これからWebデザイナーを目指す方にも、操作が簡単なのでオススメです。 グラフィック用のツールと違いUIデザイン用に作られたツールなので、ページもサクサク増やせ、移動や複製も小数点以下が出にくく、更にシンプルなのでデータ自体とても軽いので、細かいことは気にぜず邁進できます。 移動や変形、オブジェクトを作成する際も小さな文字で数字が表示され自分の動作が可視化されるので、作りながら考えることができ後で細かい確認が削減されます。 注意 :バナーやイメージ画像のような凝ったものは作れないので、完成するためにはPhotoshopやIllustratorも併用します。 Adobeのショートカットがそのまま使える 使えるショートカットの一例 機能 Mac / Windows グループ ⌘+G / Ctrl+G グループ解除 Shift+⌘+G / Shift+Ctrl+G コピー ⌘+C / Ctrl+C カット ⌘+X / Ctrl+X ペースト ⌘+V / Ctrl+V 全面に移動 ⌘+[ / Ctrl+[ 最全面に移動 Shift+⌘+[ / Shift+Ctrl+[ 最背面に移動 Shift+⌘+] / Shift+Ctrl+] カーソル1px移動 方位キー / 方位キー カーソル10px移動 Shift+方位キー / Shift+方位キー 2. データが軽い・共有もワンプッシュ Web上にデータを上げてすぐに共有 共有してすぐに端末で操作確認ができるのも魅力です。 共有は InVision や Prott などのプロトタイプツールにもある機能ですが、デザインデータを別で持っている場合もあり、デザインしてそのデザインをプロトタイプツールに反映し、更にプロトタイプを変更して共有しなければなりません。 XDでは、1度オンラインで共有したのち、 「再共有」→「リンクを更新」 するだけで変更が反映されます。 この早さ、手順の少なさは特記すべき魅力です。 3. デザインとプロトタイプの2in1 同時にできることでデザインを効率的に進められる 他のプロトタイプツールは、プロトタイプとデザインを別のツールで行う必要があります。 初回に組んでしまえば修正段階での煩わしさはあまりないのですが、アプリケーションを横断した作業が面倒、且つタイムラグがあったりもします。 XDなら 作りながらプロトタイプ でき、ページを少しづつ増やすことも簡単です。 増やしたページを繋げながらデザインが進められるので、ページを繋いだプロトタイプを頭のなかで考えながら作業する必要がありません。 労力が格段に少なくどんどんデザインが作れます! 4. こだわり過ぎる方にオススメ 細かく変更できないのが正解 画像はアートボートを移動したときの例ですが、一定の間隔で置いた場合その数値の場所にスナップするので並び替えもグイグイできます。 個人的にはドラックアンドドロップして出る端数が苦手でデザイン作成時にドラックはぜずXYの数値変更かカーソルキーでしか移動しないのですが、 スナップが的確 且つ 思ったところにガイドが出る ので、細かいことを気にせずとても気持ちよく移動・変更ができます。 細かく変更や調整ができないので、余計な細かい作業に捉われる事無くUIデザインを完成させることができます。 「1pxずれちゃった!コピペしたこの素材もこれの相対で動かしたパーツ達も全部ずれてる!整えなきゃ!(また2時間かかるわ…)」なんてことが激減します。 自動の機能が時短の鍵 XDに出会う前はIllustratorでUIデザインをしていました。 感覚的にはIllustratorに近い操作感なのでIllustratorや InDesign などのAdobeレイアウトツールを使っていた方からすると自動化された便利な機能が沢山あります。 一つは上の画像のように、右上の整列の下 「グリッドの繰り返し」 。 繰り返したボックスの中に画像を入れると、アルファベット/数字/五十音順など決まった順番、且つボックスのサイズに合わせてトリミングされ、一気に表示される機能があります。 繰り返したグリッドの変更も1箇所反映すれば全てに自動で反映されラクチンです。 また、UIデザインパーツ(UI KITS:Apple iOS、Google Materialデザイン、Microsoft Windows)が搭載されているので今まで探したり作ったりしていた手間が省けます。あとは日本語のキーボードのキットが搭載されれば最強かも?! 5. まとめ 作業時間を短縮してモックをたくさん作る UIデザインはページの前後関係や実際ページを見たときの UXを鑑みることが大切 なので プロトタイピングが必要不可欠 だと考えます。 作成する上で、遷移先がWebなのかアプリ起動なのかでも遷移元のUIは違ったものが適しているかもしれないという想定がされます。 想定だけでなく、 実際作って触ってみて ユーザーの視点に立ち考えることで全く違った考えも生まれてくるので、たくさん試して色々検討する時間の方が大事だと思います。 それを実現する為には「時間が欲しい!」と思っていましたがXDならあまり時間をかけなくても実際触れるものが作れると思いました。 今は自分の時短優先でXDを使用していますが、GithubやSlackなどチーム連携も考えると、XDよりも適したツールがあるかもしれません。 作業時間のさらなる短縮を目指し、最短でリリースが可能なUIデザインツールを模索していきたいと思っています。 他にも便利そうなプロトタイピングツール一覧 pixate CRAFT Silver Flows Affinity Designer Sketch InVision Prott
アバター
こんにちは、広告システム開発部の八代です。 今年の4月に広告システム開発部に異動し、広告の知識やシステムに携わることが多くなってきたため、そこで得た知識と最近何かと話題になって来ているVRコンテンツを融合させたバーチャルインターネット広告(ゲーム広告)を簡単に作ってみました。 今回やりたいこと 現実世界の看板と同様に仮想世界の看板に広告を表示させるような簡易的なものを作ってみる。 環境 本記事では以下のソフトウェア・プラグインなどを用いています。 Ruby Ruby on Rails 4.2 Unity 5.3 [Assets] Japanese Naniwa City (ZENRIN) [Assets] Character (Standard Assets) AdEngineの作成 AdEngineと大げさな言い方をしていますが、リクエストを受けたら表示する枠を判断して広告を返す想定のものを作 りました。 Railsが入っている想定で話を進めて行きます。 今回サーバー側で行うのは、表示する枠を判断して広告を返すものです。 まずは枠と広告を紐付ける為の処理をgenerateを使って簡単に作っていきます。      $ rails generate scaffold AdFrame title:string filename:string    $ rake db:migrate     それっぽい物が作れました。 次にリクエストを受けるためのコントローラを作成します。        $ rails generate controller home index     ルーティングの設定   .config/routes.rb . .root :to => 'home#index'   idが来たら該当ファイルにリダイレクトさせます。 home_controller.rb def index  if params[:fid].present?    file = AdFrame.find(params[:fid])    redirect_to '/images/' << file.filename  end end バリデーションチェックなどはしておらず必要最低限の実装をしていますが、ひとまずサーバー側の処理としては以上です。 UnityProjectの作成 新規3Dプロジェクトを作成し、 StandardAssetsのCharacter と JapaneseNaniwaCity を入れておきます。 まずは前準備としてマップの成形と広告を見るためのキャラクターを用意します。 キャラクターのプレハブとマップをヒエラルキーに追加。 キャラクターを地面に立たせてあげたいので Osaka_Ground と Osaka_Prop のインスペクタ Generate Colliders を有効にしておきます。 キャラクターの位置を調整し、マップ上に立たせてあげます。 次に広告を表示するためのスクリプトを作成します。 using UnityEngine; using System.Collections; public class AdRequest : MonoBehaviour { public string url = "http://local.unity-ad-engine.mediba.jp/?fid=1"; IEnumerator Start() { using (WWW www = new WWW (url)) { yield return www; if(!string.IsNullOrEmpty( www.error )){ yield break; } Renderer renderer = GetComponent<Renderer>(); renderer.material.mainTexture = www.texture ; } } void Update () { } } また、広告を表示するための面を用意します。 Create -> 3D Object -> Plane Planeのコンポーネントに先ほど作ったスクリプトを適応させてあげ、位置を調整して実行してみると・・・ 看板広告を表示することが出来ました! また、街中にあるビジョン広告のように動画を流すために同様にスクリプトを作ってあげ、動画表示用のマテリアルに適応させてコンポーネントに追加すれば、動画も表示することが出来ました! まとめ Unityだと簡単に広告を表示させられる仕組みが整っているので、 広告配信するためのサーバさえ立ててしまえばスクリプト1つを入れるだけで出し対面に広告を表示することができることがわかりました。 今後VRコンテンツが出てきた際にそのコンテンツにプラスαの収益元として広告が注目されるのかもしれないですね。
アバター
こんにちは。メディアシステム開発部の土方です。 最近社内でも色々なプロジェクトに関わらせていただくようになったのですが、その中でも長く携わっているプロジェクト、「 au Webポータル (スマートフォン版)」についてご紹介させていただきます。 上記サービスでは開発プロセスとしてスクラムを採用してもうすぐで丸3年になります。先日100Sprintを超えたということもあり、ちょっと振り返ってみようと思います。 「スクラム」「アジャイル」自体の情報は本もたくさん出ていますし、 検索すれば簡単に触れることが出来ると思うので、 そのあたりの用語や概念はそちらに譲らせていただき、 ここでは現場での自分の体験や思い出を中心にお伝えしたいと思います。 はじめに まず、現時点での開発プロセスの選択思想は大筋以下となります。 要件の内容や優先順位が1週間/2週間くらいの周期で変更され続ける開発現場で、 より効率的にアウトプットを出したい。 継続的にアウトプットを出し続けたい。 そのために、 「アジャイル宣言の背後にある原則」 「アジャイルソフトウェア開発宣言」 を大事にして、 「アジャイル開発ソフトウェア開発手法」の一つとして 「スクラム」のプラクティスを採用しています。 という整理です。     時系列的にはなんかイイ開発手法らしいぜ→「スクラム」のプラクティスを導入。それから開発宣言や原則を学んで理解していった、という流れなのですが、、チームが自己組織化されていって、チーム内部で定期的に細かな改善が為されるようになっていったのは、開発宣言/原則 に触れるようになってからじゃないかなと感じています。 なので、いま実践しているけどなんか上手く行かないなぁ、 と思ったら、プラクティスやTODOを確認するのもいいんですが、 まずは何のために何を大事にしてどうやってるんだっけ?ってアジャイルソフトウェア開発宣言、アジャイル宣言の背後にある原則から振り返ってみることをお勧めします。ただ妄信的にプラクティスをやるだけじゃなくてそのプラクティスの意味を考えなおす機会にしたらいいんじゃないでしょうか。先週も朝会で使っているカンバンが、形骸化してうまく対話の元になっていない気がしたので改善しないとね、と話をしてみました。 100Sprint体験記 導入期(2013/08頃): この時期に乗り越えた壁:導入 乗り越えるための鍵になったこと: 上役からの支援、これまで達成してきた成果などから、関係者に信頼してもらって導入に協力してもらった、という感じです。やっぱり新しいこと始める時に銀の弾丸って無いですよね。参考にならなくてゴメンナサイ。 思い出 開発チームとして「スクラム」を導入したい!と提案 「要員が固定されている(社内特有の事情による)のに、納期と要件ありきで激務続きだと長期的にはチームが保たないよ!要員を固定しているんだから、作業量が増減したらリリース日が一緒に動くっていうことだよ!」というメッセージとともに、「スクラム」は無駄な開発を抑止して、効率的により多くのユーザバリューを届けるためのよい手法で、要件変更が頻繁に起きる現場に適切だ、と説明した記憶があります。 もちろん、「約束したリリース日を後から調整するプロセスなんておかしくないですか?」などの質問含め議論になりました。いままでのウォータフォールのプロセスで考えたらそれは真っ当な疑問。 でもプロジェクトは現実に色々変更が起きるの常だし、メリットデメリットを整理した上で、納得してくれてとりあえず導入してもらうことにしました。リリース日は一度決めたら変更が困難であるということなのであれば、 最初にそれよりも短い期間でバーンダウンしきれるように計画/提案することにしました。 当時の印象的な調整 「リリース日はもっと細かく出来ないの?文言変更とかどう考えても3営業日もあればできるでしょう?」 確かにそれだけみればそうですよね。でも当初はできるだけ断ってました。 Sprintを1week単位に設定して、細かく要件の状況をヒアリングできるようにチームのリズムを設計はしたものの、チームのリズムや、開発者達が継続的に安定してアウトプットを出すことを大事にしようとしたからです。 どこまでは割り込んでいいのかの基準が曖昧になっていって、際限なくイレギュラーな割込みが入り込むことでリズムが崩れることを嫌ったというのが正直なところです。 でもいま振り返ると、スクラムのプラクティスに偏重していてアジャイルソフトウェア宣言に従ってなかったんじゃないか、とも思ったりします。この辺りの時期はお客さまや社内のプロダクトオーナー役によく我慢してもらったと思います。 講師を招いてアジャイル研修会 開発担当だけではなく、ディレクション担当も含めてアジャイル研修会を受講しました。何となく社内でも「アジャイル」という用語が認知されるようになった頃かなと思います。 学習期(2013/10〜2014/03): この時期に乗り越えた壁:教科書からの脱却 乗り越えるための鍵になったこと: 私が頭でっかちタイプなので、本を読んではそのプラクティスを極力厳密に適用しようとしていました。スクラムを実施する際の細いルール(例えばユーザストーリーの書きっぷり)に拘って、現場が疲弊しがちだったんですが、講習や勉強会など様々な実践例などを聞いて、現場現場でやり方は工夫していけばいい、教科書通りにしなくてもうまくいくまで改善すればいい、とわかってきて少し肩の力を抜けるようになった気がします。拘るべきはプラクティスじゃなくて、ユーザに継続的にvalueを届けるにはどうしたらよいのかいつでも考えるコト、困ったり良くないことがあったら改善していけばいいんだというコト、に気が付いてきました。 思い出 とにかく試行錯誤 「SCRUM BOOT CAMP THE BOOK」や「アジャイルサムライ」を読みながら、ああでもないこうでもないと実践と改善を繰り返していたように思います。社内で同じようにスクラムを採用しているチームのメンバと情報交換などしながら、デイリースタンドアップについて議論したり、計画/振り返りのMTGが長くなりがちなのでバックログの精査のタイミングを調整したり・・・ととにかく手探りですが意識的にトライアンドエラーをしていました。 安定期(2014/08〜2016/03): この時期に乗り越えた壁:お客さまに「スクラム」を認知してもらう 乗り越えるための鍵になったこと: プロダクトオーナー役兼お客さまとの調整役から、粘り強く「スクラム」によるメリットを伝えて続けて貰った結果、一定の理解と協力を頂けるようになりました。 具体的には、 プロダクトバックログの優先順位の整理実施の協力 ベロシティを基に各要件のリリース時期の見通しが分かることの理解 割込みでの要件差し込みはなんらかのタスクとのトレードになること など。 納期ギリギリまで頑張りきったけどやっぱり無理です、ということが減り、 どうして計画を変更しないといけないのか、が伝わりやすくなったのが大きいように思います。これにより、安定/安心して開発に集中でき、チーム内での信頼関係の構築、変化への対応力などが改善されたように感じていました。 思い出 チームに自律性が育ってきた 要員を固定する、とはいったものの、ベロシティを中長期的に増大させていくため、メンバが徐々に増大していき、徐々に情報共有のコストが高まり始めた時期でもありました。メンバの入れ替わりもありましたが、全体の仕組みが定着してきているので、新規参画メンバも比較的すぐに慣れることが出来るようになってきた時期だったように思います。 チームの要員が増大することで出てくる問題も、以下の様なチーム内での改善で対処するようになりました。 デイリースタンドアップで、今日のTODOと合わせて帰る時間を宣言する→忙しさが何となく伝わる→助け合える Sprintの途中で全員の進行状況を明示的に確認する係を立てて、輪番で廻してみる 開発の時間をよりまとめて確保するためにSprintの単位を1週間から2週間に変更してみる 変動期(2016/04〜): これから乗り越えないといけない壁: お客さまの担当変わっちゃったし、その流れでデカイ要件がガッツリ納期ありきで入ってきた!(これまでの現場のアウトプットのペースは評価されていないのかも!?という不安とともに) 乗り越えるために頑張ってること: なぜその納期でこの要件をやり切らないといけないのかについて、開発メンバへの説明 要員を急激に増員してもアウトプットは急激には上がらないことをお客さまに説明 タスクを分割して作業量の見える化 → 達成できるアウトプット量を見える化することで、優先順位下位の要件の整理 デカイ要件が変更なし、で進められるのであれば、細かく要件を変更するための時間は削減して良いハズ → タイムボックスのバランスを変更して一時的にでもアウトプットを上げる などなど・・・。 おわりに アジャイルは死んだ? アジャイルは死んだ、なんて記事をよく見かけるようになりました。「アジャイル宣言の背後にある原則」は全面的に正しいかと言われるとちょっと悩ましいところもあるものの、いまのところは出会えてよかった考え方だなと感じています。開発のプロセスやプラクティスは解決したい課題や状況に応じて選ぶべきものとは理解しているつもりですが、スクラムは細かく情報共有しながら進んでいくので、楽しいなぁと感じることが多く、もうしばらくは贔屓にしたい気持ちです。会議の時間がちょっと多すぎる嫌いもあるので、そのあたりはプロジェクトごとに調整しながら、ですね。 また、アジャイル/スクラムを通して、小さく速くPDCAを廻していくことが如何に大事なのかを考えさせられることが多いです。100Sprintを振り返るにあたって、導入、学習、安定、変動と分けてみたものの、考えたらいつもその瞬間は変動期だったなぁと思います。イライラしながらストレスフルにお仕事している時間は勿体無いから、上手くいかなかったら継続して改善していくしかないですよね! 求む!永続改善スタイルエンジニア! ということで、medibaでは、共に問題を見据えて改善に取り組み続けることが出来るエンジニアを求めています。少しでも興味を持って頂けましたら 募集ページ より詳細をご確認頂けるとなによりです。
アバター
こんにちは、制作部の今野です。 数年前からGoogle検索や翻訳などに、マイクのアイコンを見かけるようになり、好奇心でマイクをクリックすると、マイクが反応したり、ブラウザが喋り出したりしてビックリしました。 最近、どんな技術で実装しているのかな?と、色々調べていくうちに、「Web Speech API」というのを知り、これをうまく使えば、Botも楽しくなるんじゃ?と思い、調査しました。 Web Speech APIとは? ブラウザで音声の入出力ができる2つの機能があります。 テキスト読み上げ(Speech Synthesis API) テキストの内容をブラウザが喋る 音声認識(Speech Recognition API) 自分が喋った内容を、テキスト化などしてくれる(マイクが必要) 今回は、Speech Recognition APIについては以降出てこないので、実際に体験したい方は、Googleの デモ をご覧ください。 ブラウザに喋らせてみる 以下コードを、ChromeのConsoleか、HTMLのスクリプトに貼り付けてみてください。(喋るので音量注意!) speechSynthesis.speak( new SpeechSynthesisUtterance('Nice to meet you') ); おお、喋った! ん?でも、何か発音悪いですね。 そうなんです、言語設定をしてあげないと、正しい発音してくれません。Chrome日本語版では、デフォルト日本語設定にしているようです。 では、英語の設定にしてみましょう。 var synthes = new SpeechSynthesisUtterance(); synthes.lang = 'en-US'; synthes.text = 'Nice to meet you'; speechSynthesis.speak(synthes); ちゃんとした英語の発音になりましたね。 synthes.lang に指定した言語を入れると、その言語の発音になります。 synthes.text は、喋らせたい内容を入れます。 次に使い回ししやすいように、関数化してみました。 (オプションで音量・速度・音程なども調整できます) function say(msg, lang){ var synthes = new SpeechSynthesisUtterance(); // 発話機能をインスタンス化 // オプション設定 synthes.volume = 1; // 音量 min 0 ~ max 1 synthes.rate = 1; // 速度 min 0 ~ max 10 synthes.pitch = 1; // 音程 min 0 ~ max 2 synthes.text = msg; // 喋る内容 synthes.lang = lang || 'ja-JP'; // デフォルト:日本語、他 en-US etc.. speechSynthesis.speak(synthes); // 発話実行 synthes.onstart = function(event){ console.log('喋り始めました'); } // 開始時の処理 synthes.onend = function(event){ console.log('喋り終わりました'); } // 終了時の処理 } say('How are you?', 'en-US'); Botを作る ブラウザに喋らせるところまでいきましたが、毎回コードを修正して喋ってもらうのは、面倒ですし対話している感がありません。 そこで、対話ができるBotを作り、喋らせてみましょう。 Botを作る方法は色んな選択肢がありますが、今回は docomo Developer support の 雑談対話API を使ってみます。 docomo Developer support APIについて 他にも画像認識など様々なAPIを提供しており、Web・ネイティブアプリ開発時に活用できます。 また、今回は実施しておりませんが、日本語ではブラウザの音声は女性の1種類しか利用できないのですが、docomoの音声合成APIを使用すると「元気なお姉さん」お婆さん」「メイド」「癒やし系お兄さん」などの音声にもでき、人の声に限りなく近い喋り方になるようです。ブラウザだと棒読みに聞こえるので今度使ってみたいです。 まず、以下のようなJSONを用意します。 { 'utt' : 'こんにちは', // 送信するメッセージ 't' : 20 // キャラクタ設定。20は関西弁の女子高生 } 続いて上記JSONをAPIに投げてみます。(サンプルコードはFetchを使用していますが、普通にXMLHttpRequestでもOKです) fetch( 'https://api.apigw.smt.docomo.ne.jp/dialogue/v1/dialogue?APIKEY=' + '発行されたkeyを入れる', { method: 'POST', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' }, body: JSON.stringify(apiJson) }).then(function(response){ return response.json(); }).then(function(json){ console.log(json.yomi); }); すると、json.yomiというオブジェクトに返信内容が格納され返ってきますので、先ほど作成した関数の say() で返信内容を喋らせます。 例 送信:こんにちは 返信:よっ、こんー、やあ ※毎回同じ回答がくるわけじゃないです。(女子高生なので喋り方がゆるいですw) 画面を作る Botも毎回コードを修正するのは煩わしいので、テキストボックスからBotとチャットできるように作ってみました。(入力した内容を上記APIに渡しているだけなのでコードは割愛します) ※メッセージは、上から最新順です おお、チャットで会話してる感が出てきました。 でも、見た目が寂しいですね。 ちょっと装飾してみます。 API仕様で「関西弁の女子高生」という設定なので、カワイイ女子高生の写真素材を、 ぱくたそ からいただいてきました。 どうでしょうか? 一気に、Botに親近感が湧くようになりましたね。 (作りながら思ったのですが、マイクロソフト 女子高生AIの りんな に似てきた。。) 静止画だと分かりづらいので、雰囲気が伝わるようにアニメGIF動画も載せておきます(途中、内容がアレですが。。) なお、画像からでは分かりませんが、きちんとテキストの内容にあわせてブラウザが喋ってくれてます。 Speech Synthesis API対応ブラウザ 残念ながら全てのブラウザが対応しているわけじゃなく、主にChromeとSafariが対応しています。 Edgeの最新版から対応し始めたようですので、使用するサービスが増えてきそうですね。 まとめ 今回はBotとの会話で使用しましたが、他にはチュートリアルのナビゲーターとして喋ってもらったり、ブラウザに歌わせてBGM(※)としてするというやり方もできるかと思います。 ※他の方が作られたものですが、 歌わせているサンプル です ただ、日本語の喋り方が棒読みなので、今回の女子高生写真と凄く違和感があります。 もっと人間に近い喋り方をしてくれることに今後に期待です。 また、今回、テキスト入力での会話でしたが、前述の「Speech Recognition API」の音声認識を使えば、実際の声と声でしBotと会話するということもできます。 おまけ 今回、docomoの雑談対話APIを利用しましたが、プログラミング無しでもAIが搭載されたBotを作成できる Repl-AI というサービスもβ版のリリースがされたようです。 プログラミング知識無い方でも利用できるようなので、今後様々なサービスでもBotやAIの技術が取り入れられてくると思います。 今回動作確認した環境 PC版 Google Chrome Ver. 50.0.2661.102 日本語版
アバター
はじめまして。インフラストラクチャー部の山下です。 各種サービスのAWSインフラを担当する傍ら、社内ではRubyやRailsなどを書いてたりしています。 私が参加しているプロジェクトで、AWSの各リソースが正しく構成されているかを確認したいという話が出たため、 awspec を導入してみました。 awspecとは? Serverspecのように、AWSの各リソースをテスト出来ます。 構成 本記事では、Rubyやawspecは以下のバージョンを使用しています。 Ruby: 2.2.4 awspec: 0.37.7 対応しているAWSリソース awspecでテストできるAWSリソースの代表的なものは以下の通りです。 詳しくは公式ページをご参照下さい。 https://github.com/k1LoW/awspec/blob/master/doc/resource_types.md EC2 RDS ELB Elasticache IAM VPC SecurityGroup インストール gemパッケージをインストールします。 本記事では単純にgemでインストールしていますが、Bundlerなどで管理するほうがおすすめです。 $ gem install awspec 次に任意のディレクトリに移動し、以下のコマンドで必要なファイルを生成します。 $ awspec init initを実行すると、以下の様なディレクトリとファイルが生成されます。 . ├── Rakefile └── spec └── spec_helper.rb 構成自体はServerspecとほぼ同じです。 specディレクトリ以下に、各テストを書いていきます。 specファイルを書いてみる specディレクトリ以下に リソース名_spec.rb という名前でファイルを作成します。 ここではEC2のspecを書いてみます。 $ vi spec/ec2_spec.rb require 'spec_helper' describe ec2('server1') do it { should exist } it { should be_running } its(:instance_id) { should eq 'i-00000000' } its(:image_id) { should eq 'ami-c8xxxxxx' } its(:private_dns_name) { should eq 'ip-10-0-0-95.ap-northeast-1.compute.internal' } its(:public_dns_name) { should eq 'ec2-52-xxx-xx-xxx.ap-northeast-1.compute.amazonaws.com' } its(:instance_type) { should eq 'c3.large' } its(:private_ip_address) { should eq '10.0.0.95' } its(:public_ip_address) { should eq 'xx.xxx.xx.xxx' } %w{ hoge-access-sg huga-access-sg default-sg }.each do |sg| it { should have_security_group(sg) } end it { should belong_to_vpc('vpc-0') } it { should belong_to_subnet('front-subnet-a') } it { should have_eip('xx.xxx.xx.xxx') } it { should have_ebs('vol-xxxxxxxx') } it { should have_ebs('Created for server1') } end このように、Serverspec(RSpec)ライクにテストが書けます。 Generate ただ、既存リソースを全て手で起こすのは苦行だと思います。 awspecには既存のリソースをテストに起こしてくれる機能があるので、使ってみましょう。 AWSのCredentialは登録されているものとします。 また、IAMで各リソースのRead権限が付与されている必要があります。 $ export AWS_REGION=ap-northeast-1 $ awspec generate ec2 vpc-xxxxxxxx --profile staging >> spec/ec2_spec.rb ※ vpc-xxxxxxxxは実際に存在するVPC IDを指定して下さい。 実行 上記で書いたテストを実行してみましょう。 Rakefileがある場所まで移動し、以下のコマンドを実行します。 $ export AWS_REGION=ap-northeast-1 AWS_PROFILE=staging $ rake spec 以下のように結果が出力されます。 ec2 'server1' should exist should be running should have security group "hoge-access-sg" should have security group "huga-access-sg" should have security group "default-sg" should belong to vpc "vpc-0" should belong to subnet "front-subnet-a" should have eip "xx.xxx.xx.xxx" should have ebs "vol-xxxxxxxx" instance_id should eq "i-00000000" image_id should eq "ami-c8xxxxxx" private_dns_name should eq "ip-10-0-0-95.ap-northeast-1.compute.internal" public_dns_name should eq "ec2-52-xxx-xx-xxx.ap-northeast-1.compute.amazonaws.com" instance_type should eq "c3.large" private_ip_address should eq "10.0.0.95" public_ip_address should eq "52.xxx.xx.xxx" Finished in 0.48842 seconds (files took 2.11 seconds to load) 16 examples, 0 failures なんとも見やすい! ちなみに失敗した時は以下の様な出力になります。 ec2 'server2' should exist should be running should belong to vpc "vpc-0" should belong to subnet "front-subnet-a" should have eip "xx.xx.xx.xx" should have ebs "vol-xxxxxxxx" instance_id should eq "i-xxxxx" (FAILED - 1) image_id should eq "ami-c8xxxxxx" private_dns_name should eq "ip-10-0-10-96.ap-northeast-1.compute.internal" public_dns_name should eq "ec2-xx-xx-xx-xx.ap-northeast-1.compute.amazonaws.com" instance_type should eq "t2.large" (FAILED - 2) private_ip_address should eq "10.0.10.96" public_ip_address should eq "xx.xx.xx.xx" Failures: 1) ec2 'server2' instance_id should eq "i-xxxxx" Failure/Error: its(:instance_id) { should eq 'i-xxxxx' } expected: "i-00000000" got: "i-12345678" (compared using ==) # ./spec/ec2_spec.rb:6:in `block (2 levels) in <top>' 2) ec2 'server2' instance_type should eq "t2.large" Failure/Error: its(:instance_type) { should eq 't2.large' } expected: "t2.large" got: "t2.small" (compared using ==) # ./spec/ec2_spec.rb:10:in `block (2 levels) in <top>' Finished in 0.61169 seconds (files took 2.87 seconds to load) 15 examples, 2 failures Failed examples: rspec ./spec/ec2_spec.rb:6 # ec2 'server2' instance_id should eq "i-xxxxx" rspec ./spec/ec2_spec.rb:10 # ec2 'server2' instance_type should eq "t2.large" 上記では、以下の2項目が失敗しています。 server2のインスタンスIDが、テストケースと実際の値で差異がある インスタンスサイズが、テストケースではt2.largeだが、実際はt2.smallとなっている このように、テストに失敗しても簡単に追うことが可能です。 YAMLに落としてみた テスト対象が増えてくるとメンテナンスコストが高くなる可能性があります。 頻繁に修正が行われるのを考慮し、YAMLを読むようにしました。 まず、Rakefileに以下を追加します。 $ vi Rakefile require 'yaml' 次にYAMLを記述します。 $ vi config.yml ec2: - name: "server1" instance_type: "t2.micro" security_groups: - "hoge-access-sg" - "huga-access-sg" - "default-access-sg" eip: "52.xxx.xx.xxx" private_ip_address: "10.0.0.95" subnet: "front-subnet-a" - name: "server2" instance_type: "t2.micro" security_groups: - "hoge-access-sg" - "huga-access-sg" - "default-access-sg" eip: "52.xxx.xx.xxx" private_ip_address: "10.0.0.96" subnet: "front-subnet-c" specヘルパーを修正します。 YAMLで読んだ値を @properties という変数に格納し、各specファイルから参照できるようにします。 $ vi spec/spec_helper.rb require 'awspec' require 'yaml' Awsecrets.load(secrets_path: File.expand_path('./secrets.yml', File.dirname(__FILE__))) @properties = YAML.load_file("config.yml") 最後に、各specを修正します。 今回はEC2を例として、以下に記述します。 $ vi spec/ec2_spec.rb require 'spec_helper' properties = @properties properties['ec2'].each do |ec2| describe ec2(ec2['name']) do it { should exist } its(:instance_type) { should eq ec2['instance_type'] } its(:private_ip_address) { should eq ec2['private_ip_address'] } ec2['security_groups'].each do |s| it { should have_security_group(s) } end it { should belong_to_subnet(ec2['subnet']) } it { should have_eip(ec2['eip']) } unless ec2['eip'].nil? end end 悩んだポイント インスタンスロールに対応していない awspecが使用しているawsecretsというgemが、 インスタンスのIAMロールに対応しておらずcredentialが存在しない環境では使用できませんでした。 こちらはawsecretsにPullRequestを行う予定です。 CloudFrontなど一部のリソースに対応していない 特にCloudFrontはほしかったです…。 こちらも時間があるときに書いてみます。 まとめ これまで、アプリケーションやOS/ミドルウェアのテストはありますが、AWSリソース自体のテストはあまり一般的ではありませんでした。 awspecの登場により、より簡単に構築したリソースのテストが可能となりました。 やはりテスト大事ですね!
アバター
こんにちは。広告システム開発部の瀬川です。 medibaの開発プロジェクトでは スクラムを採用するチームをよく目にします。 私も開発メンバーとしてスクラムに参加してきましたが、 利用するツールに迷っていた時期がありました。 今回はそんな時期に出会った、 『オープンソースのスクラムツール「Taiga」をAmazon EC2で使えるようにするまで』 をご紹介させていただきます。 なぜTaiga? 今まで使ったWebツールの詳細については控えさせていただきますが みなさんはスクラムツールを使ってきて、 こんな思いを抱いたことありませんでしたか? ツール利用料がなんとなく高い気がする。。 設定項目が多すぎる。。 画面が味気ない。。 私は上記のような不満を多少なりとも感じていました。 一見、スクラムを実行するのに上記項目は無関係に思われるかもしれませんが、 「使いたくなるツール」は きっとスクラムを継続することを後押ししてくれると考えました。 「参加したくなる会議の仕組み」と同様に大切なことではないでしょうか。 したがって、 料金的に敷居が低く 触っていて楽しい モチベーションをあげてくれる そんなツールを探していました。 Taigaとは? そんなときに出会ったTaigaの特徴は オープンソース 無料で使える(有料プランもあり) シンプルに使える Best Agile Tool 2015 授賞 と公式ページに記載があります! その他の特徴については オシャレな公式ページで、是非チェックしてください! https://taiga.io この公式ページを見たときに「なにこれ、カッコいい!」と思い、 「使ってみたい欲」がぐぐぐっと高まりました! いざ!インストール! さて、それではインストールしてみましょう。 Taigaは ネットワーク経由で利用できるプランだと1プライベートプランつき25人まで無料 です。 (詳細は 料金表参照 ) ですが、今回はカスタマイズも視野に入れて、 AWSのEC2(ubuntu)に GitHubからソースをcloneし、Amazon EC2にインストールしてTaigaを利用 しましょう! これならAWSの利用料金だけを考慮すればよいですね。 (この場合の注意事項は後ほど記述します。) 準備するもの AWS EC2 (ubuntu 14.04 ) こちらは割愛いたします! 特別な設定はいりません。t2.microで作成しました。 ※Amazon EC2でubuntu利用する場合、デフォルトユーザーは「ubuntu」なのでご注意ください。 ssh -i ~/.ssh/taiga.pem ubuntu@ec2-12-345-678-901.ap-northeast-x.compute.amazonaws.com インストール手順 Taigaのインストールに必要なのでgitをインストールします。 sudo apt-get install git 次にTaigaのインストール用スクリプトをcloneします。 git clone https://github.com/taigaio/taiga-scripts.git そしてインストール用のシェルを実行します。 cd taiga-scripts bash setup-server.sh インストールは以上です。 こちらがアクセスした際の画面です。 ID/PASSWORD: admin/123123 が管理者の初期設定となっております。 早速 プロジェクトを作る ↓ スプリントを作成する ↓ ユーザーストーリーを作る ↓ タスクを作る ↓ ユーザーストーリーをスプリントに登録する の手順でやってみましょう! まずプロジェクトを作成します。 KANBANだけでよければKANBANを、 そうでなければSCRUMを選択してください。 ちなみにSCRUMでもKANBANモジュールを後で追加できますのでご心配なく。 こちらがプロジェクトのトップ画面です。 この画面で、 プロジェクトゴールまであと何ポイントか 優先度の高いユーザーストーリーはどれか 今のスプリントのポイント数と消化されたユーザーストーリー などがわかります。 次にスプリントを作成しましょう。 sprint1、続けてsprint2の2つを作ってみます。 スプリントの作成フォームでは「last sprint is xxxx;-)」のように前のスプリント名がサポート表示されるのがちょっとだけ嬉しいです。 こちらがsprint1、2を作成したあとのプロジェクトトップ画面です。 次にユーザーストーリーを作成してその中の必要なタスクを作り、 sprint1に登録しましょう! ユーザーストーリー作成画面では タイトル ストーリーポイント 詳細 を入力します。他にもタグ付けやファイル添付もできます。 次にユーザーストーリーに必要なタスクを作成します。 タスクの作成が完了したらバックログからsprint1に移動しましょう。 作成したユーザーストーリーは一旦バックログに追加されます。 バックログからスプリントへの移動はドラッグ&ドロップで可能です。 複数件の移動も対象ユーザーストーリーのチェックボックスを チェックしてドラッグ&ドロップします。 これでスクラムを開始する準備が整いましたね! また、Taiga内でのアクションに応じて加点され、 得点の高いメンバーには称号?がもらえます!スプリントの終わりに話題になることもあります。 また、ユーザーストーリーに対して必要な項目を追加設定できます。 (カスタムフィールド) 例えばTaigaにはタイムトラッキングの機能が用意されていません。 その場合、カスタムフィールドの利用を方法の一つとしてTaigaは提案しています。 Why is there no Time Tracking? 注意事項 まず、taiga-script、ASP版によらず Taigaは日本語対応がされていません。説明などは全部英語です。 日本語入力はなんとかできます。 そして taiga-scriptでのインストール時の注意事項 ですが、 その一つに The installation is not 100% ready for production. とあります。 いいですよ、無料で使わせてもらうのですから。。。 さぁ、いざチームメンバーをご招待!!… あれ?招待メールが飛びません!! せっかく入れたのにこのままでは一人ぼっちスクラムです。 早速修正していきましょう。 招待メールが飛ばない ~解決方法~ 調べてみるとMTAがインストールされていないので、 postfixをインストールします。 sudo apt-get -y install postfix そして設定ファイル taiga-back/settings/local.py を書き換えます。 cd ~/taiga-back/settings vi local.py 修正前) #EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend" #EMAIL_USE_TLS = False #EMAIL_HOST = "localhost" #EMAIL_HOST_USER = "" #EMAIL_HOST_PASSWORD = "" #EMAIL_PORT = 25 修正後) EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend" EMAIL_USE_TLS = False EMAIL_HOST = "localhost" #EMAIL_HOST_USER = "" #EMAIL_HOST_PASSWORD = "" EMAIL_PORT = 25 そしてnginxとcircusで動いているので circusを再起動すると招待メールが送れるようになりますが 詳細メール記載の登録用URLのドメインが localhost:8000になっているので下記修正も同時にやりましょう! 修正前) # This should change if you want generate urls in emails # for external dns. SITES["front"]["domain"] = "localhost:8000" 修正後) # This should change if you want generate urls in emails # for external dns. SITES["front"]["domain"] = "EC2のドメイン" そしてcircusの再起動。 sudo service circus restart これで招待メールが送れるようになりました! こちらが招待されたユーザのサインアップ画面です。 Taiga is a project management platform for agile developers & >designers and project managers who want a beautiful tool that makes >work truly enjoyable の通り、Beautifulですね。 おわりに 無事にインストールできましたか? 他のツールが中々見つからなくてもやもやしてる方! AWSを利用できる環境であれば 簡単に始められるのでぜひ試してみてください。 すでにもっとこうしたい!!という改善点がありますので、 せっかくのオープンソースですからチームにあったカスタマイズに チャレンジしていきたいと思います。
アバター
VimでGoする
メディアシステム開発部の北田です。 auスマートパスのサーバサイド開発を担当しております。 担当サービスのQCDと個人的な問題意識から、 Go言語 の導入に取り組んでいます。 今回は、 Vimmer がGo言語と向き合う際のtipsについてまとめました。 はじめに 無用な混乱を避けるため、注意事項及び前提条件を設けました。 はじめにお読みください。 ソースコード 本稿で利用するソースコードは、 GitHubの公開リポジトリ に格納してあります。 断りがない場合、カレントディレクトリは、以下の通りです。 $ pwd /path/to/mediba-kitada/mbo/2016first/blog_vim-go 省略したこと Vim及びGo言語の概要については省略しています。 参考にした書籍 Software Design 2016年5月号 スターティングGo言語 以下のツールを利用しますが、概要については省略します。 direnv neobundle neocomplete サポートする環境 Mac(OS X)オンリーです。以下のバージョンをサポートしています。 Yosemite El Capitan 以下のVimをサポートしています。 ディストリビューション バージョン 備考 MacVim 7.4 筆者環境は、luaサポートをonにしてあります 利用するVim pluginは、以下の通りです。 名称 バージョン 備考 vim-go master - 以下のGo言語をサポートしています。 1.1以上 以下のshellをサポートしています。 zsh 想定読者 上記を踏まえ本稿の想定読者は、以下の通りです。 Go言語のチュートリアル をやってみて、何かを見出した Go言語と長くお付き合いしたい一般的なVimmer 私です。 導入 vim-go をインストールしていきます。 vim-goは、Go言語向けVim pluginで他のpluginに比べ 圧倒的な支持 を得ています。 neobundleを用いて以下の様に導入していきましょう。 $ vi ~/.vimrc " golang NeoBundle 'fatih/vim-go' " コマンドモードで.vimrcをリロード :source $MYVIMRC " コマンドモードでNeoBundleInstall :NeoBundleInstall 試運転をしてみましょう。 フォーマットが出鱈目なエントリーポイントを用意し、セーブしてみます。 # フォーマットが出鱈目なエントリーポイントを開く $ vi src/main/main.go " コマンドモードでGoFmt :GoFmt vim-goの :GoFmt コマンドでフォーマットされていることがわかります。 カスタマイズ ツール群のインストール vim-goは、 :GoInstallBinaries コマンドを用いてGo言語での開発に便利なツール群をインストールしてくれます。プロジェクトで開発するパッケージとvim-goで利用するパッケージの混同(同一ディレクトリに配置される)を避けるため、shellで環境変数GOPATHを利用して、インストールしていきましょう。 # 共有のGo言語環境の各ディレクトリを作成 $ mkdir -p {$HOME/go,$HOME/go/bin,$HOME/go/pkg,$HOME/go/src} # shellの設定ファイルにGOPATHを設定 $ vi $HOME/.zshrc # 共通のGo言語環境変数とそのパス export $GOPATH="$HOME/go" # ツール群のバイナリファイルが格納される export $GOBIN="$GOPATH/gin" # shellの設定を更新 $ source $HOME/.zshrc Vimを起動し、 :GoInstallBinaries コマンドでツール群をshellの設定ファイルで指定したパスにインストールします。 # プロジェクトのルートディレクトリ(.envrcが格納されているディレクトリ)から離れる $ cd $HOME # Vimを起動しコマンドモードで GoInstallBinaries を実行 $ vi :GoInstallBinaries # ツール群が共通のGo言語環境のパスにインストールされていることを確認する $ tree $GOPATH/bin /Users/mediba-kitada/go/bin ├── asmfmt ├── errcheck ├── gocode ├── gogetdoc ├── goimports ├── golint ├── gometalinter ├── gorename ├── gotags ├── guru └── motion ツール群の試運転をしてみましょう。animalsパッケージのmonkey.goを修正します。 $ vi src/animals/monkey.go " コマンドモードでGoImports :GoImports vim-goの :GoImports コマンドでfmtパッケージがインポートされていることがわかります。 vim-goの設定 デフォルトではシンタックスハイライトがオフになっていますので、設定ファイルを用いて設定をカスタマイズしていきましょう。 :GoFmt コマンドを実行した際の挙動もカスタマイズしてみます。 $ vi $HOME/.vimrc " vim-go "" mapping """ go runのキーマッピング au FileType go nmap gr (go-run) """ go testのキーマッピング au FileType go nmap gt (go-test) "" highlight let g:go_hightlight_functions = 1 let g:go_hightlight_methods = 1 let g:go_hightlight_structs = 1 let g:go_hightlight_interfaces = 1 let g:go_hightlight_operators = 1 let g:go_hightlight_build_constraints = 1 "" GoFmt時にインポートするパッケージを整理(GoFmtはファイル書き込み時に自動的に実行される) let g:go_fmt_command = "goimports" 試運転をしてみましょう。先ほどと同様にanimalsパッケージのmonkey.goを修正します。 $ vi src/animals/monkey.go " バッファの内容をファイルに書込 :w ファイル書き込み時に :GoFmt によるフォーマットと、 :GoImports コマンドによるパッケージのインポートが実行されていることがわかります。 テストも実行してみましょう。 $ vi src/animals/elephant.go " テストの実行 \gt 自動補完 neocompleteをインストール、設定していれば自動補完もかっこ良くなります。 課題 本稿では対応出来ていないvim-goのカスタマイズについて簡単に触れておきます。 Tagbar ctagsを適切に設定することで関数の一覧バッファをサイドビューとして表示し、タグジャンプにも利用出来ます。 Neovim 対応 vim-go開発者のfaithさんもNeovimを利用しだした とのことですが、まだうまく動いておらず、ベータとして提供している様です。 おわりに Vimmer向けにGo言語開発環境の実践的な内容をまとめてみました。ここでは触れていませんが、ビルドコマンドも提供されており、vim-goさえあればVimmerのGo言語開発は何とかなるのではと見込んでおります。これは、シンプルな規約に沿ってデザインされたGo言語の恩恵のひとつなのかもしれません。 弊社では、バックエンド(サーバサイド)エンジニアを 鋭意募集 しています。Go言語の他にもモダンな技術の導入には前向きに取り組んでおりますので、腕自慢の方はぜひご応募ください。
アバター
こんにちは。制作部の西野です。 mediba制作部では、制作過程でデザインレビューを取り入れています。制作したデザインに対して、そのプロジェクト内のメンバーだけではなく、プロジェクトに携わっていないエンジニア・デザイナーが第三者からの視点でデザインに対してコメントをし合っています。そして、デザインレビューをする際に「 InVision 」というwebサービスを使っています。もう、すでに導入されている会社も多くあると思いますが、名前は聞いたことはあるが、まだ実は使う機会がなくて使ったことがない方も多くいらっしゃるのではないでしょうか?今回はその「InVision」についてご紹介いたします。 InVisionとは? 無料でも使える、プロトタイピングツールです。 webブラウザを用いて、簡単にプロトタイプを制作することや、ファイルを共有することができます。 なぜ InVisionを使うようになったのか? Invisionを使うようになった理由は3つあります。 1つに、簡単にコメントが残せること。 2つに、簡単にデザインデータを共有できること。 3つに、簡単にプロトタイプが作れること。 とにかく簡単で便利なツールです。 以下で詳しく説明いたします。 InVisionのここが便利! 1.簡単にコメントが残せる InVision導入前、デザインレビュー時のコメントについてこんな不便がありました。 『page_01.pngの青いボタンのところですが…』 『メインイメージの下のタイトルとキャプションのマージンなんですが…』 このように、指摘箇所について文字で説明することとなってしまい、伝える側/受け取る側ともに 指摘箇所が伝えにくい、わかりにくい こととなってしまっていました。 しかしInVisionでは、場所を直観的に指定してコメントすることができます。 共有されたURLにアクセスし、COMMENT MODEをONにします。そして、画像の上でクリックをするとコメントを残すことができます。 コメントの色を3色から選ぶことが出来ますので、チーム内でルールを決めて「絶対に直した方がいいこと」、「メモ」、「相談、提案」、のようにコメントの用途別で色を使うことも便利かもしれません。また、tour pointを選択するとpreviewモードで閲覧する際にアニメーションが付きます。特に注意を促したい時などに利用できます。 さらにペンのアイコンを押下することでペイントツールやシェイプツールに切り替わります。コメントだけではなく柔軟にレビューすることが出来ます。 また、コメントが複数つきやりとりが長くつづくと、修正したかどうか?/なぜこのように変更をしたのか? コメントに対してのアクションが見えづらく、追いづらく なってしまいがちです。こういった問題もInVisionでは解決することができます。 コメントの「Marked resolved」のチェックボックスをチェックすることで、ステータスを解決済みに変更することができます。 課題の状態を管理することができ、また残課題について確認しやすいUIとなっています。 コメント毎に、コメントと画像が 登録したメールアドレスに送信されるのも、やり取りの履歴を確認する上で便利な点の一つです。 2.簡単にデザインデータを共有できる InVision Syncをインストールすると、ローカル環境にあるデータをInVisionサーバーに同期することができます。データの種類は、.psd, .sketch, .pdf, .png, .jpg, .gifに対応しています。 保存するたびに同期する設定にもできるので、デザインデータを格納する手間が省けたり、共有し忘れることもなくなります。 さらに、同期されるたびに履歴が残るので、前のバージョンにも簡単に戻すことができます。 ※あまりないとは思いますが、複数人で同じファイルを編集する際は、一番最後に保存されたデータがマスターデータになってしまうので上書きし合わないように注意が必要です。しかし、履歴を辿ることもできるので、注意さえしていれば大丈夫だと思います。 またInVisionが提要している「InVision Sync」だけではなく、「Dropbox」や「Google Drive」などの様々なアプリケーションと連携していますので、環境に応じて同期方法が選べることも魅力の一つです。 3.簡単にプロトタイプを作れる スマートフォンサイトを作る際、PCの画面上で見ていた印象と、実機で確認した時の印象がなんだか違う!なんてことがよくあると思います。そのため、デザインの段階でも実機の確認は必須なのですが、毎回画像を書き出してテスト用のサーバーにアップロードするのはちょっと面倒です。InVisionはそんな手間を省いてくれます。 デザインデータをアップロードするだけで、プレビュー用のURLが作られます。嬉しいことに、PSDデータからの書き出しはアートボードごとにInVisionが勝手に書き出してくれるので、先ほど紹介した「InVision Sync」を使用していれば、保存をするだけで変更が即時反映されます。iOSでは専用アプリで確認することができます。 またモバイルブラウザでも同じようにデザインを確認することが出来ますので、アプリを入れる手間なく実機確認することもできます。   また、もっと実機で動きのイメージを見たい人にもオススメです。 リンクをつけたり、アニメーションを指定することもできます。 BUILD MODEを選択したのち、アニメーションやリンクをつけたいエリアを選択し、動きを指定するだけで簡単に動きのあるプロトタイプが出来上がります。 簡単なプロトタイプを作るには十分な種類の、動作タイミング/アニメーションの詳細指定があります。 まとめ プロトタイプツールとしても、デザインレビューのツールとしても非常に便利なwebツールです。 簡単であるため、学習コストがあまりかからずに導入できると思います。ローカル環境とInVisionサーバーの同期方法の中にAdobe Creative Cloudの連携も示唆されており、ますます便利な機能が追加されていくことを期待したいです。 実は面倒だと思っていた、ちょっと不便だと思っていたことを解決してくれるサービスです。 InVisionを知っていてもまだ使ったことがない方、こんなに便利な機能がたくさんあります。百聞は一見に如かず。ぜひ一度試してみてはいかがでしょうか?
アバター
こんにちは。制作部の苅部です。 今回WebStormというIDEついてご紹介したいと思います。 私自身これまでエディタとしてSublime Textを使っていてIDEを触ったことがなかったのですが、実務の中でWebStormを試す機会があったので、備忘録も兼ねて便利な機能をまとめてみたいと思います。 私のように選ばずに一つのエディタを使い続けてきた方に向けて、WebStormの実際に使ってみないと分からない部分を簡単にご紹介できたらと思います。 IDEと聞くと敷居が高く感じるかもしれませんが、使いこなすと手放せなくなると思います。 エディタやIDE選択の一助となれば幸いです。 WebStormとは チェコ共和国のプラハに本社を置くJetBrainsというソフトウェア会社が開発しているIDE(統合開発環境)です。 同社が開発しているIntelliJ PlatformをベースにWeb開発に機能を絞ったものがWebStormとなります。 “The smartest JavaScript IDE"のキャッチコピーの通りJavaScriptでの開発を強力に支援してくれますが、フロントエンドのサポートは幅広くモダンな開発環境に限らずとも生産性を高める事が出来ると思います。 今回は2016年4月時点の最新バージョン、WebStorm 2016.1(OS X)をベースに以下の項目でまとめてみました。 さまざまな機能の統合 豊富なコード支援 デバッグ支援とコード品質の安定化 プラグインでの拡張 ※WebStormとしての紹介となりますが、おそらくPhpStormでも同等の機能があるのではないかと思います。 1) さまざまな機能の統合 WebStormでは開発を支援する多くの機能を内包しているので、同時に利用するアプリケーションやウインドウの数を減らし一つの画面に集中して作業をする事ができます。 例えばテストツール・タスクランナー・仮装環境などはそれぞれ別のCLIにせずIDEの中にインターフェイスを統合できます。 テストランナー テストランナーのKarmaをサポートしており各テストコードをRunタスクとして登録することができ、UI上でテスト実行やテスト結果の確認をすることができます。 タスクランナー GruntやGulp,npm-scriptsと連携可能です。 例えばファイルの変更検知などで常時利用するようなタスクは、プロジェクトを開いた際に自動実行する事によって、CLIから開始する必要もなくなります。 それぞれのタスクはGUIで一覧表示が可能で、任意のタスクの実行やコード行へのジャンプも可能です。 タスクの数が増えてしまい全体を把握しづらくなった場合に便利です。 Vagrant VagrantのログもRunウィンドウの中で表示可能です。そしてアクションの実行もWebStorm上から可能です。 ビルトインターミナル IDEから離れることなくCLIの操作が可能で、タブでの複数表示や[Cmd+F]での検索も利用できます。 ビルトインサーバやちょっとしたNode操作/ファイル操作など、CLIを使う機会は多いため開発環境にターミナルが組み込まれているのは本当に便利です。 RESTクライアント RESTクライアントがビルトインされているため、IDE内で気軽にWEBサーバへのリクエストのテストができます。 そのためDevHTTPClientなどの別クライアントも不要になります。 VCS GIT(とGitHub)やSubversionとの連携が可能です。 VCSウインドウからコミットやブランチの切り替えなどの通常操作もできますし、ファイルツリーでは未コミットファイルのアイコン色が変化したり、メインウインドウの右下でも現在のブランチ名表示やブランチ切り替えができます。 WebStormのUIは全体的にVCSとの親和性が高いように思います。 またGithubとコネクトする事によりPullRequestやIssue操作もできます。 大抵のGit操作はWebStorm内で完結させる事ができるのではないでしょうか。 TODO エディタでの開発ではあまり馴染みがないですが、コメントアウトの文字列でTODO管理ができます。 後回しにする実装などを"TODO"から始まるコメントアウトで残しておくことで、TODOウインドウ上に一覧表示させる事が可能です。 開発が長期にわたるプロジェクトで重宝すると思います。 マークダウンプレビュー マークダウンファイルのプレビューが可能です。 ATOMのようなライブプレビューではないものの、簡易的なレイアウト確認で役に立ちます。 FTPクライアント FTPクライアントも組み込まれていますので、CyberduckやFileZillaのような個別のクライアントも不要になります。 使い勝手についてもアップロード程度の作業であれば全く問題ないと思います。 ローカルヒストリー ローカルでの作業履歴が逐一保存されているので、思わぬアクシデントでのコード消失を防ぐことができます。 個々のログで差分を比較したりある時点の内容へリバートできるので、安心して作業をする事ができます。 横断検索 [Shift]キーを2回押下する事で、ファイルやIDE機能の横断検索が可能です。 キーボードショートカットやIDEの機能名、ファイル名を忘れてしまった場合でも、曖昧な検索ワードで目的の機能やファイルを見つけ出す事ができます。 構造表示 HTML/CSS/JavaScriptファイルの内容を構造的に表示できます。 コードの行数が長かったりネストが深い場合でも見通しが良くなるため、設計の全体を俯瞰できるようになります。 プレゼンテーションモード IDE自体のインターフェイスやコードの文字を大幅に拡大するプレゼンモードがあります。 画面を使って人に説明するときや、プロジェクタなどに映し出すときに便利です。 プレゼンテーションモードでの文字サイズは設定画面から変更する事が可能です。 以上のように、多くの機能がIDEに統合されているので、コーディングに必要なUIをほぼ1画面で収めて快適に作業を進める事ができます。 参考レイアウト: ある程度解像度の余裕があればWebStormとブラウザを並べる事でさらに作業が捗ると思います。(OS XのSplitViewを使うとうまく収まります) 2) 豊富なコード支援 高機能なエディタによって、入力支援や入力中でのコード解析が可能です。 JavaScriptではES6/Node.js/AngularJS/React/Angular2/AltJSをサポートしていて、それぞれコード補完やハイライティング、コンパイル機能などを搭載しています。 プロジェクトごとに設定画面からJavaScriptのバージョンを選ぶ事ができるのでES6への切り替えも簡単ですし、宣言元へのジャンプ機能も備えています。 HTMLはEmmetやHTMLタグの入力補助、CSSも各種メタ言語をサポートしています。 コードヒント 推測精度の高いコードヒントを利用可能で、例えば以下のような形で頭文字で候補を絞り込むことが可能です。 gebi > getElementById() qsa > querySelectorAll() euc > encodeURIComponent() duc > decodeURIComponent() Postfixコード補完 任意の値の後に.(ドット)でキーワードを繋げて[Tab]を押下するとコードブロックとして整形できます。 console.log()のようなものは利用頻度が高くタイプ数も減らせるので便利だと思います。 ※Postfixの候補を自分自身で作成する事も可能です。 hoge.log > console.log(hoge) hoge.return > return hoge hoge.null > if (hoge == null) {} hoge.notnull > if (hoge != null) {} hoge.if > if(hoge) Emmetサポート 標準でHTML/CSS/JSXファイル上でのEmmet記法に対応しているので、タイプ数を減らしコーディングスピードを上げる事ができます。 私はまだEmmetを使いこなせていないのですが、シンプルなhtml入力はEmmetに頼るようになりました。 Imgタグ入力補助 Imgタグにsrc属性が指定されている場合は、画像のサイズを候補で表示をしたり、画像のプレビューを表示をすることができます。 マークアップの最中にFinderで画像の情報を確認するのは煩わしいので、これはなかなか便利です。 ※src属性にフォーカスしながら[Shift]でサムネイル表示、[Cmd]で画像を別ウインドウで開く事ができます。 LiveEdit JetBrains社製のChrome拡張を追加してLiveEdit機能を有効にすることで、HTML/CSSの変更をリアルタイムにChromeでプレビューすることができます。 そのためファイル変更の度にブラウザをリロードする必要がなくなります。 JetBrains IDE Support - Chrome Web Store またLiveEditではJavaScriptのConsoleもChrome側ではなくIDE上でも確認できます。 CSS SASS/LESS/Stylusなどのメタ言語に対応しているためシンタックスハイライトや色ピックアップ、変数の候補表示が可能です。 JavaScriptと同様にコード分析によってQuick-Fixが利用可能で、例えばショートハンドプロパティが利用可能な場合では、その場で[Alt]+[Enter]でQuick-Fixができます。 リファクタ可能な箇所はスマートに修正ができるようになります。 またCSS Stylesタブでは、ブラウザのインスペクタを使っているような感覚で選択中のHTMLのCSSStyleを確認する事が可能です。 JavaScript 宣言元ジャンプ [Cmd]+[b]で宣言元へのジャンプや、呼び出し元へのジャンプも可能です。 開発では特にこの機能を使う事が多いのではないでしょうか。 JSDoc JSDocをサポートしていて、関数の内容を解析した上でコメント生成の補助をしてくれます。 改行位置も綺麗に整いますしドキュメント整備が快適に進められます。 以上ご紹介した機能以外にも [Option]+[Enter]での正規表現のバリデータを利用できたり、 Auto IndentやReformatCodeによるコード整理 Refactorによる安全なファイル・クラス置換 LiveTemplateでのスニペット機能 などなど、多くの機能を備えています。 3) デバッグ支援とコード品質の安定化 クライアントサイド/サーバサイドのJavaScriptのデバッグができ、テストランナーやLinterと連携ができます。 またIDEでのコードスタイル定義や定義ファイル自体のバージョン管理により、一貫したコードスタイルを保てると思います。 テストランナーとの連携 Karmaに採用されているカバレッジエンジンのIstanbulに対応しており、npmでkarma-coverageをインストールすればカバレッジ状況を表示することも可能です。 UI上にカバレッジを表示する事で、普段からテストを意識する事に繋がると思います。 コード行におけるcovered/uncoveredの表示もハイライトで確認する事ができます。 Linter JSHint/JSLint/ESLint/JSCSなどのLinterやスタイルチェッカーを搭載していて、コードは常時解析されているためエラーがあればタイプ直後に反応します。 Grunt/GulpなどのタスクランナーでLinterを走らせるよりもレスポンスが早くなると思います。 また、コードに問題がある場合はQuick-Fixオプションで即座に修正可能です。 JSHint/JSLintそれぞれの設定では、チェック対象を細かく指定できます。 この他にもデバッグをサポートする、 Spy-jsによるコード実行のトレース Node.jsアプリケーションのCPU profilesやHeap snapshotsの確認 JSTestDriver/Cucumber.jsのサポート ES6やTypeScript、CoffeeScriptでもソースマップをサポート といった様々な機能が含まれているようです。 4) プラグインでの拡張 IDE Plugin Repositoryに数多くのプラグインが用意されていて、好みに合わせてIDEをカスタマイズする事が可能です。 ここでは私が利用しているプラグインを2つご紹介します。 Material Theme UI UIの見た目をすっきりとしたマテリアルデザイン風に変更できます。 ※テーマはプラグイン経由に限らず非公式のWebサイトから探す事もできます。 Color Themes gfm マークダウンのプレビューをGFM(GitHub Flavored Markdown)で表示する事ができます。 WebStorm標準のマークダウンプレビューは簡素なため、見た目を良い具合にGithub風に整えてくれるこのプラグインは重宝しています。 gfm-plugin ライセンス体系 JetBrains社は2015年にライセンス体系をサブスクリプションタイプに変更していて、2016年4月時点でのライセンスは以下のような設定になっています。 個人利用 商用利用 ・年間契約   初年度 $59.00   2年目 $47.00   3年目 $35.00 ・月間契約   毎月 $5.90 ・年間契約   初年度 $129.00   2年目 $103.00   3年目 $77.00 ・月間契約   毎月 $12.90 いずれもサブスクリプションのため継続した支払い体系になりますが、必要な期間だけ月間契約で継続してみる、といった選択肢があるのもメリットだと思います。 また、JetBrains社は機能追加やバグフィックスへの対応のために 製品のリリース頻度を高めて いるようです。 2016.1のリリースで既にCSS custom variableのサポートがされていますし、2016.1のリリース以降も短いスパンでバグフィックスや機能追加のアップデータが繰り返し配布されています。 Web開発は技術のアップデートサイクルがとても短いので、常に最新の状態で利用できることのメリットは大きいと思います。 まとめ WebStormは"全部入り"のIDEとなっており私もすべての機能を把握できていませんが、使いこなす事が出来れば多機能ゆえの楽しさもあると思います。 実務の中でしばらく使ってみて、 充実したコード支援機能でのコード品質の安定化 キーボードショートカット中心の操作による作業スピード向上 インターフェイスの統一による作業効率の向上 を実感できて、JavaScriptメインの開発環境でなくても十分活用できると感じました。 またチーム内でのコーディングスタイルを統一しやすくなりますし、UIの分かりやすさはデザイナーが開発に携わる場合にもメリットがある思います。 エディタに比べると高機能ですのでそれなりのマシンパワーが必要とされますし、シンプルか多機能かは好みでもありますが、統一されたGUIならではの扱いやすさは確実にあると思います。 今回は全体の機能を通しでまとめましたが、 もう少し使い慣れてきたら個々の機能にフォーカスしてまたご紹介したいと思います。  参考URL WebStorm WebStorm 2016.1 Help YouTube - JetBrains Playlist YouTube - WebStorm - Things You Probably Didn’t Know Qiita - WebStorm
アバター
こんにちは、品質管理グループの 山本久仁朗です。 みなさんの組織では、テスト計画を作成していますか? テスト計画について、さまざまな効果があることは、みなさんご承知のことと思いますが、 今回はマスターテスト計画の活用について、お話しいたします。 マスターテスト計画の役割 IEEE 829 や ISO/IEC/IEEE 29119 などに含まれる、ソフトウェア・システムのテスト計画書の多くは、 PMBOK のプロジェクト計画と親和性が高く、テストの要件・目的・対象・期間・範囲・粒度・工数・開始終了基準・成果物等が含まれ、合意形成や手戻りに抑止等の効果があります。 ただし大きなプロジェクトにおいて、各機能・フェーズに対応した各レベルテスト計画だけでは整合性が取れず、サービスを全体的に見ると、テストの抜け・漏れ・重複が発生するリスクが残ってしてしまいます。 マスターテスト計画は、このような抜け・漏れ・重複を削減し、よりスムーズにテストを進めるために、プロジェクト・サービス全体を俯瞰して考える役割を担っています。 マスターテスト計画導入の効果 サービス全体でMECE(抜け・漏れ・重複なし)なテストを実施 各レベルテストのテスト目的・観点・スコープを確認し、サービス全体のテスト観点として抜け・漏れ・重複は無いかを精査することで、より効率的なテストが可能になります。 直近の事例として、マスターテスト計画をベースに各レベルテストのテスト観点の抜け漏れが判明したので、 下流のレベルテスト計画を見直しすることにより、初期計画時の3倍の不具合を検出することができました。 レベルテストをブラッシュアップして、早期に不具合を検出 各レベルテストの目的から、テスト観点毎にテスト対象・実施時期を再調整して、 早期にテストを段階的に実施することで、品質状況も段階的に可視化・共有可能になります。 実際に、設計側と相談して、主要な機能をプレリリースしてもらうことで、1~2週間ほど先行して一部機能のテストが実施できるようになりました。 上記のように、マスターテスト計画には大規模・複雑なシステム・サービスをテストする際に、レベルテスト計画を補完する効果がありますが、決して良い話ばかりではありません。そこには、陥りがちなアンチパターンがたくさんありました。 代表的な例を列挙いたしますので、お気を付けください。 マスターテスト計画導入時のアンチパターン マスターテスト計画を導入してみた方は、ご存知のことと思いますが、さまざまな課題が発生します。例えば、そもそもどうやったらいいのか分からない・工数がかかる・効果が分からない等いろいろな理由から、なかなか安定運用できていないのが現状だと思います。 そこで、いままでの経験をベースに、いくつかのアンチパターンを列挙します。 レベルテスト・フェーズが相互補完していない テスト観点が漏れて、後工程・市場で障害が発生する 同じテストケースを使いまわしているが、最適化されない 規格に規定されているすべての項目を列挙して、膨大なテスト計画になる 計画書の項目を埋めるために、必要性が低い情報を収集して工数がかかる メンテナンスに工数がかかる → 工数がかかるので見直しをしない → 現実と乖離する 各レベルテストを分割せずに変化に対応できない 829-1998等をベースに、1つの大きなテスト計画を作成し、変更し辛いドキュメントになる 目的・スコープ等から分割した方が柔軟性が高まるが、結合・総合・システム等のフェーズに縛られ分割できない マスターテスト計画導入時のおすすめパターン 私が、マスターテスト計画でもっとも重要視しているのは、integrity(完全性)です。 サービス全体で実現すべきテスト観点が過不足ないか確認しています。 しかし、各レベルテストのテスト設計・実装内容まで確認すると、対費用効果が低くなり、いくら工数があっても足らなくなります。 そこで、下記のようなポイントを軸に、ライトに何度も見直しができるような運用を心がけています。 テスト観点ベースでのヒアリング実施(目的・スコープ・アプローチ程度) 各レベルテストの状況から、不足しているテスト観点・タスクを検討・調整 計画時のテスト観点をもとにテスト設計・実装を実施 まとめ 上記の事例は、非常に効果的に実施できた例になりますが、マスターテスト計画の活用方法を見直すことで、不具合を早期に検出して、検出効率を向上させることができました。 みなさんの現場において、テスト計画の見直し、より早期の不具合検出、効率的なテストの推進・実施等の一助になれば幸いです。
アバター
こんにちは。年度が替わりましてスマートパス開発部からインフラ部へ異動した子安です。最近すっかり暖かくなって、日本人で良かったなと思っています。 さて、近年いろいろな動画学習サービスが提供されていて楽しいですね。動画学習といえば、ブラウザで動画を見ながらエディタでコードを書いてコンソールで実行、をコツコツやるわけですが、3つのことを同時に進めていくのは大変面倒です。面倒なので書いたコードを保存したら自動的に実行されるようにしたいと考え始めました。そこで、タスクランナーとしてよく聞くGulpとかGruntとかをこの機会に試してみます。 そもそもGulpもGruntも今まで触った経験がないのですが、どっちがナウいんでしょうか。社内で聞いてみました。 え、オワコン・・・!? そうなのか・・・まあでも、実際に自分でも触ってみないとわかりませんよね。 初めに実現したいことをまとめておきます。 今は自分的にGoが流行ってるのでGoの動画を見て勉強するつもり goファイルはプロジェクト配下のsrc/ディレクトリに格納する コードを編集・保存したら、そのファイルに対して自動的に go run を実行する 実行結果はコンソールに表示する。実行する度にコンソールをクリアする 環境はMac OS X(10.11.4)で、Go・Node.jsをインストール済み Gulp まずはロゴがかっこいいGulpから。 npmを初期化してGulpをインストールします。Gulpの設定ファイルはせっかくなのでCoffeeScriptで書きましょう。CoffeeScriptもインストールしておきます。 npm init npm install --save-dev gulp coffee-script ファイルの変更監視は gulp.watch で行います。設定ファイル:gulpfile.coffeeはこんな感じになります。 # gulpfile.coffee gulp = require 'gulp' {spawn} = require 'child_process' gulp.task 'watch', () -> gulp.watch 'src/**/*.go', (event) -> if event.type in ['added', 'changed'] console.log '\x1b[2J' spawn 'go', ['run', event.path], {stdio: 'inherit'} gulp.task 'default', ['watch'] これを下記のコマンドで実行します。 $(npm bin)/gulp Grunt 次はGruntでやってみましょう。npmを初期化してGruntをインストールします。こちらも設定ファイルはCoffeeScriptで書くことにしますが、依存関係に含まれるためCoffeeScriptを個別にインストールする必要はありません。代わりにファイルの変更監視のためのプラグインが必要になります。 npm init npm install --save-dev grunt grunt-contrib-watch watchプラグインを設定し、イベントを捕まえて go run コマンドを実行するようにします。設定ファイル:Gruntfile.coffeeはこんな感じになります。 # Gruntfile.coffee {spawn} = require 'child_process' module.exports = (grunt) -> grunt.initConfig watch: files: ['src/**/*.go'] options: event: ['added', 'changed'] grunt.event.on 'watch', (action, filepath, target) -> console.log '\x1b[2J' spawn 'go', ['run', filepath], {stdio: 'inherit'} grunt.loadNpmTasks 'grunt-contrib-watch' grunt.registerTask 'default', ['watch'] これを下記のコマンドで実行します。 $(npm bin)/grunt npm-scripts 最近はGulpやGruntを使わずにnpm-scriptsを使うんだという話なので、その方法も試してみたいと思います。例によってnpmを初期化して、CoffeeScriptとファイルの変更を監視してくれるchokidarというパッケージをインストールします。 npm init npm install --save-dev coffee-script chokidar package.json(抜粋)はこんな感じで "scripts": { "watch": "coffee watch.coffee" }, 呼び出すwatch.coffeeはこうなります。 # watch.coffee chokidar = require 'chokidar' {spawn} = require 'child_process' chokidar.watch './src/**/*.go', ignoreInitial: true .on 'all', (event, path) -> if event in ['add', 'change'] console.log '\x1b[2J' spawn 'go', ['run', path], {stdio: 'inherit'} これを下記のコマンドで実行します。 npm run watch まとめ Gulp・Grunt・npm-scriptsのいずれの方法でも、実現したかったことを実現できました。 出来上がった設定ファイルを見てみると、GruntはGulpに比べて決まり文句的な記述が多く、設定を宣言的に行うようになっているため柔軟性に欠けるという印象です。Gulpとnpm-scriptsに関しては、記述内容自体にほとんど差がないですね。 それよりもそれぞれのプラグインを探すことや、Gulp・Gruntの書き方を調べることに時間がかかりました。使うために調べることが多くなってしまうのはツールとして本末転倒なので、熟れるまで使い込めるかどうかを判断する必要がありそうです。 使ったもの Node.js: v5.10.0 npm: v3.8.3 Gulp: v3.9.1 Grunt: v1.0.0 CoffeeScript: 1.10.0 コードは GitHub に置いています。 おまけ というようなわけで、今回はnpmでどうやるかということを調べてきたのですが、ここまで来るともうnpmである必要もない気がしてきました。 探してみたところ、Homebrewにfswatchというコマンドがありました。 brew install fswatch このコマンドは監視対象のファイルにイベントがあると、標準出力にファイルパス等を書き出してくれます。つまり、 fswatch -0 --include='\.go$' --exclude='.*' --event=Created --event=Updated ./src | \ while read -d '' file; do echo $'\e[2J' go run "${file}" done おやおや、一番簡単でした!これが正解だったみたいですね。
アバター