Kuzunoha-NEのブログ

プログラミングなどの勉強をしてます

【Markdown】シンタックスハイライトっていうのを使いましょう

葛の葉です。

マークダウンでしばしば色のついたコード書きを見るかなぁって思います。

import {hogehoge} from "./hogehoge"

console.log(JSON.stringify(hogehoge));

let hugahuga;

for ( let piyo of piyos ){
   hugahuga += piyo;
};

これはシンタックスハイライトといって、マークダウンについている機能のようです。はてなでもMarkdown形式なら使うことができます。結構いろんな言語に対応しています。

help.hatenablog.com

書き方は以下のように最初のバッククオート3つに対応する言語を書き込むだけです。

f:id:Kuzunoha-NE:20191029212957j:plain
シンタックスハイライトの書き方

【MongoDB】DockerComposeでクラスター構成のMongoDBを作る

こんばんは葛の葉です。今回から土曜更新に変更します。

さて、今回はDockerComposeでクラスター構成のMongoDBを構築します。

imageのバージョン

mongo:4.2.1-bionic

DockerComposeの記述

クラスター構成のDBを構築するのだったら、ネットワークやら何やら色々設定してくれるDockerComposeとの相性が良いでしょう。

version: '3'
services:
  a:
    image: mongo:4.2.1-bionic
    command: ["--replSet", "hogehoge"]

  b:
    image: mongo:4.2.1-bionic
    command: ["--replSet", "hogehoge"]

  c:
    image: mongo:4.2.1-bionic
    command: ["--replSet", "hogehoge"]

  node:
    image: node:8.16.1-alpine
    tty: true
    working_dir: /var/work
    volumes:
      - "./work:/var/work"

このような感じでcommand:["--replSet", "hogehoge"]と与えます。

docker-composeでのcommandはコンテナ生成時に実行されるコマンドです。そのため今回のような--replSet hogehogeというコマンドの指定の方法は正しくないように思えます。ですが、mongodbのイメージにおいてはmongodというコマンドをエントリーポイントにしているので、今回のこのような記述が可能となっています。

dockerにおけるエントリーポイントについては過去記事も参考にしてください。

kuzunoha-ne.hateblo.jp

--replSet hogehogeとはhogehogeという名前領域をもつレプリカセット名になります。このコマンドをそれぞれのMongoDBにこしらえることでhogehogeという領域の中で3つのMongodbが存在していることになりました。

docker-compose up -dとすれば4つのサービスが立ち上がります。

$ docker-compose up -d
Creating network "*****_default" with the default driver
Creating ***_b_1    ... done
Creating ***_node_1 ... done
Creating ***_c_1    ... done
Creating ***_a_1    ... done

そうして立ち上がったmongodbのサービス一つのMongoDBクライアントを呼び出し,rs.initiate()というコマンドを実施します。このrs.initiate()こそがレプリカセットを構築するコマンドになります。

$ docker-compose exec a mongo
~~~~なにやらMongoDBめいた文章がたくさん~~~~~

> rs.initiate();
{
    "info2" : "no configuration specified. Using a default configuration for the set",
    "me" : "fd9679ed9f56:27017",
    "ok" : 1,
    "$clusterTime" : {
        "clusterTime" : Timestamp(1571919088, 1),
        "signature" : {
            "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
            "keyId" : NumberLong(0)
        }
    },
    "operationTime" : Timestamp(1571919088, 1)
}
hogehoge:SECONDARY> 

帰ってきたコンソールの文字がhogehoge:SECONDARY>となっているかと思います。(あるいはPRIMARY)

帰ってきたら今度は他のMongoDBをレプリカセットに追加します。rs.add({ host:"b:27017" })rs.add({ host:"c:27017" })としましょう。

hogehoge:PRIMARY> rs.add({host: "b:27017" })
{
    "ok" : 1,
    "$clusterTime" : {
        "clusterTime" : Timestamp(1571919442, 1),
        "signature" : {
            "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
            "keyId" : NumberLong(0)
        }
    },
    "operationTime" : Timestamp(1571919442, 1)
}
hogehoge:PRIMARY> rs.add({host: "c:27017" })
{
    "ok" : 1,
    "$clusterTime" : {
        "clusterTime" : Timestamp(1571919445, 1),
        "signature" : {
            "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
            "keyId" : NumberLong(0)
        }
    },
    "operationTime" : Timestamp(1571919445, 1)
}
hogehoge:PRIMARY> 

rs.conf()と打てば今の設定がきちんとクラスター構成になっているか確認できます。members以下が先程追加したDBたちであれば成功しています。

