株式会社クリアコード > ククログ

ククログ


Tokyo WebExtensions Meetup #3

クリアコードでFirefoxやThunderbirdの法人サポートに従事している結城です。

去る10月30日、御茶ノ水・デジタルハリウッド大学院の1教室をお借りして、Firefox(やその他様々なブラウザ)の拡張機能に関心を持つ人達の交流イベントであるTokyo WebExtensions Meetup #3が開かれました。日にちが日にちだったため、会場にはハロウィン的な仮装のための小道具が用意されていたり、Mozillaのスポンサードにより提供されたピザがハロウィン仕様(黒い生地にオレンジのチーズ)だったりと、ささやかながらお祭り気分を味わいつつの進行でした。

筆者も発表者の一人として参加しましたので、このエントリでは発表の内容の概要をお伝えします。

発表1:タブの複数選択APIのつかいかた

最初は筆者による、Firefox 63以降で使える「タブの複数選択機能」に関する発表です。発表資料のスライドはQiitaにて公開中です。また、公開に際しては口頭で発表した詳細な説明も書き加えてあります。

タブの複数選択機能はFirefox 64からの新機能(予定)ですが、基本的な機能はFirefox 63の時点でも既に使える状態になっています。about:configを開いてbrowser.tabs.multiselectを検索し、値をtrueに設定すれば、機能が使えるようになります*1

筆者は予てよりマルチプルタブハンドラというアドオンを個人的に開発しています。これはFirefox 56以前においては、Firefoxのタブにまさにそのような性質を加える物でした。しかしWebExtensionsではFirefox自体のタブの振る舞いを変えることはAPIの制約上できません。そのため、WebExtensions移行に際しては、このアドオンの内部でタブの選択状態を管理し、browser.runtime.sendMessage()による明示的なAPI呼び出しに対してその情報を返すという形で、他のアドオンに対して「タブの複数選択」相当の事ができるようにするAPIプロバイダのように振る舞う*2アドオンとして再出発したという経緯があります。

今回、Firefox本体にタブの複数選択機能が入り、同時にその機能を呼び出すためのWebExtensions APIが追加された事によって、アドオン同士がお互いの存在を知らないまま暗黙的に連携しあえる場面がまた一つ増えたと言う事ができます。また、次のリリースとなるFirefox 64ではタブやブックマークのコンテキストメニューに機能を追加するアドオン同士の暗黙的な連携も可能になる予定です。このように暗黙的な連携が図られる場面が増えることで、各アドオンが単独で動作するだけの状態に比べて、機能の有用さは何倍にも高まります。

XUL時代のアドオンの有用性を支えていた要素の一つであったその性質が、WebExtensionsへの移行で大きく失われてしまった、という体験をした身としては、今後もこういった形でアドオン同士の暗黙的な連携が促進されるような改良が続く事を切に期待するばかりです。

発表2:Webページ中のテキストの動的な置換

2番目の発表は、あきみね氏による「Webページ中に現れる特定の文字列を、任意の文字列に強制的に置き換える」アドオンの紹介でした。

このアドオンでは、コンテントスクリプトでDOMツリーを走査して個々のテキストノードを編集するという基本の処理に加えて、MutationObserverを使って動的に挿入されるテキストも編集するという工夫により、Facebookなどのような「スクロールに応じてページの内容が次々と継ぎ足されていく」形式のWebサービスにも対応できるようにした事が特長であるという紹介がありました。

ただ、Webページ中の全テキストノードを走査するという事は、それだけ処理に時間がかかるという事でもあります。そのため、処理に時間がかかるようなページのための対策には悩まれているそうです。

これについて、browser.findのAPIを使用すればより効率よく指定の文字列の置き換えができるのではないかという情報提供を筆者から行いましたが、このAPIは今のところFirefoxが対応しているのみのため、Chromeの使用を考えると残念ながら使う事はできないとの事でした*3

ブラウザを問わずに行えそうな別方向での最適化としては、worker-domを活用するという方法も考えられます。これはDOMツリーを丸ごとWorker上に再現して、ボトルネックになりがちなDOMの操作を別スレッドに逃がしてしまうという試みです。一般的なWebページの処理にどこまで使えるかというのは未知数ですが、もし使えるのであれば、Webページの内容と密接に関わる種類のアドオンの大幅な高速化が可能になるかも知れず、興味深い技術です。

発表3:WebExtensionsとテスト

3番目の発表は、Firefox上でvim風の操作体系を実現するアドオンの一つであるVim Vixenの作者のUeoka氏による、アドオンの自動テストの実現方法についてのお話でした。
こちらは発表資料がSpeakerDeckにて公開されています

Vim Vixenの開発にあたっては一般的なJavaScriptでのアプリケーション開発のノウハウが活かされていて、自動テストもKarmaMochaWebExtensionsの各種APIをモックするライブラリを組み合わせて大量のユニットテストを行っているそうです。また、自動テストやESLintによる検証をCircleCIで継続的に実行されているとの事でした。

プロダクト自体を自動テストが容易になるように設計するという事は、純粋なロジック部分と外部要因になる部分とをなるべく切り離すという事です。例えばタブを指定の条件でフィルタリングしてリストアップするためのフィルター処理自体browser.tabsに一切触れておらず、その自動テストも純粋なロジックのみに特化して検証できています。Vim Vixenでは全体をこのように設計し、テストをなるべくしやすいようにされているとのことでした。

発表の後半はE2Eテスト(ユーザーの実操作に近いテスト)にフォーカスした話でした。Vim Vixenでは163あるE2Eテスト項目のうち現時点で45件を自動化できているとのことですが、その実現にあたっては、ambassadorと名付けられたテスト実行専用のアドオンを使われているそうです。WebExtensionsベースのアドオンの開発・テスト支援ツールであるweb-extの改造版を用いてVim Vixenとambassadorを同時に読み込ませ、テストケース内で指示された操作をambassadorで行ってVim Vixenの動作をテストしているとの事でした。

ユーザーの入力を再現するような箇所は非同期処理が多くなりがちですが、スクロール操作のE2Eテストなどを見ると分かるように、非同期処理が絡む自動テストもawaitを使うとシーケンシャルに記述できます。async/awaitはテストの実装にも有用という事を示す一例と言えるでしょう。

Vim Vixenはコンテンツ領域内にUIを挿入する設計のためこのようなテスト手法が可能となっているそうですが、言い換えると、サイドバーやツールバー上のポップアップ内でUIを提供するアドオンについては、このアプローチでのテストは行えないという事でもあります。Ueoka氏自身も、より広い範囲のE2Eテストを自動化する方法について試行錯誤を続けられているそうです。

アドオンの自動テストについては、Firefox本体におけるWebExtensions APIそのものの自動テストの実装方法が参考になりそうな所ではあるのですが、Firefox内のWebExtensions APIのテスト群は専用のテストランナー上で専用のユーティリティを使って実行する形を取っており、しかもその内容はFirefoxの名前空間上で動作するという、現状のFirefoxの実装に強く依存した形式となっています。web-extやその後継ツールには、Firefox本体の内部事情に詳しくなくても同等の事ができるような自動テスト支援の仕組みを提供してくれる事が期待されます。

ambassadorや実際の自動テストも含めて、この発表の中で紹介された要素はすべてVim Vixenのリポジトリで公開されています。拡張機能の自動テストに関心がある方は必見ですね。

11月8日追記:発表をされたUeokaさんご自身による発表内容の解説記事も併せてご覧下さい。

発表4:開発ツールを拡張しよう!

最後の発表は、Mozillaで主にJavaScriptエンジンの開発を担当されているarai氏による、Webページのソースを任意の内容で置き換えるDITMというアドオンの紹介でした。

DITMとは「Developer In The Middle」の略ですが、これは「Man In The Middle Attack(中間者攻撃)」という攻撃手法をもじった物です。

MITM攻撃では、ユーザーが操作するクライアントとWebサーバの間に何らかの方法で中継者が入り込み、ユーザーが送るリクエストの内容を書き換えてWebサーバーに送ったり、Webサーバーから返された内容を書き換えてクライアントに送ったりすることで、ユーザーが入力したパスワードやクレジットカード番号などの機密情報を盗み出したり、ユーザーを望ましくない行動に誘導したりといった事が行われます。昨今のいわゆる「常時SSL化」は、このような攻撃を防ぐ事を目的の1つとしています。

このようにMITM攻撃を防ぐ事のできるSSL(TLS)ですが、アドオンの権限ではTLSで通信して取得した後の内容を任意の物に置き換える事ができます*4。これを応用して、Webサーバー上のファイルを実際に編集せず手元で書き換えてデバッグするという事を可能にするのが、DITMです。

発表では、「Minifyされたライブラリのソースを一旦スクラッチパッドに貼り付けてpretty printし、それを元のファイルの代わりに使うようにDITMで設定した上で、任意のコードをそこに追加して実行してみる」というデモが行われていました*5。「Firefoxでだけ動かないページ」に遭遇した時の原因調査などに活用できるかもしれません。

DITMの主要な機能はbrowser.webRequest.filterResponseData()によって実現されているため、Web上のコンテンツをクライアント側で置き換える*6実装の例と言う事ができます。また、開発ツールに独自のタブを追加する例としても参考になるでしょう。

次回以降のイベントについて

メインの発表が終わった後は、このイベント自体の今後の方向性についてヒアリングも兼ねたディスカッションが行われました。以下はその中で聞かれた意見です。

  • 一般的なセミナー形式ではなくワークショップをやりたい。
    • アドオンを作ってみたい、という初心者向けの内容はどうか。
    • 既存のアドオンの不具合の修正や機能追加のプルリクエスト、WebExtensionsに移行できていないアドオンの移行をやってみるのはどうか。
    • 日本語ロケールが無いアドオンを翻訳してプルリクエストするのはどうか。
    • Firefox本体に対するWebExtensionsの新しいAPIの提案や、実装が行われないまま停止しているAPI案へのパッチの提供など、やや高度な事に挑戦してみるのはどうか。
    • モブプログラミング形式(手を動かす人一人に対し周囲に複数人が集まって意見を出し合う形式)にしてはどうか。
  • 温泉旅館などでハッカソンのように泊まりがけてディープな事をやってみたい。
  • セミナー形式のイベントにニーズが無いわけではない。今回の発表も刺激的な内容だった。

ひとまず、次回開催は1月下旬から2月上旬頃を目標として、どのようなイベントが望まれているかのアンケートなどを近日中にMozilla Japan コミュニティのSlackで行う事を予定しています。次回の参加を検討されている方は、自分が参加したいと思えるイベントにしていく機会として、是非Slack上で意見を表明してみて下さい。

また、「OSSであるFirefoxやアドオンにフィードバックしてみたいが、やり方が分からなかったり、恐怖感があったりして、一人でやるのは尻込みしてしまう」という人向けに、Firefox関係に限らず広くOSS一般を対象にしたOSS Gateという取り組みも行っています。こちらはワークショップが定期的に開催されていますので、開催予定のイベント一覧から、参加しやすいエリア・日次のワークショップを探してビギナーとして参加してみてはいかがでしょうか。

まとめ

以上、Tokyo WebExtensions Meetup #3で行われた発表やディスカッションの内容をご紹介しました。

*1 なお、タブの複数選択機能はChromeには遅くとも2013年頃の時点で既に実装されていた模様です。

*2 とはいえ実際には、大多数の他のアドオンはこのアドオンの存在自体を知らないために、明示的な連携が図られる事はほぼ無く、「アドオン同士の連携」といってもほとんど絵に描いた餅に過ぎないという状態が長く続いていました。

*3 同様の事をクロスブラウザで行う物ための技術としては、かつてW3Cで提案されていたRangeFinder APIという仕様もありますが、こちらは現在の所実装しているブラウザは存在しません。

*4 そのため、怪しいアドオンを不用意にインストールするのは非常に危険な事と言えます。

