d.sunnyone.org
sunnyone.org

ページ

2017-10-20

Kotlinでコマンドラインツールを作成する(Gradle/fat jar)

Kotlinを始めるにはコマンドラインツールを作ってみてはと言いつつ、そういう話あんまりなくない?と思ったので書くことにした。 Gradleでfat jarを生成し、javaがあれば実行できるようにするところまで。

sdkmanでGradleをインストール

http://sdkman.io/install.html からsdkmanをインストールして、gradleをインストールする。
$ sdk install gradle

gradle initで雛形の作成

適当なディレクトリを作成し、gradle initでbuild.gradle他ファイルを生成する。

$ mkdir kotlin-cmd-example
$ cd kotlin-cmd-example
$ gradle init

build.gradleの編集

Using Gradle - Kotlin Programming Languageを参考にして、build.gradleを記述する。

kotlin-gradle-pluginを適用して、kotlin-stdlib他必要なライブラリを追加すればOK。

ここではlog出力のためのlogbackのほか、fat jarを生成するための johnrengelman/shadow を使っている。 shadowの設定については Shadow Plugin User Guide & Examples を。

plugins {
    id 'org.jetbrains.kotlin.jvm' version '1.1.51'
    id 'com.github.johnrengelman.shadow' version '2.0.1'
}

repositories {
    mavenCentral()
}

dependencies {
    compile 'org.jetbrains.kotlin:kotlin-stdlib-jre8'

    compile 'org.slf4j:slf4j-api:1.7.25'
    compile 'org.slf4j:jcl-over-slf4j:1.7.25'
    compile 'org.slf4j:jul-to-slf4j:1.7.25'
    compile 'ch.qos.logback:logback-classic:1.2.3'
}

apply plugin: 'application'
mainClassName = "com.example.kotlincmdexample.KotlinCmdExampleKt"

注意点としては、特に設定しないとJava側からはクラス名にはKtがついているのでつける。

src/main/kotlin に Kotlin ソースコードを作成

デフォルトではソースの場所はsrc/main/kotlinなので、ここにソースコードを配置していく。 IntelliJ IDEAであれば、build.gradleを開けば開始できる。

src/main/kotlin/com/example/kotlincmdexample/KotlinCmdExample.kt

package com.example.kotlincmdexample

import org.slf4j.LoggerFactory

fun main(args: Array<String>) {
    val log = LoggerFactory.getLogger("com.example.kotlincmdexample.KotlinCmdExample")

    if (args.isEmpty()) {
        println("Usage: kotlin-cmd-example name")
        return
    }

    log.info("Hello, {}", args[0])
}

あえてclassを作ってないのでgetLoggerが冗長な感じがするが、中途半端なのでloggerの名前を適当にしてもいいし、クラスを作って渡すかしたほうがいいかも。

./gradlew runでテスト実行

./gradlew runすれば実行できる。IDEAであれば右クリックしてRunあるいはDebugでもOK。

$ ./gradlew run 

> Task :run
Usage: kotlin-cmd-example name


BUILD SUCCESSFUL in 0s
2 actionable tasks: 1 executed, 1 up-to-date

./gradlew buildでビルド(jar生成)

./gradlew buildするとbuild/distributionsにkotlin-cmd-example-shadow.tarと.zipが入っているので、これを展開すると起動スクリプトがあるので実行できる。

build/libsに-all.jarがあるので、java -jarで直接起動することも可能。

$ java -jar build/libs/kotlin-cmd-example-all.jar 
Usage: kotlin-cmd-example name

$ java -jar build/libs/kotlin-cmd-example-all.jar MyFirstKotlinProject
19:49:00.796 [main] INFO com.example.kotlincmdexample.KotlinCmdExample - Hello, MyFirstKotlinProject

今回のプロジェクト

GitHubのsunnyone/kotlin-cmd-exampleに置いておくので、参考になれば。

2017-06-27

Kotlin で引数の名前つきでの呼び出しを強制する裏ワザ

小ネタ。

