Test::Specに関するメモ

2024年にもなってperl5の話題。

古いスクリプトをいじっているとTest::Moreだけで書いてあったテストが読みづらくて仕方なかったのでTest::Specで書き直してみた。

以下のような書き方が良い。

describe "ほげほげすると" => sub {
  it "ふがふができる" => sub {
    ok $result or diag "情報";
  };
};

こうするとテスト失敗時に以下のような出力となる。

#   Failed test 'ほげほげすると ふがふができる'
#   at example.t line 10.
# 情報

ポイントは`ok`の使い方。

`ok($result,"test name")`と書いてしまうと、test nameが出力されてしまい、describeとitに書いた文が出力されない。

またdiagを書けばテスト失敗時の手がかりを増やせる。

俺はただwindows 10のvscodeからgit pushしたかっただけ

前書き

Visual Code Studioが最近人気ですね。(要出典)

会社のMacで便利に使ってます。

家のWindowsでも使いはじめたのですが、なぜかvscodeのgit pushを実行するとPermission Deniedのアラートが表示されてしまっていました。

Git Bashからならば(毎回sshパスフレーズは聞かれていましたが)git push出来ていたので放置していたのですが、ちょっと気になってきたので腰をあげて解決することにしました。

状況

Version Control in Visual Studio Code にてWindowsVisual Studio CodeでGitを使う場合はGit for Windowsをインストールすることが必要とされていたため従いました。

Git for Windowsをインストールしたし、bashに慣れていたため、Integrated Terminal in Visual Studio Code に従いvscodeのターミナルにGit Bashを採用しました。

解決例

まとめ

  1. 2019/3時点でWindows 10にインストール済みのOpenSSH(ssh.exe)を使う
  2. ssh-agent.exeを使う(windowsのサービスとして起動する)
  3. Windows起動時に毎回ssh-add.exeで秘密鍵ssh-agent.exeに登録する

 これでvscodeからもGit Bashからもgit pushできるようになりました。

詳細

2019/3時点でWindows 10にインストール済みのOpenSSH(ssh.exe)を使う

vscodeからgit pushするときにOpenSSHを使うため、C:\Users\<username>\.gitconfigに以下を記述。


[core]
        sshCommand = C:/Windows/System32/OpenSSH/ssh.exe

 

Git Bashからgit pushするときに使われるsshをOpenSSHにするため、C:\Users\<username>\.bash_profileに以下を記述。

alias ssh="/c/Windows/System32/OpenSSH/ssh.exe"

おまけで、WindowsのOpenSSHを使うようになったので、C:\Users\<username>\.ssh\configにあるIdentityFileのパス記述形式を修正。

Host bitbucket.org
  IdentityFile C:\Users\<username>\id_rsa
  User git
ssh-agent.exeを使う(windowsのサービスとして起動する)

こちらの通り。

qiita.com

Windows起動時に毎回ssh-add.exeで秘密鍵ssh-agent.exeに登録する

コマンドプロンプトを起動し以下を実行。

C:\Users\ajishixo>ssh-add C:\Users\<username>\id_rsa
Enter passphrase for C:\Users\<username>\id_rsa:
Identity added: C:\Users\<username>\id_rsa (C:\Users\<username>\id_rsa)

試行錯誤のメモ(箇条書き)

VSCodeをUbuntu 14.04 (32bit)にインストールする方法

2017/05/14での状況です。(この時点であまり存在しない環境だろうけどメモ。)

成功事例

まずは以下から .deb (32 bit) をダウンロード。

code.visualstudio.com

そして以下のコマンドを実行。

$ sudo dpkg -i <file>.deb

終わり。

失敗事例

.tar.gzをダウンロードして展開したものを起動すると以下のエラーが出力されて失敗する。

$ bin/VSCode-linux-ia32/Code 

[6064:0514/111845:ERROR:browser_main_loop.cc(173)] Running without the SUID sandbox! See https://code.google.com/p/chromium/wiki/LinuxSUIDSandboxDevelopment for more information on developing with the sandbox on.

bash: 端末プロセスグループを設定できません (-1): デバイスに対する不適切なioctlです

bash: このシェルではジョブ制御が無効になっています

[6064:0514/111852:ERROR:desktop_window_tree_host_x11.cc(810)] Not implemented reached in virtual void views::DesktopWindowTreeHostX11::InitModalType(ui::ModalType)