*5 Minifyやコンパイルされたコードに対するデバッグ方法としては、変換前のソースとの対応表を提供するSource Mapという技術もあります。Source Mapが提供されていない場合でもそれと同様の事を行えるというのが、DITMの利点の一つという事になります。

*6 あきみね氏の発表はDOMツリーが構築された後の置き換えなのに対し、arai氏のDITMはDOMとして解釈されるより前の時点での置き換えという違いがあります。

タグ: Mozilla
2018-11-02

WebDriver APIを使ったLua用のWebブラウザー自動操作ライブラリー LuaWebDriver

WebDriver APIを使ってWebブラウザーを自動操作できるLuaのライブラリーを開発しました。LuaWebDriverといいます。
これは、クリアコードが株式会社セナネットワークス様からの発注を受けて開発したライブラリーです。MITライセンスで公開しています。

今のところ、操作できるWebブラウザーはFirefoxのみです。
LuaWebDriverはWebDriver APIを使って、LuaからFirefoxを操作することができます。Firefoxはデフォルトでヘッドレスモードで起動します。(GUIでの起動もできます。)

Webブラウザーを利用してWebサイトをレンダリングするため、JavaScriptを使用して動的に生成されるWebページをスクレイピングすることや、HTMLのパーサーライブラリ等で解釈できないような正しくないHTMLで書かれたWebページのスクレイピング等に使用できます。(正しくないHTMLで書かれたWebページもWebブラウザーはなんとか表示するため。)
また、Webブラウザーを操作できるので、ログインが必要なページに対して、ログインし、ログイン後のWebページをスクレイピングするようなこともできます。

インストール方法

LuaWebDriverは、LuaRocksで公開しており、luarocksコマンドを使ってインストールできます。
また、Firefoxを操作するためにgeckodriverを使っているため、本ライブラリーの他にgeckodriverのインストールも必要です。

例えば、CentOS7では、以下のようにインストールします。

% yum install -y gcc gcc-c++ firefox lua-devel luajit-devel luarocks make openssl-devel
% curl -L -O https://github.com/mozilla/geckodriver/releases/download/v0.23.0/geckodriver-v0.23.0-linux64.tar.gz
% tar xf geckodriver-v0.23.0-linux64.tar.gz -C /usr/local/bin
% chmod +x /usr/local/bin/geckodriver
% sudo luarocks install web-driver

CentOS7以外のインストール方法は、LuaWebDriverのドキュメントのインストールを参照してください。
LuaWebDriverは、CentOS7の他にmacOSに対応しています。

基本的な使い方

LuaWebDriverを使ってFirefoxを操作するには、web-driver.Firefoxクラスのオブジェクトが必要です。
web-driver.Firefoxクラスのオブジェクトは以下のように作成します。

local Firefox = require("web-driver/firefox")
local firefox = Firefox.new()

web-driver.Firefoxクラスのオブジェクト作成時には、様々なオプションが設定できますが、ここでは、わかりやすさのため、オプションは設定せず、全てデフォルト値で実行します。
設定可能なオプションについては、web-driver.Firefoxクラスのリファレンスを参照してください。

web-driver.Firefoxクラスのオブジェクトを作成しました。次は、作成したオブジェクトを使って、Firefoxを起動し、操作します。
Firefoxの起動と操作は、web-driver.Firefox:start_session()を使って行います。

web-driver.Firefox:start_session()は引数にコールバック関数を指定でき、LuaWebDriverは、Firefox起動後、引数に指定されたコールバック関数を実行します。このweb-driver.Firefox:start_session()の引数に与えるコールバック関数内にFirefoxを操作して実行したい処理を実装します。

例えば、指定したURLのWebサイトへアクセスする場合は以下のように実装します。

local Firefox = require("web-driver/firefox")
local firefox = Firefox.new()

local URL = "https://clear-code.gitlab.io/lua-web-driver/sample/"

-- コールバックの作成とセッションの開始
firefox:start_session(function(session)
  -- 指定したURLのWebサイトへアクセス
  session:navigate_to(URL)
end)

コールバック関数の引数には、起動したFirefoxとのセッションがweb-driver.Sessionクラスのオブジェクトとして渡されます。
このオブジェクトを使って、ブラウザーの操作を実装します。
Webサイトへのアクセスは、web-driver.Session:navigate_to()を使って行います。このメソッドの引数にアクセスしたいWebサイトのURLを指定すると、そのURLへアクセスできます。

Webサイトへのアクセス以外の操作については、web-driver.Sessionクラスのリファレンスに記載がありますので、合わせて参照してください。

主な機能

LuaWebDriverの主な機能を紹介します。
LuaWebDriverを使うとLuaで以下のようなことができます。

Webサイトへのログイン、ページ内のリンククリック

ここでは、ログインが必要なWebサイトへアクセスし、ログインを実施、ログイン後のページ内のリンクをクリックしてWebページを移動、移動後のページの要素のテキストを取得する例を示します。
少し複雑な例ですが、Webサイトの操作によく使う機能を紹介できるので、このような例にしました。

ログインの実行やリンククリックを行うには、特定のフォームへのテキスト入力、ボタン操作、リンクのクリック操作が必要です。
LuaWebDriverでは、これらを、web-driver.Session:css_selectweb-driver.ElementSet:send_keys()web-driver.ElementSet:click()web-driver.Session:link_search()というメソッドで実現します。

具体的な使い方は、以下の実装例を使って説明します。

local Firefox = require("web-driver/firefox")
local firefox = Firefox.new()

local URL =
  "https://clear-code.gitlab.io/lua-web-driver/sample/move.html"

-- コールバックの作成とセッションの開始
firefox:start_session(function(session)
-- ログインが必要なページへアクセス
  session:navigate_to(URL)

-- Webサイト内のフォームを取得
  local form = session:css_select('form')
-- ユーザー名を入力するためのフォームを取得
  local text_form = form:css_select('input[name=username]')
-- フォームにユーザー名を入力
  text_form:send_keys("username")
-- パスワードを入力するためのフォームを取得
  local password_form = form:css_select('input[name=password]')
-- フォームにパスワードを入力
  password_form:send_keys("password")

-- ユーザー名とパスワードを送信するためのボタンを取得
  local button = form:css_select("input[type=submit]")
-- ユーザー名とパスワードを送信
  button:click()

-- リンク操作をするための要素オブジェクトを取得
  local link = session:link_search("1")
-- リンクをクリック
  link:click()
  local elements = session:css_select("p")
-- 取得した要素のテキストを取得
  print(elements:text())
-- 1
end)
ログイン

まずは、web-driver.Session:css_select()を使って、ユーザー名を入力するフォームを取得します。 web-driver.Session:css_select()は、CSSセレクターを用いて取得したい要素を指定できます。上記例では、input[name=username]としているので、name属性がusernameとなっているinput要素を取得します。

取得した結果は、web-driver:ElementSetクラスのオブジェクトとして返ってきます。
返ってきたオブジェクトを使って、web-driver:ElementSet:send_keys()を実行します。このメソッドは、取得した要素に対して指定したテキストを入力します。上記例では、取得したinput要素にusernameという文字列を入力しています。
これで、ユーザー名の入力は完了です。

次にパスワードの入力を実施します。パスワードの入力もユーザー名の入力と同様web-driver.Session:css_select()を使ってinput要素を取得し、取得したinput要素に対して、web-driver.ElementSet:send_keys()を使ってパスワードを入力します。

ユーザー名とパスワードの入力が完了したら、次は、ユーザー名とパスワードを送信するために送信ボタンを押下します。
送信ボタンの押下は、web-driver.Session:css_select()を使って、送信ボタンを取得します。ボタンの押下は、web-driver.ElementSet:click()を使います。

ここまでで、ユーザー名とパスワードを入力し、それらを送信しました。
入力したユーザー名とパスワードに間違いがなければ、ログイン成功し、ログイン後のページヘ遷移します。

リンク先のページへ遷移

次は、ログイン後のページ内のリンクをクリックし、別のページへ遷移します。
ページ内のリンクを取得するには、web-driver.Session:link_search()を使います。このメソッドの引数にa要素のname属性の値を指定します。
上記の例では、リンクテキストが「1」のリンクを取得しています。取得したリンクをクリックするには、web-driver.ElementSet:click()を使います。
これで、リンク先のページへ遷移します。

ページ内のテキストを取得

リンク先のページへ遷移後は、ページ内要素を取得し、その要素のテキストを取得します。
要素の取得は、今までと同様、web-driver.ElementSet:css_select()で取得します。
取得した要素のテキストを取得するには、web-driver.ElementSet:text()を使用します。web-driver.ElementSet:text()は要素のテキストを文字列として返します。
上記の例では、遷移後のページのp要素のテキストを取得し、printを使ってテキストを標準出力に出力しています。

このようにして、LuaWebDriverでは、Webブラウザーを操作し、必要なページや要素へアクセスしその値を取得することができます。
今回の例では、要素の取得にCSSセレクターを使いましたが、XPathなどその他の方法で検索、取得することもできます。
CSSセレクター以外の取得方法については、web-driver.Searchableモジュールのリファレンスに記載しています。

終わりに

LuaWebDriverの基本的な使い方と主な機能を紹介しました。ここで紹介した機能以外にも便利な機能があります。
LuaWebDriverのその他の機能については、以下のドキュメントを参照して下さい。

チュートリアル
リファレンス

このライブラリーは、XMLuaLuaCSと同様株式会社セナネットワークス様からの依頼を受けて開発した受託開発の成果物です。

成果物をフリーソフトウェアとして公開すると、様々なユーザーがライブラリーを使うことによって、いままで気が付かなかったバグを発見できたり、ユーザーからの要望によって、当初想定していなかった、便利な新機能を実装するきっかけとなったり、様々なメリットがあます。

このように成果物の公開によって、ライブラリーの品質も高まるので、お客さんにとっても成果物を公開するメリットがあります。

ご依頼主の株式会社セナネットワークス様には、上記のようなメリットにご理解をいただき、成果を公開できました。ありがとうございます!

2018-11-06

YoctoでFcitxをビルドする

以前の記事で、Yoctoのweston環境向けにuimをビルドして日本語入力を実現する方法を紹介しました。

その後、別のお客様向けにYocto上でのFcitx 4のクロスビルドを支援する機会があり、その成果をmeta-inputmethodとして公開しましたので紹介します。

meta-inputmethodの現在のステータス

クリアコードは、仕事で書いたコードは出来る限りお客様からの許可を頂いてフリーソフトウェアとして公開するようにしています。今回作成したFcitxのYoctoレシピについても許可を頂いたので公開することとしました。

その際、Yoctoプロジェクトにフィードバックして、uimと同じようにpokyに入れてもらうという案もあったのですが、Yoctoのコンセプト的には別のYoctoレイヤに分けた方が管理が楽であろうと考え、meta-inputmethodとして公開することとしました。現在、meta-inputmethodには以下のレシピが含まれています。

  • IMフレームワーク: Fcitx 4
  • 日本語変換エンジン: Anthy
  • 上記の依存ライブラリ

X11環境ではおそらく動作するものと思われますが、実はクリアコードではX11環境での検証はまだ行っておりません。このレシピは、主にWayland環境でFcitxを動作させることが可能かどうかを検証するために作成したものだからです。Wayland環境では動作を検証していますが、以下の問題があることを確認しています。

  • 候補ウィンドウを表示させるにはXWaylandを有効化させる必要がある
  • 上記を行っても、候補ウィンドウを正しい位置に表示させることができない
  • Ctrl + Spaceで日本語入力のON/OFFをすることができない

Fcitx 4はもともとWaylandには正式対応はしていないため、これは致し方ないところです。

ビルド方法

meta-inputmethodおよび依存レイヤであるmeta-qt5を他のYoctoレイヤと同ディレクトリにcloneします。