Kotlinでは引数を名前つきで呼び出せる。なしでもOK。
fun add(x: Int, y: Int) = x + y

fun f() {
    add(1, 2)
    add(x=1, y=2)
    add(1, y=2)
}

名前付きで呼んでほしいときはどうするか?名前つきにしてほしいところの前にvararg Voidをおく。

fun add(vararg useNamed: Void, x: Int, y: Int) = x + y

fun f() {
    // add(1, 2) // コンパイルエラー!
    add(x=1, y=2)
    // add(1, y=2) // コンパイルエラー!
}

途中からでもOK。
fun add(x: Int, vararg useNamed: Void, y: Int) = x + y

fun f() {
    // add(1, 2) // コンパイルエラー!
    add(x=1, y=2)
    add(1, y=2)
}

…いやいやだめだろこれ。もっといい方法があれば教えて下さい。
参考:How can I force calls to some constructors/functions to use named arguments?

2017-01-29

RTX810 からmydnsにIPアドレスを通知する(Luaをスケジュール実行する)

変わるIPを知ってるのはルータなので、ルータに通知してもらおうということでRTX810にmydnsの更新をさせることにする。なんのことはない、shiwork氏のLuaスクリプトをRTXで動かそうというだけの話。

ただし、いわゆるフラッシュ領域にtftpでスクリプトをもってきて実行するというネットワーク機器にありがちそうなやり方をしようとすると、いくつかのドキュメントにまたがって追わないといけないので、あとで追えるようにまとめておく。

スクリプトの準備

gistからスクリプトを持ってきて、ユーザID/パスワードを書き換えておく。

tftpによるLuaスクリプトの転送

