20190324のNode.jsに関する記事は10件です。

npm initの-fオプションについて解説。どう変わる?

こんにちは。俳句や川柳から動くGIF画像を生成できるWebサービス「五七五メーカー」をリリースしたアカネヤ(@ToshioAkaneya)です。
npm initの-fオプションについて解説します。

npm initの-fオプションについて解説

たまに、技術記事などで
$ npm init -fを実行して下さい
と書かれているのを見たことがあると思います。(僕も時々使います。)
これは、通常$ npm initした時に出てくる質問に対して、全てエンターを押した場合と同じ様な動作をするコマンドになります。
npm initした後の質問に全てエンターを押すつもりの場合は、$ npm init -fを実行してね、と伝えた方が楽なのでよく使われます。
以上です。

はてなブックマーク・Pocketはこちらから

はてなブックマークに追加
Pocketに追加

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

yarn install でハマった

yarn installしたらエラーになりました。
結論としては、Webpackfseventsがnode.jsの古いバージョンに依存しているので、node.jsのバージョンを11.9.0から10.15.1にしたらインストールできました。

user_name-Mac:app_name user_name$ yarn install
yarn install v1.13.0
[1/4] ?  Resolving packages...
[2/4] ?  Fetching packages...
[3/4] ?  Linking dependencies...
warning "@rails/webpacker > postcss-cssnext@3.1.0" has unmet peer dependency "caniuse-lite@^1.0.30000697".
warning " > webpack-dev-server@2.11.2" has unmet peer dependency "webpack@^2.2.0 || ^3.0.0".
warning "webpack-dev-server > webpack-dev-middleware@1.12.2" has unmet peer dependency "webpack@^1.0.0 || ^2.0.0 || ^3.0.0".
[4/4] ?  Building fresh packages...
[-/3] ⠁ waiting...
[2/3] ⠁ fsevents
warning Error running install script for optional dependency: "/Users/user_name/Projects/app_name/node_modules/fsevents: Command failed.
Exit code: 1
Command: node install
Arguments: 
Directory: /Users/user_name/Projects/app_name/node_modules/fsevents
Output:
node-pre-gyp info it worked if it ends with ok
node-pre-gyp info using node-pre-gyp@0.10.0
node-pre-gyp info using node@11.9.0 | darwin | x64
node-pre-gyp info check checked for \"/Users/user_name/Projects/app_name/node_modules/fsevents/lib/binding/Release/node-v67-darwin-x64/fse.node\" (not found)
node-pre-gyp http GET https://fsevents-binaries.s3-us-west-2.amazonaws.com/v1.2.4/fse-v1.2.4-node-v67-darwin-x64.tar.gz
node-pre-gyp http 404 https://fsevents-binaries.s3-us-west-2.amazonaws.com/v1.2.4/fse-v1.2.4-node-v67-darwin-x64.tar.gz
node-pre-gyp WARN Tried to download(404): https://fsevents-binaries.s3-us-west-2.amazonaws.com/v1.2.4/fse-v1.2.4-node-v67-darwin-x64.tar.gz 
node-pre-gyp WARN Pre-built binaries not found for fsevents@1.2.4 and node@11.9.0 (node-v67 ABI, unknown) (falling back to source compile with node-gyp) 
node-pre-gyp http 404 status code downloading tarball https://fsevents-binaries.s3-us-west-2.amazonaws.com/v1.2.4/fse-v1.2.4-node-v67-darwin-x64.tar.gz 
gyp info it worked if it ends with ok
gyp info using node-gyp@3.6.2
gyp info using node@11.9.0 | darwin | x64
gyp info ok 
gyp info it worked if it ends with ok
gyp info using node-gyp@3.6.2
gyp info using node@11.9.0 | darwin | x64
gyp info spawn /usr/bin/python
gyp info spawn args [ '/Users/user_name/Projects/app_name/node_modules/node-gyp/gyp/gyp_main.py',
gyp info spawn args   'binding.gyp',
gyp info spawn args   '-f',
gyp info spawn args   'make',
gyp info spawn args   '-I',
gyp info spawn args   '/Users/user_name/Projects/app_name/node_modules/fsevents/build/config.gypi',
gyp info spawn args   '-I',
gyp info spawn args   '/Users/user_name/Projects/app_name/node_modules/node-gyp/addon.gypi',
gyp info spawn args   '-I',
gyp info spawn args   '/Users/user_name/.node-gyp/11.9.0/include/node/common.gypi',
gyp info spawn args   '-Dlibrary=shared_library',
gyp info spawn args   '-Dvisibility=default',
gyp info spawn args   '-Dnode_root_dir=/Users/user_name/.node-gyp/11.9.0',
gyp info spawn args   '-Dnode_gyp_dir=/Users/user_name/Projects/app_name/node_modules/node-gyp',
gyp info spawn args   '-Dnode_lib_file=/Users/user_name/.node-gyp/11.9.0/<(target_arch)/node.lib',
gyp info spawn args   '-Dmodule_root_dir=/Users/user_name/Projects/app_name/node_modules/fsevents',
gyp info spawn args   '-Dnode_engine=v8',
gyp info spawn args   '--depth=.',
gyp info spawn args   '--no-parallel',
gyp info spawn args   '--generator-output',
gyp info spawn args   'build',
gyp info spawn args   '-Goutput_dir=.' ]
xcode-select: error: tool 'xcodebuild' requires Xcode, but active developer directory '/Library/Developer/CommandLineTools' is a command line tools instance

xcode-select: error: tool 'xcodebuild' requires Xcode, but active developer directory '/Library/Developer/CommandLineTools' is a command line tools instance

Traceback (most recent call last):
  File \"/Users/user_name/Projects/app_name/node_modules/node-gyp/gyp/gyp_main.py\", line 16, in <module>
    sys.exit(gyp.script_main())
  File \"/Users/user_name/Projects/app_name/node_modules/node-gyp/gyp/pylib/gyp/__init__.py\", line 545, in script_main
    return main(sys.argv[1:])
  File \"/Users/user_name/Projects/app_name/node_modules/node-gyp/gyp/pylib/gyp/__init__.py\", line 538, in main
    return gyp_main(args)
  File \"/Users/user_name/Projects/app_name/node_modules/node-gyp/gyp/pylib/gyp/__init__.py\", line 523, in gyp_main
    generator.GenerateOutput(flat_list, targets, data, params)
  File \"/Users/user_name/Projects/app_name/node_modules/node-gyp/gyp/pylib/gyp/generator/make.py\", line 2170, in GenerateOutput
    part_of_all=qualified_target in needed_targets)
  File \"/Users/user_name/Projects/app_name/node_modules/node-gyp/gyp/pylib/gyp/generator/make.py\", line 795, in Write
    self.Pchify))
  File \"/Users/user_name/Projects/app_name/node_modules/node-gyp/gyp/pylib/gyp/generator/make.py\", line 1190, in WriteSources
    cflags = self.xcode_settings.GetCflags(configname)
  File \"/Users/user_name/Projects/app_name/node_modules/node-gyp/gyp/pylib/gyp/xcode_emulation.py\", line 551, in GetCflags
    archs = self.GetActiveArchs(self.configname)
  File \"/Users/user_name/Projects/app_name/node_modules/node-gyp/gyp/pylib/gyp/xcode_emulation.py\", line 420, in GetActiveArchs
    xcode_archs_default = GetXcodeArchsDefault()
  File \"/Users/user_name/Projects/app_name/node_modules/node-gyp/gyp/pylib/gyp/xcode_emulation.py\", line 118, in GetXcodeArchsDefault
    xcode_version, _ = XcodeVersion()
  File \"/Users/user_name/Projects/app_name/node_modules/node-gyp/gyp/pylib/gyp/xcode_emulation.py\", line 1265, in XcodeVersion
    version = re.match(r'(\\d\\.\\d\\.?\\d*)', version).groups()[0]