$ git clone https://gitlab.com/clear-code/meta-inputmethod.git
$ git clone https://github.com/meta-qt5/meta-qt5.git

Yoctoビルドディレクトリのconf/bblayers.conf に以下を追記します。pathは環境に応じて調整して下さい。

BBLAYERS += "
  /path/to/meta-qt5 \
  /path/to/meta-inputmethod
"

また、conf/local.conf に以下の内容を追記します。こちらも、必要の無い物は削除するなどして調整して下さい。

IMAGE_INSTALL_append = " \
  fcitx \
  fcitx-data \
  fcitx-anthy \
  fcitx-gtk2.0 \
  fcitx-gtk3 \
  fcitx-qt5 \
  fcitx-ui-classic \
  fcitx-module-dbus \
  fcitx-configtool \
  fcitx-module-x11 \
"

bitbake core-image-sato などを実行することで、ブートイメージにFcitxを追加することができます。

実行時の設定は今のところレシピでは追加していませんので、手動で設定する必要があります。

例えば、/etc/environment 等に以下を追記し

export GTK_IM_MODULE=fcitx
export GTK3_IM_MODULE=fcitx
export XMODIFIERS="@im=fcitx"
export QT_IM_MODULE=fcitx

手動でFcitxデーモンを起動することで、動作を確認できるかと思います。

$ fcitx

DBusが起動していない場合は、dbus-launch等で事前にセッションバスを立ち上げ、同セッション上でfcitxを起動する必要があります。

また、Waylandで使用する場合には、XWaylandを有効化していないと候補ウィンドウを表示することができません。WaylandコンポジターとしてWestonを使用する場合、以下の設定を/etc/xdg/weston/weston.iniに追加します。

[core]
modules=xwayland.so
xwayland=true

まとめ

meta-inputmethodを用いてYoctoでFcitxをビルドする方法を紹介しました。現在はFcitx + Anthyのみであり、動作確認も十分に出来ていない状態ではありますが、今後Yoctoでの多言語入力環境を充実させる機会があれば、同レイヤにレシピを追加させていきたいと思います。

2018-11-08

Supershipさまで「既存のコードを変更するノウハウ」を学ぶ研修を行いました

クリアコードの結城です。

この度Supershipさまからご依頼を頂き、先方のグループ各社内の新入社員の方々を対象とした研修の一環として、「既存のコードを変更するノウハウ」を学ぶワークショップを2018年9月に開催しました。
この記事では研修の実施に至った背景事情と、実際に研修の中でお伝えした内容をご紹介します。

(研修中の様子の写真)

「社会人レベルのコードを書けるようになる」とはどういう事か?

本件は元々、Supershipさまのグループ各社横断で行われる新人研修の一環で、「社会人レベルのコードを書けるようになる事」をテーマに1日かけて行う特別プログラムとしてご相談を頂きました。

そこでまずは、「社会人レベルのコードを書ける」とはどういう事か、「学生レベル」や「趣味レベル」との違いはどこにあるのかという事から考え直してみることにしました。
その結果、以下のようなトピックが挙がりました。

  • 「チームでプログラムを開発する人」としての能力
    • 既存のコードを自然な形(周囲のコードに馴染む形)で変更する能力。
    • 発生した問題の原因を調べる能力。
    • 他の人が見て分かりやすい(他の人がメンテナンスしやすい、他の人が調査しやすい)コードを書く能力。
    • テストしやすいコードを書く能力。
  • 「ビジネスマン」としての能力。
    • 納期に間に合わせる能力。
    • 予算(想定工数)の範囲内に収める能力。
    • 「納期が厳しくてメンテナンス性を諦めないといけない」のような二律背反の状況で、物事の優先順位を付けて判断する能力。

ただ、研修の実施日程を考慮すると、これら全てを扱うことはできません。
そこでご依頼主さまと相談し、この中で特に「既存のコードを自然な形(周囲のコードに馴染む形)で変更する」という話題に焦点を当てた内容とする事にしました。

課題の選定

研修にあたっては「課題のための課題」は避けたいという思いがありました。やるなら、そこまでに実施された研修の内容を踏まえて、なるべく日常的に実プロダクトで行う事に近い事をしてもらうのが良いはずです。

ただ、ご依頼主さまの会社内の機密情報に基づく内容とした場合、資料の二次利用は困難になります。
当社の期待としては「成果をフリーソフトウェアにしたい」「実施した事の内容を公開して、同様の事でお悩みの会社さまに見付けていただく」という事があるため、できれば公開可能な情報のみに基づく資料としたい所でした。

事前に伺った情報では、ご依頼主さまの事業においてRuby on Rails の大規模なコードベースを長期わたって運用されているとの事でした。
実際の業務では、そのようなプロダクトに対し変更を行っていく機会がそれなりに見込まれます。
そこで、著名な公開プロジェクトの中でも条件が近い物として、Redmineを題材にすることにしました。

また、Redmineに対してどのような変更をするかに関しても、題材に使いやすそうな規模の既知の要望が見つかりました。
目標を「この要望を実現するパッチを作る事」に置けば、既存の大きな実装に対して変更を行う際の一通りの作業を体験できるはずですし、進度次第では成果をそのまま公開でき、ご依頼主さまの会社自体の社外向けのアピールにも繋がります。

この要望については、既に同様の事を実現するプラグインが存在しているというのも好都合でした。
実装が難しい部分はプラグイン自体のコードをそのまま流用する事で手間を省けるため、「既存のコードに馴染むように機能を組み込む」という点に集中して取り組めるはずです。

以上の事を踏まえ、研修当日にソースコードのダウンロードやHTTPサーバーのセットアップ等の本質的でない部分で手間取る事がないよう、必要なソフトウェアやソースコードなど一式を用意した状態のVagrant Boxを準備し、研修当日に臨みました。

座学:既存のコードを変更する時の心得

当日の研修開始直後からいきなり「さあ、やってみましょう」で作業を始めてしまうと、参加者の皆さんが途方に暮れてしまうのは目に見えています。
そこで、研修当日の内容は2部構成とし、まず最初に「既存のコードに変更を行う」という作業の一般的な流れを説明する座学の時間を設け、実作業を伴う演習はその後に行う事にしました。

ここで用いたプレゼンテーション資料はgitlab.com上のリポジトリにて公開しています
また、スライドはSpeaker Deckでも見る事ができます。

新機能の追加にせよ不具合の修正にせよ、継続的に運用されるサービスに関わる開発業務のかなりの部分は「既存のコードの変更」となります。

既存のコードに合わせた変更を行う・既存のコードに馴染むコードを書くためには、既存のコードを読んで内容を把握する必要があります。
しかし、膨大な量のコードを頭から1行ずつ読んでいくというのは非現実的です。
業務でコードを書く人は常に時間に追い立てられているなので、見るべきポイントを押さえて少しでも効率よくコードを読みたいものでしょう。

そのため、座学の前半では「どんな情報を読み取るためにコードを読むのか」という点に焦点を当てて、「コーディングスタイル」「設計方針」「背景事情」のそれぞれを読み取るために注目するべきポイントやコードの読み進め方を説明しました。

座学の後半では、コードを読んで得た情報を元にして変更を行う時の具体的な流れとして、「変更計画の立て方」「(TDDでの)自動テストの書き方」「自動テストをしやすい形での実装の仕方」などについても説明しました。
ただ、前半の話題に比べるとこれらの点は一般化が難しく、一般的に語れるレベルの話となると抽象度が高すぎて研修の趣旨にそぐわなくなると予想されたため、後半の説明は軽めにして、なるべく長い時間を実際にコードと向き合う演習に充てられるようにする事にしました。

演習:Railsアプリケーションへの機能追加

休憩を兼ねた昼食を挟んだ後は、研修の第2部であり本題となる演習の時間としました。

教材のセットアップ

最初にgitlab.com上のリポジトリで公開されている資料を皆さんの手元にcloneしていただき、記載された手順に則って教材用の環境をセットアップしていただきましたが、ここで1つ多くの方が躓かれていた点がありました。

この手順でセットアップした環境はVagrant Box(実体はVirtualBoxの仮想マシン)となり、その中に置かれたファイルを編集するためにはSSHでリモートログインするなどの手順を踏む必要があります。
今回の研修に参加された方々のほとんどは、それまでの研修ではVisual Studio CodeやAtomなどのテキストエディタを使われていたことから、ここで「VS CodeやAtomでVagrant上のファイルを閲覧・編集する」という事を実現するための設定が必要となっていました。

この時は、手間取っていた人には既にうまく行っていた人が情報を共有するという形で相互に助け合ってもらい、本題に取り組む段階に無事進む事ができました。

コードの読解と設計の把握、実際の変更

準備が整った事でようやく本題に着手です。

前述の通り、今回の課題はRedmineでコメントの変更があった時にメールで通知できるようにするという物です。
これについて、とりあえずソースコードを検索して読んでみる人、送信されるメールのテンプレートを起点に探してみる人、怪しそうな所をとりあえず変更してみる人、同様の事を実現する既存のプラグインを実際に組み込んでその動作の様子を追う人など、各人各様のアプローチで課題に取り組まれている様子が窺えました。

ただ、中には、いきなり大規模のソースコードに触れた事で混乱が生じたためか、どこから切り崩していけばよいか判断に迷って途方に暮れている人も見られました。
そのような場合の取り組み方について座学でいくつか方法を紹介したつもりではあったのですが、知識として知ったという事と、それを実際の場面に適用できるという事の間には隔たりがあるのかもしれません。
そのため、室内を巡回しながら、戸惑っている様子が見られた方には個別に声をかけて、「座学で述べたこの話をここで活かすと、こういう風に調べていける」という補足説明をしていく事にしました。

そうこうするうちに実装案を思いつく所に到達した人も現れてきて、講師が意見を求められたため確認した所、Controllerのレイヤで目的を実現するという方針を取っていました。
このアプローチは、目的とする機能を実現できているという点は良いのですが、Redmineの他の部分に自然に馴染むかという点では、違和感が大きいというのが講師視点での印象でした。
というのも、Redmineには既に「チケット自体の変更をメールで通知する」という仕組みが実装されており、それらとはかなり異なるアプローチでの実装と言えます。
将来的にRedmineプロジェクトに変更を取り込んでもらうという場面までを考慮に入れると、このように「既存の実装に馴染まない」「収まりの悪い」変更内容は、マージに至らない事が多々あります。
そのため、その人に対してはアプローチの再検討を試みてはどうかと講師から促しました。

実装の進度には受講者同士の間で差が生じていたため、進度が高い人には適宜、他の詰まっている人にノウハウや情報を共有するという事もしてもらいました。
何かを自分で人に説明してみるというのは、説明対象についての理解を深める上で非常に効果的です。
これは、筆者がワークショップに関わる際に意識している点の1つです。

より小さい規模での実践の機会も

研修の実施中、思いがけない出来事もありました。
参考になる実装例として紹介した既存のプラグインの動作を確認していた人が、その動作に奇妙な点があると気付いたので深掘りしてみた所、なんとプラグインの方に不具合があるという事が明らかになったのです。

対象とするソフトウェアの規模的には想定から外れますが、この問題に取り組む事は、「既存のソフトウェアに変更を加える」という本旨の部分では研修の趣旨に沿った物である言えます。
そのため、講師の側で判断して、その人には当該プラグインに対するフィードバックを行ってみてもらう事にしました。

こちらは、最終的にはプルリクエストの形で変更を提案し、いくつかのやりとりを経て微修正後に無事マージされるに至っています。

成果の報告とまとめ

研修は1日の予定でしたが、当日に台風が接近しており帰宅困難となる恐れがあったため、後半の演習を途中で打ち切り、日を改めて続きを実施しました。
初日に引き続いての作業の後、研修の最後の30分はそこまでの成果の報告会としました。