hogehoge:PRIMARY> rs.conf()
{
    "_id" : "hogehoge",
    "version" : 3,
    "protocolVersion" : NumberLong(1),
    "writeConcernMajorityJournalDefault" : true,
    "members" : [
        {
            "_id" : 0,
            "host" : "fd9679ed9f56:27017",
            "arbiterOnly" : false,
            "buildIndexes" : true,
            "hidden" : false,
            "priority" : 1,
            "tags" : {
                
            },
            "slaveDelay" : NumberLong(0),
            "votes" : 1
        },
        {
            "_id" : 1,
            "host" : "b:27017",
            "arbiterOnly" : false,
            "buildIndexes" : true,
            "hidden" : false,
            "priority" : 1,
            "tags" : {
                
            },
            "slaveDelay" : NumberLong(0),
            "votes" : 1
        },
        {
            "_id" : 2,
            "host" : "c:27017",
            "arbiterOnly" : false,
            "buildIndexes" : true,
            "hidden" : false,
            "priority" : 1,
            "tags" : {
                
            },
            "slaveDelay" : NumberLong(0),
            "votes" : 1
        }
    ],
    "settings" : {
        "chainingAllowed" : true,
        "heartbeatIntervalMillis" : 2000,
        "heartbeatTimeoutSecs" : 10,
        "electionTimeoutMillis" : 10000,
        "catchUpTimeoutMillis" : -1,
        "catchUpTakeoverDelayMillis" : 30000,
        "getLastErrorModes" : {
            
        },
        "getLastErrorDefaults" : {
            "w" : 1,
            "wtimeout" : 0
        },
        "replicaSetId" : ObjectId("5db194f08de825642be0bed5")
    }
}

今回の接続に関して

同じDockerCompose内にあるnodeサービスから接続する場合、URIは以下のようになります。

mongodb://a:27017,b:27017,c:27017/?replicaSet=hogehoge

Nodejsから接続をするにはMongoDBのドライバなどをインストールしましょう。

mongodb.github.io

www.npmjs.com

【C言語】ファイル読み書きを頑張るぞ

こんばんは葛の葉です。今日もC言語です。

FILE型を使うんざ

# include <stdio.h>

これが宣言されていればFILE型は使えるようになってます。ポインタ変数を渡して作成しましょう。FILE型で作ったポインタをfopen関数に渡してあげるとファイルが読み込まれるようです。

#include <stdio.h>

int main (){
    FILE *filep;
    filep = fopen("text.txt", "r");
}

fopen関数の第二引数に"r"としていますが、これはReadOnlyの意味のようです。ファイル読み込み時のモードがあって、今回は読み取り専で開いているということです。

#include <stdio.h>

int main (){
    FILE *filep;
    char str[255];
    filep = fopen("text.txt", "r");
    while(fgets(str, 255, filep) !=NULL){
        printf("%s", str);
    }    
    fclose(filep);
    return 0;
}

WHILE文でfget関数を回しています。これはfilepポインター変数から一行256バイト分をstr変数に代入しろという関数になります。フォルダの値が何もない場合はNULLを返します。つまり、最後の行が何もない場合はNULLを返します。NULLの場合はWHILEから抜けるように条件を記載しています。

Render unto Caesar.
Parable of the Good Samaritan.

text.txtは適当な文章を入れときます。あとはいつものようにコンパイルしてみましょ。

gcc ./file.c
./a.out
Render unto Caesar.
Parable of the Good Samaritan.

書き込みもしてみよう

fopen関数の第二引数を"w"とします。これはWriteの意味のようです。また、書き込むための関数であるfputs関数を使います。

#include <stdio.h>

int main (){
    FILE *filep;
    char str[255] = "パイロキネシス\n";
    filep = fopen("text.txt", "w");
    if (filep == NULL) {
        printf("ファイルがないよ\n");
    } else {
        printf("%sと書き込みます。\n", str);
        fputs(str, filep);
    }
    fclose(filep);
    return 0;
}

fileが見つからない場合はNULLになるので、if文でNULLかそうでないかを判定します。NULLでないのならfputs関数を使用します。fputs(文字列, FILEポインター)とします。

$ gcc file2.c 
$ ./a.out 
パイロキネシス
と書き込みます。

text.txtの中身はこのようになります。

パイロキネシス

前回まで存在していた2文章が消えました。どこいったんだ?

追記してみる

fopen関数の第二引数に"a"としています。これはAddの意味のようです。また効果がわかりやすいようにstrの値も変更します。

#include <stdio.h>

int main (){
    FILE *filep;
    char str[255] = "クリオキネシス\n";
    filep = fopen("text.txt", "a");
    if (filep == NULL) {
        printf("ファイルがないよ\n");
    } else {
        printf("%sと書き込みます。\n", str);
        fputs(str, filep);
    }
    fclose(filep);
    return 0;
}

