JavaDoc内に記述したコードを強調表示する方法
メモ。.NETならこんな面倒なことせんでもいいのになぁ。
あらかじめ、http://shjs.sourceforge.net/ より強調表示のためのファイル群をダウンロードしサーバー(Jenkinsサーバが適当だと思う)に配置しておく。ここでは、/shjs-0.6 ディレクトリに配置したものとする。
pom.xmlに下記を記述。configurationタグ内がミソ。CDATA内に記述したコードが、生成されたJavaDoc htmlのbodyの最初と最後に付加されてしまうが、うまく動いているのでとりあえずこれで良しとしとく。。。
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-javadoc-plugin</artifactId> <configuration> <header><![CDATA[ <link type="text/css" rel="stylesheet" href="/shjs-0.6/sh_style.css" /> <script type="text/javascript" src="/shjs-0.6/sh_main.min.js"></script> <script type="text/javascript" src="/shjs-0.6/lang/sh_java.min.js"></script> <script type="text/javascript" src="/shjs-0.6/enable_sh.js"></script> ]]> </header> <links> <link>http://docs.oracle.com/javase/jp/7/api/</link> </links> </configuration> </plugin>
上記scriptタグの3つ目、enable_sh.jsは後から独自に作成し配置したスクリプトファイルだ。内容は下記とした。
window.addEventListener("load", function() { var tlist = document.getElementsByTagName("pre"); for(var i = 0 ; i < tlist.length ; i++) { tlist[i].setAttribute("class", "sh_java"); } sh_highlightDocument(); });
/** * ・・・中略・・・ * <pre> * //あーするこーする * HogeClass c = new HogeClass(); * c.piyoMethod(); * </pre> * ・・・中略・・・ */
以上で、preタグで囲ったコードがJavaコードとして強調表示される。もっとイケてる方法があれば教えてください。
Nashornを使ってみた。
JDK8 Early Access Releases に、JVM上で動くJavaScriptエンジンであるNashornが入ったみたいですね。そんな折、下記の記事を見つけたので自分も試してみました。
http://d.hatena.ne.jp/tomoTaka/20130426/1366932045
これはいいですね。サーバーとクライアントでロジックを共有できる予感がいたします。パフォーマンスはどうなんでしょう。またためしてみよう。
package com.mycompany.mypj1; import java.io.Reader; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import org.junit.*; import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; public class NashornTest { @Test public void Nashornテスト() throws Exception { ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName("Nashorn"); // *** ここで「Nashorn」を指定 System.out.println(String.format("*** Engine:%s", engine.getFactory().getEngineName())); Path path = Paths.get("src/main/resources/hello.js"); // 対象のJavaScriptファイルを指定 Reader reader = Files.newBufferedReader(path, Charset.forName("utf8")); engine.put(ScriptEngine.FILENAME, path.getFileName().toString()); //関数の定義を読み込む engine.eval(reader); //関数を呼ぶ Object ret = engine.eval("sayHello('社畜');"); assertThat(ret.toString(), is("ハロー社畜!")); } }
下記のスクリプトを、hello.jsとしてsrc/main/resources/に配置。
function sayHello(name){ return "ハロー" + name + "!"; }
TypeScript 0.8と0.9の間での破壊的変更
追記:@cocoa_ruto 様の指摘を受けて修正しました。ありがとうございます。勉強になります。訳しただけじゃなくて内容を理解しないと意味ないですね。。。
TypeScript0.9のalpha版が先日公開されました。このバージョンではかなり破壊的変更が入っています。それを調べるついでに、こちらのページを訳してみます。おかしな所があればご指摘ください。
読んでみると結構変わってますね。staticメソッドの呼び出しにクラス名の修飾が必須になったのが結構痛いです。
0.8と0.9の間の既知の破壊的変更 - Known breaking changes between 0.8 and 0.9
言語の成熟やECMAScript5および6、またコンパイラの仕様自身の一般的な準拠に資するために、0.9系は破壊的変更が入っている。われわれは、破壊的変更の数が少なくなるよう努めてはいるが、1.0に到達する前に必要な変更を行うことが重要である。*1
仕様に対するコンパイラの強化 - Tightening of compiler to spec
説明:仕様上厳密には許されていないが0.8.3コンパイラでは許されていた機能があった。例えば、protected をシミュレートするためのアクセス修飾子の利用など。
理由:われわれは、緩いコンパイラ実装に依存するのを避けるために、より厳密に仕様に忠実になるようコンパイラを強化した。
boolはbooleanになった。 - The type ‘bool’ is now ‘boolean’
説明:真理値の名前は、最初は'bool'だった。0.9.0の間は'bool'はdeprecatedとしてマークされ予定で、'boolean'をかわりに推奨する。0.9.1では'bool'は利用できなくなる予定だ。
理由:ECMAScriptにおける真理値型の名前は 'boolean'である。標準に合わせるために変更する。
'module'キーワードはもはや型を作らない - The ‘module’ keyword no longer creates a type
説明:0.9.0 において、名前空間、型、そして値の役割が明確に区別される。Moduleは名前空間と値のみを提供し、型は提供しない。
理由:Moduleの役割を簡素化することにより、モジュールがクラスと関数をより簡単に拡張できるようになる。
ジェネリック型の導入 - Array型の変更 - Introduction of generic types – Change to ‘Array’ type
説明:0.9.0 において、ジェネリック型の説明に関連するが、Array型はジェネリックとなった。
理由:以前は、Array型はより特別ではなかった。*2しかしジェネリックの降臨により配列内の型をより明確にできる。
ジェネリック型の導入 - 構文解釈の変更 - Introduction of generic types – Change to syntax parsing
説明:0.9.0 において、ジェネリック型の説明に関連するが、構文解釈の方法の違いが説明された。これは、ES5で許容されると以前は解釈されていた構文のいくつかのformsに影響を与える可能性がある。特に次の例は、two comparisonsのカンマ区切り式としてはもはや解釈されず、かわりに2つの引数をとるジェネリック関数の呼び出しとして解釈される。
myfun<x,y>(z)
このあいまいさを回避するために括弧を使うこと。例えば下記。
(myfun < x), y > (z)
理由:われわれは、ジェネリック構文に関してほかの言語によって位置づけられる優位性を追従する。残念ながら、このことはいくつかのケースであいまいさをもたらす。
オーバーロード宣言は実装のすぐ前に書かなければならない - Overload declarations must immediately precede implementation
説明:0.8系はオーバーロード宣言とその実装に関して何が許されるかが、緩かった。
追記)foo(a:string); foo(a:number); foo(a:any) {...}のように書くようになったという意味とのこと。@cocoa_ruto 様より。
理由:文法に合わせるためにコンパイラを強化する
インデクサは型を完全に明示しなければならない - Indexers must have full type annotation now
説明:
interface a1 { [n: number]; //<-- was allowed in 0.8.x, now an error } interface a1 { [n: number]: string; //<-- correctly specifies return type, valid in 0.8.x and 0.9.x }
理由:文法に合わせるためにコンパイラを強化する。
静的メンバは完全に修飾されなければならない - Statics must be fully-qualified
説明:静的メンバ宣言の中では、以前は完全に修飾しないことができた。
理由:単純にクラスは"this."を必要とするので*3、そのシンボルがどこに由来しているか示すために静的メンバはクラス名を必要とする。
メソッドの最後にセミコロンを付けるのはもはや許容しない - No longer accept semicolon at the end of methods
説明:
class Todo extends Backbone.Model { // Default attributes for the todo. defaults() { return { content: "empty todo...", done: false } }; }
理由:0.9.0の忠実な(fidelity)パーサにおいて構文により厳密に追従することに由来しているかもしれない。われわれは、将来この制限を緩くするかもしれない。
"new number[]"
はもはや許容しない - No longer accept "new number[]"
説明:0.8系において、newの後には式が続くべきというルールに対するいくつかの例外をわれわれは許した。ここで、newに続くものとして"number[]"という型をわれわれは許していた。
理由:0.8系のコンパイラにおいて意図せずに許されたものだ。0.9系の作業の一部として、われわれはコンパイラを強化し、そのようにするのをもはや許さない。
.d.tsファイルにおいて、interfaceでない型の宣言にdeclareが必須 - 'declare' now required for top level non-interface declarations in .d.ts files
説明:0.8系は.d.tsファイルにおいて多くの緩さを持っていた。われわれは構文を強化したい。
理由:緩い構文ルールはユーザーを混乱させており、.d.tsファイルに非一貫性をもたらしている。
文字列インデクサはインターフェースにおいてメンバーの型に制約を課す - String indexers constrain the types of members on the interface
説明:element型のFooが付随した文字列インデクサは、interfaceの全てのメンバがFooに対して互換性のある型を持つという制約である。唯一の例外は、いかなるobject型においても呼び出されるObjectを暗黙的に継承したメンバーが、明示的にオーバーライドされない限りObjectにおいて提供される型を持ちつづけることだ。したがってこれは、
export interface IEventListenerMap { hasOwnProperty(eventName: string): bool; [eventName: string]: IEventCallback[]; }
interfaceの利用者に変更がないようにするために、このように変更される。(すなわち、彼らは.hasOwnPropertyを使い続けることができる)
export interface IEventListenerMap { [eventName: string]: IEventCallback[]; }
理由:0.9において、文字列インデクサはプロパティのルックアップに統合された。したがって、foo.bar と foo["bar"]は同じく振る舞う。
やまーのAPIを使ってみた
yammerいいですよね。長く使っていると良いナレッジベースになりますし、他部署の方ともつながりができます。MSが買ってくれたおかげで、えらいひとにバレてもなんとか言い訳できるだろうし。できませんか、そうですか。
ところで、yammerを使っているとやってみたくなるのが社内システムとの連携です。今回ちょっとした連携の仕掛けを作ってみました。全部公開するわけにはいきませんが、yammerのAPIを叩く部分を紹介しようと思います。
開発および実行環境は下記です。
アプリケーションの登録
画像がなくてすみません。察してください。
このURL を開き、左側のメニューにある「Register New App」をクリックします。
入力項目を適当に入力し、continueをクリックします。
すると、登録したアプリのClientIDとClient secretが表示されます。
アクセストークンの取得
以下のことを一気通貫でやってくれるgemもあるんでしょうが、 自分はこちらを参考に手作業でやりました。
アプリケーションの登録時に表示されていたClientIDを下記にあてはめたURLを、ブラウザで開きます。 今回作ったのはサーバーアプリなのでredirect_uriは不要なのですが省略するとエラーします。なので適当に入力しておきます。ここでは、http%3A%2F%2Flocalhost としました。URLエンコードは不要かもしれません。。
https://www.yammer.com/your_network/dialog/oauth?client_id=[:client_id]&redirect_uri=http%3A%2F%2Flocalhost
するとoauthの認証画面が表示されるので「許可」をクリックします。すると、codeというクエリ文字列がついたURLにリダイレクトします。
次に、先に取得したclient_id、client_secret、codeを下記にあてはめたURLを、ブラウザで開きます。
https://www.yammer.com/oauth2/access_token.json?client_id=[:client_id]&client_secret=[:client_secret]&code=[:code]
すると、JSON文字列が表示されます。ここで目をこらしてみると、tokenというキーがあります。以下、このトークンを使ってAPIを叩きます。
rubyの開発環境の準備
gemが入っている前提で。
Gemfileは以下です。下の2個はテストのためのものなので、プロダクトとしては上の2個で十分です。
gem 'yam' gem 'httpclient' gem 'rspec' gem 'timecop'
yammer API の簡単な紹介
require 'yam' #APIのインスタンスを作る。 yam = Yam.new(アクセストークン, 'https://www.yammer.com/api/v1/') #メッセージを取得 res = yam.get('/messages') res.messages.each({|message| #本文 puts message.body.parsed #ユーザー情報を取得してみる。 user = yam.get('/users/' + message.sender_id.to_s) puts user.contact.email_addresses[0].address } #メッセージを投稿 yam.post('/messages', body: '今日はサビ残')
APIの戻り値の内容というかデータ構造を調べたかったのですがruby歴3日の私には良い方法が分からず、putsでコンソールに出して調べました。。。
利用したgemは、生のAPIに沿って作られているようなので、APIリファレンスを見れば細かい使い方も何となく分かるんじゃないでしょーか。
長野県の路線バス検索ページ(のフロントエンド)をAngularJS/TypeScriptで作った
概要
実物はこちらです。 http://b-sw.in/albus/
このページは、大きく
- フロントエンド・・・HTML+JavaScript
- バックエンド・・・RoR(らしい)
の2部構成でできています。このうちバックエンドについては、@mz_ken 氏が作っているandroidアプリ「あるバス」のAPIを使わせていただいています。多謝。 本格的にバス検索を利用したい長野県民androidユーザーは、ぜひとも当該アプリを使ってください。
今回AngularJSとTypeScriptの勉強を兼ねて、HTMLアプリでフロントエンド作りました。
開発環境は以下です。ほかにjQueryやらTwitter Bootstrapやらを使っていますが本題から外れますので言及しません。
- windows 7
- Visual Studio 2012 Express For Web
- TypeScript 0.8くらい
.NETのコードは一切出てこず静的ファイルのみで構成されているため、Visual Studioを使わなくても、例えばLinux上のEmacsでも開発はできます。TypeScirptの開発環境はnpmからも入れられますからね。
TypeScriptの環境作成
windows上での開発なので、TypeScript for Visual Studio 2012 をインストールします。
JavaScriptのMVCフレームワーク
AngularJSは、JavaScriptのUIフレームワークです。この種のフレームワークをMVCフレームワークというらしいです。MVCフレームワークの機能は大まかに数種類のものがあり*1、それぞれの機能をサポートしているフレームワークとしていないフレームワークがあります。このうちUI Bindingsは.NETのWPFも備えている機能です。私はもともとWPFをかじっていたため、JavaScriptでWPFのバインディングみたいなことができないかなぁという動機で、JavaScriptのMVCフレームワークを使ってみることにしました。
AngularJS
ここ1, 2年でJavaScriptのMVCフレームワークはいろいろ出現しています。この中でAngularJSが今後一番有力というか使われるようになると個人的には思っています。ほかに私が使ってみたことがあるMVCフレームワークにKnockoutJSがありますが、双方向バインディングをするためにはfunctionオブジェクトを介す必要がある点がイマイチです。AngularJSはそれがなく通常のJavaScriptオブジェクトをバインドできます*2。またgoogle製という点も信頼できます。何となくだけど。。。
AngularJSについて知りたい場合は、本家のサイトのチュートリアルをひととおりやってみると良いです。git のブランチを切り替えてトピックを進める形になっており学習を進めやすいです。
とここまで調べた段階で、冒頭に紹介した@mz_ken 氏のandroidアプリの存在を知り、これの縮退Web版を作ってみることにしました。
AngularJSの基本的な話から書こうと思ったんですが、すでにTypeScriptと組み合わせた形で記事にしている方がいるので、そちらを参照して下さい。
JSONP
webサービスのAPIをコールする部分は下記です。
$http.jsonp("http://www9264ui.sakura.ne.jp/busstops/result_bts_lines?format=json&format=js&callback=JSON_CALLBACK") .success(data => { for (var i = 0 ; i < data.busstops.length ; i++) { $scope.busstops.push(data.busstops[i].busstopname); } this.busstopList = data.busstops; this.putMarkers($scope); });
AngularJSのAPIを使わないと、コールバックの中での変更がDOMに反映されません。また、callbackの関数名はJSON_CALLBACKにしておく必要があります。
AngularUI
バス停を地図に表示するためにGoogle Maps APIを使っています。ところがそのままではAngularJSから使いづらいので、AngularUIというAngularJSのmodule群を使いました。AngularUIの中にmapsというズバリ今回使いたかった機能があるため、結局自分でmoduleやカスタムタグを作らずに済みました。ほかにもjQueryUIを利用するためのモジュールがあるみたいですね。
まずはscriptタグを記述します。
<script src="Scripts/angular-ui/angular-ui.min.js"></script>
地図に関する記述は以下です。マーカー用のdivを書かなければいけないということが分からずけっこうハマりました。
<!-- map body --> <div id="map_canvas" data-ui-map="myMap" data-ui-options="mapOptions" data-ui-event="{'map-click': 'mapClick($event)'}"></div> <!--for marker--> <div data-ng-repeat="marker in busstopMarkers" data-ui-map-marker="busstopMarkers[$index]" data-ui-event="{'map-click': 'openMarkerInfo(marker)'}"> </div> <!--infowindow--> <div data-ui-map-info-window="busstopInfoWindow"> <h1>{{currentMarker.title}}</h1> <button data-ng-click="setFromBusStop($event)">乗車</button> <button data-ng-click="setToBusStop($event)">降車</button> </div>
対応するfunctionやモデルは$scopeのプロパティとして定義しておきます。*3
$scope.setFromBusStop = $event => { $scope.fromBusStop = $scope.currentMarker.title; }; $scope.setToBusStop = $event => { $scope.toBusStop = $scope.currentMarker.title; }; $scope.openMarkerInfo = function (marker) { $scope.currentMarker = marker; //$scope.currentMarkerLat = marker.getPosition().lat(); //$scope.currentMarkerLng = marker.getPosition().lng(); //$scope.currentBusstopName = marker.title; $scope.busstopInfoWindow.open($scope.myMap, marker); }; $scope.mapOptions = { zoom: 15, mapTypeId: google.maps.MapTypeId.ROADMAP };
何も考えないと、コントローラクラスのconstructorにだらだらと記述してしまい行数増えてしまいます。$scopeを持ちまわってメソッドアウトする方が良いかもしれません。
オートコンプリート
便利かどうかは分かりませんが、やってみたかったのでバス停名のテキストボックスに自動補完機能をつけました。jQueryUIのオートコンプリートをそのまま使ったのでは、DOMだけが変わりmodelに反映されません。ググってこちらのコード辺まんまを使わせてもらってます。カスタムディレクティブですね。
angular.module('albus', ['ui']).directive('autoComplete', function () { return function (scope, iElement, iAttrs) { scope.$watch(iAttrs.uiItems, function (values) { iElement.autocomplete({ source: values, select: function () { setTimeout(function () { iElement.trigger('input'); }, 0); } }); }, true); }; });
uiItemsという属性の値を監視し変更があった場合にautocompleteを適用しなおす、という記述のようです。autocompleteのselectイベント内でinputイベントを遅延発生させているのはAngularJSのmodelからviewへの反映のタイミングの関係なんでしょう。ちょっとトリッキーですね。
作ったディレクティブを、下記のようにauto-omplete属性として利用します。
<input class="inputbusstop" type="text" placeholder="到着バス停" data-ng-model="toBusStop" data-auto-complete data-ui-items="busstops" />
まとめ
結果として、app.ts(app.js)にはviewに関する記述(DOM要素のidを指定する等)はありません。ちょっとだけあるけど、それは地図の表示非表示およびcss切替えというviewで完結している処理です。より複雑な画面だとまた話は変わってくるでしょうが、この程度の画面であればそこそこ容易にviewとロジックを分離することができました。
画面をリフレッシュせずにDOMの一部だけを書き換える、いわゆるシングルページアプリケーションが今後増えてくると思います。Web系ではこの傾向は顕著でしょう。エンタープライズ系であっても、Web系ほどではないもののこの流れはあるでしょう。それにあたりJavaScriptのMVCフレームワークの検討は避けて通れません。AngularJSはその有力な候補となるでしょう。
また、ある程度の規模のJavaScriptアプリケーションを作るには静的な型システムが欠かせません。コンパイルするとJavaScriptになる言語はほかにCoffeeScriptやHaxeなどありますが、TypeScriptは学習コストやコンパイル後のJSの可読性に長所があります。JavaScriptのフリーダムさも包含してはいますが。
AngularJSもTypeScriptも情報が少なく調べるのがちょっと大変です。もっと使われるようになって情報が増えてほしいものです。
*1:出典:http://www.publickey1.jp/blog/12/javascript_mvc.html
*2:かわりに、modelを更新する場所とタイミングに気を使う必要はありますが。
*3:TypeScriptの強調表示ってできないんかな?
Mediawikiのextension、Semantic Formsの紹介(環境構築まで)
最近Mediawikiを使っています。Mediawikiはextensionが豊富で様々な機能拡張が可能です。その中で便利だなと思ったextensionのひとつをご紹介します。
一般的に、wikiは自由な記述が特徴ですが、自由度がありすぎてある程度の人数で使うには内容の統一がしにくいという側面もあります。そんなときは、accessのフォームのようにある程度定型的な入力を強制したくなります。
mediawikiの場合は、Semantic Formsというextensionで可能です。
環境
事前要件の準備
Validator extension のインストール
http://www.mediawiki.org/wiki/Extension:Validator の右側メニューの「Download snapshot」というリンクをクリックして、アーカイブをダウンロードし、通常のextensionと同様 extension ディレクトリに配置します。
次にLocalSettings.phpに追記して有効化します。
require_once( "$IP/extensions/Validator/Validator.php" );
Semantic Mediawiki extensionのインストール
http://semantic-mediawiki.org/wiki/Help:Download から、アーカイブをダウンロードするか git cloneして、通常のextensionと同様 extension ディレクトリに配置します。
次にLocalSettings.phpに追記して有効化します。enableSemanticsの引数には、wikiのホスト名かまたはIPアドレスを設定するようです。
include_once( "$IP/extensions/SemanticMediaWiki/SemanticMediaWiki.php" ); enableSemantics('example.org');
次に、管理者権限のあるユーザーでログインし、
Semantic Forms のインストール
同様に、Semantic Forms のアーカイブをダウンロードします。 http://www.mediawiki.org/wiki/Extension:Semantic_Forms/Download_and_installation を参照して、アーカイブをダウンロードするか git cloneするかして、通常のextensionと同様 extension ディレクトリに配置します。
次に、LocalSettings.phpに追記して有効化します。
include_once("$IP/extensions/SemanticForms/SemanticForms.php");
と以上で環境構築できます。どう使っていくんだ?という話はまた別の機会に。単語集に使うといいかな?と思ってます。