報告会では複数の参加者から、既存の実装を読んでメソッドの呼び出し元を辿り処理の流れを追う事の意義を実感したという声が聞かれました。

Railsというフレームワーク自体の理解が浅かった参加者からは、メソッドの呼び出し関係を辿るのに苦労したという声も聞かれました。
フレームワークを使ったアプリケーションの処理を追う上では、そのフレームワークの一般的な作法の知識が必要になります。基本的にはフレームワーク自体のチュートリアルを一通り実施して、そのフレームワークの作法に触れた上で、実際に作られたプロダクトに触っていくのが望ましいでしょう。

メールの送信に関わっていそうな部分を当てずっぽうで変更してみたものの期待した結果を得られず、後になってみると見当違いの所ばかりを見てしまっていた、という反省の声もありました。コードの全体を把握するのは時間がかかりすぎるという状況においては、ある程度は推測に基づいて調査範囲を絞り込む必要がありますが、完全な当てずっぽうでは、当たるか当たらないかは運次第になってしまいます。仮説を立てる際は、「ここで表示されているメッセージがここで定義されているので」や「必要な処理がここで呼ばれているので」のように、その仮説の根拠を説明できるようにしておくと良いでしょう。

進捗が良かった参加者の中には、処理を追ってControllerの該当部分を特定できたという人(プラグインを参考に処理を組み込む方法を検討している所で時間切れになった)や、実際に動作してコメントの変更内容の差分がメール送信される所にまで到達した人もいました。この方々には思考の整理も兼ねて、どのように調査してその実装に至ったのかについて他の参加者に情報を共有する事をお薦めしました。

また、成果発表の時間の最後には模範解答例として、講師がこの作業を行った際の手順もご紹介しました。この時は担当した講師にRailsとRedmineの知見があったため、クラスの定義箇所に見当を付けて探すなど、効率よく調査を行えたケースとなっていました。

今回の研修で使用した課題そのものは一般に公開可能な内容のため、研修の終了後に自主的な取り組みとして、以下のような事にも挑戦してみて頂きたい旨ご案内しました。

  • 差分を出力する処理について、Redmine独自の処理と、プラグインが使っているGemのどちらを使うべきか(あるいは全く別の選択肢を取るべきか)の検討。
  • 通知設定に基づいて、自分自身の変更を送らないようにする方法の検討。
  • 実際にRedmineプロジェクトの当該チケットにパッチを提出してみる。

まとめ

以上、Supershipさまにて実施した「既存のコードを変更するノウハウ」を学ぶ研修の様子についてご報告しました。

当社はフリーソフトウェアとビジネスの両立という理念に則り、フリーソフトウェア・OSSの開発に長く関わる中で培ってきた技術的な知見をビジネスに繋げる形で、開発やテクニカルサポートなどの業務を行っています。
その中には「知見そのものを伝える事」も含まれており、過去にはSEゼミのリーダブルコード勉強会アジャイルアカデミー「実践リーダブルコード」などのようなワークショップ形式のイベントの開催に度々携わってきました。また、テクニカルサポートやコンサルティングのような形で顧客企業内のOSS推進の取り組みを支援させて頂いた事例もあります。
今回の研修も、その流れの中にある事例の1つと位置付ける事ができます。

本件の研修と同内容の研修や、その他のフリーソフトウェア・OSSの開発に実際に関わる形での研修を社内で実施したいとお考えの会社さまは、ぜひお問い合わせフォームよりお問い合わせ下さい。

2018-11-12

Debianで医用画像を閲覧するためのアプリケーション三選

はじめに

最近骨折を経験した林です。
病院を紹介してもらうときに、紹介状とともに医用画像を含むメディア(CD-ROM)を渡されることがあります。
メディアには医用画像を閲覧するための専用のビューワーが付属していたりしますが、そのビューワーがWindowsでのみ動作するアプリケーションだったりするとそのままではDebianで閲覧できません。
そこで、今回はDebianでこのような医用画像(いわゆるDICOMと呼ばれるフォーマットのもの)を閲覧する方法をいくつか紹介します。

閲覧時のサンプルの画像には日本画像医療システム工業会:DICOMの世界を利用しました。

aeskulap

Aeskulap - DICOM Viewer

2005年から開発されているDICOMビューワーです。
公式には2007年の0.2.1が最新ですが、Debianではベータ版である0.2.2-beta2がパッケージ化されています。
使ってみたことはないのですが、ネットワークを経由してPACSとよばれるノードからDICOM画像を検索する機能も備えているようです。

画像によっては埋め込まれている患者名の扱いによって落ちる不具合があります。この点についてはすでにアップストリームにバグ報告しています。

[Aeskulap-users] Stack smashing bug when invalid patient name parsing

インストール方法
% sudo apt install aeskulap
起動方法

aeskulapコマンドを実行するとアプリケーションを起動できます。

% aeskulap
医用画像の閲覧方法

メニューの[ファイル]-[開く]から対象のファイルを開くと閲覧できます。拡大といった操作はサポートされていません。

aeskulapによる閲覧

pixelmed-apps

PixelMed Publishing, LLCによって開発されているJava DICOM ToolkitにDICOMビューワーが含まれています。
公式には20181018が最新ですが、Debianでは20150917がpixelmed-appsとしてパッケージ化されています。
DICOMビューワーのほかにもいくつかツールがバンドルされています。

インストール方法
% sudo apt install pixelmed-apps
起動方法

pixelmed-appsパッケージに含まれているdicomimageviewerコマンドを実行するとアプリケーションを起動できます。

% dicomimageviewer
医用画像の閲覧方法

[Local]タブの[File]ボタンをクリックして対象のファイルを開くと閲覧できます。拡大といった操作はサポートされていません。

dicomimageviewerによる閲覧

DICOMscope

OFFISによって開発されているDICOMビューワーがDICOMscopeです。
公式には3.5.1が最新ですが、Debianでは3.6.0のパッケージが提供されています。

インストール方法
% sudo apt install dicomscope
起動方法

dicomscopeコマンドを実行するとアプリケーションを起動できます。

% dicomscope
医用画像の閲覧方法

[Load image file]ボタンをクリックして対象のファイルを開くと閲覧できます。Zoom機能があるので、細部を拡大してみてみたいときにはおすすめです。

DICOMscopeによる閲覧

まとめ

今回は、Debianで医用画像を閲覧するためのアプリケーションを三つ紹介しました。
もし医用画像を閲覧する機会があれば(ないほうがいいのですが)ぜひ使ってみてください。

2018-11-13

2018年末にApache Arrow関連の話を聞けるイベント

Apache Arrowの開発に参加している須藤です。

2018年11月、12月にApache Arrow関連の話を聞けるイベント(私がApache Arrow関連の話をするイベント)がいくつかあるので紹介します。どれも開催場所は東京です。

2018-11-17(土)13:30- RubyData Tokyo Meetup

11月17日(今週の土曜日!)開催のRubyData Tokyo MeetupでApache Arrowの話をします。Apache Arrow全体の話というよりRubyに特化したApache Arrowの話をします。RubyでApache Arrowを使いたいという人に向いているイベントです。

このイベントでは最近Apache Arrowのコミッターになった@shiro615の話も聞けます。Apache Arrowのコミッターになるまでの話です。

このイベントではApache Arrow以外にもディープラーニング・高速行列演算・可視化をRubyで実現するための話を聞けます。Rubyでデータを扱いたい人にオススメのイベントです。

2018-12-04(火)18:00- Apache Arrow - データ処理ツールの次世代プラットフォーム

12月4日(火)開催のApache Arrow - データ処理ツールの次世代プラットフォームでApache Arrowの話をします。こちらはRubyに特化せずにApache Arrow全体の話をします。基本的なことから話すので、Apache Arrowの名前を聞いたことがあってよさそうな雰囲気はするけどまだよく知らない、という人でも大丈夫です。

質疑応答の時間が長めにあるので、Apache Arrowの情報収集をしたいという人はこのイベントがオススメです。

今回紹介するイベントの中で唯一平日夜の開催なので、平日の方が参加しやすいという人はこのイベントがオススメです。

2018-12-08(土)13:30- Apache Arrow東京ミートアップ2018

12月8日(土)開催のApache Arrow東京ミートアップ2018でApache Arrowの話をします。このイベントでもApache Arrow全体の話をします。12月4日のイベントとの違いは開発者向けの話かどうかです。このイベントではユーザー寄りというより開発者寄りの話をします。

このイベントの目的は開発に参加する人を増やすことです。対象プロダクトはApache Arrowおよび「Apache Arrowを活用するとうれしくなりそうなプロダクト」もろもろです。そのため、開発者寄りの話になります。

このイベントではApache Arrow本体の話だけでなく、次の「Apache Arrowを活用するとうれしくなりそうなプロダクト」の話もあります。

Apache Arrowおよびこれらのプロダクトの開発に参加したい人にオススメのイベントです。

このイベントの前半はApache Arrowおよびこれらのプロダクトの話を聞く時間で、後半は実際に開発に参加しはじめる時間です。参加しはじめるというのは、パッチを書きまくるというわけではなく、どこから始めるのがよさそうかを相談しはじめるという意味です。

このイベントにはApache Arrowの開発に参加している人(私も含む)、Apache Sparkの開発に参加している人、Red Data Toolsに参加している人、pandasの開発に参加している人などすでに開発に参加している人も多く参加します。そのような人たちと相談してイベントの時間内で最初の一歩を踏み出せるようにします。

さらに、このイベントの後にいくつかフォローアップイベントを開催します。こうすることで、このイベントで開発に参加し始めたあとも継続して開発できることを狙っています。

まとめ

2018年11月、12月に東京でApache Arrow関連の話を聞けるイベントを3つ紹介しました。それぞれ少しずつ趣向が違うので、適切なイベントを選んで参加してみてください。

2018-11-14

PhabricatorでのFirefoxへのパッチ投稿方法

以前の記事で、Firefoxへのパッチ投稿の一手段としてMozReviewという仕組みがあることと、その使用方法を紹介しました。しかし、その後すぐにMozillaのコードレビューシステムがPhabricator完全移行してしまい、MozReviewの運用は止まってしまったようです。記事公開時点ではまだMozReviewを使う開発者が大半のように見えていたため、MozReviewの運用停止が近づいているということを把握できていませんでした。

今回は改めてPhabricatorでのパッチ投稿を実践してみたため、その方法を紹介します。

セットアップ

本記事で紹介するセットアップ方法はMozilla Phabricator User Guideを元にしています。詳細はそちらを参照して下さい。

事前に用意するもの
  • BMO(bugzilla.mozilla.org)のアカウント
    • まだBMOアカウントを取得していない場合は https://bugzilla.mozilla.org/createaccount.cgi で作成します。
    • Real nameにはYour Name [:ircnick]のような形で末尾にニックネームを付けておきましょう。
      • Phabricator上ではこのニックネームがユーザー名として表示されます。
      • ただし、既に存在するニックネームは使用できません。
    • Phabricatorにアクセスするためには、Bugzilla側で2段階認証を有効化しておく必要があります。
  • mozilla-centralのワーキングコピー
    • Firefoxへのフィードバックの仕方:Windows編等を参考に、hg clone https://hg.mozilla.org/mozilla-centralで取得して下さい。
    • 本記事ではバージョン管理システムとしてMercurialを使用する場合のみを対象とします
      • PhabricatorはGitでも使用できるようですが、本記事では対象としていません。
Phabricatorへのログイン確認

MozillaのPhabricatorは https://phabricator.services.mozilla.com/ でアクセスできます。
Loginボタンを押して、ログインを試みます。

Phablicatorログイン

認証はBugzilla側で行われるため、以下のようなボタンが表示されます。

Bugzilla認証

最初のアクセス時には、Bugzilla側での認証成功後、Phabricator上でのアカウント登録を促されます。

Phabricatorユーザー登録