AttributeError: 'NoneType' object has no attribute 'groups'
gyp ERR! configure error 
gyp ERR! stack Error: `gyp` failed with exit code: 1
gyp ERR! stack     at ChildProcess.onCpExit (/Users/user_name/Projects/app_name/node_modules/node-gyp/lib/configure.js:336:16)
gyp ERR! stack     at ChildProcess.emit (events.js:197:13)
gyp ERR! stack     at Process.ChildProcess._handle.onexit (internal/child_process.js:254:12)
gyp ERR! System Darwin 17.7.0
gyp ERR! command \"/usr/local/Cellar/node/11.9.0/bin/node\" \"/Users/user_name/Projects/app_name/node_modules/node-gyp/bin/node-gyp.js\" \"configure\" \"--fallback-to-build\" \"--module=/Users/user_name/Projects/app_name/node_modules/fsevents/lib/binding/Release/node-v67-darwin-x64/fse.node\" \"--module_name=fse\" \"--module_path=/Users/user_name/Projects/app_name/node_modules/fsevents/lib/binding/Release/node-v67-darwin-x64\" \"--napi_version=4\" \"--node_abi_napi=napi\" \"--python=/usr/bin/python\"
gyp ERR! cwd /Users/user_name/Projects/app_name/node_modules/fsevents
gyp ERR! node -v v11.9.0
gyp ERR! node-gyp -v v3.6.2
gyp ERR! not ok 
node-pre-gyp ERR! build error 
node-pre-gyp ERR! stack Error: Failed to execute '/usr/local/Cellar/node/11.9.0/bin/node /Users/user_name/Projects/app_name/node_modules/node-gyp/bin/node-gyp.js configure --fallback-to-build --module=/Users/user_name/Projects/app_name/node_modules/fsevents/lib/binding/Release/node-v67-darwin-x64/fse.node --module_name=fse --module_path=/Users/user_name/Projects/app_name/node_modules/fsevents/lib/binding/Release/node-v67-darwin-x64 --napi_version=4 --node_abi_napi=napi --python=/usr/bin/python' (1)
node-pre-gyp ERR! stack     at ChildProcess.<anonymous> (/Users/user_name/Projects/app_name/node_modules/fsevents/node_modules/node-pre-gyp/lib/util/compile.js:83:29)
node-pre-gyp ERR! stack     at ChildProcess.emit (events.js:197:13)
node-pre-gyp ERR! stack     at maybeClose (internal/child_process.js:978:16)
node-pre-gyp ERR! stack     at Process.ChildProcess._handle.onexit (internal/child_process.js:265:5)
node-pre-gyp ERR! System Darwin 17.7.0
node-pre-gyp ERR! command \"/usr/local/Cellar/node/11.9.0/bin/node\" \"/Users/user_name/Projects/app_name/node_modules/fsevents/node_modules/node-pre-gyp/bin/node-pre-gyp\" \"install\" \"--fallback-to-build\"
node-pre-gyp ERR! cwd /Users/user_name/Projects/app_name/node_modules/fsevents
node-pre-gyp ERR! node -v v11.9.0
node-pre-gyp ERR! node-pre-gyp -v v0.10.0
node-pre-gyp ERR! not ok 
Failed to execute '/usr/local/Cellar/node/11.9.0/bin/node /Users/user_name/Projects/app_name/node_modules/node-gyp/bin/node-gyp.js configure --fallback-to-build --module=/Users/user_name/Projects/app_name/node_modules/fsevents/lib/binding/Release/node-v67-darwin-x64/fse.node --module_name=fse --module_path=/Users/user_name/Projects/app_name/node_modules/fsevents/lib/binding/Release/node-v67-darwin-x64 --napi_version=4 --node_abi_napi=napi --python=/usr/bin/pyt[-/3] ⠐ waiting...
[-/3] ⠐ waiting...
error /Users/user_name/Projects/app_name/node_modules/node-sass: Command failed.
Exit code: 1
Command: node scripts/build.js
Arguments: 
Directory: /Users/user_name/Projects/app_name/node_modules/node-sass
Output:
Building: /usr/local/Cellar/node/11.9.0/bin/node /Users/user_name/Projects/app_name/node_modules/node-gyp/bin/node-gyp.js rebuild --verbose --libsass_ext= --libsass_cflags= --libsass_ldflags= --libsass_library=
gyp info it worked if it ends with ok
gyp verb cli [ '/usr/local/Cellar/node/11.9.0/bin/node',
gyp verb cli   '/Users/user_name/Projects/app_name/node_modules/node-gyp/bin/node-gyp.js',
gyp verb cli   'rebuild',
gyp verb cli   '--verbose',
gyp verb cli   '--libsass_ext=',
gyp verb cli   '--libsass_cflags=',
gyp verb cli   '--libsass_ldflags=',
gyp verb cli   '--libsass_library=' ]
gyp info using node-gyp@3.6.2
gyp info using node@11.9.0 | darwin | x64
gyp verb command rebuild []
gyp verb command clean []
gyp verb clean removing "build" directory
gyp verb command configure []
gyp verb check python checking for Python executable "/usr/bin/python" in the PATH
gyp verb `which` succeeded /usr/bin/python /usr/bin/python
gyp verb check python version `/usr/bin/python -c "import platform; print(platform.python_version());"` returned: "2.7.10\n"
gyp verb get node dir no --target version specified, falling back to host node version: 11.9.0
gyp verb command install [ '11.9.0' ]
gyp verb install input version string "11.9.0"
gyp verb install installing version: 11.9.0
gyp verb install --ensure was passed, so won't reinstall if already installed
gyp verb install version is already installed, need to check "installVersion"
gyp verb got "installVersion" 9
gyp verb needs "installVersion" 9
gyp verb install version is good
gyp verb get node dir target node version installed: 11.9.0
gyp verb build dir attempting to create "build" dir: /Users/user_name/Projects/app_name/node_modules/node-sass/build
gyp verb build dir "build" dir needed to be created? /Users/user_name/Projects/app_name/node_modules/node-sass/build
gyp verb build/config.gypi creating config file
gyp verb build/config.gypi writing out config file: /Users/user_name/Projects/app_name/node_modules/node-sass/build/config.gypi
gyp verb config.gypi checking for gypi file: /Users/user_name/Projects/app_name/node_modules/node-sass/config.gypi
gyp verb common.gypi checking for gypi file: /Users/user_name/Projects/app_name/node_modules/node-sass/common.gypi
gyp verb gyp gyp format was not specified; forcing "make"
gyp info spawn /usr/bin/python
gyp info spawn args [ '/Users/user_name/Projects/app_name/node_modules/node-gyp/gyp/gyp_main.py',
gyp info spawn args   'binding.gyp',
gyp info spawn args   '-f',
gyp info spawn args   'make',
gyp info spawn args   '-I',
gyp info spawn args   '/Users/user_name/Projects/app_name/node_modules/node-sass/build/config.gypi',
gyp info spawn args   '-I',
gyp info spawn args   '/Users/user_name/Projects/app_name/node_modules/node-gyp/addon.gypi',
gyp info spawn args   '-I',
gyp info spawn args   '/Users/user_name/.node-gyp/11.9.0/include/node/common.gypi',
gyp info spawn args   '-Dlibrary=shared_library',
gyp info spawn args   '-Dvisibility=default',
gyp info spawn args   '-Dnode_root_dir=/Users/user_name/.node-gyp/11.9.0',
gyp info spawn args   '-Dnode_gyp_dir=/Users/user_name/Projects/app_name/node_modules/node-gyp',
gyp info spawn args   '-Dnode_lib_file=/Users/user_name/.node-gyp/11.9.0/<(target_arch)/node.lib',
gyp info spawn args   '-Dmodule_root_dir=/Users/user_name/Projects/app_name/node_modules/node-sass',
gyp info spawn args   '-Dnode_engine=v8',
gyp info spawn args   '--depth=.',
gyp info spawn args   '--no-parallel',
gyp info spawn args   '--generator-output',
gyp info spawn args   'build',
gyp info spawn args   '-Goutput_dir=.' ]
xcode-select: error: tool 'xcodebuild' requires Xcode, but active developer directory '/Library/Developer/CommandLineTools' is a command line tools instance

