CORDEA blog

Android applications engineer

Docker で MediaWiki を立てる

自分の作っているライブラリで、WikipediaAPI 叩けないよー的な話が来たので
MediaWiki を Docker で立てて検証しようと思ったところ、
これが面倒だったので記録しておきます。

検証用にローカルで立てた手順です。

手順

Docker Hub で引っ張ってこられる MediaWiki はバージョンが古く、バグが残っていたため、最新のものを使用するよう修正しました
mediawiki-docker を clone します

$ git clone https://github.com/wikimedia/mediawiki-docker.git

Dockerfile を修正します
私の場合は以下のように修正しています

diff --git a/Dockerfile b/Dockerfile
index 5a4a8a4..115c32d 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,9 +1,9 @@
-FROM debian:sid
+FROM debian:latest
 MAINTAINER Gabriel Wicke <gwicke@wikimedia.org>
 
 # Waiting in antiticipation for built-time arguments
 # https://github.com/docker/docker/issues/14634
-ENV MEDIAWIKI_VERSION wmf/1.27.0-wmf.9
+ENV MEDIAWIKI_VERSION wmf/1.30.0-wmf.4
 
 # XXX: Consider switching to nginx.
 RUN set -x; \

Pull Request 投げようかとも思いましたが同じような奴が放置されてました...


build します

$ docker build --rm -t mediawiki .


今回は検証のために立てただけなのでパスワードとかも適当です
MySQL 5.7.5 以上だと動かないみたい*1のでご注意ください (もしかしたら修正されている問題かも)

$ docker run -it -d -e MYSQL_ROOT_PASSWORD=pass --name mysql mysql:5.6
$ docker run -it -d -e MEDIAWIKI_RESTBASE_URL="http://localhost/api.php" --name mediawiki --link mysql -p 80:80 mediawiki

これでとりあえずアクセスはできるようになったはず
後は、UI に従ってインストールします

データベースに接続の画面で入力するデータベースのホストは localhost じゃないです

$ docker inspect -f '{{ .NetworkSettings.IPAddress }}' mysql 


インストールが完了すると LocalSettings.php を移せと言われるので移します

$ docker cp ./LocalSettings.php mediawiki:/usr/src/mediawiki/  
$ docker exec -u www-data mediawiki ln -s /usr/src/mediawiki/LocalSettings.php /var/www/html/LocalSettings.php


これでとりあえず動くはず

余談

MediaWiki コンテナを restart しようとすると失敗します
この場合は entrypoint.sh を修正します

$ docker cp mediawiki:/entrypoint.sh ./
diff --git a/entrypoint.sh b/entrypoint.sh
index 37e7d33..40c14b9 100755
--- a/entrypoint.sh
+++ b/entrypoint.sh
@@ -115,7 +115,7 @@ EOPHP
 
 cd /var/www/html
 # FIXME: Keep php files out of the doc root.