調べた限りではLinux SUID Sandboxは使えず、むしろ使わないほうが良いとのこと。

https://chromium.googlesource.com/chromium/src/+/master/docs/linux_suid_sandbox_development.md

Cloud Functions for FirebaseでVue.jsのSSRを試してみる

Firebaseがcloudfunctions.netと連携したことにより、Javascriptを使って動的にHTMLを構築して返すことが出来るようになりました。

さらに流行りのVue.jsによるサーバサイドレンダリングが出来れば最高です。試してみます。

※以下はfirebase上にprojectを作成済みであるとして記述しています。

init&deploy

まずは動的にHTMLを返すまでの手順を確認します。

$ firebase init functions
$ tree -L 2
.
├── firebase.json
└── functions
    ├── index.js
    ├── node_modules
    └── package.json

functions/index.jsを開いてコメントアウトされている行を有効にします。

var functions = require('firebase-functions');

exports.helloWorld = functions.https.onRequest((request, response) => {
  response.send("Hello from Firebase!");
})

そしてfirebase deployコマンドでデプロイします。

$ firebase deploy
// 省略
Function URL (helloWorld): https://us-central1-(project).cloudfunctions.net/helloWorld

出力されたURLにWebブラウザでアクセスすると"Hello from Firebase!"と表示されました。

Vue.jsの導入

サーバサイドレンダリングはクライアントサイドレンダリングとの共通化を考えなければシンプルです。

まずは必要なnpmをダウンロードします。

$ cd functions
$ npm install vue vue-server-renderer --save

またfunctions/index.jsを開いて編集します。


var Vue = require('vue');
var renderer = require('vue-server-renderer').createRenderer();
var functions = require('firebase-functions');

exports.helloWorld = functions.https.onRequest((request, response) => {
  renderer.renderToString(
    new Vue({
      template: ''
        + '<!DOCTYPE html>'
        + '<html>'
        + '<head>'
        + '  <title>Vue SSR Sample</title>'
        + '</head>'
        + '<body>'
        + '  <p>Server Side Rendered</p>'
        + '</body>'
        + '</html>'
    }), 
    (error, html) => {
      if (error) { return response.status(500).send('Server Error') }
      response.send( html )
    }   
  )
})

これでfirebase deployすると"Vue SSR Sample"というタイトルのページが返るようになりました。

realtime databaseの読み書き

サーバサイドレンダリングに出来てクライアントサイドレンダリングに出来ないこととして大きなことは、metaタグの出力を動的に変更することです。

realtime databaseにあるデータを出力するようにコードを修正します。

まずはdatabaseにデータを入れます。以下は初期化から行っています。


$ firebase init database
$ echo '{"description": "Vue SSR Sample with Firebase Realtime Database"}' | firebase database:set /helloworld --confirm
$ firebase database:get /
{"helloworld":{"description":"Vue SSR Sample with Firebase Realtime Database"}}

そしてmeta descriptionをdatabaseの内容に書き換えるようコードを修正します。


var Vue = require('vue');
var renderer = require('vue-server-renderer').createRenderer();
var functions = require('firebase-functions');
var admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);

var data = {description: ""};
var vm = new Vue({
  template: ''
    + '<!DOCTYPE html>'
    + '<html>'
    + '<head>'
    + '  <title>Vue SSR Sample</title>'
    + '  <meta name="description" v-bind:content="description" />'
    + '</head>'
    + '<body>'
    + '  <p>Server Side Rendered</p>'
    + '</body>'
    + '</html>',
  data: data
});

exports.helloWorld = functions.https.onRequest((request, response) => {
  admin.database().ref("/helloworld/description").once("value")
    .then(snapshot => {
      data.description = snapshot.val();
      renderer.renderToString(
        vm, 
        (error, html) => {
          if (error) { return response.status(500).send('Server Error') }
          response.send( html )
        }   
      )   
    })  
    .catch(error => {
      console.log(error);
    })  
  ;
})

webブラウザでアクセスしてhtmlソースコードを見てみるとmeta descriptionが期待通りに「Vue SSR Sample with Firebase Realtime Database」になっていました。

今後

クライアントサイドレンダリングとサーバサイドレンダリングの共通化を試してみたいですね。

また別の話ですけど生成されるURLがシンプルではないので改善されることを期待してます。

perl6をインストールした後cat.plをcat.p6に書き換える