xcode-select: error: tool 'xcodebuild' requires Xcode, but active developer directory '/Library/Developer/CommandLineTools' is a command line tools instance

Traceback (most recent call last):
  File "/Users/user_name/Projects/app_name/node_modules/node-gyp/gyp/gyp_main.py", line 16, in <module>
    sys.exit(gyp.script_main())
  File "/Users/user_name/Projects/app_name/node_modules/node-gyp/gyp/pylib/gyp/__init__.py", line 545, in script_main
    return main(sys.argv[1:])
  File "/Users/user_name/Projects/app_name/node_modules/node-gyp/gyp/pylib/gyp/__init__.py", line 538, in main
    return gyp_main(args)
  File "/Users/user_name/Projects/app_name/node_modules/node-gyp/gyp/pylib/gyp/__init__.py", line 523, in gyp_main
    generator.GenerateOutput(flat_list, targets, data, params)
  File "/Users/user_name/Projects/app_name/node_modules/node-gyp/gyp/pylib/gyp/generator/make.py", line 2170, in GenerateOutput
    part_of_all=qualified_target in needed_targets)
  File "/Users/user_name/Projects/app_name/node_modules/node-gyp/gyp/pylib/gyp/generator/make.py", line 795, in Write
    self.Pchify))
  File "/Users/user_name/Projects/app_name/node_modules/node-gyp/gyp/pylib/gyp/generator/make.py", line 1190, in WriteSources
    cflags = self.xcode_settings.GetCflags(configname)
  File "/Users/user_name/Projects/app_name/node_modules/node-gyp/gyp/pylib/gyp/xcode_emulation.py", line 551, in GetCflags
    archs = self.GetActiveArchs(self.configname)
  File "/Users/user_name/Projects/app_name/node_modules/node-gyp/gyp/pylib/gyp/xcode_emulation.py", line 420, in GetActiveArchs
    xcode_archs_default = GetXcodeArchsDefault()
  File "/Users/user_name/Projects/app_name/node_modules/node-gyp/gyp/pylib/gyp/xcode_emulation.py", line 118, in GetXcodeArchsDefault
    xcode_version, _ = XcodeVersion()
  File "/Users/user_name/Projects/app_name/node_modules/node-gyp/gyp/pylib/gyp/xcode_emulation.py", line 1265, in XcodeVersion
    version = re.match(r'(\d\.\d\.?\d*)', version).groups()[0]
AttributeError: 'NoneType' object has no attribute 'groups'
gyp ERR! configure error 
gyp ERR! stack Error: `gyp` failed with exit code: 1
gyp ERR! stack     at ChildProcess.onCpExit (/Users/user_name/Projects/app_name/node_modules/node-gyp/lib/configure.js:336:16)
gyp ERR! stack     at ChildProcess.emit (events.js:197:13)
gyp ERR! stack     at Process.ChildProcess._handle.onexit (internal/child_process.js:254:12)
gyp ERR! System Darwin 17.7.0
gyp ERR! command "/usr/local/Cellar/node/11.9.0/bin/node" "/Users/user_name/Projects/app_name/node_modules/node-gyp/bin/node-gyp.js" "rebuild" "--verbose" "--libsass_ext=" "--libsass_cflags=" "--libsass_ldflags=" "--libsass_library="
gyp ERR! cwd /Users/user_name/Projects/app_name/node_modules/node-sass
gyp ERR! node -v v11.9.0
gyp ERR! node-gyp -v v3.6.2
gyp ERR! not ok 

参考

https://github.com/yarnpkg/yarn/issues/5962
https://www.npmjs.com/package/fsevents
https://www.npmjs.com/package/node-pre-gyp

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[翻訳]JavaScriptプロジェクトでいずれは学ぶ10のこと

以下は、The Cat with a Dragon Tattoo によって2018/8/3にMediumに投稿され、現在1万Clap以上受けている記事、"10 Things You Will Eventually Learn About JavaScript Projects"=「JavaScriptプロジェクトでいずれは学ぶ10のこと」の日本語訳です。

特定のライブラリを超えた、しかしフロントエンド開発者を主眼とした学びがまとまっており、僕がまさに読みたかったものでした。著者に感謝を。

表示 - 非営利 - 継承 4.0 国際 (CC BY-NC-SA 4.0)
https://creativecommons.org/licenses/by-nc-sa/4.0/deed.ja


JavaScriptは冒険です。
様々な業界で10年近く開発してきましたが、誰だってこれには同意してくれると思います。

フロントエンド開発は、プログラマーである私たちに、多くの選択の自由、柔軟性、創造の余地を与えてくれます。しかし代わりに、ちょっとした知識、計画、責任が求められます。

jQuery、require.js、Angular、React、ExtJs、その他思い出せない(または思い出したくない)何十ものプロジェクトを経験してきましたが、想像もしなかった酷いモノを見てきました。きっと皆さんもそうでしょう。

しかしどんな時代でも、まとまりのないプロジェクトをどうにか扱いやすく変えられる「共通のパターン」がありました。以下では、その中でも特に重要な10のパターンを記します。個人的な経験から導いた意見ですが、経験豊富なエンジニアの多くは頷いてくれるのではないでしょうか。これらのパターンは、どんなフレームワーク・方法論・チームの規模であっても、ドキュメンテーションやリファクタリングの必要性を減らし、エンジニアの目から零れる涙を減らし、プロジェクトの盤石な基礎となってくれるでしょう。

あなたに新しい発見があり、これらのパターンを便利だと感じ、またこれらを使って素晴らしい何かを作ってくれることを願います!:muscle:

1.分割統治

聞いたことはありながら、多くの人がこのルールを過小評価しています。CommonJS、Webpack、Nodeではコードを複数のファイルに分割できます。でも、何故わざわざそんな事をするのでしょう?

一貫性

プロジェクトを1つのexportを持つファイルに分割すれば、プロジェクトが大きくなった時、検索や依存性の管理がずっと簡単になります。「ファイルの名前はそれがexportするものを示す」というルールは直感的で、脳みそを疲弊させずにアーキテクチャを概観することができます。

管理

各exportを独立ファイルに分割することで、必要なときに素早く移動できるようになり、分業が促進されます。ヘルパー関数がアプリケーションの他の場所で必要になれば、/sharedフォルダを作ってファイルを移動すれば良いわけです。そうすれば、また他の部分からも参照できます。

2.恥ずかしいくらい明確に

変数、関数、ファイルの名前は、自分の子供の名前だと思って時間をかけて決めましょう。変数xとすれば今日の0.3秒が節約できるかもしれませんが、1ヶ月後には2日かけて意味を解明し、4日かけてリファクタすることになります。先々のことを考え、長い名前を恐れないようにしましょう。

ハック的な手法や、MITに入学したくなるような難しいやり方は避けましょう。その解決法は本当にスマートなのかもしれません。未来のあなたやチームメートも、コードの解読にたっぷり時間を費やした後で「賢いね!」と同意するかもしれません。しかし、物事をシンプルに保つことに集中し、なるべくドキュメントやコメントが必要ないようなコードを目指しましょう。

3.マジックナンバーやマジック文字列を解消する

