【Markdown】シンタックスハイライトっていうのを使いましょう
葛の葉です。
マークダウンでしばしば色のついたコード書きを見るかなぁって思います。
import {hogehoge} from "./hogehoge" console.log(JSON.stringify(hogehoge)); let hugahuga; for ( let piyo of piyos ){ hugahuga += piyo; };
これはシンタックスハイライトといって、マークダウンについている機能のようです。はてなでもMarkdown形式なら使うことができます。結構いろんな言語に対応しています。
書き方は以下のように最初のバッククオート3つに対応する言語を書き込むだけです。
【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におけるエントリーポイントについては過去記事も参考にしてください。
--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
のドライバなどをインストールしましょう。
【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; }
実行すると以下のようになります。
パイロキネシス クリオキネシス
【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
経由でインストールしてきたモジュールをフロントで使用することができます。
インストールします
npm i --save-dev
を使ってwebpackをインストールをします。またwebpackはver4.0なのでwebpack-cliというものもインストールします。ver3.0以下の場合はwebpack-cliはいらないみたい。
npm i --save-dev webpack webpack-cli
また、今回はTypescriptを使いたいのでwebpack用のモジュールを使います。
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.js
のoptimization
のminimize
を変更すればもっと人間が読みやすい形になります。
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というモジュールを使います。これはきれいにオートコンプリート機能を実装することができる代物です。
npm i --save awesomplete
とnpm 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で表現しています。そのCSSはnode_modules/awesomplete/awesomplete.css
にあります。そのCSSをdist
ディレクトリにコピーします。
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のようにオートコンプリートが動作しているはずです。
【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は使わないようにしてみました。
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にシングルトンパターンを組み合わせる。
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に問い合わせをしたいので async
とawait
を使う。ここは前回から結構変更があるので注意。
// ./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で作ったようなものを作成します。
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 へるごー