アカウントの登録を完了させて、Phabricatorを使用できる状態にしておきましょう。

Arcanistおよびmoz-phabのセットアップ

パッチをコマンドラインから投稿するためには、ArchanistというPhablicatorのコマンドラインツールと、そのラッパーコマンドであるmoz-phabをインストール必要があります。

Archanist

Archanistのインストール方法は以下に記載されています。

Ubuntuの場合は比較的簡単で、依存パッケージをインストール後、ArchanistのGitリポジトリをcloneして、パスを通します。

$ sudo apt install php php-curl
$ mkdir somewhere/
$ cd somewhere/
$ git clone https://github.com/phacility/libphutil.git
$ git clone https://github.com/phacility/arcanist.git
$ export PATH="$PATH:/somewhere/arcanist/bin/"

Windowsの場合のインストール方法は以下に記載されています。

こちらについては筆者の手元で検証できていないため、本記事では省略します。

MozillaBuild を使用している場合の具体的な手順は以下の通りです。

  1. Visual Studio 2017 の Microsoft Visual C++ 再頒布可能パッケージをダウンロードし、インストールする。

  2. Windows版PHPのzipファイルをダウンロードし、展開した物をC:\PHPに置く。(検証はPHP 7.2の「VC15 x64 Non Thread Safe」と書かれている物で行いました)

  3. C:\PHP\php.ini-development の位置にあるファイルを C:\PHP\php.ini にコピーし、以下の通り編集する。

    • ;extension=php_curl.dll または ;extension=curl と書かれた行の行頭の ; を削除する。
    • ;extension_dir = "ext" と書かれた行を extension_dir = "C:\PHP\ext" に書き換える(行頭の ; を削除し、実際のパスを記入する)。
  4. MozillaBuild のシェル上で /c/PHP/php.exe -i | grep curl と実行し、結果に curl という行が含まれている事を確認する。

  5. Windows版Gitをダウンロードし、インストールする。

  6. GitBash を起動し、以下の操作で必要なツールをダウンロードする。

    $ mkdir ~/phabricator
    $ cd ~/phabricator
    $ git clone https://github.com/phacility/libphutil.git
    $ git clone https://github.com/phacility/arcanist.git
    
  7. MozillaBuild で以下のコマンド列を実行し、各ツールにパスを通す。

    $ echo 'export PATH=${PATH}:/c/PHP:${HOME}/phabricator/arcanist/bin:/c/Program\ Files/Git/bin' >> ~/.bash_profile
    $ echo 'export EDITOR=/usr/bin/vim' >> ~/.bash_profile
    $ source ~/.bash_profile
    

    MozillaのドキュメントにはテキストエディタとしてVim以外を使用する場合の手順も書かれていますので、他のテキストエディタを使いたい場合はそちらも併せて参照して下さい。

Ubuntu、Windowsのそれぞれの方法でArchanistのインストールが完了したら、APIキーを設定します。mozilla-centralのソースディレクトリ下で以下のコマンドを実行します。

$ arc install-certificate

表示されたURLをブラウザで開いてログインするとAPIキーが表示されますので、そのAPIキーをコマンドラインにコピー&ペーストすると、APIキーが取り込まれます。
APIキーは以下のようなJSON形式で~/.arcrcに書き込まれます。

{
  "hosts": {
    "https://phabricator.services.mozilla.com/api/": {
      "token": "xxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxx"
    }
  }
}
moz-phab

moz-phabのインストール方法は、moz-phabREADME.mdに記載されていますので、そちらを参照して下さい。

単にmoz-phabコマンドをパスの通ったディレクトリにコピーし、実行権限を付けるだけで良いようです。以下はその操作例です。

$ cd ~/phabricator
$ git clone https://github.com/mozilla-conduit/review.git
$ echo 'export PATH=${PATH}:${HOME}/phabricator/review' >> ~/.bash_profile
$ source ~/.bash_profile

レビューリクエストの作成

以前の記事で紹介したように、Firefoxで何かパッチを投稿したい場合は、全てBugzilla上の該当Bugを起点に作業を行います。何か新機能を追加したり、不具合修正を行いたい場合は、まず該当するBugが既に存在するかどうかを確認し、無い場合は新たに自分で新しいBugをfileします。

該当Bugでソースコードに対して何か変更を行って、いざPhabricatorにパッチを投稿したいという状況になった場合、まずはMercurialで手元のリポジトリに変更をコミットします。

$ hg branch fix-bug-xxxxx
$ hg commit

このとき、コミットメッセージの形式に注意しましょう。具体的には以下のような形式にする必要があります。

Bug [Bug番号] - [Bugの概要(一行)]

以下、Bugの詳細についての記述...

Mercurialでリモートリポジトリにpushする際、上記のコミットメッセージのBug番号から自動的にBugzillaの該当Bugにパッチが投稿されます。

また、末尾にr?ircnickという形式でレビュアーを指定すると、push後に自動的に該当レビュアーにレビューリクエストを投げることもできます。このレビュアーの指定は、パッチを送信した後にPhabricatorのWeb UIから行うこともできますので、必ずしもコミットメッセージに含める必要はありません。

以下に、筆者が実際にパッチを投稿した際のコミットメッセージを示します。

Bug 1502786 - Break cycle between PureOmxPlatformLayer and OmxDataDecoder r?jya

OmxDataDecoder, OmxPromiseLayer and PureOmxPlatformLayer consist
circular reference by RefPtr, and no one sever the reference. As a
result their refcount never decrease to 0.
This commit sever it at PureOmxPlatformLayer::Shutdown() which is
called by OmxDataDecoder.

詳細な議論はBug番号から辿ることができるため、コミットメッセージには必ずしも詳細な記述は必要ないようです。有った方が好ましいとは思いますが、慣れていない場合には、まずはBug番号と一行サマリを適切に記載することに注力すると良いでしょう。

ローカルリポジトリへのコミットが完了したら、リモートリポジトリにsubmitします。

$ moz-phab submit

submitが完了した後、先ほどコミットした内容をhg exportで確認してみると、以下のようにDifferential Revision:という行が追加されていることに気が付きます。

# HG changeset patch
# User Takuro Ashie <ashie@clear-code.com>
# Date 1541472583 -32400
#      Tue Nov 06 11:49:43 2018 +0900
# Node ID 25c8e78baa9aa8189ca7026d7ac7868c69d483f3
# Parent  9f9a9234959f114825f58beee0cffbab82d0bb29
Bug 1502786 - Break cycle between PureOmxPlatformLayer and OmxDataDecoder r?jya

OmxDataDecoder, OmxPromiseLayer and PureOmxPlatformLayer consist
circular reference by RefPtr, and no one sever the reference. As a
result their refcount never decrease to 0.
This commit sever it at PureOmxPlatformLayer::Shutdown() which is
called by OmxDataDecoder.

Differential Revision: https://phabricator.services.mozilla.com/D10028

...

この行はレビュー結果を受けてパッチを修正する際に必要になります。また、この行に記載されているURLをブラウザで開くと、Phabricator上でレビューリクエストを参照することができます。以後、レビュアーとのやりとりはこのページで行うことになります。

パッチの修正

レビュアーによってパッチがレビューされ、Phabricator上で修正箇所を指摘されたら、パッチを修正して再度Phabricatorにsubmitすることになります。この際、同一のレビューリクエストに対する修正であることを指定するために、先ほどと同じDifferential Revisionをコミットメッセージに含めてhg commitし、moz-phab submitします。

Mercurialでのパッチ管理方法は本記事のスコープ外のため割愛しますが、パッチ(コミット)が1つのみで、ローカルリポジトリに過去のバージョンが不要である場合、もっとも簡単な修正方法はhg commit --amendで前回のコミットをやり直す方法でしょう。この方法の場合、コミットメッセージは特に修正しなければ前回のままとなりますので、Differential Revisionも前回と同じものが使用されます。ローカルリポジトリの修正は上書きされてしまいますが、リモートリポジトリ上では過去のバージョンも管理され、その差分を確認することもできます。

Phabricator diffリビジョン

修正をsubmitしたら、Phabricator上でその修正に対応するコメントの「Done」にチェックを入れ、レビュアーのコメントに返信をします。この際も、最後にSubmitボタンを押すことを忘れないで下さい。なお、MozReviewの時とは違い、Phabricator上での会話が自動的にBugzillaにも投稿されるという機能は無いようです。

Bugzilla上でパッチを添付する場合とは事なり、Phabricator上でレビュアーの情報が紐付けられているため、変更の度に改めてレビュー依頼をし直す必要はありません。再度レビューしてもらえるのをおとなしく待ちましょう。

レビューが通ったら

レビュアーによってパッチに問題ないと判断された場合、以下のようにAcceptedのマークが付きます。

Phabricator Accepted

この状態になったら、パッチのランドが可能になります。Mozilla Phabricator User GuideのLanding Patchesの項によると、パッチのランドにはLandoというシステムを使うことを強く推奨するとなっていますが、mozilla-centralへのコミット権限が無い場合、このシステムを使用することはできません。実際に試してみたところ、以下のように弾かれてしまいました(筆者の権限はLevel 1)。

Landoエラー

コミット権限が無い場合は、これまでと同様に、Bugzilla側で「Keywords」欄にcheckin-neededというキーワードを付加しておいて、権限のある開発者にコミットしてもらえば良いようです。この際、Bugzilla側ではレビュー承認済みであるr=ではなくレビューリクエスト中であるr?のマークのままになっていることがあるようですが、Phabricator側でAcceptedになっていれば、構わずcheckin-neededにしてしまって問題無いようです。

Accept後のパッチ修正

単にAcceptされただけであればそのままランドしてしまえば良いだけですが、場合によっては「Acceptするけど、こことここだけは修正しておいてね」と言われる場合があります。この場合はAcceptedのマークは付きますが、パッチは修正して再度送信する必要があります。すると、マークが以下のように変わります。

Phabricator Other Diff Accepted

この場合、修正版のパッチを再度レビューしてもらう必要があるのか疑問に思うところでしょう。結論から言えば、特にレビューしてもらう必要は無いようです。自分で修正できたと判断すれば、そのままランドしてしまうことができます。ただし、指摘された箇所は全て「Done」にチェックを入れておきましょう。

Phabricator Doneフラグ

この時も、「Done」のチェック後にSubmitボタンを押す必要があります(チェックを付けただけでは送信されません)。

Backoutされたパッチの修正

一旦ランドされたパッチが自動テストの失敗によってBackoutされる事もあります。ここのような場合、Phabricator上のパッチは既に一旦Closedになってしまっているため、そのままでは修正を継続できません。

FAQによると、このようなケースでは以下の手順でパッチをReopenする必要があります。

  1. ページ最下部のコメント入力欄までスクロールする。
  2. 「Add Action...」のドロップダウンリストを開き、「Revision Actions」配下の「Reopen Revision」を選択する。
  3. 「Submit」をクリックしてアクションを確定する。

この操作を行うとパッチが「Accepted(レビュー完了済み)」の状態に戻りますので、hg commit --amendでパッチを修正してmoz-phab submitし、再度レビュアーの反応を待つ事になります。

まとめ

PhabricatorでのFirefoxへのパッチ投稿方法について紹介しました。

なお、本記事内で紹介した実例はBug 1502786: Memory leaks in OpenMAX PDMになります。以前Firefox本体にフィードバックしたOpenMAX IL対応パッチにバグがあることを発見したので、その修正を再度フィードバックしています。

元となるOpenMAX対応パッチについては、特にレビュアーを指定せずにとりあえずMozReviewで上げてみただけだったのですが、Mozillaの開発者の目に止まって勝手にレビューされ、本体にマージされるところまで進みました。やはりコードレビューシステムで登録しておいた方が開発者としてもレビューが捗るのかもしれませんね。

2018-11-15

