お知らせ

現在サイトのリニューアル作業中のため、全体的にページの表示が乱れています。
投稿日:
言語::PHP

手順

  1. composer initでプロジェクトを作成
  2. composer require [パッケージ名]でパッケージを入れる
  3. 名前空間を作る場合、 composer.jspnに以下のセクションを作成
    1. リファレンス: getcomposer.org
"autoload": {
    "psr-4": {
        "App\\": "src/App" // 名前空間と対応するパス
    },
    "classmap": [
        "src/App" // クラスを読み込むディレクトリのルート
    ]
}
  1. autoloadclass_aliasなどを設定するbootstrap.phpを適当に作成
    1. このbootstrap.phpはエントリポイントからrequire_onceなどで読み込無事で有効化する
  2. phpunitを組み込む場合
    1. composer require --dev phpunit/phpunitでインストール
    2. composer.jsonに以下のセクションを作成
    "scripts": {
        "test": [
            "phpunit --bootstrap bootstrap.php test"
        ]
    }
投稿日:
言語::JavaScript::CommonJSNode.js::Jest開発::テスト

確認環境

Env Ver
Node.js 12.18.3
Jest 26.4.2

やりたいこと

以下の実装のときに、parentFunc()を呼んだ時にchildFunc()が呼ばれることをテストしたい

function parentFunc() {
  console.log('called parentFunc');
  childFunc('XXXX');
}

function childFunc(param) {
  console.log(`called childFunc ${param}`);
}

各ケース紹介

Case1 そもそも構文がおかしい

テストするためには関数をexportする必要あるが、愚直過ぎて構文的に実行不能になるケース

exports.parentFunc = () => {
  console.log('called parentFunc');
  // childFuncは別モジュールなので呼べない
  childFunc('XXXX');
};

exports.childFunc = (param) => {
  console.log(`called childFunc ${param}`);
};

Case2 スコープ違いでテストが失敗する

この実装を実行すると期待通り動作するので、一見すると大丈夫そうに見える

function parentFunc() {
  console.log('called parentFunc');
  childFunc('XXXX');
}

function childFunc(param) {
  console.log(`called childFunc ${param}`);
}

module.exports = { parentFunc, childFunc };

しかしこのテストを流すと失敗する
これはparentFunc()が呼び出すchildFunc()が下記case2の中にないため
parentFunc()のスコープ内にchildFunc()がいないことが原因

const case2 = require('./case2');
// こうするとjest.spyOn()の第一引数を満たせないので落ちる
// const { parentFunc, childFunc } = require('./case2');

describe('inside call test', function () {
  it('parentFunc', function () {
    const spy = jest.spyOn(case2, 'parentFunc');
    case2.parentFunc();
    expect(spy).toHaveBeenCalled();
  });
  it('childFunc', function () {
    const spy = jest.spyOn(case2, 'childFunc');
    case2.parentFunc();
    // childFuncはcase2に属していないため呼ばれない
    expect(spy).toHaveBeenCalled();
  });
});

Case3 テストが成功するケース

const parentFunc = () => {
  console.log('called parentFunc');
  // parentFunc()の中にchildオブジェクトを注入することで、
  // jestがchildFunc()を認識できるようにする
  child.childFunc('XXXX');
};

const child = {
  childFunc: (param) => {
    console.log(`called childFunc ${param}`);
  },
};

// childFuncでなく、childオブジェクトをexportするのが味噌
module.exports = { parentFunc, child };
const case3 = require('./case3');

describe('inside call test', function () {
  it('parentFunc', function () {
    const spy = jest.spyOn(case3, 'parentFunc');
    case3.parentFunc();
    expect(spy).toHaveBeenCalled();
  });
  it('childFunc', function () {
    // 注入している側のオブジェクトを参照する
    const spy = jest.spyOn(case3.child, 'childFunc');
    case3.parentFunc();
    // child.childFuncはcase3に属しているため呼ばれる
    expect(spy).toHaveBeenCalled();
  });
});
投稿日:
言語::正規表現

パターンを思い付く都度に更新される予定。

