[JavaScript] 改行コード \r\n \r \n を半角スペースに置換する正規表現とスニペット

JavaScript で改行文字を半角スペースに置換する正規表現とスニペットです。

var text = 'spam\r\nspam\rspam\nspam';
console.log(text);
// spam
// spam
// spam
 
var replacedText = text.replace(/\r\n|\r|\n/g, ' ');
console.log(replacedText);
// spam spam spam spam

\r\n \r \n どのパターンでも半角スペースに置き換えてくれます。

参考情報

psql したら Library not loaded: libreadline エラーが出たので brew upgrade postgresql した

ひさしぶりに PostgreSQL を使おうと思い psql コマンドを実行したら Library not loaded: libreadline エラーが出たので brew upgrade postgresql して解決したというお話です。

psql コマンド実行で Library not loaded: libreadline エラー

$ psql
dyld: Library not loaded: /usr/local/opt/readline/lib/libreadline.6.2.dylib
  Referenced from: /usr/local/bin/psql
  Reason: image not found
[1]    61053 trace trap  psql

brew upgrade postgresql で PostgreSQL のバージョンアップ

brew update
brew upgrade postgresql
launchctl unload -w ~/Library/LaunchAgents/homebrew.mxcl.postgresql.plist
mv /usr/local/var/postgres /usr/local/var/postgres9.3.1
initdb /usr/local/var/postgres
pg_upgrade \
  -b /usr/local/Cellar/postgresql/9.3.1/bin \
  -B /usr/local/Cellar/postgresql/9.6.1/bin \
  -d /usr/local/var/postgres9.3.1 \
  -D /usr/local/var/postgres
cp /usr/local/Cellar/postgresql/9.6.1/homebrew.mxcl.postgresql.plist ~/Library/LaunchAgents/
launchctl load -w ~/Library/LaunchAgents/homebrew.mxcl.postgresql.plist

たまに PostgreSQL を使うぞってときに version アップデートからハマりたくはないですよね。

参考情報

Promise 参考記事まとめ [JavaScript/ECMASCript6]

JavaScript 標準仕様の ECMASCript6 (ES2015) の Promise について理解が定着していないので、よく読み返す記事を自分用にまとめました。

Node.js における Promise についてはこちらの記事もどうぞ。

ひとまず、以上です。

自分用のよく使うパターンみたいなのも、まとめていきたいなと思います。

WordPress ファイルとデータベースを自動バックアップして Google ドライブにアップロードする

やっと当ブログの WordPress のファイルとデータベースをバックアップして Google ドライブにアップロードするまでの処理を自動化しました。

前提

扱っている技術的なものは以下のとおりです。

バックアップ処理を書いたシェルスクリプト

WordPress データベースのバックアップ

ひとまず、書いたシェルスクリプトを書き残しておきます。

# backup_db.sh
DB_NAME=mydbname
DB_USER=mydbuser
DB_PASSWORD=mydbpassword
DB_HOST=localhost
 
DATE_STR=$(date +\%Y\%m\%d-\%H\%M\%S)
BACKUP_PATH=/tmp
BACKUP_FILE=$DB_NAME-$DATE_STR.sql
GDRIVE_DIR_ID=0B840dMOrdKT5am9aRDlNUUFic0k
KEEP_DAYS=3
 
## backup DB
cd $BACKUP_PATH
mysqldump $DB_NAME --default-character-set=binary \
-h $DB_HOST -u $DB_USER -p$DB_PASSWORD > $BACKUP_FILE
tar zcvf $BACKUP_FILE.tar.gz $BACKUP_FILE
gdrive upload --parent $GDRIVE_DIR_ID $BACKUP_FILE.tar.gz
rm -f $BACKUP_FILE.tar.gz $BACKUP_FILE
 