ownCloud/Nextcloud関連プロダクトの翻訳に参加してみよう

最近ownCloud日本語翻訳チームのコーディネーターになってしまいました、結城です。

当社では長らく、業務のスケジュール管理にGoogleカレンダーを使っていていました。しかし、フリーソフトウェアを推進するという社の理念からすると、せっかくownCloudのような自由なライセンスの実装があるのにそちらを使っていないというのは望ましくありません*1。そこで最近、ownCloudの自社運用を開始しました。

ownCloudおよびその派生版のNextcloudは、DropboxやGoogle Driveなどのようなオンラインストレージを自社でホスティングするためのソフトウェアですが、アプリ(プラグイン)で様々な機能を追加でき、中でもカレンダー機能はGoogleカレンダーと似た感覚で使う事ができます。

ただ、実際に運用してみると細かいところで不満や不具合が見つかりました。そのため、当社では現在の所全社的に、業務と並行してそういった問題を解決していくためのフィードバックに取り組んでいます。

この記事ではその一つとして、翻訳への取り組みの紹介を兼ね、Transifexを使った翻訳作業の実際の流れや注意点を解説します。

翻訳に参加するまでの流れ

ownCloudのカレンダーを利用していると、イベントの繰り返し条件の設定画面に未訳箇所があるという点が目につきました。そこで、まずはできる所からという事で、翻訳に参加してみる事にしました。

OSS Gateワークショップでは「公式サイトからフィードバック先を見付けよう」という方針を取っていますが、今回は横着して、まずGoogleで「ownCloud translate」と検索してみました。すると、以下のページが最上位にヒットしました。

これはownCloud公式サイト上にある、翻訳コミュニティへの参加を呼びかけるドキュメントです。これによると、ownCloudの翻訳を行うにはTransifex上のownCloudチームに参加するように書かれています。

これにはまずTransifexのサイト上でアカウントを作成する必要があります*2が、この時「やりたいこと」の項目で「既存のプロジェクトに参加」を選択します。

(スクリーンショット:目的の選択時に「既存のプロジェクトに参加」を選ぶ)

その後、使用言語としてJapanese (ja)Japanese (Japan) (ja_JP)をそれぞれ追加します。Japanese (ja)は一般的な意味での「日本語」を、Japanese (Japan) (ja_JP)はその中でも特に「日本地域で使われる日本語」を指します*3

(スクリーンショット:言語の選択時に「Japanese (ja)」と「Japanese (Japan) (ja_JP)」を選ぶ)

アカウントができたらプロジェクト選択の画面に遷移しますが、この画面は一旦閉じて、ownCloudチームのダッシュボードを訪問します。

(スクリーンショット:ownCloudチームのダッシュボード)

「チームに参加」というボタンが表示されていますので、これをクリックすると、翻訳プロジェクトに参加する言語の選択が表示されます。「Japanese (Japan)」を選択して「参加」ボタンを押して下さい。

(スクリーンショット:言語選択の画面)

送信が完了すると、「Japanese (Japan) (ja_JP) 語に参加しました。 」という表示が出て、参加申請の承認待ちの状態になります。この後、プロジェクトの管理者がユーザーのプロフィールや活動実績などから判断して参加を承認してくれれば、晴れて翻訳作業に参加できるようになります。

実際の翻訳作業の進め方

参加申請が承認された後で再びownCloudチームのダッシュボードを訪れると、画面の左側にメニューが出るようになっており、また同時に、画面右側のボタンが「翻訳」に変化しています。

(スクリーンショット:参加承認後のダッシュボードの画面)

「翻訳」ボタンをクリックすると、言語とリソース(翻訳対象のアプリケーション)を選択する画面に遷移しますので、画面の指示に従ってそれぞれを選択すれば、翻訳の編集画面に遷移します。

(スクリーンショット:翻訳開始時のナビゲーション)

翻訳の編集画面(Transifexエディタ)では、以下の流れで翻訳を行います。

(スクリーンショット:翻訳作業の流れ)

  1. 未翻訳の文字列を選択
  2. 未翻訳の文字列一覧から、翻訳したい物を選択
  3. 選択した文字列が「未翻訳の文字列」欄に表示された事を確認
  4. 「翻訳文を入力してください」欄に訳文を入力
  5. 「翻訳を保存」ボタンで訳文を保存

保存された訳文は「レビュー待ち」という状態になり、レビュー権限がある人のレビューを通過すれば、その訳文は実際の製品のリリース版に含まれるようになります。

誤訳をしないために気をつける事

ただ、この時には淡々と機械的に訳していけば良いというわけではありません。翻訳対象の文字列はフレーズ単位で切り出されている事が多いため、同じ単語でも文脈によって適切な訳は変わってきます

例えば、本記事執筆時点では、カレンダーの予定の編集画面でリマインダーを詳細設定しようとすると、以下のような奇妙な選択肢があります。

(スクリーンショット:誤訳の例)

「秒」と「時間」の間に「最低」という語が登場しています。これは英語では「min」(minutesの略)となっていた部分で、正しい訳は「分」です。「min」を文脈を踏まえないまま「minimum(最低、最小)」の略と誤解してしてしまったために発生した誤訳という事になります。このような事が発生しないよう、特に短いフレーズを翻訳する時は、実際に運用中のownCloudの画面上でその文字列がどのように表示されているかをまず確認する必要があります。

とはいえ、短い文字列が画面内のどこに登場しているかを適切に探り当てるのはなかなか大変です。そこでヒントになるのが、訳文の入力欄の下にある「文脈」タブの中の「出現場所」という情報です。

(スクリーンショット:「文脈」タブ内の「出現場所」)

リソースによってはこのメタ情報が登録されている場合があり、これを参考にすると、原文が登場している部分の前後のソースコードを一緒に見る事ができます。この事例でもjs/app/controllers/valarmcontroller.js:61が出現位置であると記載されており、実際の当該箇所を見ると、前後の他の文字列やtimeUnitReminder〜という名前付けから、「これは最大・最小という文脈ではなく、リマインダーの時間の単位の文脈である」という事が読み取れます。

(スクリーンショット:「min」の出現場所)

文脈を確認しやすくするFirefoxアドオンの使用

いざ文脈を確認しようとした時に躓く点があります。それは、出現位置の情報はリンクになっておらず、Transifexのサイト上からは辿れないという事です。

筆者が調べた範囲では、これらの「出現位置」の情報はプロジェクト運営者がリソースを登録した時に任意の形式で付与するメタ情報で、Transifexのシステムには統合されていないようでした。そのため、対応するプロジェクトのソースコードがどこで公開されているかは自分で調べなくてはいけません。また、場所が分かったとしても、Transifexの画面を表示しているブラウザとソースコード管理ツールや端末の画面とを行ったり来たりする羽目になるため、作業効率が良いとは言い難いです。

そのため、翻訳の作業を補助するためのツールとして、Firefox用のアドオンを開発・公開することにしました。

Firefoxにこのアドオンをインストールすると、Transifexのページ上での右クリックメニューから「この翻訳の出現場所を開く」を選択するだけで、画面上に記載された「出現場所」の情報を自動的に検出し、GitHubなどのWebサイト上のリポジトリの対応する位置を新しいタブで開けるようになります。

(スクリーンショット:「Transifex Open Occurrences(出現場所を開く)」が追加したコンテキストメニュー項目)

Transifex上のプロジェクトとソースコードが公開されているWebサイトとの関連付けは、手動で作成したリストに基づいています。本記事執筆時点では、ownCloudチームの各プロジェクトのほとんどのリソースに対応する情報はあらかじめビルトインの情報として登録済みとなっています。

翻訳が難しいケース

前述のような単純な誤訳は元の語の出現位置を確認すれば防げますが、それだけではどうにもならない場面もあります。例えば、本記事執筆時点ではカレンダーの予定の編集画面でリマインダーの詳細設定と繰り返しの詳細設定において以下のような訳があります。

(スクリーンショット:同じ原文が異なる文脈で登場している例)

リマインダーの設定では「前」の次の選択肢が「回数で指定」となっていますが、これは原文ではそれぞれ「before」と「after」になっています。何故このような訳があてられているかというと、繰り返しの設定で終了条件を設定する箇所の「after N event(s)」の「after」に対して設定された「回数で指定」という訳文が使われているためです。

この問題は、多くのソフトウェアで表示文字列の国際化に使われている仕組みである「gettext」(およびそれを参考にした各言語のライブラリ)の仕様上の欠点に由来します。gettextは原文をそのままコードの中に埋め込んでおき、原文の文字列そのものをキーとして訳文を対応付けるという仕組みです。そのため、同じフレーズの原文が複数の異なる文脈で使われていると、訳文もそれに引きずられて、それぞれの文脈に対して同じ物しか設定できないのです。

このような問題が起こらないよう、gettext形式で国際化に対応するソフトウェアを作成する場合、可能な限り同じ原文が異なる文脈で使われないように気をつける必要があります。あるいは、gettext形式ではなく、同じ原文に対して異なるコンテキスト情報を持たせられる仕組み*4に切り替えるという方法もあります。この事例も本来的には、そのようにカレンダーアプリの実装を訂正することが望ましいと言えます。

翻訳コミュニティへの参加案内の後半を見ると、翻訳の元になる原文の側を変更したい場合は各プロジェクトのGitHub上のIssue Trackerに報告をするよう書かれていましたので、本件についても改善案を起票してみました。また、暫定的ではありますがそれぞれの場面に共用できる訳語として「後」をあてるように変更しました(これなら「前/後」「後 N 回」となるため、まだ違和感は少ないでしょう)。

用語を統一しよう

誤訳とは違った部分で気をつけなければいけない点として、「用語の統一」があります。同じ英単語に対して同じ文脈でも複数の訳語(同義語や、「ユーザ」と「ユーザー」のような音引きの有無の違いなど)がある場合、それぞれの箇所で異なる訳語を選んでしまうと混乱の元になります。

Transifexの翻訳画面で訳語の入力欄の下の「用語集」タブを選択すると、原文に含まれている各単語について、これまでに登録された訳語を一覧表示することができます。

(スクリーンショット:用語集を表示した所)

原文に用語集に登録された単語が含まれていて、訳文でそれに対応する訳語が使われていないと、編集画面上に警告が表示されます。その場合は用語集を確認して、なるべくそこに載っている訳語を使うようにしましょう。また、新登場の語句に新しい訳語を割り当てた場合には、その情報を用語集に忘れずに登録しておきましょう。

とはいえ、このシステムも完璧ではないため、杓子定規に警告が表示されてしまう場合もあります。例えば「Delete」という単語について「削除する」という訳語が割り当てられている時に、ボタンのラベル文字列としては一般的には「削除」と体言止めする事が多いですが、そのように訳すと「削除する」という訳語が含まれていないと判定されてしまう場合があります。

(スクリーンショット:訳語の警告が出ている様子)

また、原文で「event(s)」のように単数形か複数形かあやふやな物を括弧書きで表している場合に、訳語を「回」とあてると「括弧書きに対応する部分が無い」という警告がなされる場合もあります。文脈によっては直訳ではなく意訳した方が分かりやすく自然になる場合もあるという事も考慮すると、警告の内容が常に絶対的に正しいとは限りませんので、あくまで意図しないミスを防ぐための目安として考えるに留めると良いでしょう。

レビューする側にも回ってみよう

前述の通り、Transifexでの翻訳は訳文を保存した段階では「レビュー待ち」の状態となっており、レビュー権限がある人のチェックを経て初めて実際のリリースに反映されます。

今回翻訳に参加してみた所、ownCloudの日本語の翻訳については未訳の項目はそれほど多くなく、それよりも「レビュー待ち」の項目が数百件といった単位で存在している事が目につきました。これはレビューが滞っている事の顕れで、このままではどれだけ翻訳を行ってもその結果が実際のリリースに反映されないという事になってしまいます。このような場合には、レビューする側として協力することで、プロジェクトを円滑に進める手助けができるはずです。