どんなにやりたくなっても、マジックナンバー・マジック文字列は使ってはいけません。小さなもの、さして重要でないものでも、意味ある名前の変数に格納して、スコープの冒頭に置きましょう。

明示的にコードに書いた値は多くの場合どこかで再利用されます。すぐに変数に格納すれば、コードの重複は減り、修正は楽になり、値に意味を与えられます。

4.ネストと戦う

コードが横に120文字/縦に500行以上になるかif文が3層以上になったら、全力で分割しましょう。

深くネストされたif文を別々の関数、Promise、Observableに分割することで、条件文の複雑性を解消することができます。非同期呼び出しが多いなら、async/awaitも大幅にコードをシンプルにしてくれます。

5.configを頑張る

グローバル変数、APIエンドポイント、フィーチャートグル、サードパーティーの認証情報を使うなら、分離したconfigファイルに置きましょう。

WebでもNodeでも、config管理を助けてくれるconfigのようなパッケージが多数あります。あなたのアプリケーションはある時点でサーバーとローカル開発環境の両方で動くことになります。configファイルの作成は遅いより早いほうが簡単で、各環境ごとに挙動・使う認証情報・その環境で動く機能などなどを調整することができます。

6.フレームワークはあなたを助けるためにある

知っているから、人気だからという理由でフレームワークが使われている事がよくあります。

自分のプロジェクトにフレームワークが必要なのか、必要であればどれなのか、じっくり考えましょう。あなたのサイトがGitHubで10万starを獲得したフレームワークを活用していようがいまいが、エンドユーザーにとっては全くどうでも良いことです。
経験から、私はフレームワークを以下のように分類しています。

React

コンポーネントベースのWebアプリケーションで、アーキテクチャを完全にコントロールしたい場合に使えます。Reactエコシステムでの開発は、時間もかかり、事前に十分な計画が必要です。十分な知識がある場合に限り、Reactは多くのメリットをもたらします。

Angular / VueJS / Ember

信頼性の高いWebアプリケーションを素早く作りたい場合に使えます。引き換えに大きなブラックボックスを抱えることになります。これらのフレームワークは多くの仕事をしてくれるので、自前アーキテクチャの利点・欠点の両方がなくなります。構造が厳しく決められているため、多少間違いをおかしてもReactほど困ることは少ないでしょう。

jQuery / lodash / その類型

Webページを素早く作りたくて、何キロバイトかファイルが増えても良い場合に使えます。開発時間を劇的に短縮してくれますが、メンテナンス不能なコードも書けてしまうので注意が必要です。

Vanilla JS / フレームワークなし

WebページでもWebアプリケーションでも、開発と計画に多くの時間が使える場合に使えます。ピュアなJavaScriptは、実験的なこと...WebGL,Worker,深い最適化,ブラウザアニメーション...を行うのに合っています。最後には自分が新しいフレームワークを作ることになるでしょう。トランスパイラを使えば、より良く軽量なjQueryの代替物にもなります。

このリストは1つの提案として受け取ってください。フレームワークを使うか、何を使うか、プロジェクトにとってベストな選択をじっくり考えましょう。

7.プロトタイプでないなら、テストを書く

単体テスト、スモークテスト、EtoEテスト、ざっとした確認...。
プロジェクトがすぐに書き換えられるプロトタイプで無い限り、テストを書くべきです。コードベースが複雑になるにつれて、管理・メンテナンスは大仕事になります。テストはそれを肩代わりしてくれます。

未来のいつか、あなたはバグに遭遇し、青空を見上げて、テストを書いてくれた過去の自分に感謝することになります。新機能を追加したときに色々な所をひっそり壊してしまったことを、テストがなければ気づかなかっただろうからです。

8.バージョン管理システムを使う

プロトタイプだろうが、フルスケールの企業Webアプリケーションだろうが、小さな楽しい趣味プロジェクトだろうが、1行目のコードを書く瞬間からgitなどのバージョン管理システムを使いましょう。毎日コミットし、ブランチを使い、マージ・競合の解消・コミットの巻き戻しを学びましょう。意味のあるコミットメッセージを残しましょう。

バージョン管理によって、時間を移動し、壊したものを直し、過去の変更を見ることができます。この記事から1つだけ覚えてもらえるなら、日々バージョン管理システムの基本を学ぶこと、です。なぜなら、記事の残りの部分を無視して間違った道に進んでも、バージョン管理をしていれば直せるからです!それがなければ最初からやり直す運命になります。

9.確実な状態管理

状態管理のデザインパターンかライブラリを探して、自分の命がかかっているかのように齧りつきましょう。本当に命がかかる日がくるかもしれません。

フロントエンドエンジニアとして、通常私たちの大きな挑戦は2つだけです。データを表示することと、保管することです。長期間メンテナンスするという点では保管のほうが遥かに大変で、これを無視して開発するのと楽ができます。そして、数カ月後にプロジェクトは事実上メンテナンス不可能になるでしょう。

データの保管、すなわち状態管理は難しいものです。アプリケーションでは普通、サーバーがデータベースに持つデータとクライアントの画面に表示される内容が同期している必要があります。私たちのゴールは、その中間に置くJavaScriptで余計な複雑性を加えないことです。コンポーネントはサーバーと同じデータを提供し、ユーザーによる変更を同期させ、サーバー側の変更に追従しなければなりません。どうすればこれを実現できるでしょう?

React

Reactはとてもオープンなエコシステムを持っているため、ソリューションも十分にあります。FluxアーキテクチャにはRedux、observableベースならMobXがあります。それぞれメリット・デメリットがあります。使い始める前に各ライブラリの基礎を理解するようにしましょう。

Angular、Ember、VueJS

これらはビルドインで専用の状態管理の仕組みを持っており、コンセプトはObservableを基礎にしています。必須ではありませんが、追加のライブラリとしてngRx、Akita、Vuexがあります。

他のフレームワーク、Pure JavaScript

Redux、MobX、または独自のステート管理の仕組みを使えます。主なゴールは、アプリケーション全体で1つの「source of truth」(信頼できる情報源)を持つことです。この「情報源」は、サービスでも、ライブラリでも、単純なObservableでもありえます。

10.流行を疑う

最後に、コミュニティーに耳を傾け学びつつも、全ての記事、コメント、Mediumへの猫の長い投稿(訳注:この記事。著者のハンドルが猫)、あなたのコードへのフィードバックはよく吟味し、疑ってかかりましょう。フロントエンドの世界ではたくさんの新しいアイデアが勢いよく登場してきます。これらにはオープンな態度でありつつも、お祭り騒ぎを「追いかけるために追いかける」ことのないように気をつけましょう。こうした態度のために忘れさられてしまったプロジェクトが山ほどあります。

古く成熟したフレームワークで書かれたプロジェクトが、新しいものが出たからといって2つのフレームワークを混ぜたようなプロジェクトよりはるかに優れていることは珍しくありません。新しい流行の何かがアプリケーションや開発効率を少し向上させるとしても、一貫性には敵いません。メンテナンス性を保つために自分の最初の選択を守り、必要なときにだけ流行を取り入れましょう。


以上です!
ここまで読んでくれてありがとう。コメントであなたの意見や話を聞かせてくれるのを楽しみにしています。上述の通り、これらは私がJavaSriptとフロントエンド開発に関わる中で得られた一番大事な経験に過ぎません。私たちが経験する挑戦の大海のほんの一滴です。

これらのパターンがあなたの役に立ちますように。

(訳終)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[翻訳]JavaSciptプロジェクトでいずれは学ぶ10のこと

以下は、The Cat with a Dragon Tattoo によって2018/8/3にMediumに投稿され、現在1万Clap以上受けている記事、"10 Things You Will Eventually Learn About JavaScript Projects"=「JavaSciptプロジェクトでいずれは学ぶ10のこと」の日本語訳です。