実行すると以下のようになります。

パイロキネシス
クリオキネシス

f:id:Kuzunoha-NE:20191017203025p:plain

【C言語】関数へのポインタをちょっと

こんばんは葛の葉です。

基本情報に備えてC言語をちょろっと勉強しています関係で、さっきちょろっと勉強した関数ポインタを書いてみることにします。

Version

$ gcc --version
gcc (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

関数を作ってやる

今回は引数を2つ受けるとその引数を乗算して-1するというhogehoge関数を作ります。イマイチ限定されてそうな使いどころがない関数だ。

// sandbox.c
#include <stdio.h>

int hogehoge (int pri,int sec){
    return pri * sec -1;
};

int main (void){
    int foo = 31;
    int bar = 3;
    int ret = hogehoge(foo, bar);
    printf("%d\n", ret);
}

コンパイルして実行するとこんな感じです。さもありなん。

$ gcc sandbox.c 
$ ./a.out
92

関数ポインタを使う

今回は関数ポインタを使います。

// sandbox.c
#include <stdio.h>

int hogehoge (int pri,int sec){
    return pri * sec -1;
};

int main (void){
    int foo = 31;
    int bar = 3;
    int (*func)(int, int); // <- この辺が関数へのポインタ要素。
    func = hogehoge; // <- func変数にhogehoge関数のポインタを代入している。
    printf("%d\n", func(foo, bar)); // <- ここでfunc変数に格納されたポインタに引数を渡して結果を表示している。
}

コンパイルして実行するとおんなじです。まぁそうなるな。

$ gcc sandbox.c 
$ ./a.out
92

こんな使い方なのか?

2つの関数を状況によって使い分けたい、というようなときに、こんな感じで使うのかなぁって思った。

// sandbox.c
#include <stdio.h>

// プロトタイプ宣言
int hogehoge (int pri,int sec);
int piyopiyo (int pri, int sec);
// typedef
typedef int (*Two)(int pri, int sec);

int main (void){
    int foo;
    printf("数字を入力してください:");
    scanf("%d", &foo);
    int bar = 3;
    Two func; // typedefにて新しいTypeが追加されている。
    if (foo > 25){
        printf("big\n");
        func = hogehoge;
    } else {
        printf("small\n");
        func = piyopiyo;
    }
    printf("%d\n", func(foo, bar)); 
}

int hogehoge (int pri,int sec){
    return pri * sec - 1;
};

int piyopiyo (int pri,int sec){
    return pri * sec + 13;
};

main関数最後のprintf("%d\n", func(foo, bar));funcという変数名を使っています。このfunc変数は入力された数字によって参照する関数へのポインタが異なります。実行すると表示される数字を入力してください:に対し25の数字を超える値を入力するとhogehoge関数へのポインタを受取り、そうでない場合はpiyopiyo関数へのポインタを受取るようになります。

$ gcc sandbox.c 
$ ./a.out
数字を入力してくだし:14 
small
55
$ ./a.out
数字を入力してくだし:67
big
200

ところで

基本情報のpythonって次の試験からなんだね👀

【TypeScript】Webpackでフロントにnpmモジュールを使う

こんばんは、葛の葉です。

今回はWebpackを使います。

Webpackとは

Webpackとはざっくり説明すると、複数のJavascriptをまとめて一つにしてくれるものです。これを使用することでnpm経由でインストールしてきたモジュールをフロントで使用することができます。

webpack.js.org

インストールします

npm i --save-devを使ってwebpackをインストールをします。またwebpackはver4.0なのでwebpack-cliというものもインストールします。ver3.0以下の場合はwebpack-cliはいらないみたい。

npm i --save-dev webpack webpack-cli

また、今回はTypescriptを使いたいのでwebpack用のモジュールを使います。

www.npmjs.com

npm i --save-dev typescript ts-loader 

ts-loaderを使うにはtsconfig.jsonが必要です。

npx tsc --init

上記のコマンドを使うとtsconfig.jsonが出来上がります。しかし、strict": trueのところはコメントアウトしておきます。以下のような形になります。

// tsconfig.json
{
  "compilerOptions": {
    "target": "es5",                          /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
    "module": "es2015",                       /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
    "esModuleInterop": true                   /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
  }
}

次にwebpackを使うために以下のようなwebpack用のコンフィグを作ります。名前はwebpack.config.jsです。

// webpack.config.js

module.exports = {
    entry: './src/index.ts',
    output: {
        path: __dirname + '/dist',
        filename: 'app.js',
    },
    optimization: {
        minimize: true,
    },
    module: {
        rules: [
            {
                test: /\.ts$/,
                use: 'ts-loader'
            }
        ]
    },
    resolve: {
        extensions: [ '.ts', '.js' ]
    }
};

ディレクトリの構造を以下のようにします。index.tsの中身は全く無しにします。

// index.ts
// 何も書かない
├── dist
│   ├── index.html
│   └── (app.js #あとから出来上がる)
├── package-lock.json
├── package.json
├── src
│   └── index.ts
├── tsconfig.json
└── webpack.config.js

srcディレクトリに今後TypeScriptを書いていきます。そしてwebpackを使ってdistディレクトリにapp.jsを出力するようになってます。

npx webpack 

そうすると少しWarningが出ますが、dist/app.jsが出来上がります。内容を見てみると以下のようなものでした。

!function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=0)}([function(e,t){}]);