## Housekeeping
LIMIT_TIMESTAMP=$(date -d "$KEEP_DAYS days ago" +%s)
gdrive list --no-header --max 1000 --query "'$GDRIVE_DIR_ID' in parents" | while read ln
do
  ITR_ID=$(echo $ln | awk '{print $1}')
  ITR_DATE=$(echo $ln | awk '{print $(NF-1),$NF}')
 
  if [ $(date -d "$ITR_DATE" +%s) -lt $LIMIT_TIMESTAMP ]
  then
    gdrive delete $ITR_ID
  fi
done

WordPress ファイルのバックアップ

# backup_file.sh
DATE_STR=$(date +\%Y\%m\%d-\%H\%M\%S)
 
TARGET_PATH=/var/www/vhosts/
TARGET_FILE=codenote.net
BACKUP_FILE=$TARGET_FILE-$DATE_STR.tar.gz
GDRIVE_DIR_ID=0B840dMOrdKT5SFg5UkZ2c05aWkU
 
KEEP_DAYS=7
 
## backup files
cd $TARGET_PATH
tar cfz $BACKUP_FILE $TARGET_FILE/
gdrive upload --parent $GDRIVE_DIR_ID $BACKUP_FILE
rm -f $BACKUP_FILE
 
## Housekeeping
LIMIT_TIMESTAMP=$(date -d "$KEEP_DAYS days ago" +%s)
gdrive list --no-header --max 1000 --query "'$GDRIVE_DIR_ID' in parents" | while read ln
do
  ITR_ID=$(echo $ln | awk '{print $1}')
  ITR_DATE=$(echo $ln | awk '{print $(NF-1),$NF}')
 
  if [ $(date -d "$ITR_DATE" +%s) -lt $LIMIT_TIMESTAMP ]
  then
    gdrive delete $ITR_ID
  fi
done

gdrive コマンドの Tips

gdrive list でディレクトリの一覧を表示する。

$ gdrive list --query "mimeType='application/vnd.google-apps.folder'"
Id                             Name   Type   Size   Created
0B840dMOrdKT5am9aRDlNUUFic0k   db     dir           2016-12-13 00:17:46

随時、内容をアップデートしていきます。

参考情報

[Mongoose][timekeeper] TypeError: Undefined type `FakeDate` at `fieldName`

Node.js + Mongoose な構成のウェブサービスのテストコードで timekeeper という時間操作モジュールを利用していてハマったことをメモ。

timekeeper.freeze と new mongoose.Schema でエラーの事例

timekeeper.freeze と new mongoose.Schema でエラーが発生するテストコード

'use strict';
 
const mongoose = require("mongoose");
const timekeeper = require("timekeeper");
 
describe("FakeDate error", () => {
  timekeeper.freeze(new Date());
  let schema = new mongoose.Schema({
    createdAt: {
      type: Date
    }
  });
  timekeeper.reset();
});

エラーメッセージ TypeError: Undefined type `FakeDate`

$ mocha test.js
 