筆者はFirefoxの翻訳コミュニティの活動の情報も耳に入れていた事から、上記のような翻訳の注意点についてはいくつか知見がありましたので、レビュアーに立候補してみる事にしました。

先のownCloud公式サイト内の翻訳協力者向けの情報を見ると、Transifexではユーザーの権限にいくつかの種類がある事と、議論はownCloud Centralというフォーラムで行うよう案内されていました。そこで、翻訳のレビュアーの自薦や他薦を受け付けているスレッドはないかと思い「translate」という単語でフォーラム内を検索してみましたが、残念ながらそれに特化した既存のスレッドはすぐには見付けられませんでした。

何か他の手段で連絡を取れないものかと思い関連情報を辿っていったところ、上記のページからリンクされているTransifexのサイト内でのユーザーの各権限の説明に、「管理者またはコーディネーターの権限がある人は、ユーザーの権限の変更とレビューができる」という旨の情報が書かれていました。そこで日本語の翻訳に関わっているユーザーの一覧を見てみたところ、「レビュアー」は一人もおらず「コーディネーター」は日本語話者らしき方が一人だけいるという状況でした。そこで、唯一のコーディネーターであった方宛にTransifex上で以下のメッセージを送ってみました。

Subject: コーディネーター/レビュワーへの自薦

はじめまして。Piro / 結城洋志と申します。
大変不躾な申し出で恐縮ですが、ownCloudプロジェクトの日本語の翻訳についてレビュー権限のあるロールを与えていただく事はできませんでしょうか?
最近自社でownCloudの運用を始めた際に、Calendarに一部未訳箇所があったことに気付き、翻訳に参加しようと思い立ちました。
その際、現在レビュー待ちの翻訳が非常に多い状態であることと、日本語のレビューを行える方がguitarmasakiさんお一人であるらしい事に気がつきました。
自分はownCloudの翻訳には今日からようやく関わり始めた状況で、貢献量もまだまだ微々たる物ではあります。
しかしながら、個人的にMozilla Firefoxの(主にアドオンの)コミュニティで長く活動しており、GUIの翻訳についてはある程度の知見があると自負しております。
ですので、ownCloudの日本語翻訳レビューにも貢献が可能ではないかと考えております。
以上、急な申し出で失礼かとは存じますが、ご検討いただけましたら幸いです。
https://docs.transifex.com/teams/understanding-user-roles/ を見たところ、言語ごとのユーザーのロール変更はコーディネーターの方に権限があるとの事でしたので、メッセージにて連絡させていただいております。)

すると早速お返事を頂けて、ロールをコーディネーターに引き上げていただく事ができました。

これで未レビュー部分のレビューを進める事ができるようになります。レビュー権限がある状態で翻訳画面を開くと、訳文の保存ボタンの横に「レビュー」というボタンが表示されるようになります。

(スクリーンショット:レビュー権限があるユーザーで翻訳画面を見た様子)

前述の翻訳上の注意点をチェックした上で「レビュー」ボタンを押せば、その翻訳はレビュー済みとなります。

まとめ

以上、Transifexを使ったownCloudの翻訳作業の進め方と、レビュー権限を得るための自薦の様子をご紹介しました。

今回の一連の過程でレビュー権限を得る事となりましたが、依然としてアクティブなレビュアーの数が少ない状態である事には変わりありません。そのため、独断ながらリスクヘッジとして、個人的に信頼のおける方や推薦を頂いた方など何名かのロールをレビュアーに引き上げさせていただきました。
現在はこの体制で未レビュー分の消化を進めていますが、文脈の正しさの検証をきちんとやるにはそれなりの手間がかかるため、すべて消化するまでにはまだまだ時間がかかりそうです。
レビュアー側として翻訳に協力できると自負される方がいらっしゃいましたら、Transifex上のpiroorユーザー宛に自薦のご連絡を頂けましたら幸いです。

また、Transifex上での翻訳作業を補助するFirefox用アドオンのTransifex Open Occurrences(出現場所を開く)は、ownCloud以外のプロジェクトで広く利用できる設計となっていますが、他のプロジェクトで使うためには「出現場所」を実際のリポジトリに紐付けるための情報を手動で登録する必要があります。
他のプロジェクトでお使いになる場合には、次の参加者の方が手動での登録をしなくても良くなるように、ビルトインの情報の追加手順を参考にしてマージリクエスト(GitHubでいうプルリクエスト)をお送り下さい。

「OSS開発に参加する」というと何か特別な事のように思う人もいるかも知れませんが、基本的には

  • ownCloudのカレンダーに未訳のままの箇所があるという問題があるので、翻訳に協力して解決する。
  • 翻訳に使うWebサービスが使いにくいという問題があるので、補助するツールを開発して解決する。
  • 翻訳のレビューが滞っていてせっかくの翻訳が反映されにくくなっているという問題があるので、レビュー作業に協力して解決する。

といった要領で、何か問題に遭遇した時にそこで諦めず、その時に自分の取れる手段で問題の解決を図っているという事になります。
皆さんも普段使っているソフトウェアやサービスで「ここ、何かおかしいな」「どうにかならないのかな」と感じた時には、ぜひとも、そのソフトウェアやサービスに適切にフィードバックする方法を調べて、問題を見過ごさずに解決を図るようにしてみて下さい。
(実際にフィードバックしてみるのは不安があるという方は、OSS Gateのワークショップへの参加も検討してみて下さい。)

*1 自由なライセンスの実装を積極的に使っていかないと、品質が改善されず、いつまでも「使い物にならない」状態が放置され、フリーソフトウェアがますます使われないという事になってしまうからです。

*2 Transifexのサイト上でアカウントを作成する以外にも、GitHubやGoogle、Twitterなどの認証情報を参照する形でもアカウントを作成できます。

*3 「_JP」の部分が地域を表しています。これは、「イギリス英語(en_GB)」と「アメリカ英語(en_US)」のように、分類上は同じ言語でも地域によって単語の綴りや使われ方に差異がある場合を想定した表記です。

*4 C言語やDjangoではpgettext、Rubyではp_()などがそれにあたります。

2018-11-16

LuaRocksにモジュールを登録する方法

Luaには、LuaRocksというパッケージマネージャーがあります。RubyでいうとRubyGemsのようなものです。
LuaRocksに登録されているモジュールは、luarocks install モジュール名で簡単にインストールできます。
Luaは言語に組み込まれている機能が少ないので、LuaRocksに登録されている外部のモジュールを使って機能を拡張することが多いです。

この記事では、自作したモジュールをこのLuaRocksに登録する方法を紹介します。
モジュールの登録に必要なことは以下の通りです。順に説明していきます。

  • LuaRocksのアカウント作成、APIキーの取得
  • luarocksコマンドのインストール
  • rockspecの作成
  • rockspecとモジュールのアップロード
LuaRocksのアカウント作成、APIキーの取得

作成したモジュールは、LuaRocksに公開されるので、まずは、このサイトのアカウントを作成します。
アカウントを作成後は、モジュールのアップロードに必要なAPIキーを取得します。

アカウントの登録は、LuaRocksにアクセスして、右上にあるRegisterをクリックし、ユーザー名とパスワード、メールアドレスを記入、送信すれば完了です。
APIキーは、LuaRocksにログイン後、右上にあるSettingsAPI keysタブで生成できます。

luarocksコマンドのインストール

モジュールをアップロードするのは、LuaRocksのサイト上で行うか、luarocksコマンドを使って行います。
コマンドラインで操作できたほうが、モジュールの登録を自動化しやすいので、この記事では、luarocksコマンドを使ってモジュールの登録をおこないます。
luarocksコマンドのインストールはお使いのOSのパッケージマネージャーを使ってできます。
例えば、Debian GNU/Linuxでは以下のコマンドでインストールできます。

% sudo apt install luarocks
rockspecの作成

モジュールを登録するには、rockspecというファイルが必要です。
これは、登録するモジュールの情報(パッケージ名やバージョン、依存モジュール等)を記載します。 

rockspecは、公開したモジュールのトップディレクトリで、luarocks write_rockspecと実行すると、雛形を作成してくれますので、その雛形に必要な情報を追記すれば完成です。

rockspecの記載が完了したら、lockspec makeコマンドを実行して、ビルド出来ることを確認しましょう。
luarocks makeがエラー無く完了すれば大丈夫です。

rockspecの書き方については、公式ドキュメントにも記載がありますので、こちらも参考にしてください。

rcokspecに記載する主な内容は以下の通りです。

  • package

    • このモジュールの名前を記載します。
  • version

    • このモジュールのバージョンを記載します。
  • packageversionは必須です。packageversionは、rockspecのファイル名に使用するためです。

  • description: 公開するモジュールについて記載します。maintainer以外の内容は、LuaRocks上のモジュールのページに表示されます。

    • summary: モジュールの概要を記載します。
    • detailed: モジュールの詳細を記載します。
    • license: モジュールのライセンスを記載します。
    • homepage: モジュールのプロジェクトのURLを指定します。
    • maintainer: モジュールのメンテナーを記載します。
  • source: モジュールをビルドするためのソースを取得する方法を記載します。

    • url: モジュールのソースアーカイブのURLを指定します。
    • dir: ソースアーカイブが解凍された時に作成されるディレクトリの名前を指定します。
  • build: このモジュールのビルド方法を記載します。

    • copy_directories: モジュールのインストールディレクトリ配下にそのままコピーされるディレクトリを指定します。
      • ビルドが必要ないドキュメントやサンプルコード等をインストールするのに便利です。
    • modules: 後述するtypebuiltinを指定したときのみ使用できます。
      • モジュールのコンパイル方法を記載します。
    • type: このモジュールのビルド方法を指定します。
      • 幾つかのビルド方法を選ぶことができます。
      • make: Makefileを使ってモジュールをビルドする際に使用します。
      • cmake: CMakeを使ってモジュールをビルドする際に使用します。
      • builtin: これを指定すると、後述するmodules内にこのモジュールをビルドする方法を定義できます。
        • 登録するモジュールがLuaで書かれたソースコードのみの場合は、コンパイルの必要はないので、このbuiltinを選択して、モジュールの動作に必要なソースコードをコピーするよう記載します。

        • 例えば以下のように書くと、xmluaモジュールは、XMLuaモジュールのトップディレクトリに配置されます。また、xmlua.attributeモジュールは、xmluaディレクトリの下に配置されます。

          build = {
            type = "builtin",
            modules = {
              ["xmlua"] = "xmlua.lua",
              ["xmlua.attribute"] = "xmlua/attribute.lua"
            },
          }
          
  • dependencies

    • 依存しているLuaのモジュールを記載します。また、このモジュールの動作に必要なLuaのバージョンも指定できます。
    • ここに記載されたモジュールがシステムにインストールされていない場合は、モジュールのインストールに失敗します。
    • また、ここに記載されたLuaのバージョンに満たないLuaがシステムにインストールされていた場合は、同様にインストールに失敗します。
  • external_dependencies

    • 依存している、Cのライブラリーを指定できます。
      • ここに記載されたライブラリーは、モジュールインストール時に存在をチェックされるため、ここで指定したライブラリーがシステムにインストールされていない場合は、モジュールのインストールに失敗します。
rockspecとモジュールのアップロード

モジュールの登録には、rockspecとrockファイルをアップロードする必要があります。

rockファイルの作成は、luarocks pack rockspecファイルで作成できます。
rockspecには、ファイル名の規定があり、name-version-revision.rockspecという形式のファイル名でないとrcokファイルを作成してくれないので注意しましょう。
ここまでで、アップロードに必要なファイルは揃いました。
あとは、以下のコマンドでアップロードできます。

% luarocks upload rockspecファイル --api-key=APIキー
終わりに