yyyy-MM-dd HH-mm-ssをyyyy-m-dに変換

s/(\d{4})-0?(\d{1,2})-0?(\d{1,2}).+/$1-$2-$3/

3桁区切りカンマ

s/(\d)(?=(\d{3})+(?!\d))/$1,/g

直後に\d{4}でなく、\d{3}のパターンのある\dをキャプチャし、カンマを付与

ネストプロパティの2段目(bar)以降を配列に置換

s/(?<=\$request->.+?)->((?<!\$request->)(?<=->)([a-z_]+))+/['$2']/gi
  1. まず(?<=\$request->.+?)はキャプチャされず、この次に->があるのが前提、->は削除するので先読み・後読み・キャプチャはしない
  2. 次に式などで文章が両辺にあることを考慮して、文字列内に\$request->が再度含まれないことを(?<!\$request->)として条件にする
    1. これを入れてないと一行中の一文と解釈されるので、別の文と解釈させるため
  3. 次にプロパティ名の前には->が来るので、それを(?<=->)として条件付けする
  4. 最後に([a-z_]+)でプロパティ名であろうものを指定する
  5. 2-4の繰り返し(ルートにある2個目のカッコ)に対して置換を掛けることでプロパティ名を配列に置換できる
  6. 但し1で指定している->単体は繰り返し対象外なのに繰り返し適用されているが、この理由はよくわかってない
    1. 恐らく単純に(?<=\$request->.+?)の後ろにある->が全部消えている?
// source text
$request->foo
$request->foo;
$request->foo->bar;
$request->foo['bar'];
$request->foo->bar->fb->fBfb;
$arr = [
  'bar' => $request->foo->bar
];
if ($request->foo->bar->fb= $request->foo->bar->fb) {
  $request->foo->bar->fb = $request->foo->bar->fb;
}

// replaced text
$request->foo
$request->foo;
$request->foo['bar'];
$request->foo['bar'];
$request->foo['bar']['fb']['fBfb'];
$arr = [
  'bar' => $request->foo['bar']
];
if ($request->foo['bar']['fb']= $request->foo['bar']['fb']) {
  $request->foo['bar']['fb'] = $request->foo['bar']['fb'];
}

但しメソッドは対象外にしたい場合

s/(?<=\$request->.+?)->((?<!\$request->)(?<=->(?![a-z_]+\(\)))([a-z_]+))+/['$2']/gi
  • ->の後に来るメソッド構文を除外するために、(?![a-z_]+())を追加
  • (?<=->(?![a-z_]+()))とすることで->の後に来る(?![a-z_]+())を除外しつつ、->の後に来る([a-z_]+)をキャプチャできる
// source text
$request->foo
$request->foo;
$request->foo->bar;
$request->foo['bar'];
$request->foo->fooBar();
$request->foo->bar->fooBar();
$arr = [
  'bar' => $request->foo->bar
];
if ($request->foo->bar->fb= $request->foo->bar->fb) {
  $request->foo->bar->fb = $request->foo->bar->fb;
}

// replaced text
$request->foo
$request->foo;
$request->foo['bar'];
$request->foo['bar'];
$request->foo->fooBar();
$request->foo['bar']->fooBar();
$arr = [
  'bar' => $request->foo['bar']
];
if ($request->foo['bar']['fb']= $request->foo['bar']['fb']) {
  $request->foo['bar']['fb'] = $request->foo['bar']['fb'];
}

参考

投稿日:
言語::PythonOS::Linuxジャンル::ガジェット

Pythonでデーモンを作ったので、その手順のメモ

確認環境

Env Ver
OS 2020-05-27-raspios-buster-arm64
Python 3.7.3
python3-systemd 234-2+b1

やったこと

  1. python-systemdのインストール
    1. sudo apt-get install python-systemd python3-systemd
  2. ~/foo.pyを以下の内容で作成