リリースされてそろそろ一年。

使ってみないとperl6のことがわからないので使ってみる。

まずは一番単純なcatコマンドのように動くコマンド作りから。

rakudo & panda インストール

rakudobrewというPerlBrewのようなスクリプトでインストール。簡単。

How to get Rakudo Perl 6 | rakudo.org

ドキュメントをインストール

そもそもperl6の書き方がわからない。

webで調べてもいいのですが、perldocを重宝していたので同じものが無いかと調べてみると p6doc というものがあったのでインストール。

$ panda install p6doc

自分の環境では libssl-dev の追加が必要だった。

また、p6docコマンドがPATHの通ったところに置かれなかったので調整した。

さらに、使っていると以下のコマンドの実行を勧められたので実行した。

$ p6doc-index build

cat.plからcat.p6へ

cat.pl

cat.plの挙動は以下を想定している。

$ cat helloworld
Hello
World

$ ./cat.pl helloworld
Hello
World

$ cat helloworld | ./cat.pl
Hello
World

実装は以下の通り。

#!/usr/bin/env perl

use strict;
use warnings;

while (<>) {
    print;
}

cat.p6

先に結果を載せておく。

#!/usr/bin/env perl6

use strict;

for (lines) {
    .say;
}

try & error

perl6で実行

cat.plをperl6で実行するよう書き換え。

#!/usr/bin/env perl6

use strict;
use warnings;

while (<>) {
    print;
}

実行すると以下のエラーが出る。

$ ./cat.pl helloworld 
===SORRY!===
Could not find warnings at line 4 in:
    /home/ajishixo/.perl6
    /home/ajishixo/.rakudobrew/moar-nom/install/share/perl6/site
    /home/ajishixo/.rakudobrew/moar-nom/install/share/perl6/vendor
    /home/ajishixo/.rakudobrew/moar-nom/install/share/perl6
    CompUnit::Repository::AbsolutePath<189992464>
    CompUnit::Repository::NQP<191589208>
    CompUnit::Repository::Perl5<191589232>

warningsプラグマは無くなった様子。

use warnings を削る

#!/usr/bin/env perl6

use strict;

while (<>) {
    print;
}

実行した結果