改行や空白がなく人間には非常に読みにくいjsが出てきました。これがwebpackで生成されたjsになります。このように空白がない方が読み込みは早くなるようです。これはwebpack.config.jsoptimizationminimizeを変更すればもっと人間が読みやすい形になります。

    optimization: {
        minimize: false,
    },
/******/ (function(modules) { // webpackBootstrap
/******/    // The module cache
/******/    var installedModules = {};
/******/
/******/    // The require function
/******/    function __webpack_require__(moduleId) {
/******/
/******/        // Check if module is in cache
// 長いので省略

モジュールを使おう

今回はAwesompleteというモジュールを使います。これはきれいにオートコンプリート機能を実装することができる代物です。

leaverou.github.io

npm i --save awesompletenpm i --save-dev @types/awesompleteと実行しインストールします。

そうしましたらindex.tsを以下のようにしましょう。

import Awesomplete from 'awesomplete';

const input = document.getElementById("awesome");

// wordsの配列の中身は好きに変更してください。
const words = ["徳川家康", "徳川吉宗", "徳川綱吉", "天草四郎", "パトリック・ジェーン"];

const awesom = new Awesomplete(input, {list:words});

また、AwesompleteはオートコンプリートをCSSで表現しています。そのCSSnode_modules/awesomplete/awesomplete.cssにあります。そのCSSdistディレクトリにコピーします。

 cp node_modules/awesomplete/awesomplete.css dist/

今度はWebpackで合体したjavascriptを読み込むindex.htmlを作成します。

<html>
    <header>
        <link rel="stylesheet" type="text/css" href="awesomplete.css">
        <meta charset="utf8">
    </header>
    <body>
        <div>
            <input id="awesome">
        </div>
        <script src="app.js"></script>
    </body>
</html>

そうしたらnpx webpackと打ちましょう。app.jsが生成されているはずです。そうして、index.htmlを表示してみましょう。以下のgifのようにオートコンプリートが動作しているはずです。

f:id:Kuzunoha-NE:20191002234413g:plain

【TypeScript】PostgreSQLとExpressをつなぐ

こんばんは、葛の葉です。

今回はPostgreSQLとExpressを繋いでみましょう。

諸々のバージョン

postgres (docker) postgres:11.5-alpine
node (docker) 8.16.1-alpine
pg(npm package) 7.12.1

データベースの情報

今回はPostgresを使用します。Herokuでも使えると思います。

hostname = postgres
user = postgres
password = example
databaseName = demo
tableName = posts

uri = postgres://postgres:example@postgres:5432/demo

テーブルの中身

demo=# select * from posts;

id |       title        |                         text                         |     name      
----+--------------------+------------------------------------------------------+---------------
  1 | タイトルマン       | メロンはとてもおいしいと思うが、唇が張れると思います | 明後日マン
  2 | はいはい           | ところでドラゴンかわいいよね                         | スター
  3 | いやー探しましたよ | これがおれの本気なのさ                               |  明後日マン
  4 | タイトルです       | ここが本文です                                       |  あっピーマン
  5 | ここがタイトル     | ここが本文                                           |  ここが名前

pgをインストール

nodeのモジュールの一つであるpgを使用します。勉強も兼ねてなのであえてORMは使わないようにしてみました。

www.npmjs.com

npm i pg --save

このような感じのクラスを作る

コンストラクタにPostgreSQLのPoolのオブジェクトを渡すようにする。Poolでなくても良いかも。

// Posts.ts

import {Pool} from "pg"

export class Posts{
    private readonly client:Pool;
    public tableName: String;
    constructor(conn:Pool){
        this.client = conn;
        this.tableName = "posts";
    }

    async row(id:string) {
        let message;
        let query = `SELECT * FROM ${this.tableName} WHERE id=${id}`;
        console.log({ query })
        await this.client.query(query)
        .then(function(result){
            console.log({result})
            message = result.rows
        })
        .catch(function(reason){
            console.log({reason});
            message = reason.message;
        });
        return message;
    }
}

このようにインスタンスを作成する。

import {Pool} from "pg"
import {Posts} from "./Models/Posts"

const uri = "postgres://postgres:example@postgres:5432/demo";
const conn = new Pool({connectionString: uri});
const postsClient = new Posts(conn);
postsClient.row("1").then((data)=>{
    console.log(data);
});

/*
[ { id: 1,
    title: 'タイトルマン',
    text: 'メロンはとてもおいしいと思うが、唇が張れると思います',
    name: '明後日マン'} ]
*/

前回のExpressを見る

前回作ったExpressにシングルトンパターンを組み合わせる。

kuzunoha-ne.hateblo.jp

create_posts関数の引数にURIを渡すようにしている。

// singletons.ts // 新規

import { Pool } from "pg";

import { Posts } from "./Models/Posts"

export let postsClient: Posts;

export function create_posts(uri:string){
    const conn = new Pool({connectionString: uri});
    postsClient = new Posts(conn); 
};

create_posts関数には直接URIを記載しているけれどもprocess.envなどを使って環境変数から取得してくるのもありだと思う。

// factory.ts
import * as express from "express";
import * as bodyParser from "body-parser";

import testRouter from "./controllers/posts"
import { create_posts } from "./singletons" // 追加

export function createApp(){
    const app = express();
    app.use(bodyParser.urlencoded({ extended: true }));
    app.use(bodyParser.json());
    app.use('/test', testRouter);
    create_posts("postgres://postgres:example@postgres:5432/demo"); // 追加
    return app
}

コントローラーにシングルトンを渡すようにする。また、コントローラーがクエリを受け取った際にPostgresに問い合わせをしたいので asyncawaitを使う。ここは前回から結構変更があるので注意。

// ./controllers/test.ts // 結構変更点あり
import * as express from "express";

import {postsClient} from "../singletons";

const testRouter = express.Router();

testRouter.get('/', async function (req, res) { 
    var ret = await postsClient.row(req.query.id); 
    res.json({
        message:ret
    });
})

export default testRouter;

あとはtypescriptをコンパルして出現したapp.jsを実行する。

$ node app.js
Listen to port http://localhost:8888

http://localhost:8888/test/に対してgetメソッドのURLパラメーターidを渡してあげます。すなわちhttp://localhost:8888/test/?id=1と行った感じです。

$ curl http://localhost:8888/test/?id=1
{"message":[{"id":1,"title":"タイトルマン","text":"メロンはとてもおいしいと思うが、唇が張れると思います","name":"明後日マン"}]}```

と表示されているはずです。

セキュリティ的に

たぶんこれSQLインジェクションできるんじゃないかなって思ったりするんですけど、どうでしょう?自分でテストしてみるのもいいと思います。

【TypeScript】ExpressとApplicationFactory

こんばんは葛の葉です。

さて、ExpressでApplicationFactoryを作りましょう。

以前Flaskで作ったようなものを作成します。

kuzunoha-ne.hateblo.jp

version

express == 4.17.1

プログラム

ディレクトリはこんな感じなのだ

.
├── app.ts
└── factory.ts

Expressのアプリケーションを生成するcreateApp関数を作る。

// factory.ts
import * as express from "express";
import * as bodyParser from "body-parser";

export function createApp(){
    const app = express();
    app.use(bodyParser.urlencoded({ extended: true }));
    app.use(bodyParser.json());
    return app
};

createAppをexportしてappを作成する。

// app.ts

import {createApp} from './factory'

const app = createApp();

app.listen(8888, ()=>{
    console.log('こんにちは');
})

コンパイルして実行する。

$ npx ts-node app.ts 
こんにちは
// 別のBashで
$ curl 127.0.0.1:8888/
はろわ

ルーティングを別に作成する。

Express.Routerでルート用プログラムを作る。

// ./controllers/test.ts
import * as express from "express";

const testRouter = express.Router();

testRouter.get('/', function (req, res) {
    res.send('へるごー')
})

export default testRouter;

factory.tsに読み込ませる

// factory.ts
import * as express from "express";
import * as bodyParser from "body-parser";

import testRouter from "./controllers/test"

export function createApp(){
    const app = express();
    app.use(bodyParser.urlencoded({ extended: true }));
    app.use(bodyParser.json());
    app.use('/test', testRouter);
    return app
}

curlする

$ curl 127.0.0.1:8888/test
へるごー