from systemd import journal
journal.send('Hello world')
journal.send('Hello, again, world', FIELD2='Greetings!', FIELD3='Guten tag')
journal.send('Binary message', BINARY=b'\xde\xad\xbe\xef')
  1. 実行権限を付与する
    1. chmod 755 ~/foo.py
  2. systemdの定義ファイルを次の要領で作成
    1. sudo nano /etc/systemd/system/foo.serviceとして以下の内容を書く
[Unit]
Description = Foo

[Service]
ExecStart = python3 /home/pi/foo.py
Restart = always
Type = simple

[Install]
WantedBy = multi-user.target
  1. デーモンを有効化する
    1. sudo systemctl enable foo.service
  2. デーモンを開始する
    1. sudo systemctl start foo.service
  3. ジャーナルを見る
    1. systemctl status roomenv.service
  4. なんかログが出てれば成功
    1. ExecStartsudoを書くと動かない(root権限で動作する模様)
投稿日:
言語::C#

なぜこんな実装が必要なのかさっぱり不明ですが、必要になって困ったのでメモ。

DataTableを内部結合したList<Dictionary>をLINQで作る

var loc = new DataTable();
var pep = new DataTable();
loc.Columns.Add("ID");
loc.Columns.Add("LOCATION");
loc.Columns.Add("DELETE_FLG");
loc.Rows.Add("100", "US", "0");
loc.Rows.Add("101", "AU", "1");
loc.Rows.Add("102", "JP", "1");
loc.Rows.Add("103", "CH", "1");
loc.Rows.Add("104", "UK", "1");
loc.Rows.Add("105", "RU", "1");

pep.Columns.Add("LOCATION_ID");
pep.Columns.Add("NAME");
pep.Columns.Add("DELETE_FLG");
pep.Rows.Add("100", "TAKANA", "0");
pep.Rows.Add("101", "YAKATA", "1");
pep.Rows.Add("100", "WADA", "1");
pep.Rows.Add("101", "SHINODA", "1");
pep.Rows.Add("100", "HARUKA", "1");
pep.Rows.Add("105", "SAIONJI", "0");


var result = (
    from l in loc.AsEnumerable()
    join p in pep.AsEnumerable()
    on new {
        a = l.Field<string>("ID"),
        b = l.Field<string>("DELETE_FLG")
    } equals new {
        a = p.Field<string>("LOCATION_ID"),
        b = p.Field<string>("DELETE_FLG")
    }
    select new Dictionary<string, string> {
        {
            "LOCATION_ID",
            l.Field<string>("ID")
        },
        {
            "NAME",
            p.Field<string>("NAME")
        }
    }
).ToList();

List<Dictionary>DataTableを外部結合してList<Dictionary>を操作する

var location = new List<Dictionary<string, string>>();
var fruits = new DataTable();

location.Add(
    new Dictionary<string, string> {
        {
            "ID",
            "001"
        },
        {
            "NAME",
            "Hokkaido"
        }
    }
);
location.Add(
    new Dictionary<string, string> {
        {
            "ID",
            "002"
        },
        {
            "NAME",
            "Aomori"
        }
    }
);
location.Add(
    new Dictionary<string, string> {
        {
            "ID",
            "003"
        },
        {
            "NAME",
            "Iwate"
        }
    }
);

fruits.Columns.Add("LOCATION_ID");
fruits.Columns.Add("NAME");
fruits.Columns.Add("FRUITS_FLG");
fruits.Rows.Add("001", "ikura", "1");
fruits.Rows.Add("001", "uni", "1");
fruits.Rows.Add("002", "ringo", "0");

// dummyは処置待ち用のダミー。locationの操作が目的
var dummy = location
    .Join(
        fruits.AsEnumerable()
        , loc => loc["ID"]
        , fruit => fruit.Field<string>("LOCATION_ID")
        , (loc, fruit) => new { lo = loc, fru = fruit }
    )
    .Select(
        joined => {
            joined.lo["FRUITS_NAME"] = joined.fru.Field<string>("NAME");
            joined.lo["FRUITS_FLG"] = joined.fru.Field<string>("FRUITS_FLG");
            return new Dictionary<string, string>();
        }
    )
    .ToList();