/Users/bakorer/works/myapp/node_modules/mongoose/lib/schema.js:483
    throw new TypeError('Undefined type `' + name + '` at `' + path +
    ^
 
TypeError: Undefined type `FakeDate` at `createdAt`
  Did you try nesting Schemas? You can only nest using refs or arrays.
    at Function.Schema.interpretAsType (/Users/bakorer/works/myapp/node_modules/mongoose/lib/schema.js:483:11)
    at Schema.path (/Users/bakorer/works/myapp/node_modules/mongoose/lib/schema.js:415:29)
    at Schema.add (/Users/bakorer/works/myapp/node_modules/mongoose/lib/schema.js:310:12)
    at new Schema (/Users/bakorer/works/myapp/node_modules/mongoose/lib/schema.js:88:10)
    at Suite.<anonymous> (/Users/bakorer/works/myapp/test.js:8:16)
    at context.describe.context.context (/Users/bakorer/works/myapp/node_modules/mocha/lib/interfaces/bdd.js:73:10)
    at Object.<anonymous> (/Users/bakorer/works/myapp/test.js:6:1)
    at Module._compile (module.js:409:26)
    at Object.Module._extensions..js (module.js:416:10)
    at Module.load (/Users/bakorer/works/myapp/node_modules/coffee-script/lib/coffee-script/coffee-script.js:211:36)
    at Function.Module._load (module.js:300:12)
    at Module.require (module.js:353:17)
    at require (internal/module.js:12:17)
    at /Users/bakorer/works/myapp/node_modules/mocha/lib/mocha.js:157:27
    at Array.forEach (native)
    at Mocha.loadFiles (/Users/bakorer/works/myapp/node_modules/mocha/lib/mocha.js:154:14)
    at Mocha.run (/Users/bakorer/works/myapp/node_modules/mocha/lib/mocha.js:326:31)
    at Object.<anonymous> (/Users/bakorer/works/myapp/node_modules/mocha/bin/_mocha:350:7)
    at Module._compile (module.js:409:26)
    at Object.Module._extensions..js (module.js:416:10)
    at Module.load (module.js:343:32)
    at Function.Module._load (module.js:300:12)
    at Function.Module.runMain (module.js:441:10)
    at startup (node.js:139:18)
    at node.js:974:3

FakeDate は timekeeper が用意した Date

timekeeper が以下のように JavaScript の Date オブジェクトと、timekeeper で独自定義した FakeDate オブジェクトを入れ替える処理をしています。

  /**
   * Replace the `Date` with `FakeDate`.
   */
  function useFakeDate() {
    Date = FakeDate
  }
 
  /**
   * Restore the `Date` to `NativeDate`.
   */
  function useNativeDate() {
    Date = NativeDate
  }

Date が Date でなく FakeDate に入れ替わってるタイミングで、new mongoose.Schema を呼び出すが FakeDate は MongooseTypes に定義されていない SchemaType なので例外が投げられてるっていう感じです。ザックリ説明すると。

new mongoose.Schema で SchemaType チェックしている Mongoose のコードはこの辺です lib/schema.js#L652-L671

テストコードの書き方次第でエラーは避けられる

timekeeper.freeze して timekeeper.reset するまでの間に、明示的に new mongoose.Schema したいテストコードを書くことはほぼないと思ってます。なので、new mongoose.Schema の処理を書いてる Model を定義してる module などは事前に require() しておくことでエラーを退避したいですね。

[MongoDB] ISODate の年月日の文字列を取得する

MongoDB の ISODate から年月日の部分だけ文字列を取得するには、toISOString() と substring() を使って以下のようなコードで取得できます。

MongoDB

now = ISODate()
// ISODate("2016-12-06T05:02:20.675Z")
 
now.toISOString().substring(0, 10)
// 2016-12-06

年月日だけ使いたいことがあったので自分用のメモとして記事に残しておきます。

substring() じゃなくても slice() でもよいです。その辺はお好みでどうぞ。

異なるバージョンの Moment.js と Moment Timezone で変数を使い回したら見事にバグった

最新バージョンの moment-timezone@0.5.5 で生成した moment object を、古いバージョンの moment@2.4.0 を使ってる method に引数で渡して、日付処理をおこなったら華麗にバグったというお話です。

前提

どちらも Thu Dec 01 2016 09:00:00 GMT+0900 (JST) の値が保存されていることを期待してます。

調査結果

古いバージョンの moment@2.4.0 で作成した変数を console.log した結果

期待通り _d: Thu Dec 01 2016 09:00:00 GMT+0900 (JST) が入っていることが確認できました。

  { [Number: 1480579200000]
  _i: [ 2016, 11, 1 ],
  _f: undefined,
  _l: undefined,
  _strict: undefined,
  _isUTC: true,
  _pf: 
   { empty: false,
     unusedTokens: [],
     unusedInput: [],
     overflow: -1,
     charsLeftOver: 0,
     nullInput: false,
     invalidMonth: null,
     invalidFormat: false,
     userInvalidated: false,
     iso: false },
  _a: [ 2016, 11, 1, 0, 0, 0, 0 ],
  _d: Thu Dec 01 2016 09:00:00 GMT+0900 (JST),
  _z: 
   { name: 'america_los_angeles',
     displayName: 'America/Los_Angeles',
     zones: [ [Object], [Object], [Object], [Object] ] },
  _offset: 480 }

最新バージョンの moment-timezone@0.5.5 で作成した変数を console.log した結果

_d: Fri Nov 25 2016 00:00:00 GMT+0900 (JST) には、期待している 2016/12/01 の値が入ってませんでした。

代わりに _i 以下に存在する _d: Thu Dec 01 2016 09:00:00 GMT+0900 (JST) の方に期待している 2016/12/01 の値が入ってました。

{ [Number: 1479999600000]
  _isAMomentObject: true,
  _i: 
   { [Number: 1480579200000]
     _i: [ 2016, 11, 1 ],
     _f: undefined,
     _l: undefined,
     _strict: undefined,
     _isUTC: true,
     _pf: 
      { empty: false,
        unusedTokens: [],
        unusedInput: [],
        overflow: -1,
        charsLeftOver: 0,
        nullInput: false,
        invalidMonth: null,
        invalidFormat: false,
        userInvalidated: false,
        iso: false },
     _a: [ 2016, 11, 1, 0, 0, 0, 0 ],
     _d: Thu Dec 01 2016 09:00:00 GMT+0900 (JST),
     _z: 
      { name: 'america_los_angeles',
        displayName: 'America/Los_Angeles',
        zones: [Object] },
     _offset: 480 },
  _isUTC: false,
  _pf: 
   { empty: false,
     unusedTokens: [],
     unusedInput: [],
     overflow: -1,
     charsLeftOver: 0,
     nullInput: false,
     invalidMonth: null,
     invalidFormat: false,
     userInvalidated: false,
     iso: false,
     parsedDateParts: [],
     meridiem: null },
  _locale: 
   Locale {
     _calendar: 
      { sameDay: '[Today at] LT',
        nextDay: '[Tomorrow at] LT',
        nextWeek: 'dddd [at] LT',
        lastDay: '[Yesterday at] LT',
        lastWeek: '[Last] dddd [at] LT',
        sameElse: 'L' },
     _longDateFormat: 
      { LTS: 'h:mm:ss A',
        LT: 'h:mm A',
        L: 'MM/DD/YYYY',
        LL: 'MMMM D, YYYY',
        LLL: 'MMMM D, YYYY h:mm A',
        LLLL: 'dddd, MMMM D, YYYY h:mm A' },
     _invalidDate: 'Invalid date',
     ordinal: [Function],
     _ordinalParse: /\d{1,2}(th|st|nd|rd)/,
     _relativeTime: 
      { future: 'in %s',
        past: '%s ago',
        s: 'a few seconds',
        m: 'a minute',
        mm: '%d minutes',
        h: 'an hour',
        hh: '%d hours',
        d: 'a day',
        dd: '%d days',
        M: 'a month',
        MM: '%d months',
        y: 'a year',
        yy: '%d years' },
     _months: 
      [ 'January',
        'February',
        'March',
        'April',
        'May',
        'June',
        'July',
        'August',
        'September',
        'October',
        'November',
        'December' ],
     _monthsShort: 
      [ 'Jan',
        'Feb',
        'Mar',
        'Apr',
        'May',
        'Jun',
        'Jul',
        'Aug',
        'Sep',
        'Oct',
        'Nov',
        'Dec' ],
     _week: { dow: 0, doy: 6 },
     _weekdays: 
      [ 'Sunday',
        'Monday',
        'Tuesday',
        'Wednesday',
        'Thursday',
        'Friday',
        'Saturday' ],
     _weekdaysMin: [ 'Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa' ],
     _weekdaysShort: [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ],
     _meridiemParse: /[ap]\.?m?\.?/i,
     _abbr: 'en',
     _config: 
      { calendar: [Object],
        longDateFormat: [Object],
        invalidDate: 'Invalid date',
        ordinal: [Function],
        ordinalParse: /\d{1,2}(th|st|nd|rd)/,
        relativeTime: [Object],
        months: [Object],
        monthsShort: [Object],
        week: [Object],
        weekdays: [Object],
        weekdaysMin: [Object],
        weekdaysShort: [Object],
        meridiemParse: /[ap]\.?m?\.?/i,
        abbr: 'en' },
     _ordinalParseLenient: /\d{1,2}(th|st|nd|rd)|\d{1,2}/ },
  _a: [ 2016, 10, 25, 0, 0, 0, 0 ],
  _d: Fri Nov 25 2016 00:00:00 GMT+0900 (JST),
  _z: null,
  _isValid: true }

まとめ

同じ _d というプロパティの扱いが異なっているのでバグったみたいでした。

ライブラリのバージョンの混在は本当に危険なので、絶対に真似しないでくださいね。

git log -S で過去に削除したコードを探せる

git で過去に削除したコードを探すには git log -S 検索ワード で検索ワードが含まれている commit を検索できるみたいです。

Use git log -S to search in the history for commits that changed . Add an optional -p to see the diff as well.

commit の内容まで確認したい場合は -P オプションを併用するとよいです。

git log -P -S 検索ワード

「あのコードっていつ削除されたんだっけ?」って、たまに調べたいときに git log -S は便利ですね。

[JavaScript] iframe で読み込み完了後に処理を実行するには onload イベントを使おう

iframe 内のコンテンツの読み込みが完了したら何か処理をさせたい場合 onload イベントを使うことで実現できます。

例えば以下のコードは、インラインフレーム内で frame.html の読み込み完了後に alert(‘complete’); という JavaScript の処理を実行しています。

<iframe src="frame.html" onload="alert('complete');">

(例) iframe 内のリンクをクリック禁止にする

もう少し具体的なテーマとして iframe 内のリンクをクリック禁止にする処理の実装について考えてみました。

onload を利用して以下のようなコードになります。

HTML

<iframe id="js-iframe" src="frame.html" onload="replaceIframeHref();">

JavaScript

window.replaceIframeHref = function(){
  $("#js-iframe").contents().find("a").each(function(){
    $(this).attr("href", "javascript:void(0);");
  });
};

以上です。

[Node.js] socket.io ではなくて socketio を npm install して Cannot find module ‘socket.io’ エラーでた

socket.io を使いたくて npm install socketio –save でインストール後に require(‘socket.io’) したら Error: Cannot find module ‘socket.io’ が発生して「なんでだろう…?」って数十分ぐらい悩みました。

$ node index.js
 
module.js:327
    throw err;
    ^
 
Error: Cannot find module 'socket.io'
    at Function.Module._resolveFilename (module.js:325:15)
    at Function.Module._load (module.js:276:25)
    at Module.require (module.js:353:17)
    at require (internal/module.js:12:17)
    at Object.<anonymous> (/Users/bakorer/works/sample/index.js:9:16)
    at Module._compile (module.js:409:26)
    at Object.Module._extensions..js (module.js:416:10)
    at Module.load (module.js:343:32)
    at Function.Module._load (module.js:300:12)
    at Function.Module.runMain (module.js:441:10)

結果としては socketio が使うべきモジュールではなくて正しくは socket.io というオチでした。package.json を下記のように修正して npm install し直したら無事に require(‘socket.io’) できました。

diff --git a/package.json b/package.json
index 30cdfac..84192a7 100644
--- a/package.json
+++ b/package.json
@@ -16,6 +16,6 @@
-    "socketio": "^1.0.0"
+    "socket.io": "^1.5.1"
   }
 }

socketio という紛らわしいパッケージ名はつけないで頂きたいですね。