RTXのいわゆるフラッシュ領域はRTFSというらしい。ここにディレクトリを作ってスクリプトを転送する。
(→RTFS

/scripts/ディレクトリの作成

ルートにカスタムなスクリプトをべたに置くと気持ち悪いので、/scriptsというディレクトリを作って置くことにする。
# make directory /scripts
# show file list /
2017/01/29 21:12:50 <DIR>           scripts

スクリプト転送元端末からのtftpアクセスを許可する

# tftp host 192.0.2.10

tftpで送り込む

c:\> tftp 192.0.2.254 PUT mydns.lua /scripts/mydns.lua/(administratorパスワード) 
adminパスを入れる必要があるのに気づかずにはまった。
(→RTシリーズのインストールとリビジョンアップに関するFAQ tftpの機能と注意事項

確認。
# show file list /scripts
2017/01/29 21:12:55            2769 mydns.lua

tftpの無効化

# tftp host none

Luaスクリプトの実行

単発実行

単発の実行はlua パスでOK。show status luaでsuccessなことを確認する。
# lua /scripts/mydns.lua
# show status lua
Lua Library Version:            Lua 5.1.5
Lua Script Function Version:    1.07

[running]
There is no running script.

[history]
(1)
Running Trigger:     executed by 'lua' command
Command Line:        lua /scripts/mydns.lua
Script File:         /scripts/mydns.lua
Running Counts:      1
Error Counts:        0
First Starting Date: 2017/01/29 21:14:52
Last Starting Date:  2017/01/29 21:14:52
Last Ending Date:    2017/01/29 21:14:53
Last Result:         success

スケジュール実行

scheduleコマンドで実行をスケジュールする。
schedule at 2 */* *:0,10,20,30,40,50 * lua /scripts/mydns.lua
これでおしまい。
(→Lua スクリプト機能

2016-11-02

Vue.js 1.x から 2.0 への移行のポイント

先日Vue.js 1.xから2.0への移行を行った。まるっと書き換えないといけないというレベルではないが、メジャーバージョンアップだなぁと感じる程には違いを感じられたのでいくつかポイントを書いておく。

もちろん、Vue.jsの機能を全て使いこなしているわけではないため、すべて網羅しているとはとても考えていないので、詳しくは「Migration from Vue 1.x」を参照してほしい。

大きな違い:テンプレートが事前コンパイルできるようになった

Vue 1.xと2.xの大きな違いは、テンプレートのコンパイルと実行を分離できるようになったこと。実際に移行してみた感じ、このコンパイル処理の邪魔になるようなことに関しては排除するような形で変更されているように見える。

このコンパイルというのがどのようなものかは、browserify + vueifyの処理結果を見るとわかりやすい。

Single File Components (.vue)という形でコンポーネントを記述した場合、スクリプトやスタイル、テンプレートを同一のファイルに記述できる。browserifyを使う場合、vueifyというプラグインがこの.vueをJavaScript から読める形に変換してくれるのだが、この処理結果が1.xから2.xで大きく変わっている。

コンパイル対象のテンプレートが以下だとする。

Vue 1.xの場合は以下の形。

template変数に文字列として代入するだけになっている。

Vue 2.xの場合は以下の形。

render関数に、DOM要素を作っていくような関数がセットされていることがわかる。JSX中の要素がReact.createElement関数に変換される構造に近い。

移行の方法

Migration from Vue 1.x」の頭のほうに書いてあるが、vue-migration-helperを実行し表示されたメッセージの対処後、実行時に表示される警告を見つつ対処していく形になる。

移行時に変更する必要のあった点

browserify + vueifyで利用しているため、それを前提とする。webpackでも似たようなことをしないといけないはず。

npmインストールのデフォルトがRuntime-only Buildとなることへの対応

npmを利用すると、「Runtime-only build」を利用するようにインストールされる(Standalone vs. Runtime-only Build)。このRuntime-only buildは、template: によるテンプレートコンパイルが行えないため、HTML中にテンプレートを書いて実行する方式で実装されているアプリケーションは実行できなくなる。対処する方法は2通り。

render関数の実装(Runtime-only buildで実行できるようにする)

「Failed to mount component: template or render function not defined」と言われるので、その通りrender関数をnew Vue()の指定につける。この場合、ルートとなるコンポーネントも.vueに記述し、HTML側にはマウントされる要素しか用意しない。

HTMLは、

などとし、.js側は

というようにする。

SPA的なアプリケーションであれば十分であるかもしれないが、ちょいちょいVueを利用しているようなアプリケーションの場合、マウントの呼び出しがちょいちょいあるということなので、propsを受け取れるようなマウント関数を作っておくと便利である。


詳しくは「Render Functions」を参照。

Standalone buildの利用

既存のHTML中のテンプレートもコンパイルする場合、Standalone buildを利用するように変更する。

browserify + vueifyの場合は、aliasifyによってimport Vue from "vue"の動きを書き換える

まずaliasifyをインストール。
$ npm install aliasify --save-dev

package.jsonに以下の記述を追加。

browserifyの呼び出しに「.transform(aliasify)」を追加する。

属性中の{{ }}をv-bindと式に変更する

属性中の{{ hoge }}は解釈されなくなったため、v-bindと式に変更する必要がある。

今まではこのような形だったものは


このように変更する必要がある。


v-model中のフィルタの廃止

今まではv-model="obj.val | filter"という形で、v-modelにもフィルタが適用できていたのだが、これが廃止になった。

「v-model="something"」が「<input v-bind:value="something" v-on:input="something = $event.target.value">」のsyntax sugarである(Form Input Components using Custom Events)ということを利用して、コンポーネントを作る必要がある。

実際の例は、Migration from Vue 1.xに書いてあるが、v-modelに対するフィルタに比べると手間なので、多用していた場合は、結構痛いと思う。

Vue.config.delimitersをコンポーネントレベルに変更する

Vue.config.delimitersは廃止になり、"{{"と"}}"の変更はコンポーネントレベルでしか受け付けなくなった。

サーバサイドテンプレートとの混乱を避けるため、デリミタは変更していたのだが、このような場合new Vue()の呼び出しにそれぞれつける必要がある。

ただし、先述の通りvueifyがテンプレートをコンパイルするようになったのだが、vueifyが.vueに設定されたdelimitersを解釈しないため、変更ができなくなった。そのため、.vueで変更されたデリミタを使用している場合、デリミタ自体を全て書き換える必要がある。

propsでのJavaScript予約語の利用不可

コンパイル結果を見たらそれはそうかという感じだが、コンポーネントプロパティに予約語を使うと怒られるようになった。地味に痛いのが"class"で、注意が必要。

readyライフサイクルイベントをmounted+αに

"ready"ライフサイクルイベントが廃止になった。

一番近いのがmountedで、必要であればvm.$nextTickでDOM要素が構築されてから処理を実行する必要がある。

v-for中の$indexの代わりに(value, index)で受ける

$indexは削除されたので、(value, index) in valuesの形で受ける必要がある。

v-elとv-refはref="hoge"とvm.$refs.hogeへ

テンプレート中のDOM要素及びコンポーネントをスクリプトから利用できるv-refはref属性に変更になった。ref="hogehoge"とつけると、this.$refs.hogehogeと参照できる。

transitionの構造変更

transitionの構造が変更になった。

今までは"transition"属性をつけると適宜v-transition, v-enter, v-leaveクラスが付与されるという形だったが、transition要素で囲むと〜-enter, 〜-enter-active, 〜-leave, 〜-leave-activeが付与されるようになった。

タイミングも違うので、「Transition Classes」の図を見て対処する必要がある。

おわりに

自分のところではまったのはこんな感じ。ガイドはもっと長いので、もっと大きなアプリケーションでは他にもあると思う。感想としては、それ削らなくてもいいじゃないという点もあるが、基本的にはテンプレートコンパイル結果を見て納得という感じだった。

Migration from Vue 1.x」の他に、Vue.js 2 アップデートメモも参考になる。

2016-08-08

Java → Kotlin 連携時は検査例外に注意する

Kotlinには検査例外はない(Kotlin does not have checked exceptions)。しかし、Javaにおいては検査例外として取り扱われるクラスを普通にthrowすることができる。

コード例にすると、以下の通り。




もともとJavaでも宣言していなくても検査例外を投げてくるときは投げてくる(ERR06-J. 宣言されていないチェック例外をスローしない)が、throwsが目印にもならないという点で注意が必要である。

2016-06-15

Rust で Git クライアントを書き始めた

Twitterでちょいちょいスクリーンショットをあげてたので、もはや見慣れた人もいるかもしれないですが、Xで使うためのGitクライアントを書き始めました。
https://github.com/sunnyone/metal-git

リリースにUbuntu 14.04でビルドしたバイナリが置いてありますが、おまけレベルです。


言語はRust。以前にチュートリアルっぽいものを書いたgtk-rsと、libgit2のラッパーであるgit2-rsを使って書いています。最初はコマンドのラッパーで行こうかなと思ったけど、今にして思うとgit2-rsの手助けがあって本当によかった。

Rustはまだまだ慣れてないので、ここはこうするとすっきり行くみたいなのは、細かいところでも教えてもらえるとうれしいです(ただマクロは控えられるところは控えようかなと思っていますが)。

もうちょっとそこそこ使えるようになってから公開しようかなとも思いましたが、まぁコミットの部分はとりあえず使えるようになったので書き始めたということで出そうかなと。無駄なコードが多いので結構恥ずかしいのだけど。

一応gtk-rsもgit2-rsのどちらもmingwでビルドできるみたいなことは見たので、Windowsでもビルドできるかもしれませんが、とりあえず当面は考えない方向でいきます。

ひとまずMetal Gitという名前にしました。良い名前があれば変えるかもしれません。

---

今のソースの規模はこんな感じ。モデルの分離もうまくできていないし、もう少し分割したい。
metal-git/src$ wc -l *.rs
  621 commit_window.rs
   81 gobject_utils.rs
   70 gtk_utils.rs
  166 history_window.rs
   40 main.rs
  337 railway.rs
   29 repository_manager.rs
   16 static_resource.rs
  241 station_cell_renderer.rs
   98 station_renderer.rs
  107 station_wrapper.rs
   62 window_manager.rs

作り始めたのはこれがきっかけなので、なんやかんやもう3ヶ月。長かったなぁ。


2016-05-10

Xubuntu 16.04 をインストールして最初に行う設定

Xubuntuは、XFceを使ったUbuntuのフレーバー。ここからダウンロードできる。デフォルトではGNOME2ライクな見た目の画面が使える。


そのままでも使えるのだが、自分はもうちょっとWindowsライクに使いたいので、いつもやっている設定を書いておく。

パネルを下側に移動する(いわゆる「タスクバー」を下に)

デフォルトでは上側にある黒いバー「パネル」には、デフォルトではアプリケーションを呼び出すメニュー、ウィンドウ一覧、インジケータ、時計が置いてある。要はタスクバーのようなものだ。これを下側に移動する。


下側に移動するには、パネルを右クリックし、パネル>パネルの設定を選択し、「パネル」ウィンドウを開く。

そして「パネルをロックする」チェックボックスをオフにして、パネル左側に出る取っ手を掴んで下に移動する。

Whisker Menu を押しやすくする(「スタート」ボタンを作る)

左下にある(左下に移動された)青いボタンを押すといわゆるスタートメニュー的なもの(Whisker Menuという名前らしい)が出てくるが、青いアイコンだけなので押しにくい。領域を増やして押しやすくする。

領域を増やすには、まずWhisker Menuを右クリックして「プロパティ」を選択し、「Whisker Menu」ウィンドウを開く。


「パネルボタン」の「表示」を「アイコンとタイトル」に変更して、「タイトル」を表示するようにする。「タイトル」はデフォルトでは「Application Menu」であるが、これはちょっと大きすぎるので自分は「Start」にしている。

時計の表示内容を変更

右下の時計はデフォルトでは「4 5月, 15:03」みたいな変な感じなので、カスタマイズしてそれっぽくする。

時計の表示内容をカスタマイズするには、時計を右クリックして「プロパティ」を選択して「時計」ウィンドウを表示する。


あとは「表示形式」を「カスタム」に設定し「%m/%d(%a) %H:%M:%S」などとする。選べる値はヘルプボタンを押せば飛べるがhttp://docs.xfce.org/xfce/xfce4-panel/clockを見ればわかる。

クイック起動(ピン)のショートカットを追加

クイック起動やピン留めのように、アプリケーションをパネルから起動したい。

パネルにアプリケーションの起動ボタンを配置するには、Whisker Menuを起動し、アプリケーションを右クリックして「パネルに追加」を選ぶ。

すると、パネル右側にアイコンが現れるので、右クリックして移動を選び、好きな場所に配置する。

これでだいたい定番の設定はおしまい。パネルを右クリックして「新しいアイテムの追加」を選ぶと、他にも増やせるものがあるので、好きに選ぶといいかも(ワークスペーススイッチャーとか)

おまけ: $HOMEのディレクトリを英語にする

デフォルトでは$HOMEに置いてある「デスクトップ」や「ピクチャ」はカタカナでcdしにくい。Xubuntuに限った話ではないが、これでは不便なのでいつもこれをしている。

$ LANG=C xdg-user-dirs-gtk-update

変えていいか確認するウィンドウが現れるので、従えばOK。

おまけ2: キーボードショートカットの変更(2017/6/1追記)

画面では設定>キーボード>アプリケーションショートカットキーから設定できる。

毎回やるのはだるいのでコマンドで覚えておく

# 一覧
xfconf-query -c xfce4-keyboard-shortcuts -lv

# Ctrl+Fn でのワークスペース切り替えを無効化
xfconf-query -c xfce4-keyboard-shortcuts -l | grep '/xfwm4/custom/F' | xargs -l1 xfconf-query -c xfce4-keyboard-shortcuts -r -p

# Ctrl+Alt+Lでのロックを無効化し、Super+Lに変更
xfconf-query -c xfce4-keyboard-shortcuts -p "/commands/custom/l" -r
xfconf-query -c xfce4-keyboard-shortcuts -p "/commands/custom/l" --create -t string -s xflock4

参考:ターミナルなどからXfceの設定をする