特定のライブラリを超えた、しかしフロントエンド開発者を主眼とした学びがまとまっており、僕がまさに読みたかったものでした。著者に感謝を。

表示 - 非営利 - 継承 4.0 国際 (CC BY-NC-SA 4.0)
https://creativecommons.org/licenses/by-nc-sa/4.0/deed.ja


JavaScriptは冒険です。
様々な業界で10年近く開発してきましたが、誰だってこれには同意してくれると思います。

フロントエンド開発は、プログラマーである私たちに、多くの選択の自由、柔軟性、創造の余地を与えてくれます。しかし代わりに、ちょっとした知識、計画、責任が求められます。

jQuery、require.js、Angular、React、ExtJs、その他思い出せない(または思い出したくない)何十ものプロジェクトを経験してきましたが、想像もしなかった酷いモノを見てきました。きっと皆さんもそうでしょう。

しかしどんな時代でも、まとまりのないプロジェクトをどうにか扱いやすく変えられる「共通のパターン」がありました。以下では、その中でも特に重要な10のパターンを記します。個人的な経験から導いた意見ですが、経験豊富なエンジニアの多くは頷いてくれるのではないでしょうか。これらのパターンは、どんなフレームワーク・方法論・チームの規模であっても、ドキュメンテーションやリファクタリングの必要性を減らし、エンジニアの目から零れる涙を減らし、プロジェクトの盤石な基礎となってくれるでしょう。

あなたに新しい発見があり、これらのパターンを便利だと感じ、またこれらを使って素晴らしい何かを作ってくれることを願います!:muscle:

1.分割統治

聞いたことはありながら、多くの人がこのルールを過小評価しています。CommonJS、Webpack、Nodeではコードを複数のファイルに分割できます。でも、何故わざわざそんな事をするのでしょう?

一貫性

プロジェクトを1つのexportを持つファイルに分割れば、プロジェクトが大きくなった時、検索や依存性の管理がずっと簡単になります。「ファイルの名前はそれがexportするものを示す」というルールは直感的で、脳みそを疲弊させずにアーキテクチャを概観することができます。

管理

各exportを独立ファイルに分割することで、必要なときに素早く移動できるようになり、分業が促進されます。ヘルパー関数がアプリケーションの他の場所で必要になれば、/sharedフォルダを作ってファイルを移動すれば良いわけです。そうすれば、また他の部分からも参照できます。

2.恥ずかしいくらい明確に

変数、関数、ファイルの名前は、自分の子供の名前だと思って時間をかけて決めましょう。変数xとすれば今日の0.3秒が節約できるかもしれませんが、1ヶ月後には2日かけて意味を解明し、4日かけてリファクタすることになります。先々のことを考え、長い名前を恐れないようにしましょう。

ハック的な手法や、MITに入学したくなるような難しいやり方は避けましょう。その解決法は本当にスマートなのかもしれません。未来のあなたやチームメートも、コードの解読にたっぷり時間を費やした後で「賢いね!」と同意するかもしれません。しかし、物事をシンプルに保つことに集中し、なるべくドキュメントやコメントが必要ないようなコードを目指しましょう。

3.マジックナンバーやマジック文字列を解消する

どんなにやりたくなっても、マジックナンバー・マジック文字列は使ってはいけません。小さなもの、さして重要でないものでも、意味ある名前の変数に格納して、スコープの冒頭に置きましょう。

明示的にコードに書いた値は多くの場合どこかで再利用されます。すぐに変数に格納すれば、コードの重複は減り、修正は楽になり、値に意味を与えられます。

4.ネストと戦う

コードが横に120文字/縦に500行以上になるかif文が3層以上になったら、全力で分割しましょう。

深くネストされたif文を別々の関数、Promise、Observableに分割することで、条件文の複雑性を解消することができます。非同期呼び出しが多いなら、async/awaitも大幅にコードをシンプルにしてくれます。

5.configを頑張る

グローバル変数、APIエンドポイント、フィーチャートグル、サードパティーの認証情報を使うなら、分離したconfigファイルに置きましょう。

WebでもNodeでも、config管理を助けてくれるconfigのようなパッケージが多数あります。あなたのアプリケーションはある時点でサーバーとローカル開発環境の両方で動くことになります。configファイルの作成は遅いより早いほうが簡単で、各環境ごとに挙動・使う認証情報・その環境で動く機能などなどを調整することができます。

6.フレームワークはあなたを助けるためにある

知っているから、人気だからという理由でフレームワークが使われている事がよくあります。

自分のプロジェクトにフレームワークが必要なのか、必要であればどれなのか、じっくり考えましょう。あなたのサイトがGitHubで10万starを獲得したフレームワークを活用していようがいまいが、エンドユーザーにとっては全くどうでも良いことです。
経験から、私はフレームワークを以下のように分類しています。

React

コンポーネントベースのWebアプリケーションで、アーキテクチャを完全にコントロールしたい場合に使えます。Reactエコシステムでの開発は、時間もかかり、事前に十分な計画が必要です。十分な知識がある場合に限り、Reactは多くのメリットをもたらします。

Angular / VueJS / Ember

信頼性の高いWebアプリケーションを素早く作りたい場合に使えます。引き換えに大きなブラックボックスを抱えることになります。これらのフレームワークは多くの仕事をしてくれるので、自前アーキテクチャの利点・欠点の両方がなくなります。構造が厳しく決められているため、多少間違いをおかしてもReactほど困ることは少ないでしょう。

jQuery / lodash / その類型

Webページを素早く作りたくて、何キロバイトかファイルが増えても良い場合に使えます。開発時間を劇的に短縮してくれますが、メンテナンス不能なコードも書けてしまうので注意が必要です。

Vanilla JS / フレームワークなし

WebページでもWebアプリケーションでも、開発と計画に多くの時間が使える場合に使えます。ピュアなJavaScriptは、実験的なこと...WebGL,Worker,深い最適化,ブラウザアニメーション...を行うのに合っています。最後には自分が新しいフレームワークを作ることになるでしょう。トランスパイラを使えば、よく良く軽量なjQueryの代替物にもなります。

このリストは1つの提案として受け取ってください。フレームワークを使うか、何を使うか、プロジェクトにとってベストな選択をじっくり考えましょう。

7.プロトタイプでないなら、テストを書く

単体テスト、スモークテスト、EtoEテスト、ざっとした確認...。
プロジェクトがすぐに書き換えられるプロトタイプで無い限り、テストを書くべきです。コードベースが複雑になるにつれて、管理・メンテナンスは大仕事になります。テストはそれを肩代わりしてくれます。

未来のいつか、あなたはバグに遭遇し、青空を見上げて、テストを書いてくれた過去の自分に感謝することになります。新機能を追加したときに色々な所をひっそり壊してしまったことを、テストがなければ気づかなかっただろうからです。

8.バージョン管理システムを使う

プロトタイプだろうが、フルスケールの企業Webアプリケーションだろうが、小さな楽しい趣味プロジェクトだろうが、1行目のコードを書く瞬間からgitなどのバージョン管理システムを使いましょう。毎日コミットし、ブランチを使い、マージ・競合の解消・コミットの巻き戻しを学びましょう。意味のあるコミットメッセージを残しましょう。

バージョン管理によって、時間を移動し、壊したものを直し、過去の変更を見ることができます。この記事から1つだけ覚えてもらえるなら、日々バージョン管理システムの基本を学ぶこと、です。なぜなら、記事の残りの部分を無視して間違った道に進んでも、バージョン管理をしていれば直せるからです!それがなければ最初からやり直す運命になります。

9.確実な状態管理

状態管理のデザインパターンかライブラリを探して、自分の命がかかっているかのように齧りつきましょう。本当に命がかかる日がくるかもしれません。