rockspec には、色々なパラメータが指定出来るのでモジュールの登録を難しく感じたかもしれませんが、基本的には、luarocks write_rockspecで生成されるrockspecの雛形のsourcedescriptionの内容を埋めれば、登録に必要な情報は揃います。

Luaで便利なモジュールを作成したら、より多くの人に使ってもらえるよう、LuaRocksに自作のモジュールを登録してみてはいかがでしょうか。

2018-11-19

RubyData Tokyo Meetup - Apache Arrow #RubyData_tokyo

Apache ArrowのC・Ruby・パッケージ関連を主に開発している須藤です。

RubyData Tokyo MeetupでApache ArrowのRubyまわりの最新情報を紹介しました。

関連リンク:

内容

(いつ頃か忘れましたが)前にApache ArrowのRubyまわりを紹介した時はデータ交換まわりの話がメインでした。それは、データ交換まわりの実装しかなかったからです。

しかし、最近はデータ処理まわりの実装も進んできたので、そのあたりのことも盛り込みました。たとえば、素のRubyの機能で数値演算する場合と、Numo::NArrayを使って数値演算する場合と、Gandiva(Apache Arrowの式処理モジュール)を使って数値演算する場合のコードとベンチマーク結果を紹介しました。

私のマシンで計測したところNumo::NArrayが一番高速でした。Numo::NArrayすごい!発表中、@sonotsさんがNumPyの方がさらに速いと思うけどねーと言いながら同じパターンをNumPyでも計測していました。計測したところ、NumPyよりもNumo::NArrayの方が速く、@naitohさんもその場で計測したところ、確かに速かったです。この内容はその後の@naitohさんの発表に盛り込まれています。発表をきっかけに新たな事実の発見が進むなんていい集まりですね!

他には最近Apache Arrowで実装が進んでいるCSVパーサーが速いよ!ということを自慢したりしました。

集まりに関して

今回の集まりはとてもいい集まりだなぁと思えるいい集まりでした。

@mrknさんがポジティブな話をするようになっていたのもよかったですし、Juliaバックエンド案は面白いなぁと思いました。

@shiro615さんのOSS GateワークショップでOSSの開発に参加しはじめて、Red Data Toolsで継続的にApache Arrowの開発に参加し続けて、この間コミッターになった、という話は感慨深かったです。OSS GateもRed Data Toolsもはじめてよかったな。

@hatappiさんがイベント中にRed ChainerのCumo対応ブランチをマージしていたのもよかったです。@sonotsさんの発表で変更の概要を聞いて、発表の後のコード懇親会で直接相談しながらマージ作業を進めていました。開発が進むなんて、なんていい集まりなんでしょう。

@sonotsさんはこのイベントがあったからCumo対応プルリクエストを作ったと言っていました。開発が進む集まり!

@colspanさんのMenoh-RubyとFluentdを使って推論サーバーを作る話は面白いなぁと思いました。なるほどなぁ。

Red Data ToolsとしてもMenohとMenoh-Rubyを応援していきたいので、いい感じに協力できないか少し相談しました。11月20日(火)の夜のOSS Gate東京ミートアップ for Red Data Tools in Speeeで続きを相談できそうです。

@v0droさんの発表でXND関連の理解が深まりました。調べないとなぁと思っていたんですよねぇ。型を文字列で定義するのは、いいのかな、悪いのかな。まだ判断できないんですが、面白いアプローチだなぁとは思いました。

Red Data ToolsとしてもXND関連の開発に協力していきたいな。

まとめ

2018年11月17日にRubyData Tokyo Meetupという開発が進むいい集まりがありました。

Rubyでもっといい感じにデータ処理できるようになるといいなぁ思った人は次のアクションとして以下を検討してみてください。

タグ: Ruby
2018-11-20

片手でのキーボード入力を支援するソフトウェアについて

はじめに

怪我などで手首などを負傷してしまうと、満足に両手でのタイピングが行えない事態に陥ることがあります。
今回は、そんなときでも怪我した側の手にあまり負担をかけずに入力するための方法を紹介します。*1

Half KeyboardもしくはHalf QWERTY

片手だけで入力できるようにしようというアイデアはそう目新しいものではなく、昔からあるようです。

Half KeyboardもしくはHalf QWERTYなどで検索するといろいろでてくるので興味があれば調べてみるのもよいでしょう。
専用のキーボードが製品化されていたり、ソフトウェアで特定のキーが押されたらレイアウトを変更するというものもあります。

Mirrorboardの場合は、キー配列の定義を差し替えることでCapsLockを押したらキーレイアウトが左右反転するようになっています。

xhkによる片手キーボード入力

専用のキーボードの購入は敷居が高いので、ソフトウェアによる片手入力を試してみましょう。
今回そのうちの一つであるxhkを使ってみます。

xhkは次の手順でインストールできます。

% git clone https://github.com/kbingham/xhk.git
% cd xhk
% sudo apt install build-essential autoconf automake pkg-config
% sudo apt install libx11-dev libxi-dev libxtst-dev
% ./autogen.sh
% ./configure
% make
% sudo make install

使用するには、xhkコマンドを実行するだけです。(最初から反転した状態にするには -m オプションを指定します。)

% xhk

xhkを起動後、SPACEキーを入力するとレイアウトが左右反転するようになっています。

通常のキーレイアウト

SPACEキーを押したときのキーレイアウト

上が通常のキーレイアウトで、下がSPACEキーを押したときのキーレイアウトです。
SPACEキーを押すと、QのキーはPの位置にというように左右反転した位置に配置されます。そのため本来左手で押していたキーも右手で打てるようになっています。
(このキー配列の画像は、http://www.keyboard-layout-editor.com/ で作成しました。あらかじめ選択できる候補にHalf Keyboardがなかったので、フィードバック しておきました。)

例えば、「わたしの」(WATASHINO)と右手だけでローマ字入力したい場合には次のようにキーを押すと入力できます。

キー入力 印字内容
SPACE+O W
SPACE+; A
SPACE+Y T
SPACE+; A
SPACE+L S
H H
I I
N N
O O

入力したい文章によってはキーレイアウトを左右反転させるためにSPACEキーを頻繁に打つことになるので、右手をそれなりに酷使することになります。*2

なお、xhkがパッケージとしてインストールできると導入しやすくなって嬉しいだろうということで、パッケージ化の作業を進めやすくするためにフィードバックしておきました。またドキュメントの記述がやや古かったのでそちらも報告しておきました。

まとめ

今回は、片手でのキーボード入力を支援するソフトウェアのひとつであるxhkを紹介しました。
もし、片手でなんとかキーボード入力しないといけない事態になったら、選択肢の一つとして試してみるのもよいかもしれません。

*1 怪我をしたほうの手に負担をかけないことが主目的なので、怪我をしていないほうの手にはどうしても負荷がかかります。

*2 慣れないうちはキーレイアウトの変更に頭がついていかずにかなり打ち間違えたりします。

2018-11-21

Firefox 64およびFirefox ESR60.4以降で可能になる、ルート証明書の自動インポートの方法

Firefox 52以降で可能になったエンタープライズの証明書の自動インポート機能は、WindowsでActive Directoryのグループポリシー機能を使って配布された証明書をFirefoxから認識できるようになるという物でした。そのため、Windows以外の環境では使用できず、また、WindowsでもActive Directoryを運用していない場合はレジストリを直接編集して証明書を配布する必要がありました。

Firefox 64およびFirefox ESR60.4では新たに、PEM形式の証明書ファイルとして配布されたルート証明書の自動インポートが可能になりました。この機能はプラットフォームを問わず動作するため、LinuxやmacOSを企業で運用する場合にも使用できます。

以下、Ubuntu 16.04LTSの環境でCert Importerアドオンのテスト用のダミーのルート証明書(cacert.pemをインポートさせる場合を例として、具体的な手順を解説します。

ステップ1:証明書ファイルの配布

PEM形式の証明書ファイルは、以下の位置に設置します。

  • Windows:
    • %AppData%\MozillaC:\Users\(ユーザー名)\AppData\Roaming\Mozilla\Certificates
    • %LocalAppData%\MozillaC:\Users\(ユーザー名)\AppData\Local\Mozilla\Certificates
  • macOS
    • /Users/(username)/Library/Application Support/Mozilla/Certificates
    • /Library/Application Support/Mozilla/Certificates
  • Linux
    • ~/.mozilla/certificates
    • /usr/lib/mozilla/certificates

これら以外の位置に置かれた証明書ファイルはインポートできませんので、注意して下さい。

それでは実際に、cacert.pemをダウンロードして、上記のいずれかの位置に設置します。
ここでは ~/.mozilla/certificates の位置に置く事にしました。

$ mkdir -p ~/.mozilla/certificates
$ curl https://raw.githubusercontent.com/clear-code/certimporter/master/doc/cacert.pem > ~/.mozilla/certificates/cacert.pem

ステップ2:証明書ファイルをインポートするためのポリシー設定の作成と配布

以下の形式でポリシー設定ファイルを作成し、policies.json というファイル名で保存します。

{
  "policies": {
    "Certificates": {
      "Install": [
        "ファイル名1",
        "ファイル名2",
        ...
      ]
    }
  }
}

今回は cacert.pem 一つだけをインポートするため、以下のようになります。

{
  "policies": {
    "Certificates": {
      "Install": [
        "cacert.pem"
      ]
    }
  }
}

このファイルを、Firefoxの実行ファイルと同じ位置の distribution ディレクトリに設置します。
具体的には以下の要領となります。

  • Windows:
    • C:\Program Files\Mozilla Firefox\distribution\policies.json など
  • macOS
    • /Applications/Firefox.app/Contents/MacOS/distribution/policies.json など
  • Linux
    • /usr/lib/firefox/distribution/policies.json など

今回の実験環境では ~/opt/firefox-nightly/ 配下に置いたFirefox(Nightly)を使用するため、設置先は ~/opt/firefox-nightly/distribution/policies.json とします。

$ mkdir -p ~/opt/firefox-nightly/distribution
$ cat << END > ~/opt/firefox-nightly/distribution/policies.json
> {
>   "policies": {
>     "Certificates": {
>       "Install": [
>         "cacert.pem"
>       ]
>     }
>   }
> }
> END

ステップ3:Firefoxの起動と結果の確認

以上で証明書の自動インポート機能の準備は完了です。Firefoxを起動すると policies.json に列挙された証明書ファイルが自動的に検出され、ルート証明書としてFirefoxの証明書データベースにインポートされます。

以上の手順でインポートされた証明書は、設定画面の「プライバシーとセキュリティ」配下の「証明書を表示...」ボタンをクリックして開かれる証明書マネージャーの一覧上に現れます(グループポリシーで配布されたエンタープライズの証明書はこの一覧上には現れません)。
先の cacert.pem は「!example」という組織の「example.com」という名前の証明書になっていますので、実際に証明書の一覧に現れている事を確認してみて下さい。

(画像:証明書一覧を表示した所)

まとめ

以上、Firefox 64およびFirefox ESR60.4で可能となった、PEM形式の証明書ファイルによるルート証明書の自動インポートの手順をご案内しました。

Firefox 64では他にもポリシー設定にいくつかの新機能が加わっています。また、ポリシー設定に関する変更はFirefox ESR60へも随時反映されています。5月12日付の記事のポリシー設定の解説に現時点で判明している変更点を反映済みですので、併せてご覧下さい。

また、当社ではFirefoxの法人での利用中に発生した様々なトラブルに対するテクニカルサポートを有償でご提供しています。Firefoxの運用にお悩みのシステム管理者さまは、お問い合わせフォームよりお気軽にお問い合わせ下さい。

タグ: Mozilla
2018-11-30

«前月 最新記事 翌月»
タグ:
年・日ごとに見る
2008|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|07|08|09|10|11|12|
2020|01|02|03|04|05|06|07|08|09|10|11|12|