$ ./cat.pl helloworld 
===SORRY!=== Error while compiling /home/ajishixo/dev/perl6/./cat.pl
Unsupported use of <>; in Perl 6 please use lines() to read input, ('') to represent a null string or () to represent an empty list
at /home/ajishixo/dev/perl6/./cat.pl:5
------> while (<⏏>) {

ダイアモンド演算子も無くなった様子。

ダイアモンド演算子をlinesに書き換え

#!/usr/bin/env perl6

use strict;

while (lines) {
    print;
}
$ ./cat.pl helloworld 
===SORRY!=== Error while compiling /home/ajishixo/dev/perl6/./cat.pl
Unsupported use of bare "print"; in Perl 6 please use .print if you meant $_, or use an explicit invocant or argument, or use &print to refer to the function as a noun
at /home/ajishixo/dev/perl6/./cat.pl:6
------>       print⏏;

print;print $_;$_を省略した形でしたが、$_.print;として$_を省略し.printという形になるのですね。

printを.printに書き換え

#!/usr/bin/env perl6

use strict;

while (lines) {
    .print;
}

実行すると

$ ./cat.pl helloworld 
Use of uninitialized value of type Any in string context.
Methods .^name, .perl, .gist, or .say can be used to stringify it to something meaningful.
  in block <unit> at ./cat.pl line 6
Use of uninitialized value of type Any in string context.
Methods .^name, .perl, .gist, or .say can be used to stringify it to something meaningful.
  in block <unit> at ./cat.pl line 6

stringのコンテキストでAny型の初期化されていない値が使われているとのこと。 Any型とはなんぞや、ということで先ほど入れたp6docに尋ねてみる。が、イマイチ分からず。。 .sayの使用をすすめられたので書き換える。

printをsayに書き換え

#!/usr/bin/env perl6

use strict;

while (lines) {
    .say;
}

実行すると

$ ./cat.pl helloworld 
(Any)
(Any)

またもAny。。

linesの戻り値が気になるのでp6docに尋ねてみる。

$ p6doc -f lines
We have multiple matches for 'lines'

        Type::Cool.lines Type::Str.lines
        Type::Supply.lines Type::IO::Handle.lines Type::IO::Socket::INET.lines

今回はファイルの読み込みなので Type::IO::Handle.lines を選ぶ。

$ p6doc -f Type::IO::Handle.lines
  method lines

    method lines($limit = Inf)

Return a lazy list of the file's lines read via get, limited to $limit
lines. The new line separator (i.e., $*IN.nl-in) will be excluded.

    my @data; 
     my $data-file = open 'readings.csv'; 
     for $data-file.lines -> $line { 
         @data.push($line.split(',')) 
     } 
    

分からん。

紆余曲折し、以下の記述にたどり着く。

$ p6doc variables
// 略
Calling a method on $_ can be shortened by leaving off the variable name:

    .say;                   # same as $_.say

m/regex/ and /regex/ regex matches and s/regex/subst/ substitutions work on
$_: 

    say "Looking for strings with non-alphabetic characters...";
    for <ab:c d$e fgh ij*> {
        .say if m/<!alpha>/;
    }

whileをforに書き換え

#!/usr/bin/env perl6

use strict;

for (lines) {
    .say;
}

実行して無事成功。

$ ./cat.pl helloworld 
Hello
World

$ cat helloworld | ./cat.pl
Hello
World

拡張子を変更

$ mv cat.pl cat.p6

おわりに

なぜwhileではダメでforなら良かったのかはp6doc controlを読んでみたけど分からず。

今後の使用で理解していこう。

ubuntu 14.04 から ATERM WG1800HP2 のUSBストレージにつながらない不具合を解消

以前に上手く使えていたのにいつのまにかパスワードが通らなくなってしまっていた。

iPhoneの Remote File Manager Free からは接続できているので、クライアントである Ubuntu 側の問題であると判断。

smbclientコマンドを実行した結果、以下のような出力があった。

$ smbclient //ATERM-HOGE/Fuga -Uuser
Enter user's password: 
Server does not support EXTENDED_SECURITY  but 'client use spnego = yes and 'client ntlmv2 auth = yes'
session setup failed: NT_STATUS_ACCESS_DENIED

spnegoで調べたところ、下記資料が見つかったので、/etc/samba/smb.confを修正。

smbclientでWindows 7に接続しようとするとsession setup failed: SUCCESS - 0:ほぬほぬの足跡:So-netブログ

# [global]表記直下に追記
client lanman auth = Yes
client ntlmv2 auth = No
client use spnego = No

無事にアクセスできた。

さくらのVPS上でdocker -dを実行するために四苦八苦

dockerをインストールするのは簡単。

docker -dを実行しようとするとkernelのバージョンが合わないとのこと。

対処として、kernelのバージョンアップを行うか、VPSなのでkernelバージョンの合うOSに入れ替えるかのどちらかが選べたが、行ったことが無いkernelバージョンアップを選択。

CentOS6.3をLinuxカーネル3.6にアップデートしてみました(カーネル再構築)|リナックスマスター.JP 公式ブログ

上記を参考に 3.19.8 を入れた。

さーてdockerは動くかな、と試してみると今度はiptablesのバージョンが合わないということなので、以下を参考に作業。

Ubnutu 14.04 LTSで最新版のiptablesを使う | ビットログ

libmnl, libnftnlもビルドしつつpkg-configやalternativesなんてものがあることを知る。

alternativesについては下記を参考にした。

replace centos 6.x iptables · GitHub

そこでdocker -dを実行すると、また問題が発生。iptables で nat が無効になっていたようなのでkernelを再度ビルド。

iptables | すなのかたまり

make oldconfig のログを読み返すと、確かに新しい設定として検出されていました。ええ、Enter キーを長押しにしていたので気づきませんでしたとも。こんなの気付かないよなぁ・・。

共感しました。でもって参考にしつつmake menuconfigがうまく動作しなかったので愚直に.configを手修正しました。

もいちどdocker -dをして再度問題発生。

rhel6 - Docker on RHEL 6 Cgroup mounting failing - Stack Overflow

grub.confを編集してrebootしてもうまく行かず。

Dockerで`Error: cannot mount hugetlb to /cgroup/hugetlb`と言われた時の対処法 - Qiita

上記リンク先と同様にmemoryに関する行をコメントアウトして `$ sudo /etc/init.d/cgconfig restart` して無事起動。

ようやく`docker -d`も成功。

長かったー。