フロントエンドエンジニアとして、通常私たちの大きな挑戦は2つだけです。データを表示することと、保管することです。長期間メンテナンスするという点では保管のほうが遥かに大変で、これを無視して開発するのと楽ができます。そして、数カ月後にプロジェクトは事実上メンテナンス不可能になるでしょう。

データの保管、すなわち状態管理は難しいものです。アプリケーションでは普通、サーバーがデータベースに持つデータとクライアントの画面に表示される内容が同期している必要があります。私たちのゴールは、その中間に置くJavaScriptで余計な複雑性を加えないことです。コンポーネントはサーバーと同じデータを提供し、ユーザーによる変更を同期させ、サーバー側の変更に追従しなければなりません。どうすればこれを実現できるでしょう?

React

Reactはとてもオープンなエコシステムを持っているため、ソリューションも十分にあります。FluxアーキテクチャにはRedux、observableベースならMobXがあります。それぞれメリット・デメリットがあります。使い始める前に各ライブラリの基礎を理解するようにしましょう。

Angular、Ember、VueJS

これらはビルドインで専用の状態管理の仕組みを持っており、コンセプトはObservableを基礎にしています。必須ではありませんが、追加のライブラリとしてngRx、Akita、Vuexがあります。

他のフレームワーク、Pure JavaScript

Redux、MobX、または独自のステート管理の仕組みを使えます。主なゴールは、アプリケーション全体で1つの「source of truth」(信頼できる情報源)を持つことです。この「情報源」は、サービスでも、ライブラリでも、単純なObservableでもありえます。

10.流行を疑う

最後に、コミュニティーに耳を傾け学びつつも、全ての記事、コメント、Mediumへの猫の長い投稿(訳注:この記事。著者のハンドルが猫)、あなたのコードへのフィードバックはよく吟味し、疑ってかかりましょう。フロントエンドの世界ではたくさんの新しいアイデアが勢いよく登場してきます。これらにはオープンな態度でありつつも、お祭り騒ぎを「追いかけるために追いかける」ことのないように気をつけましょう。こうした態度のために忘れさられてしまったプロジェクトが山ほどあります。

古く成熟したフレームワークで書かれたプロジェクトが、新しいものが出たからといって2つのフレームワークを混ぜたようなプロジェクトよりはるかに優れていることは珍しくありません。新しい流行の何かがアプリケーションや開発効率を少し向上させるとしても、一貫性には敵いません。メンテナンス性を保つために自分の最初の選択を守り、必要なときにだけ流行を取り入れましょう。


以上です!
ここまで読んでくれてありがとう。コメントであなたの意見や話を聞かせてくれるのを楽しみにしています。上述の通り、これらは私がJavaSriptとフロントエンド開発に関わる中で得られた一番大事な経験に過ぎません。私たちが経験する挑戦の大海のほんの一滴です。

これらのパターンがあなたの役に立ちますように。

(訳終)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

enebularでTwitter運用をしたかった話 #enebular

この資料について

3/25 enebular meetup vol9での登壇資料です。


自己紹介

  • 名前: n0bisuke
  • ステータス: 花粉が辛い、クロスミーに続いてペアーズも始めた

彼は悩んでいた

「目立ちたいんですよね。」「くんみたいに」


そういうTwitterアカウントを作った

コンセプトは「エンジニアわかってない筋肉キャラ」


蛇足: 最初は「筋肉ないけど筋肉キャラ」でやろうとしたキャラぶれ期がありました。


内容はエンジニアジョーク

こういうの↓


「Javaを勉強してフロントエンド能力身に付けたい」

-> それJavaScriptな!


「C言語って高級言語らしいです いくら予算あれば始められますか」

-> 無料だし、高級って金額の話じゃないから!


「フォートランっていうプログラムもあるみたいです。筋トレWebサービス作りたいのでがんばります。」

-> フォートランってWebが生まれるよりも遥か昔の言語だからね、Webは向いてないよ。うん。


「Vanilla JSてフレームワークが速いらしいですよ!」

-> 一回調べてみようか笑





みたいなアカウントです。
(ツイートによっては)けっこう好評だったので もっと盛り上げたいなと思いました。


「フォロワー増やしたい」をenebularで

フォロワーを増やしたいというモチベをenebularで解消してみます。


フォロー自動化したい

TwitterのAPIで自動化プログラム作ってみたいですね。

https://developer.twitter.com/en/docs/accounts-and-users/follow-search-get-users/api-reference/post-friendships-create.html

フォローのAPIはこんな感じであるみたい


これをenebularでやりたいけど、ノードがない

  • 既存ノード -
    • Twitter In -> ツイートを検索する
    • Twitter Out -> ツイートさせる

いい感じのがない。サードパーティ製のをググってもない。


ノード作ったよ



* node-red-contrib-twitter-follow
* functionノード使えばやれるけどfunctionノード使ったら負けという説
* functionノード最高ってのは分かる
* 無かったら作ろう
* フォローをするFollowノードと「フォローされたを検知」するFollowEventノード


使い方

  • Tweetの検索は既存のTwitter Inを利用
  • 特定のツイートを見つけたらフォローするという古典的アルゴリズム

1時間あたりにフォローする人数の制限機能付き

  • あまり多くのフォローをするとロボットだと思われてTwitterからアカウント制限される
  • 制限機能は大事

ちなみに(最近知った)delayノードでもやれた

  • メセージの制限が出来る
  • 「xxx時間(単位)あたりにxxxメッセージを流す」的な

フォロバもやれる

  • FollowEventノードFollowノードでおk

結果増えた?

  • 少し試してみたら、すこし増えた。
  • ただし、enebularは定期的に起こしてあげないといけない問題が.....
  • ということで(こっそり)ハック。良い子は真似しないでね。

  • これで長期運用が出来そうだ......!

がんがん増えた

  • フォロワー数増加に成功
  • 3日間くらいでフォロワー数が倍増
  • Twitterアカウントは自動化である程度成長させることができることが分かった。
  • 改めてフォロワーは買える時代ってのを実感


調子乗ってしまった

スクリーンショット 2019-03-05 19.46.20.png


  • Twitterアカウントって正常 -> ロック -> 凍結っていうステータスがあるんですね。

まとめ

ご利用は計画的に。

@syunpatsuryoku -> @gikyuryoku で新しくアカウント作りました。


https://twitter.com/gikyuryoku


おまけ

  • (twitterトラウマになったので)Facebookページ作りました。いいねしてね!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

pm2で1時間に1回再起動させる

Raspberry Piで実行してますが、不安定なプロセスがあって定期的に再起動出来たらと思ったやつです。

プロセス1を永続化

pm2 start process1.js

ここは通常の使い方。

プロセス1を監視する

npm init -y
npm i pm2

https://github.com/Unitech/pm2/blob/master/examples/api-pm2/api.js

pm2watch.js
'use strict'

const pm2 = require('pm2');
const INTERVAL_TIME = 1000 * 60 * 60; //1時間

const restart = () => {
    pm2.restart('process1', errback => {
        if(errback === null){
            console.log('reloaded');
        }else{
            console.log(errback);
        }
    });
}

setInterval(() => restart, INTERVAL_TIME);
pm2 start pm2watch.js

確認

$ pm2 list

┌────────────┬────┬─────────┬──────┬───────┬────────┬─────────┬────────┬──────┬───────────┬──────┬──────────┐
│ App name   │ id │ version │ mode │ pid   │ status │ restart │ uptime │ cpu  │ mem       │ user │ watching │
├────────────┼────┼─────────┼──────┼───────┼────────┼─────────┼────────┼──────┼───────────┼──────┼──────────┤
│ process1   │ 0  │ N/A     │ fork │ 27236 │ online │ 16      │ 12m    │ 0.2% │ 34.6 MB   │ pi   │ disabled │
│ pm2watch   │ 1  │ 1.0.0   │ fork │ 27701 │ online │ 0       │ 93s    │ 0.2% │ 34.2 MB   │ pi   │ disabled │
└────────────┴────┴─────────┴──────┴───────┴────────┴─────────┴────────┴──────┴───────────┴──────┴──────────┘