-ln -s /usr/src/mediawiki/* .
+ln -sf /usr/src/mediawiki/* .
 
 : ${MEDIAWIKI_SHARED:=/data}
 if [ -d "$MEDIAWIKI_SHARED" ]; then
$ docker cp entrypoint.sh mediawiki:/
$ docker start mediawiki

Razer Chroma SDK を使って Keyboard をピカピカさせる

先日 Razer Blade Stealth を買ったのですが、
キーボードの光り方を見て何か面白いことできないかと思って調べたのでメモ

Razer Chroma SDK

Razer Developer Portal でダウンロードできます。

http://developer.razerzone.com/chroma/

あと、Razer Synapse が入ってて、Chroma 対応のデバイス接続してあると勝手にインストールされるみたいです。

Razer Synapse は入っているものの SDK がインストールされている気配はなく、
Razer Developer Portal で直接 SDK ダウンロードしてインストールしたら途中でエラー吐いて進みませんでした。

困った挙句に、Razer Synapse で Chroma アプリの On/Off をおもむろに切り替えつつ Synapse 再起動とかしてたら更新が降ってきて、
更新したらインストールされてました。謎

Colore

今回 C# で書いたので、Colore という Chroma SDK のライブラリを使用しました。

github.com

何ができるかは、Corale.Colore.Tester を動かすのが一番分かりやすいと思います。

ピカピカ

ようやく本題。
Visual StudioC# Console Application とかそこらへんの Project を作りつつ、Nuget から Colore をインストールする。
ここまで来たらほぼ終わり

Keyboard の Instance は以下のようにして取得します

Keyboard.Instance

Keyboard 全体を青色に光らせる

Keyboard.Instance.SetAll(Color.Blue);

A だけ赤色にする

Keyboard.Instance.SetKey(Key.A, Color.Red);

押されたキーを青色にする

Keyboard.Instance.SetReactive(new Reactive(Color.Blue, Duration.Short));

アニメーションとか

Keyboard.Instance.SetEffect(Effect.SpectrumCycling);
Keyboard.Instance.SetWave(Direction.RightToLeft);

Clear

Keyboard.Instance.Clear();

まとめ

vim とか Visual Studio, Atom とかと連携すると面白そう。

それはともかく Razer Core 全然入荷しない...Razer Blade Stealth だけ持っててもゲームできない...つらい

OCaml で Subcommand をパースする

Arg module のドキュメント読んでもどうすればいいかいまいち分からなかったので
OCaml で Subcommand とそのオプションを Arg module でパースする方法をメモしておきます。

何か間違っているところなどあればコメント、ツイートなど下さい。

実装

parse_dynamic を使用する

let usage = ""

let options = ref []

let hoge_options = [
    ("-hoge",
        Arg.Unit(fun () -> print_endline "hoge option"),
        "");
]

let huge_options = [
    ("-huge",
        Arg.Unit(fun () -> print_endline "huge option"),
        "");
]

let parse arg =
    if !Arg.current = 1 then
        match arg with
        | "hoge" -> options := hoge_options
        | "huge" -> options := huge_options
        | _ -> raise (Arg.Bad "Invalid subcommand")

let () =
    Arg.parse_dynamic options parse usage
;;

確認

$ ocamlbuild demo.native
$ ./demo.native hoge -hoge
hoge option
$ ./demo.native hoge -huge
demo.native: unknown option '-huge'.

  -help  Display this list of options
  --help  Display this list of options
$ ./demo.native huge -huge
huge option
$ ./demo.native huge -hoge
demo.native: unknown option '-hoge'.

  -help  Display this list of options
  --help  Display this list of options
$ ./demo.native demo
demo.native: Invalid subcommand.

  -help  Display this list of options
  --help  Display this list of options

参考

Io で Unicode エスケープ形式から元に戻す

Io language で API を叩いていて、
"\u3042" とか出てきて元に戻す必要があったので調べた

結論

私の見た限りでは、一つ method 呼べばなんとかなるようには見えなかったので、以下のようにしてとりあえず解決した
簡単な方法あったら教えてください

str matchesOfRegex("u[a-f0-9]{4}") replace(x, ("0x" .. (x string exSlice(1))) toBase(10) asNumber asCharacter)

str は元の文字列
こんな感じ

Io> str := "\u3042\u3042\u3042\u3042\u3042\u3042"
==> u3042u3042u3042u3042u3042u3042
Io> str matchesOfRegex("u[a-f0-9]{4}") replace(x, ("0x" .. (x string exSlice(1))) toBase(10) asNumber asCharacter)
==> ああああああ

解説っぽいやつ

最初の方は不要だと思うので途中から

x string exSlice(1)

ここの x には "u3042" が入っているはずなので、slice で u を捨てる (replace でよかったかも)
そのあと、16 進数から 10 進数に変換したいので

"0x" .. ...

ここで "0x" を足して "0x3042" とし、
その上で 10 進数に変換 (toBase(10)) する

そうするとここで得られるのは "12354" という文字列 (Sequence)
このまま asCharacter に渡すと怒られるので、Number に変換する (asNumber)

asCharacter は渡された値に対して有効な UCS マッピングがあればそれにあたる文字を返してくれる
なので例えば先ほどの 12354 を渡すと "あ" が返ってくる

Io> 12354 asCharacter
==>

Factor で http-request の response body が byte-array になった時

あるサービスの API レスポンスで body が byte-array になったので調べた

結論

charset が指定されていなくて byte-array になったら適切な形で decode する
utf-8 なら

http-get drop body>> utf8 decode json> .

詳細

サーバー側で charset が指定されていない場合は content-encoding が binary になるので
response body は byte-array になります.

例えば,以下のようなコードを書いた場合

"http://example.com" http-get drop .
charset が指定されていない場合
T{ response
    { version "1.1" }
    { code 200 }
    { message "OK " }
    { header
        H{
            { "connection" "close" }
            { "date" "Sun, 29 Jan 2017 08:48:16 GMT" }
            { "content-length" "12" }
            { "x-content-type-options" "nosniff" }
            { "server" "WEBrick/1.3.1 (Ruby/2.4.0/2016-12-24)" }
            { "content-type" "application/json" }
        }
    }
    { cookies { } }
    { content-type "application/json" }
    { content-encoding binary }
    { body B{ 123 34 115 116 97 116 117 115 34 58 49 125 } }
}
charset が指定されている場合
T{ response
    { version "1.1" }
    { code 200 }
    { message "OK " }
    { header
        H{
            { "connection" "close" }
            { "date" "Sun, 29 Jan 2017 08:47:25 GMT" }
            { "content-length" "12" }
            { "x-content-type-options" "nosniff" }
            { "server" "WEBrick/1.3.1 (Ruby/2.4.0/2016-12-24)" }
            { "content-type" "application/json; charset=utf-8" }
        }
    }
    { cookies { } }
    { content-type "application/json" }
    { content-charset "utf-8" }
    { content-encoding utf8 }
    { body "{\"status\":1}" }
}

charset が指定されていないと,いざ body 取って json として読み込もうとした場合に

Generic word json> does not define a method for the byte-array class. 

で怒られます.

こういう場合は

"http://example.com" http-get drop body>> utf8 decode json> .

のように decode して読み込む.

以上,charset 大事という話でした.

Nim の pegs モジュールを使う

この記事は Nim Advent Calendar 2016 24 日目の記事です。

クリスマスとか年末に絡めた何かを考えていたのですが、思いつかなかったので普通に書きます。

PEG とは

PEG について、解説的なものを入れようかと思ったのですが、
Wikipedia がとても詳しく、私が生半可な知識で書くのも良くないのでそちらをご覧ください。
https://ja.wikipedia.org/wiki/Parsing_Expression_Grammar

使ってみる

使い方は PEG に慣れていればなんの抵抗もないはずですが、
私のように正規表現しか書いたことのない人間には馴染みの薄い物です。

では先日使用した json をそのまま流用して試してみます。
使用した jsonjson.org の JSON Example の一番上のやつです。
http://json.org/example.html
この json を example.json として保存し、使用しています。

"SortAs": "SGML",

json からkey, value (この場合は SortAs:SGML )を得たい場合
PEG ではこのように書くことができます

'"' {( \w )+} '":' \s+ '"' {( \w )+} '"' ','?

 

"Abbrev": "ISO 8879:1986",

この場合は

'"' {( \w )+} '":' \s+ '"' {( \w / \s / ':' )+} '"' ','?

となります。


これらの規則は非終端記号として定義できます。

grammer <- '"' {( \w )+} '":' \s+ '"' {( \w / \s / ':' )+} '"' ','?

key, value 部分をまとめて以下のような記述にもできます。

grammer <- '"' cap '":' \s+ '"' cap '"' ','?
cap <- {( \w / \s / ':' )+}


最後に、json 全体をパースしてみます

rule <- '{' \s+
    '"glossary":' \s+ '{' \s+
        title
        '"GlossDiv":' \s+ '{' \s+
            title
            '"GlossList":' \s+ '{' \s+
                '"GlossEntry":' \s+ '{' \s+
                    (glossDef / pair)*

glossDef <- '"GlossDef":' \s+ '{' \s+ pair '},' \s+
pair <- '"' cap '":' \s+ '"' cap '"' ','? \s+
title <- '"title":' \s+ '"' cap '",' \s+
cap <- {( \w / \s / [.,-:;] )+}


このままだと nim ではなく PEG の話になるので申し訳程度にコードと結果を載せておきます
ちなみに今回の json でしか機能せず汎用性は全くありませんので...

import pegs

const peg = """
rule <- '{' \s+
    '"glossary":' \s+ '{' \s+
        title
        '"GlossDiv":' \s+ '{' \s+
            title
            '"GlossList":' \s+ '{' \s+
                '"GlossEntry":' \s+ '{' \s+
                    (glossDef / pair)*

glossDef <- '"GlossDef":' \s+ '{' \s+ pair '},' \s+
pair <- '"' cap '":' \s+ '"' cap '"' ','? \s+
title <- '"title":' \s+ '"' cap '",' \s+
cap <- {( \w / \s / [.,-:;] )+}
"""
let str = "example.json".readFile()
if str =~ peg(peg):
    for i in 0..(matches.len - 1):
        if i < 2:
            echo "title: ", matches[i]
        else:
            if matches[i] != nil:
                if i mod 2 == 0:
                    echo "key: ", matches[i]
                else:
                    echo "value: ", matches[i]

この出力は以下の通りです。

title: example glossary
title: S
key: ID
value: SGML
key: SortAs
value: SGML
key: GlossTerm
value: Standard Generalized Markup Language
key: Acronym
value: SGML
key: Abbrev
value: ISO 8879:1986
key: para
value: A meta-markup language, used to create markup languages such as DocBook.
key: GlossSee
value: markup

参考

nim で json を扱う話

この記事は Nim Advent Calendar 2016 18 日目の記事です。

さて、ネタが被ったような気がしないでもないのですが
nim で json 扱うのが楽ですよって話をします。

json の扱いの簡単さはとても大切ですね。

今回例として使用した jsonjson.org の JSON Example の一番上のやつです。
http://json.org/example.html
この json を example.json として保存し、使用しています。

Version

Nim Compiler Version 0.15.2 (2016-12-18) [MacOSX: amd64]

json module を使う

json module を使えば json を簡単に扱うことができます

json -> JsonNode
import json

when isMainModule:
    let
        jsonStr = "example.json".readFile()
        jsonObj = parseJson jsonStr
    echo jsonObj["glossary"]["title"].str
  • Result
example glossary
JsonNode -> json
import json

when isMainModule:
    let
        jsonStr = "example.json".readFile()
        jsonObj = parseJson jsonStr
    echo $jsonObj
  • Result
{"glossary":{"title":"example glossary","GlossDiv":{"title":"S","GlossL...

簡単ですが、用途によってはどこか物足りません。
やっぱり object に詰めて欲しいですよね。

そこで出てくるのが marshal module です。

marshal module を使う

marshal module を使えば json <-> object が実現できます
準備として example.nim として以下を用意しました

type
    Base = object of RootObj

    Example* = object of Base
        glossary*: Glossary

    Glossary* = object of Base
        title*: string
        GlossDiv*: GlossDiv

    GlossDiv* = object of Base
        title*: string
        GlossList*: GlossList

    GlossList* = object of Base
        GlossEntry*: GlossEntry

    GlossEntry* = object of Base
        ID*: string
        SortAs*: string
        GlossTerm*: string
        Acronym*: string
        Abbrev*: string
        GlossDef*: GlossDef
        GlossSee*: string

    GlossDef* = object of Base
        para*: string
        GlossSeeAlso*: seq[string]
json -> object
import example, marshal

when isMainModule:
    let
        jsonStr = "example.json".readFile()
        ex = to[Example] jsonStr
    echo ex.glossary.title
  • Result
example glossary
object -> json
import example, marshal

when isMainModule:
    let
        ex = Example()
    echo $$ex
  • Result
{"glossary": {"title": null, "GlossDiv": {"title": null, "GlossL...

自分で object に詰める

さきほどの marshal module はとてもシンプルなもので
例えば json の key と object の key を変えたい場合などに対応することはできません。(できなかったはず...)

そのため、json の key に proc などがあるとつらい感じになります。
そういう場合は、自分で object に詰めてしまうのが楽だと思います。

今回の json は string と array しか出てこないので実装はこの程度でした

import json, sequtils

proc `[]`(t: JsonNode, key: string): JsonNode =
    result = if t.hasKey(key): json.`[]`(t, key) else: nil

proc to[T: object](node: JsonNode, data: var T) =
    for k, v in data.fieldPairs:
        when v is string:
            v = node[k].getStr
        elif v is seq:
            if node[k] == nil:
                v = @[]
            else:
                v = node[k].elems.map(proc(x: JsonNode): string = x.str)
        else:
            node[k].to v
  • Usage
import example, json, sequtils

...

when isMainModule:
    let
        jsonStr = "example.json".readFile()
        jsonObj = parseJson jsonStr
    var ex: Example
    jsonObj.to ex
    echo ex

実装も簡単ですし、依存も減るし、諸々の仕様に柔軟に対応できるので
状況次第では自分で実装してしまうのも悪くないかなと思っています。

それでは。