定期的に再起動してprocess1の

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Node.jsでSQLiteデータベースのCRUD処理を行う

概要

本記事では、Node.jsでSQLite3のデータベースのCRUD処理をES2015(ES6)で実装した例を示します。

sqlite3というNodeのモジュールを使用しています。このライブラリはすべて非同期のAPIであるため、ES2015のawait/asyncを使って呼び出すようにしています。

準備

プロジェクト作成

Node.jsでプロジェクトを作るには以下のコマンドを実行します。

mkdir usertable
cd usertable
npm init
(適当に答える, 以下の例はentry pointだけmain.jsにしています)

ES2015を使えるようにする

babelとはES2015のファイルをJSに変えるトランスパイラです。
Nodeで何か書くときは、以下のようにしてbabelを使えるようにすると大変便利です。

このプロジェクトでES2015を扱うにはbabel-clibabel-preset-envを入れます。

npm i -S babel-cli babel-preset-env
(npm i -S は、npm install --saveと同じ)

package.jsonのscriptsを以下のように書き換えます。

package.json
  "scripts": {
    "start": "babel-node main.js --presets env"
  },

nodeで実行するのではなく、babelでトランスパイルして実行するという意味です。

node-sqlite3 のinstall

SQLiteは、バイナリファイルのデータベースです。

Macの場合デフォルトでインストールされています。Windowsの場合はインストールが必要になります。
NodeでSQLiteのデータベースを扱うには、以下のsqlite3モジュールを使います。
- https://github.com/mapbox/node-sqlite3

npm i -S sqlite3

実装

実装は、2ファイルです。CRUD操作を呼び出すmain.jsとデータベース操作をするusertable.jsです。

呼び出し側の実装

先にmain.jsのコードをお見せし、その後データベース操作のコードを説明します。
Userというデータを10件作成し、3件を更新、カウントしてリストアップしたのち、削除するという処理です。

検索はページング処理しています。(この例は結局メモリにすべてあげているため、あまり意味はないです)

main.js
import UserTable, { DBCommon, User } from "./usertable"

const main = async () => {
  DBCommon.init()
  console.log("--- create table ---")
  await UserTable.createTableIfNotExists()

  console.log("--- save 10 users ---")
  for (let i = 0; i < 10; i++) {
    const user = new User(`account${i}`, `name${i}`, `aaa${i}@bbb.ccc`)
    await UserTable.save(user)
  }

  console.log("--- count after saving ---")
  let count = await UserTable.count()
  console.log(count)

  console.log("--- update 3 users ---")
  for (let i = 3; i < 6; i++) {
    const user = new User(`account${i}`, `new-name${i}`, `xxx${i}@yyy.zzz`)
    await UserTable.save(user)
  }

  console.log("--- list ---")
  let start = 0
  const limit = 3
  let users = []
  while (true) {
    users = users.concat(await UserTable.list(start, limit))
    start = start + limit
    if (start >= count) break
  }
  console.log(users)

  console.log("--- delete 10 users ---")
  users.forEach(user => { UserTable.delete(user) })

  console.log("--- count after deleting ---")
  count = await UserTable.count()
  console.log(count)
}

npm startを実行すると以下のようにコンソール出力されます。

--- create table ---
--- save 10 users ---
--- count after saving ---
10
--- update 3 users ---
--- list ---
[ User { account: 'account0', name: 'name0', email: 'aaa0@bbb.ccc' },
  User { account: 'account1', name: 'name1', email: 'aaa1@bbb.ccc' },
  User { account: 'account2', name: 'name2', email: 'aaa2@bbb.ccc' },
  User { account: 'account3', name: 'new-name3', email: 'xxx3@yyy.zzz' },
  User { account: 'account4', name: 'new-name4', email: 'xxx4@yyy.zzz' },
  User { account: 'account5', name: 'new-name5', email: 'xxx5@yyy.zzz' },
  User { account: 'account6', name: 'name6', email: 'aaa6@bbb.ccc' },
  User { account: 'account7', name: 'name7', email: 'aaa7@bbb.ccc' },
  User { account: 'account8', name: 'name8', email: 'aaa8@bbb.ccc' },
  User { account: 'account9', name: 'name9', email: 'aaa9@bbb.ccc' } ]
--- delete 10 users ---
--- count after deleting ---
0

共通クラスDBCommonの作成

DB操作に関する処理はすべてusertable.jsにまとめます。DBCommonは別に切り出しても良いかもしれません。

複数のテーブル操作を想定して、dbを扱うための共通クラスを作成します。
シングルトン的に1つのファイルに対して1つのインスタンスを使う想定です。

usertable.js
import sqlite3 from "sqlite3"

// ファイルに対応した、ただ1つのインスタンス
let database

export class DBCommon {
  static init() {
    database = new sqlite3.Database("user.sqlite3")
  }
  static get() {
    return database
  }
}

Userクラス(エンティティ)

Userクラスは、シンプルにアカウント、名前、E-mailを持つクラスとします。

export class User {
  constructor(account, name, email) {
    this.account = account
    this.name = name
    this.email = email
  }
}

CRUDの実装

CRUD処理は、すべてUserTableというクラスのstaticメソッドで実装します。

sqlite3モジュールの以下のAPIを使用しています。
- open ... データベース作成またはオープン
- serialize ... 内部の処理を同期的に実行する
- run ... SQLの実行(DDLやinsert/deleteなど)

クエリ系 ... callbackを渡して非同期で実行します。
- get ... クエリを実行して1番目の結果だけにcallbackを実行する
- all ... クエリを実行して全ての結果を1度だけcallbackを実行する(メモリ上に展開されるため取り扱い注意)
- each ... クエリを実行してそれぞれの結果に対してcallbackを実行する。 (本例では未使用)

テーブル作成 - create table if not exists

SQLiteには、テーブルがなければ作るという便利なCREATE TABLE構文があるのでそれを使います。

- create table if not exist <table_name> (<column_definitions>)

SQLの実行は、runを使います。

const userTableName = "users"

export default class UserTable {
  static async createTableIfNotExists() {
    const db = DBCommon.get()
    return new Promise((resolve, reject) => {
      try {
        db.serialize(() => {
          db.run(`create table if not exists ${userTableName} (
            account text primary key,
            name text,
            email text
          )`)
        })
        return resolve()
      } catch (err) {
        return reject(err)
      }
    })
  }

データ作成/更新 - insert or replace

SQLiteにはinsert or replaceというデータ更新の構文があるためそれをsaveというメソッドとして実装しています。

  static async save(user) {
    const db = DBCommon.get()
    return new Promise((resolve, reject) => {
      try {
        db.run(`insert or replace into ${userTableName} 
        (account, name, email) 
        values ($account, $name, $email)`,
          user.account, user.name, user.email
        )
        return resolve()
      } catch (err) {
        return reject(err)
      }
    })
  }

カウント

select count(*) from <table_name>を呼ぶだけです。
getをつかって最初の結果であるカウントだけ取得します。

  static async count() {
    const db = DBCommon.get()
    return new Promise((resolve, reject) => {
      db.get(`select count(*) from ${userTableName}`, (err, row) => {
        if (err) return reject(err)
        return resolve(row["count(*)"])
      })
    })
  }

検索

allを使って全ての結果を操作するため、offsetとlimitを指定するようにしています。

callbackの引数であるrowsは、JSの普通のオブジェクトの配列となって帰ってきます。
ページングするため、順序を一意になるようにしています。

  static async list(offset, limit) {
    const db = DBCommon.get()
    const result = []
    return new Promise((resolve, reject) => {
      db.serialize(() => {
        db.all(`select account, name, email from ${userTableName}
        order by account limit ${limit} offset ${offset}`,
          (err, rows) => {
            if (err) return reject(err)
            rows.forEach(row => {
              result.push(new User(row["account"], row["name"], row["email"]))
            })
            return resolve(result)
          })
      })
    })
  }

データ削除

runを使ってdelete文を実行するだけです。

  static async delete(user) {
    const db = DBCommon.get()
    return new Promise((resolve, reject) => {
      try {
        db.run(`delete from ${userTableName} where account = $account`, user.account)
        return resolve()
      } catch (err) {
        return reject(err)
      }
    })
  }

まとめ

まとめです。
- ES2015をNodeで使えるようにするには、babel-cli, babel-preset-envを入れる
- node-sqliteの操作は、open, run, get, all, eachで行う
- 非同期処理は、await/asyncで扱えば、呼び出し側は簡単に記述できる。

1番目が地味にメモしておきたかったのもこの記事を書いた理由です。

以上。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

node.jsインストール

sudo apt install -y git curl

node.js 最新インストール とバージョン管理nを入れる

sudo apt install -y nodejs npm
sudo npm install n -g
sudo n stable
sudo apt purge -y nodejs npm
exec $SHELL -l
node -v
npn -v

yarn

curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt update
sudo apt install yarn
yarn -v

sudo apt autoremove -y

権限を自分ように変更(開発用) 

npm i と sudo npm i -g の sudoをなくすため

sudo chown -R mazu:mazu /usr/local/{lib/node_modules,bin,share,n}

npm とyarn 違い

npm

npm install --save(-dev) xxx #パッケージ追加
npm run xxx                   #スクリプト実行
package.json
{
  "name": "myapp",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "webpack": "4.19.0"
  }
}

yarn

yarn add (--dev) xxx #パッケージ追加
yarn xxx             #スクリプト実行 
package.json
{
  "name": "myapp",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "devDependencies": {
    "webpack": "4.19.0"
  }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ubuntu18.04 node.jsインストール

最新化と開発ベースを入れておく

sudo apt-get update
sudo apt-get install -y build-essential libssl-dev git curl

node.jsのバージョン管理ついて n と nvmがあります

n は 全ユーザー共有、nvm は ユーザーごとにバージョン管理できます
私は、環境を複数生成するので、ユーザーごとのバージョン管理は使わない nを使う

node.js 最新インストール と バージョン管理 nを入れる

sudo apt install -y nodejs npm
sudo npm install n -g
sudo n stable
sudo apt purge -y nodejs npm
exec $SHELL -l
export NODE_PATH=`npm root -g`  >>~/.bashrc
node -v
npn -v
echo $NODE_PATH

yarn

curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt update
sudo apt install yarn
yarn -v

sudo apt autoremove -y

権限を自分ように変更(開発用) 

sudo npm i -g の sudoをなくすため

sudo chown -R mazu:mazu /usr/local/{lib/node_modules,bin,share,n}

npm とyarn 違い

npm

npm install --save(-dev) xxx #パッケージ追加
npm run xxx                   #スクリプト実行
package.json
{
  "name": "myapp",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "webpack": "4.19.0"
  }
}

yarn

yarn add (--dev) xxx #パッケージ追加
yarn xxx             #スクリプト実行 
package.json
{
  "name": "myapp",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "devDependencies": {
    "webpack": "4.19.0"
  }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

nodebrewでインストールしたpm2をRasbperry Piで自動起動させる

ラズパイ起動と共にpm2でスクリプト起動の記事をnodebrewでインストールした環境でやってみました。

流れ

  • 永続化させるプロセスをpm2で起動
  • pm2 startupを実行
    • ヒントっぽいのがでる
    • 無視して↓のsudo env~を実行
  • pm2 saveで保存

まずは永続化

pm2 start app.js

ここで永続化したプロセスが↓の手順で記憶される模様です。

pm2 startup

$ pm2 startup

色々でるけどスルーして↓の行を実行。
ここの行が全てな気がした。

sudo env PATH=$PATH:/home/pi/.nodebrew/current/bin /home/pi/.nodebrew/current/bin/pm2 startup systemd -u pi --hp /home/pi

ちなみにpm2 startupすると出てくるsudo env PATH=$PATH:/home/pi/.nodebrew/node/v11.6.0/bin /home/pi/.nodebrew/node/v11.6.0/lib/node_modules/pm2/bin/pm2 startup systemd -u pi --hp /home/piなどのコマンドはバージョンがv11.6.0みたいに固定で書いてあるので、これをコピペしてしまうとNode.jsのバージョンが変わってしまったら対応できなくなってしまいそう

↓こんな感じのログが出ます。利用しているcurrentバージョン(↓v11.6.0)のパスを自動的に設定してくれてますね。

[PM2] Init System found: systemd
Platform systemd
Template
[Unit]
Description=PM2 process manager
Documentation=https://pm2.keymetrics.io/
After=network.target

[Service]
Type=forking
User=pi
LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity
Environment=PATH=/home/pi/.nodebrew/node/v11.6.0/bin:/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
Environment=PM2_HOME=/home/pi/.pm2
PIDFile=/home/pi/.pm2/pm2.pid

ExecStart=/home/pi/.nodebrew/node/v11.6.0/lib/node_modules/pm2/bin/pm2 resurrect
ExecReload=/home/pi/.nodebrew/node/v11.6.0/lib/node_modules/pm2/bin/pm2 reload all
ExecStop=/home/pi/.nodebrew/node/v11.6.0/lib/node_modules/pm2/bin/pm2 kill

[Install]
WantedBy=multi-user.target

Target path
/etc/systemd/system/pm2-pi.service
Command list
[ 'systemctl enable pm2-pi' ]
[PM2] Writing init configuration in /etc/systemd/system/pm2-pi.service
[PM2] Making script booting at startup...
[PM2] [-] Executing: systemctl enable pm2-pi...
[PM2] [v] Command successfully executed.
+---------------------------------------+
[PM2] Freeze a process list on reboot via:
$ pm2 save

[PM2] Remove init script via:
$ pm2 unstartup systemd

保存

$ pm2 save

再起動して確認

  • 再起動
$ sudo reboot
  • 再起動後に確認するとappのプロセスが立ち上がってます。
$pm2 list

┌──────────┬────┬─────────┬──────┬─────┬────────┬─────────┬────────┬──────┬───────────┬──────┬──────────┐
│ App name │ id │ version │ mode │ pid │ status │ restart │ uptime │ cpu  │ mem       │ user │ watching │
├──────────┼────┼─────────┼──────┼─────┼────────┼─────────┼────────┼──────┼───────────┼──────┼──────────┤
│ app      │ 0  │ N/A     │ fork │ 878 │ online │ 1       │ 67s    │ 0.2% │ 37.5 MB   │ pi   │ disabled │
└──────────┴────┴─────────┴──────┴─────┴────────┴─────────┴────────┴──────┴───────────┴──────┴──────────┘

自動起動を解除する

自動起動に設定したスクリプトが悪さしてるみたいで、一旦解除したくなりました。

pm2 unstartup systemd

さっきとほぼ一緒ですが、~~ unstartup sytemd ~~という形で unstartupになってるので注意しましょう。

sudo env PATH=$PATH:/home/pi/.nodebrew/current/bin /home/pi/.nodebrew/current/bin/pm2 unstartup systemd -u pi --hp /home/pi

ここもstartupの箇所がunstartupに

所感

Pythonのスクリプトは/etc/rc.localとかでやってたので新鮮。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む