Quantcast
Channel: Node.jsタグが付けられた新着記事 - Qiita
Viewing all 8839 articles
Browse latest View live

nodemailer gmail めも

$
0
0

めも

  1. アプリの許可
  2. その他、gmail画面の設定は不要。
  3. ソース参照
  4. 成功するまで時間がかかる模様

node は入っているのに npm が動かなくなった

$
0
0

背景

何も考えずnpm installをしたら、そんなんないぞって言われてしまった。
npm_qiita.png

解決方法

.bashrc
export PATH="$PATH:"/usr/local/Cellar/node/NODEのバージョン/bin/""

のを追記

$ source .bashrc

で反映させる。

要はPATHが通っていないだけだった。

AWS LambdaによるスクレイピングでQiita Organizationの最新記事を取得しSlackに通知するBotを作った

$
0
0

Organizationページの「最新の投稿」をAWS Lambda(node.js)でスクレイピングし、新しい投稿の記事のタイトルとURLをSlackに投稿するBotを作ってみました

コードはここに置いてます(大分汚いです)
https://github.com/uji/qiita-organization-scraping/blob/master/app.js

AWS Lambdaの処理の流れ

  1. 「最新の投稿」にある記事をスクレイピングで取得
  2. AWS S3にあるtxtファイルを確認し、Slack未投稿の記事の有無を確認
  3. 未投稿の記事をSlackに投稿
  4. 最新記事のタイトルをtxtファイルでAWS S3に保存

投稿済みの記事を永続化する必要があるのですが、DB使うのは大げさな気がしたのでS3にtxtで保存します

スクレイピング

AWS Lambda 用のchromium chrome-aws-lambdaを使ってスクレイピングしました
Google Cloud Functionsでも使えるっぽいです
puppeteerと使用感はほぼ同じです

.of-ItemLink_header-title classを取ってくると「最新の投稿」の要素を取ってこれます

constchromium=require("chrome-aws-lambda");exports.handler=async(event,context)=>{letbrowser=null;letelems=[];try{browser=awaitchromium.puppeteer.launch({args:chromium.args,defaultViewport:chromium.defaultViewport,executablePath:awaitchromium.executablePath,headless:chromium.headless,});letpage=awaitbrowser.newPage();awaitpage.goto("http://qiita.com/organizations/"+process.env.ORGANIZATION_NAME);constselector=".of-ItemLink_header-title";elems=awaitpage.$$eval(selector,es=>es.map(e=>[e.textContent,e.href]));}catch(error){returncontext.fail(error);}finally{if(browser!==null){awaitbrowser.close();}}returncontext.succeed(elems);};

Slackに通知する

@slack/boltまたは@slack/web-apiでSlack APIを叩き、取得した記事をSlackに投げます
Slack Botでメッセージを送るには、singning secretbot tokenchannel idが必要になります

boltの場合
const{App,ExpressReceiver}=require("@slack/bolt");constreceiver=newExpressReceiver({signingSecret:process.env.SLACK_SIGNING_SECRET});constapp=newApp({token:process.env.SLACK_BOT_TOKEN,receiver:receiver});app.client.chat.postMessage({channel:process.env.SLACK_CHANNEL,text:"message",token:process.env.SLACK_BOT_TOKEN});

S3のテキストファイル読み込み、上書き

投稿済みの記事のタイトルを永続化します
aws-sdkを使うと結構簡単にS3にアクセスできます

読み込み
constAWS=require("aws-sdk");consts3=newAWS.S3({region:"ap-northeast-1"});lets3Params={Bucket:process.env.BACKET_NAME,Key:process.env.FILE_NAME};s3.getObject(s3Params,(err,data)=>{if(err)returncontext.fail(err);elselatest=data.Body.toString();});
上書き
constAWS=require("aws-sdk");consts3=newAWS.S3({region:"ap-northeast-1"});lets3Params={Bucket:process.env.BACKET_NAME,Key:process.env.FILE_NAME,Body:"タイトル"};s3.putObject(s3Params,(err)=>{if(err)returncontext.fail(err);});

AWS Lambdaの設定

ランタイムにnode.jsを指定してLambdaを作成します

まあまあ時間のかかる処理なので、基本設定からメモリ、タイムアウト時間を変更しておきます
メモリは512MB、タイムアウト時間は1分にしました

node_modulesはzipにまとめてLayerに登録します
AWS Lambda レイヤー

AWS Lambdaで動かす

コードをLambdaに書いてテストを実行してみます

スクリーンショット 2020-02-24 0.53.23.png

スクリーンショット 2020-02-23 0.20.28.png

Slackに通知されました

AWS Cloud Watch Eventsで定期実行

AWS LambdaのトリガーにAWS Cloud Watch Eventsを追加して定期実行されるようにします

スクリーンショット 2020-02-24 1.38.12.png

毎朝9時に実行される設定にしました

まとめ

今回はスクレイピングで取得しましたがQiita APIを使ってユーザーごとの最新記事をとる方法もあります
そっちの方が良さそう

javascript(node.js)のsetHeaderとwriteHeadの違い

$
0
0

setHeader

ヘッダー情報を設定する

response.setHeader(名前,);

writeHead

ヘッダー情報を出力する

response.writeHead(コード番号,メッセージ);

Node.js日記#2

$
0
0

エラーメモ

node.jsで作成したプログラム実行時、
 Cannot read property '〇〇' of undefined
と表示された。これはundefinedの〇〇が読み込めないという(直訳)ことなので、このひとつ前の行でエラーが起きている可能性が高い。

忘れない用メモ

Node.jsでは入出力はStreamという概念で扱われる

ライブラリを探す習慣をつける。

  • yarn(ヤーン)
    Node.js のためのパッケージマネージャ

  • npm
    Node.jsが標準で用意されているパッケージマネージャ

Node.js (サーバサイド JavaScript) 事始め

$
0
0

はじめに

TypeScriptを覚えたいのですが、まずはNode.jsの勉強から始めた方が良さそうな感じでしたので、Node.jsの勉強を始めました。

Noda.jsのインストール

公式サイトからインストーラーをダウンロードしてインストールします。

nodejs1.jpg

LTS版の方が無難だと思います。
Node.jsはmacやLinuxのイメージが強かったのですが、Windowsでも問題なく動きました。

環境

OS:Windows 10 Pro 64bit
DB:SQL Server 2019(Cent OS 8 on Hyper-V)
node.js:v12.16.1
npm:v6.13.4
Editor:Visual Studio Code

作業フォルダの作成

今回は「D:\Node」を作業フォルダにしました。

Hello World

作業フォルダに以下のファイルを作成します。

sample01.js
varhttp=require('http');varserver=http.createServer(function(req,res){res.writeHead(200,{'Content-Type':'text/plain'});res.write('Hello World');res.end();});server.listen(3000);console.log('サーバを起動しました');

コマンドプロンプトを起動し、作業フォルダに移動します。

D:
CD Node

Node.jsを起動します。
起動方法は「node ファイル名」です

node sample01.js

問題がなければ、コマンドプロンプトに以下の様に表示されます。

サーバを起動しました

http://localhost:3000
にアクセスします
nodejs2.jpg

ブラウザに「Hello World」と表示されればOKです。

javascriptのfor in(オブジェクトのプロパティ名についての繰り返し)

$
0
0

for-in文は、オブジェクトに存在するプロパティについて、プロパティの名前を順不同で取り出していき、処理を実行する構文です。

for in の書き方

for([プロパティ名を格納する変数]in[オブジェクト]){}

指定したオブジェクトについて、プロパティ名を順不同で取り出し、inの前に定義した変数に格納してから処理を実行します。この時、繰り返しはすべてのプロパティについて処理を行うと終了します。

for in の例

varsum=0;varobj={a:1,b:2,c:3};for(varnameinobj){// オブジェクトの中のプロパティ名を取り出す。sum+=obj[name];}

参考

http://www.ituore.com/entry/javascript-for#for-in%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%81%AE%E3%83%97%E3%83%AD%E3%83%91%E3%83%86%E3%82%A3%E5%90%8D%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6%E3%81%AE%E7%B9%B0%E3%82%8A%E8%BF%94%E3%81%97

Testable Redux ~ React と Redux のテスト戦略 ~

$
0
0

image

本記事では Redux を使用した場合の React コンポーネントに対するテスト方法を考察します。

Redux に接続された React コンポーネントは、コンポーネント内のプロパティを Redux Store の state と同期させています。 Redux に強く依存しているコンポーネントをどのように Testable にしていけば良いのでしょうか。記事のタイトルは、t-wada 先生の言葉をお借りして、Testable Reduxとつけさせていただきました。

純粋な React コンポーネントをテストする

さて、まずはじめに純粋な React のコンポーネントに対するテスト方法を振り返ってみます。Facebook に習えば、jest の公式ドキュメントでも紹介されている jestenzymeを組み合わせた方法が一般的でしょう。

以下のようなシンプルなコンポーネントを例として使用します。

next

countを保持する state をもち、onClickで state を変更します。簡単のために Functional Component とし、State Hooks を使用します。後のテストのために各タグにはカスタムデータ属性(data-test)を付与しておきます。プロダクションビルドでカスタムデータ属性を取り除く方法もあります。こちらの記事にて詳細に解説されていました。

importReact,{useState}from"react";exportdefault()=>{const[count,setCount]=useState(0);return(<div><h3data-test="count">count: {count}</h3><buttondata-test="count-up"onClick={()=>setCount(count+1)}>⬆︎</button><buttondata-test="count-down"onClick={()=>setCount(count-1)}>⬇︎</button></div>);};

これに対するテストは以下のように記述できます。初期値が 0 であること、ボタンをクリックして 1 に変わることを確認します。

importReactfrom"react";import{shallow}from"enzyme";importCounterfrom"./PureReactCounter";importtestConfigurefrom"../../testConfigure";testConfigure();constsel=(id:string)=>`[data-test="${id}"]`;describe("<Counter /> コンポーネント",()=>{constComponent=shallow(<Counter/>);it("ボタンをクリックしてカウントアップする",()=>{expect(Component.find("h3").text()).toEqual("count: 0");Component.find(sel("count-up")).simulate("click");expect(Component.find("h3").text()).toEqual("count: 1");});});

なお、testConfigureには以下を記述しておきます。enzyme を使用する場合のお約束のようなものです。enzyme の installationにて解説されています。

importEnzymefrom"enzyme";importEnzymeAdapterfrom"enzyme-adapter-react-16";exportdefault()=>Enzyme.configure({adapter:newEnzymeAdapter()});

結果は 8ms でした。このテストでは shallow()を使用しています。Shallow レンダリングは、コンポーネントをユニットテストの範囲でテストできるように制限します。テストが子コンポーネントの動作を間接的にアサートしないようにしてくれます。コンポーネントは子コンポーネントに依存させず、常にステートレスに保つことによって Shallow レンダリングによるテストができ、高速です。

PASSsrc/components/PureReactCounter.test.tsx<Counter/>コンポーネントボタンをクリックしてカウントアップする(8ms)

ちなみに、mount()は完全な DOM レンダリングを行います。DOM API とやり取りする可能性のあるコンポーネントである場合や、より高次のコンポーネントにラップされているコンポーネントをテストする必要がある場合に最適です。ただしその一方で多くの依存関係を考慮することによりテストに要する時間は増える傾向にあります。

PASSsrc/components/PureReactCounter.test.tsx<Counter/>コンポーネントShallowレンダリングボタンをクリックしてカウントアップする(8ms)Fullレンダリングボタンをクリックしてカウントアップする(8ms)

結果として実行時間はほとんど変わりませんでした。これは今回実装したコンポーネントが他のコンポーネントや API などと依存していないシンプルなコンポーネントであるためです。

Redux の使用を開始する

さて、先ほど実装したコンポーネントは、countを保持する state をもち、onClickで state を変更していました。この countという state を Redux の store で管理します。実装例を紹介する前に Redux のライフサイクルのおさらいをしましょう。

  1. View: ユーザが操作を行い、handleClick() などのイベント起動 function が実行される。
  2. ActionCreater: イベント起動 function は ActionCreater を通して action を生成する。
  3. Dispacther: action は Dispater に渡され、Reducer に流れる。
  4. Reducer: Reducer は action の type に応じて新しい state を返却する。

lifecycle

説明の粒度は荒いですが、おおまかにこのような流れで Redux による状態の管理が行われます。

さて、ディレクトリ構成は以下のようにして実装を進めていきます。

src/
├── index.tsx
├── App.tsx
├── components
│   ├── Counter.tsx
│   └── Counter.test.tsx
└── store
    ├── actions
    │   └── counterActions.ts
    ├── reducers
    │   └── counterReducer.ts
    └── store.ts

Action (actions/counterActions.ts)

Action の定義を行います。今回はカウントアップとカウントダウンする2種類の action があるので事前に定義しておき、他のファイルから参照できるようにしておきましょう。

exportconstINCREMENT="INCREMENT";exportconstDECREMENT="DECREMENT";exportinterfaceICountReducerAction{type:typeofINCREMENT|typeofDECREMENT;}

Reducer (reducers/counterReducer.ts)

Reducer は飛んできた action の type に応じて state 返却する処理を書くのでした。INCREMENTの場合は +1DECREMENTの場合は -1された新しい state を返却しています。

import{ICountReducerAction,INCREMENT,DECREMENT}from"../actions/counterActions";constinitialState=0;constcounterReducer=(state=initialState,action:ICountReducerAction)=>{switch(action.type){caseINCREMENT:returnstate+1;caseDECREMENT:returnstate-1;default:returnstate;}};exportdefaultcounterReducer;

Store

先に定義した reducerを束ねて、storeを作成しましょう。今回 reducerは1つですが、複数使用することになることも考慮して combineReducers()を使用しています。

import{createStore}from"redux";import{combineReducers}from"redux";importcounterReducerfrom"./reducers/counterReducer";constreducer=combineReducers({count:counterReducer});exportdefaultcreateStore(reducer);

Component から Redux の store を参照する

Redux の state を View に表示できるようにコンポーネントを実装していきましょう。まず初めに純粋に Redux の store を直接参照するような実装を考えます。

ReactDOM.render()storeから subscribe()します。

importReactfrom"react";importReactDOMfrom"react-dom";importAppfrom"./App";importstorefrom"./store/store";constrender=()=>ReactDOM.render(<App/>,document.getElementById("root"));render();store.subscribe(render);

コンポーネントは以下のように実装します。この場合はどうでしょう、テストは容易でしょうか。

importReactfrom"react";importstorefrom"../../store/store";import{INCREMENT,DECREMENT}from"../../store/actions/counterActions";exportdefault()=>{constcount=store.getState().count;// store から直接参照するreturn(<div><h3data-test="count">count: {count}</h3><buttononClick={()=>store.dispatch({type:INCREMENT})}>⬆︎</button><buttononClick={()=>store.dispatch({type:DECREMENT})}>⬇︎</button></div>);};

テストコードを振り返ってみましょう。このままでは Redux の store を参照ができないため、ボタンをクリックした後に発火するはずの Reducer が起動しません。結果として count は 0 のままです。

constsel=(id:string)=>`[data-test="${id}"]`;describe("<Counter /> コンポーネント",()=>{describe("Shallowレンダリング",()=>{constComponent=shallow(<Counter/>);it("ボタンをクリックしてカウントアップする",()=>{expect(Component.find("h3").text()).toEqual("count: 0");Component.find(sel("count-up")).simulate("click");// Redux store の state が変更されないexpect(Component.find("h3").text()).toEqual("count: 1");// 0 のまま});});describe("Fullレンダリング",()=>{constComponent=mount(<Counter/>);it("ボタンをクリックしてカウントアップする",()=>{expect(Component.find("h3").text()).toEqual("count: 0");Component.find(sel("count-up")).simulate("click");expect(Component.find("h3").text()).toEqual("count: 1");});});});

テスト結果(クリックして開く)
 FAIL  src/components/DirectAccessReduxStore.test.tsx
  <Counter /> コンポーネント
    Shallowレンダリング
      ✕ ボタンをクリックしてカウントアップする (11ms)
    Fullレンダリング
      ✕ ボタンをクリックしてカウントアップする (6ms)

  ● <Counter /> コンポーネント › Shallowレンダリング › ボタンをクリックしてカウントアップする

    expect(received).toEqual(expected) // deep equality

    Expected: "count: 1"
    Received: "count: 0"

      13 |       expect(Component.find("h3").text()).toEqual("count: 0");
      14 |       Component.find(sel("count-up")).simulate("click");
    > 15 |       expect(Component.find("h3").text()).toEqual("count: 1");
         |                                           ^
      16 |     });
      17 |   });
      18 |   describe("Fullレンダリング", () => {

      at Object.<anonymous> (src/components/DirectAccessReduxStore.test.tsx:15:43)

  ● <Counter /> コンポーネント › Fullレンダリング › ボタンをクリックしてカウントアップする

    expect(received).toEqual(expected) // deep equality

    Expected: "count: 1"
    Received: "count: 0"

      21 |       expect(Component.find("h3").text()).toEqual("count: 0");
      22 |       Component.find(sel("count-up")).simulate("click");
    > 23 |       expect(Component.find("h3").text()).toEqual("count: 1");
         |                                           ^
      24 |     });
      25 |   });
      26 | });

      at Object.<anonymous> (src/components/DirectAccessReduxStore.test.tsx:23:43)

Redux と React のコンポーネントが強く依存する関係になっており、React のコンポーネント単体としてテストしづらくなっています。この構成を変えていきましょう。まずは React と Redux の間の依存関係を切り離す方法を考えます。

react-reduxconnect()を使用して React と Redux の依存を引き剥がず

最も代表的な実装パターンとして react-reduxconnect()を使用する方法が挙げられます。これは、コンポーネントをマウントする時点で子コンポーネントや依存関係にある構成要素を考える必要がなく、シンプルです。

Counter コンポーネントと Redux store が強い依存関係にある実装から、なるべく疎結合になるように配慮します。いわゆる DI (Dependency Injection) の考え方にしたがって、Counter コンポーネントの store を外から props として注入できるように変更します。ただ単に props によって注入するだけではなく、Redux store との接合は connect()によって行います。詳細な手続きは公式チュートリアルを参照しましょう。このようなコンポーネントを HOC (Higher Order Component) と呼びます。詳細はこちらの記事が参考になります。

importReactfrom"react";import{connect}from"react-redux";import{Dispatch}from"redux";import{INCREMENT,DECREMENT}from"../../store/actions/counterActions";interfaceICounterState{counter:number;}interfaceICounterProps{count:number;increment:any;decrement:any;}// props として store を流し込んで DI できるるようにする。exportconstCounter=(store:ICounterProps)=>{const{count,increment,decrement}=store;return(<div><divdata-testid="count">count: {count}</div><buttononClick={increment}>⬆︎</button><buttononClick={decrement}>⬇︎</button></div>);};constmapStateToProps=(state:ICounterState)=>({count:state.counter});constmapDespatchToProps=(dispatch:Dispatch)=>({increment:()=>dispatch({type:INCREMENT}),decrement:()=>dispatch({type:DECREMENT})});// 第一引数の mapStateToProps は component に渡す props を制御する// 第二引数の mapDespatchToProps は reducer を呼び出して、redux で管理している state を更新する// Counter は取得したデータを props として扱いたい component を指定するexportdefaultconnect(mapStateToProps,mapDespatchToProps)(Counter);

さて、ここまでできれば勝てる気がしてきました。テストを書きます。

importReactfrom"react";import{mount,shallow}from"enzyme";import{Provider}from"react-redux";import{createStore}from"redux";importsinonfrom"sinon";importConnectedCounter,{Counter}from"./ReactRedux";import{reducer}from"../../store/store";importtestConfigurefrom"../../testConfigure";testConfigure();constsel=(id:string)=>`[data-test="${id}"]`;describe("<Counter /> コンポーネント",()=>{describe("Shallowレンダリング",()=>{constprops={count:0,increment:sinon.spy(),// カウントアップするボタンを押した挙動を確認するためにスパイを差し込むdecrement:sinon.spy()};constshallowComponent=shallow(<Counter{...props}/>);it("ボタンをクリックしてカウントアップする",()=>{expect(shallowComponent.find("h3").text()).toEqual("count: 0");shallowComponent.find(sel("count-up")).simulate("click");expect(props.increment).toHaveProperty("callCount",1);});});describe("Fullレンダリング",()=>{constgetWrapper=(mockStore=createStore(reducer,{count:0}))=>mount(<Providerstore={mockStore}><ConnectedCounter/></Provider>);it("ボタンをクリックしてカウントアップする",()=>{constwrapper=getWrapper();expect(wrapper.find("h3").text()).toEqual("count: 0");wrapper.find(sel("count-up")).simulate("click");expect(wrapper.find("h3").text()).toEqual("count: 1");});});});

上記のテストでは、Shallow レンダリングと Full レンダリングの2つの方法でテストを記述しました。

Shallow レンダリングでは Redux の store を含めず、コンポーネント単体でテストを行います。そのため、カウントアップするボタンを押した際のハンドラ関数は simon.spy()を使用してスパイを差し込んで挙動を確認します。

一方で、Full レンダリングの場合は Redux の store だけをモックとして作成し、<Provider>の store に DI します。こちらの手法の方が Redux の store を含めたテストにはなるのですが、実行時間が増える傾向にあるため注意が必要です。ちなみに実行時間を比較すると以下のようになります。

PASSsrc/components/container/ReactRedux.test.tsx<Counter/>コンポーネントShallowレンダリングボタンをクリックしてカウントアップする(9ms)Fullレンダリングボタンをクリックしてカウントアップする(44ms)

mount するのはインテグレーションテストだユニットテストではない、shallow の方が高速だ、などの論争があります。個人的な意見ですが、チーム開発を行う上ではシンプルかつ可読性を維持したテストコードを書いた方がむしろチームとしてアジリティが上がるのではないかと考えています。学習コストも高いですしね。
それでもプロダクトコードの量が大きくなるにつれて、テストの実行時間の増加によりアジリティが下がるケースもあります。shallow レンダリングと full レンダリングの実行時間の差は ms 程度ですが、塵も積もれば山となるということです。テストの方針を決めるのは難しいですね。

Redux Hooks を使用してコード量を減らす

怠惰で傲慢な我々は、現状に満足することはありません。上に挙げた実装は React と Redux の依存関係を引き剥がすことで、Shallow レンダリングによるテストが実現できるようになりました。その一方で少し複雑な実装をしなければならないように感じます。この章では Redux Hooksを使用して簡単に記述できる方法をご紹介します。

React の Hooks API は、Functional コンポーネントに対してローカルコンポーネントの state を使用できるようになる優秀な機能です。react-reduxでも、既存の connect()を使用して実装された HOC (Higher Order Component) の代わりとして Hooks API を提供するようになりました。これを使用すると、前章の実装のようにコンポーネントをにラップして HOC を作るような面倒な作業は必要ありません。Redux store に subscribe して action を dispatch できます。

導入は非常に簡単です。useSelector()useDispatch()を使用しましょう。

importReactfrom"react";import{useSelector,useDispatch}from"react-redux";import{INCREMENT,DECREMENT}from"../../store/actions/counterActions";exportdefault()=>{constcount=useSelector((state:any)=>state.count);constdispatch=useDispatch();constincrement=()=>{dispatch({type:INCREMENT})};constdecrement=()=>{dispatch({type:DECREMENT})};return(<div><h3>count: {count}</h3><buttondata-test="count-up"onClick={increment}>⬆︎</button><buttondata-test="count-down"onClick={decrement}>⬇︎</button></div>);};

コードがとても短くなりました。かなりスマートです。あれ? でもおかしいですね、少し振り返ってみましょう。connect()を使用して HOC を作ったときには純粋な React コンポーネントと Redux との依存関係を引き剥がすことに成功していました。今回、Redux Hooks を使用した実装では、また依存関係が強くなってしまいました。これでは Full レンダリングを行うようなテストしか記述できません。

describe("<Counter /> コンポーネント",()=>{describe("Fullレンダリング",()=>{constgetWrapper=(mockStore=createStore(reducer,{count:0}))=>mount(<Providerstore={mockStore}><ConnectedCounter/></Provider>);it("ボタンをクリックしてカウントアップする",()=>{constwrapper=getWrapper();expect(wrapper.find("h3").text()).toEqual("count: 0");wrapper.find(sel("count-up")).simulate("click");expect(wrapper.find("h3").text()).toEqual("count: 1");});});});

Redux Hooks でも Redux と React を分離する

基本的な考え方は今までと同様です。Redux と接続する部分を DI できるようにすれば OK です。useSelector()useDispatch()を使用している箇所を外に出してやりましょう。

import{useSelector,useDispatch}from"react-redux";import{IRootState}from"../../store/store";import{INCREMENT,DECREMENT}from"../../store/actions/counterActions";import{Dispatch}from"redux";interfaceICounterProps{count:number;increment:Dispatch;decrement:Dispatch;}exportconstCounter=(props:ICounterProps)=>{const{count,increment,decrement}=props;return(<div><h3>count: {count}</h3><buttondata-test="count-up"onClick={increment}>⬆︎</button><buttondata-test="count-down"onClick={decrement}>⬇︎</button></div>);};exportdefault(props:any)=>{constcount=useSelector<IRootState>(state=>state.count);constdispatch=useDispatch();const_props={count,increment:()=>dispatch({type:INCREMENT}),decrement:()=>dispatch({type:DECREMENT}),...props};return<Counter{..._props}/>;};

当然といえば当然ですが、テストコードは react-reduxconnect()を使用した場合の実装と同じになります。

テストコード(クリックして開く)

constsel=(id:string)=>`[data-test="${id}"]`;describe("<Counter /> コンポーネント",()=>{describe("Shallowレンダリング",()=>{constprops={count:0,increment:sinon.spy(),decrement:sinon.spy()};constshallowComponent=shallow(<Counter{...props}/>);it("ボタンをクリックしてカウントアップする",()=>{expect(shallowComponent.find("h3").text()).toEqual("count: 0");shallowComponent.find(sel("count-up")).simulate("click");expect(props.increment).toHaveProperty("callCount",1);});});describe("Fullレンダリング",()=>{constgetWrapper=(mockStore=createStore(reducer,{count:0}))=>mount(<Providerstore={mockStore}><ConnectedCounter/></Provider>);it("ボタンをクリックしてカウントアップする",()=>{constwrapper=getWrapper();expect(wrapper.find("h3").text()).toEqual("count: 0");wrapper.find(sel("count-up")).simulate("click");expect(wrapper.find("h3").text()).toEqual("count: 1");});});});

さいごに

結局のところ、どのアプローチを採用した場合も実際の Redux store を使用した React コンポーネントのテストは高速です。Redux の設計は、Action、Reducer、State のそれぞれが互いに分離される方法のため、テストに非常に適しています。
このように Dependency Injection しやすい設計のもと Redux が作られているので、このようなシンプルな構造を取ることができました。今回はご紹介できませんでしたが、Redux SagaRedux Thunkを使用した場合のアーキテクチャでも同様にテストはシンプルに記述できます。React をとりまくエコシステムは素晴らしいですね。


face-api.jsでニコラ○・ケ○ジになる

$
0
0

顔認識がjsだけでもそれなりに動くのを今更知ったので触ってみた。
顔晒すのに抵抗感がある古い人間なので文中画像少なめです。

成果物

Git: https://github.com/engabesi/face-nicolas
GithubPages: https://engabesi.github.io/face-nicolas/
2020-02-24_21h23_40.png

やること

  • expressでlocalhostを建てる
  • face-api.jsで顔の矩形を取る
  • 顔に画像を被せる
  • GithubPagesにdeploy

face-api.js

今回顔認識に使うライブラリはこちら
https://github.com/justadudewhohacks/face-api.js
jsだけでそれなりの精度とパフォーマンスを出せます。
複数人も認識可能。
ランドマークや表情、顔認証も出来ます。
demoはこちら
https://justadudewhohacks.github.io/face-api.js/face_and_landmark_detection/

注意点

face-api.jsはWebGLを使用しています。
その為ブラウザの設定でハードウェアアクセラレーションをオフにしている場合上手く動作しない可能性があります。

expressでlocalサーバーを建てる

android等でも気軽にテストするためにまずはlocalhostで動作するようにします。
後にgithubPagesも利用したいため、以下の構成にします。

構成
root/
├─ app.js
└─ docs/
    ├─ index.html
    ├─ images/
    └─ js/
       ├─ index.js
       └─ lib/
          ├─ face-api.min.js
          └─ models/
             ├─ tiny_face_detector_model-shard1
             └─ tiny_face_detector_model-weights_manifest.json

face-api.min.js, 及びmodels内ファイルは以下のrepoから取ってきます
https://github.com/justadudewhohacks/face-api.js/tree/master/dist
https://github.com/justadudewhohacks/face-api.js/tree/master/weights

expressを導入します。

shell
yarn init -y
yarn add express
docs\index.html
<!DOCTYPE html><htmllang="ja"><head><metacharset="UTF-8"/><metaname="viewport"content="width=device-width, initial-scale=1.0"/><title>Document</title><script defersrc="./js/lib/face-api.min.js"></script><style>body{margin:0;padding:0;width:100vw;height:100vh;display:flex;justify-content:center;align-items:center;}canvas{position:absolute;}</style></head><body><videoid="video"width="720"height="560"autoplaymuted></video><script defersrc="js/index.js"></script></body></html>
app.js
constexpress=require("express");constapp=express();constpath=require("path");app.listen(8080,()=>{console.log("Running at Port 8080...");});app.use(express.static(path.join(__dirname,"docs")));app.use((req,res)=>res.sendStatus(404));

これでshellにnode app.jsと打ち、http://localhost:8080/にアクセスするとページが開きます(今は真っ白)

webcamera設定

docs\index.js
conststartVideo=asyncvideo=>{try{constconstraints={audio:false,video:{}};conststream=awaitnavigator.mediaDevices.getUserMedia(constraints);video.srcObject=stream;}catch(error){console.error(error);}};(async()=>{constvideo=document.querySelector("video");awaitstartVideo(video);})();

これでlocalhostを開くとwebcameraの映像が取れているはずです。

顔の矩形を取る

まずmodelを読み込みます。

docs\index.js
+constloadModels=async()=>{+awaitPromise.all([+faceapi.nets.tinyFaceDetector.loadFromUri(`/js/lib/models`)+]);+};(async()=>{constvideo=document.querySelector("video");+awaitloadModels();awaitstartVideo(video);})();

次に顔を認識して矩形描画処理を追加します。

docs\index.js
(async()=>{constvideo=document.querySelector("video");awaitloadModels();awaitstartVideo(video);// --- ADD ---video.addEventListener("play",()=>{// overlay canvas作成constcanvas=faceapi.createCanvasFromMedia(video);document.body.append(canvas);constdisplaySize={width:video.width,height:video.height};faceapi.matchDimensions(canvas,displaySize);consttinyFaceDetectorOption={// default 416inputSize:224,// default 0.5scoreThreshold:0.5};setInterval(async()=>{constresults=awaitfaceapi.detectAllFaces(video,newfaceapi.TinyFaceDetectorOptions(tinyFaceDetectorOption));if(results.length<=0)return;// 検出結果をcanvasのサイズにリサイズconstresizedResults=faceapi.resizeResults(results,displaySize);// canvasの内容をクリアcanvas.getContext("2d").clearRect(0,0,canvas.width,canvas.height);// 矩形描画faceapi.draw.drawDetections(canvas,resizedResults);},100);});// --- ADD ---})();

これでscoreが記載された矩形が顔に被さって表示されるようになります。

顔に画像を被せる

検出結果から座標を取ってその位置に画像を被せてみます。

docs\index.js
(async()=>{constvideo=document.querySelector("video");+// 画像セットアップ+constimage=newImage();+image.src=`/images/cage_neutral.png`;awaitloadModels();awaitstartVideo(video);video.addEventListener("play",()=>{constcanvas=faceapi.createCanvasFromMedia(video);document.body.append(canvas);constdisplaySize={width:video.width,height:video.height};faceapi.matchDimensions(canvas,displaySize);consttinyFaceDetectorOption={// default 416inputSize:224,// default 0.5scoreThreshold:0.5};setInterval(async()=>{constresults=awaitfaceapi.detectAllFaces(video,newfaceapi.TinyFaceDetectorOptions(tinyFaceDetectorOption));if(results.length<=0)return;constresizedResults=faceapi.resizeResults(results,displaySize);canvas.getContext("2d").clearRect(0,0,canvas.width,canvas.height);-// faceapi.draw.drawDetections(canvas, resizedResults);+resizedResults.forEach(detection=>{+// 矩形のtopはデコあたりなので調整+constmarginVal=0.4;+// 矩形の情報はdetection.boxに格納されている+constwidth=detection.box.width;+constheight=detection.box.height*(1.0+marginVal);+constx=detection.box.x;+consty=detection.box.y-detection.box.height*marginVal;+canvas.getContext("2d").drawImage(image,x,y,width,height);+});},100);});})();

これで自分の顔に画像を貼り付けることが出来ました。
他にもmodelやdetection時に.with~~とするだけで表情やランドマーク等の情報を取れます。
詳しくは公式README参照。自分のgitにあげている物にも画像調達に飽きて中途半端になっていますが表情を取るコードも記載してあります。

GithubPages

コードをrepositoryに上げたら
repository > settings > Options > GithubPages > Sourceを
master branch /docs folderにすればGithubPagesにデプロイすることが出来ます。
だから、ディレクトリ名をdocsにする必要があったんですね。

注意点

上記コードでは画像のpathを直打ちしています。
github.ioにデプロイする場合は問題ありませんが、github.io/SUB_REPO/にデプロイした場合、ルートURLがgithub.io扱いとなり、SUB_REPOが飛ばされてしまってpathがおかしくなってしまいます。
jekyllの使用をgithubは推奨していますが試して遊ぶだけの場合ちょっと面倒です。
暫定対策としてpath前に直接SUB_REPO名をくっつけてあげると一応動きます。

まとめ

たった数分でブラウザ完結の顔認識ができるなんて素晴らしい世の中になりました。
現状のコードだとスマートフォンで見た場合矩形がものすごく横長になってしまっているのでその辺の対応をしだすとヘビーになるかもしれませんがちょっと遊んで見るぐらいだと非常に有用だと思います。

他にもclmtrackr.jspico.js、更にはChromeの機能だけでできるShape Detection API等様々なライブラリがありますので是非触って遊んでみてください。

同期的に複数のファイルをform-dataを使って送信するサンプル

$
0
0

こんにちは、wattak777です。

一ファイルを送信する、というサンプルは幾つかあるのですが、requestとform-dataを組み合わせて送る場合、複数ファイルの場合、例えばforループで送るようにしても1つ目、2つ目、3つ目と送りきる前にどんどん送ってしまうため同期的に送ることが出来ません。

なので、ちょっとサンプルを作ってみました。

サーバー側はmulterを使った以下のサンプル。

server.js
varexpress=require('express');varapp=express();varmulter=require('multer');app.post('/file_upload',multer({dest:ファイルを置くパス}).single('my_file'),function(req,res){console.log(req.file.path,req.file.originalname);res.sendStatus(200);});varserver=app.listen(12345,function(){console.log("listening at port %s",server.address().port);});

で、本題のクライアント側は以下の実装。
request-promiseを使って同期的に送るようにしました。

client.js
constfs=require('fs');constrequest=require('request-promise');constFileNameList=['test1.bin','test2.bin','test3.bin'];varFileNameIndex=0;varreturnCode=httpPost();functionhttpPost(){constFormData={my_file:{value:fs.createReadStream(ファイルのパス+FileNameList[FileNameIndex]),options:{filename:FileNameList[FileNameIndex],contentType:'application/octet-stream'}}}constoptions={uri:"http://サーバーのIPアドレス:12345/file_upload",formData:FormData,method:'post',headers:{'Content-Type':'multipart/form-data'}}varresponse=request(options).then(function(body){console.log('then :'+body);onEnd();}).catch(function(err){console.log('catch error :'+err);});returnresponse.statusCode;}functiononEnd(){console.log('Index '+FileNameIndex+' is finish.');FileNameIndex=FileNameIndex+1;if(FileNameIndex>=FileNameList.length){console.log('End Operation.');}else{varres=httpPost();}}console.log('Start Operation.');

とやると、クライアント側の表示は以下のようになります。

$ node client.js
Start Operation.
then :OK
Index 0 is finish.
then :OK
Index 1 is finish.
then :OK
Index 2 is finish.
End Operation.

node.jsからMongoDBに接続してDocumentを登録する。

$
0
0

はじめに

node.jsからMongoDBをインストールして、接続しようとしたところ、最初の接続で少し躓いたので、まとめる。

環境

  • Windows10 Pro 64bit
  • node.js v12.14.1
    • mongodb v6.13.4
  • MongoDB 4.2.3

環境構築

node.jsにmongodbをインストール

以下コマンドを実行
npm install mongodb

MongoDBをインストールする。

公式サイトからWindows用のインストーラをダウンロードしてインストールする。
実行して「次へ」ボタン押していく。

インストール後に環境変数にPathを追加する。
C:\Program Files\MongoDB\Server\4.2\bin

MongoDB側の準備

MongoDB Compass Communityを使用して、取得したいdatabaseとDocumentを作成しておく。
左下のボタンで押下すると、Create Databaseのダイアログが出るので、入力するだけ。

image.png

node.js側のコード

以上で準備完了。実際のサンプルコードは以下。
コメントアウト部分は旧VersionのMongoDBの記載方法です。
ネットで見つけた旧Versionの記載方法だとTypeError: db.collection is not a functionのエラーになりはまる。

node.js
constMongoClient=require("mongodb").MongoClient;constdburl="mongodb://localhost:27017/";// const dburl = "mongodb://localhost:27017/myDatabase";MongoClient.connect(dburl,(error,client)=>{constcollection=client.db('myDatabase').collection('myCollection');// const collection = client.collection('myCollection');collection.insertOne({_id:1,path:"test"}).then(()=>console.log("success")).catch(err=>console.log(err));});

最後に

MongoDB以外にも、Electronもバージョンによって、大きく記述方法が変わっており、ネットで調べたコードでそのまま動かしてもうまく動かず躓くことが多い。。。
公式サイト読めばいいのだけど、時間かかるしで、少しジレンマを感じる今日この頃。

node.jsインストール直後の状態でpackage.jsonをアップデートする方法 ~npmグローバルインストール一切不要~

$
0
0

package.jsonを更新する場合、npm-check-updatesをグローバルインストールしてncu -uとやることが多いです。ただ、nodebrewなどのnodeバージョン管理システムを使っている場合、node.jsの新バージョンがリリースされるたびにグローバルインストールをやり直す必要があります。この問題、何とかならないでしょうか。実はnpm-check-updatesをnpx経由で呼べば、node.jsインストール直後の状態でもpackage.jsonを更新することができます。

package.jsonを更新する

npx npm-check-updates -u
npm install

解説

npx経由でnpm-check-updatesを呼び出しているだけです。試してみましたが、npx ncu -uとは出来ませんでした。

注意点

環境にもよりますが、上記手順はnpx npm-check-updates -uを実行するたびにnpm-check-updatesをローカル環境のインストールします。連続してnpm-check-updatesを呼びたい場合は、npx経由で呼び出すことはお勧めしません。自分の場合は上記コマンドを呼び出すのは1週間に1回程度なので、時間ロスについてはそこまで気にしていません。

Nuxt.jsの始め方

$
0
0

Nuxt.jsを始めたい方に向けて、導入の仕方をまとめます。
ライフサイクルなど細かい内容は公式ドキュメントをご確認ください。

前提条件

  • npmの5.2.0以降のバージョンがインストールされていること

インストール

プロジェクトディレクトリの作成

$ mkdir test&&cd$_

Nuxt.jsフレームワークのインストール

$ npx create-nuxt-app

開発

開発サーバーを起動

$ npm run dev

デプロイ

SSRモードでサーバーを起動

$ npm run build && npm run start

静的HTML生成

$ npm run generate

その他

pug/stylusの導入

pug

pugのモジュールをインストール

$ npm i --save pug pug-loader pug-plain-loader

全*.vueファイルのtemplateタグにlang="pug"を追加する

<!-- <template> --><templatelang="pug">

stylus

stylusのモジュールをインストール

$ npm i --save stylus stylus-loader

全*.vueファイルのstyleタグにlang="stylus"を追加する
ここの内容を他のスタイルに影響させたくない場合はscopedも追加する

/* <style> */<stylelang="stylus"scoped>

storeの導入

データ定義

store用のファイルを作成

$ touch store/index.js

store/index.js
保持しておきたいデータをstate内にcounterという名前で定義
state内のcounterの数値を1ずつ増やすincrementメソッドを定義

exportconststate=()=>({counter:0})exportconstmutations={increment(state){state.counter++}}

データへのアクセス

state内のcounterの値を取得

this.$store.state.counter

出力結果

0

データの変更

mutations内のincrementメソッドを実行

this.$store.commit('increment')

transitionタグ

html(pug)の定義

transitionタグを追加して、子要素はv-ifなどで存在確認をさせる

transition
  p(v-if="show") Hello world!

css(stylus)の定義

v-enterv-leave-toで最初(出現時)と最後(消失時)のstyleを定義
そのスタイルにどういうtransitionをかけるかv-enter-activev-leave-activeで定義

.v-enter-active,.v-leave-activetransitionopacity.5s.v-enter,.v-leave-toopacity0

ページ遷移transition

html(pug)の定義

nuxt-linkタグを追加する

nuxt-link(to="/test/") go to test!

css(stylus)の定義

page-enterpage-leave-toで最初(出現時)と最後(消失時)のstyleを定義
そのスタイルにどういうtransitionをかけるかpage-enter-activepage-leave-activeで定義

.page-enter-active,.page-leave-activetransitionopacity.5s.page-enter,.page-leave-toopacity0

ページ読み込み時のLoading表示

Loading用のファイルを作成

$ mkdir components/Loading &&touch components/Loading/index.vue

components/Loading/index.vue

<templatelang="pug">
.loading-page(v-if="loading")
  p Loading...
</template><script>exportdefault{data:()=>({loading:false}),methods:{start(){this.loading=true},finish(){setTimeout(()=>{this.loading=false},3000)}}}</script><style lang="stylus"scoped></style>

nuxt.config.js
loadingの記述を追加

module.exports={loading:'~/components/Loading/index.vue'// 追記}

テンプレートレイアウト変更

テンプレート用のファイルを作成

$ touch layouts/another.vue

layouts/another.vue

<templatelang="pug">
.l-another
  nuxt
</template><script>exportdefault{}</script><style lang="stylus"scoped></style>

*.vue
layoutのカスタム読み込みをする記述を追加

exportdefault{layout:'another',// 追記}

エラーページ

エラー用のファイルを作成

$ touch layouts/error.vue

layouts/error.vue

<templatelang="pug">
.container
  h1(v-if="error.statusCode === 404") ページが見つかりません
  h1(v-else) エラーが発生しました
  nuxt-link(to="/") ホーム
</template><script>exportdefault{props:{error:{type:Object,default:null}}}</script><style lang="stylus"scoped></style>

コンソールの削除

nuxt.config.js
build内にdrop_consoleの記述を追加

module.exports={build:{terser:{terserOptions:{compress:{drop_console:true}}}}}

作業ディレクトリをまとめる

作業ディレクトリの作成、移動

$ mkdir src &&mv assets components layouts pages plugins static store middleware src/

nuxt.config.js
srcDirの記述を追加する

module.exports={srcDir:'src/',// 追記}

参考文献

この記事は以下の情報を参考にして執筆しました。

javascriptでcontent-typeを自動で取得する方法

$
0
0

content-typeを自動で取得する方法を探した結果、mime-typesを使うことにしました。

前提条件

  • npmがインストールされていること

インストール

$ npm i --save mime-types

使い方

設定

touchコマンドでファイルを作成します。

$ touch test.js

test.js
mime-typesモジュールの読み込みと、引数にパスを入れてメソッドを使用します。

constmime=require('mime-types')console.log(mime.lookup('test.json'))console.log(mime.contentType('test.json'))

実行

nodeコマンドでtest.jsを実行します。

$ node test

出力結果

application/json
application/json;charset=utf-8

参考文献

この記事は以下の情報を参考にして執筆しました。

Text-to-Speech APIと関連ライブラリまとめ

$
0
0

Cloud Text-to-Speech

後述するライブラリの大元となるGoogleのAPI。テキストを音声に変換できる。
APIを有効化してAPIキーを取得すれば、スクリプト実行で音声ファイルを作成できる。
ドキュメントの「クイックスタート」にそえば、好きな言語で簡単な音声ファイルを作成することができる。
https://cloud.google.com/text-to-speech/docs/quickstart-client-libraries?hl=ja

google-tts-api

https://www.npmjs.com/package/google-tts-api
Google-Text-to-Speach-APIのnode.js版。
このライブラリだけでAPIの有効化・キー取得などを行うことなく音声ファイルの作成ができる。
しかし一度に音声化できるのは200文字まで。

google-home-notifer

https://github.com/noelportugal/google-home-notifier
このライブラリ内にgoogle-tts-apiが内包されている。
音声ファイルを作成し、Google Homeにテキストを読み上げさせることができる。

おまけ:読み上げ音声の特徴

実際に作成した音声ファイルを聞いてみて気づいた音声の特徴を簡単に。
言語=日本語の場合、
* 英単語:日本語読みで発音される
* 読み上げ時のブレス:句読点、カギカッコ、カンマなど。空白はブレスにならない。


Windowsでのnpm installの使い方

$
0
0

サマリ

  • npm installでパッケージをインストールする際「-g」オプションの有無による違い
  • npm install -gによる資材の配置場所の変更方
  • npm installでインストールされたパッケージの確認方法
  • npm installでインストールされたパッケージの削除方法

目的

node.jsを学習する際、npm installの使い方がわからず挫折しそうになった。
原因として、各記事で「-g」オプションの有無にばらつきがあり、
「-g」オプションの有無による具体的な違いやnpm install実行後の確認方法がまとまって説明されている記事がなかなか見つからなかった。
同じことで困った人の参考として、自分の実行例と合わせて説明を記載する。

本文

1. npm installコマンドの効果

node.jsで利用するパッケージをダウンロードできる。
ダウンロードしたパッケージはrequireで取得・利用できるようになる。
例)expressをインストールした場合
var express = require('express')

2. 「-g」オプションの有無による違い

  • 「-g」オプションが無い場合
    npm installを実行したフォルダ配下でのみパッケージを利用可能。

  • 「-g」オプションが有る場合
    npm installを実行したフォルダ配下以外からでもパッケージを利用可能。

3. 「-g」オプションの有無によるダウンロード資材の配置場所の違い

  • 「-g」オプションが無い場合
    npm installを実行したフォルダ配下のnode_modulesフォルダ。

  • 「-g」オプションが有る場合
    npm config listコマンドを実行して表示された情報の「prefix」の場所。
    以下の実行例の場合はC:\nodejs\node_modules_global配下にnode_modulesフォルダ配下にパッケージが配置される。

実行例)

> npm config list
; cli configs
metrics-registry = "https://registry.npmjs.org/"
scope = ""
user-agent = "npm/6.13.4 node/v12.14.1 win32 x64"

; userconfig C:\Users\user\.npmrc
prefix = "C:\\nodejs\\node_modules_global"

; builtin config undefined

; node bin location = C:\Program Files\nodejs\node.exe

4. npm install -gによる資材の配置場所の変更方

npm config setコマンドでprefixの設定値を更新することで配置場所を変更可能。

例)C:\nodejs02\node_modules_globalに変更する場合
> npm config set prefix=C:\nodejs02\node_modules_global

なお、グローバルパッケージの配置場所を変更した場合、Windowsでは新しい配置場所にpathを通さないとnode.jsの実行時にエラーとなった。

5. npm installでインストールされたパッケージの確認方法

npm listコマンドで確認できる。npm ls --depth=0とすると、依存関係を除いたパッケージ情報のみ表示できる。

  • 「-g」オプションを付けずにインストールしたパッケージ(ローカルパッケージ)の確認
> npm ls
sample@0.0.0 C:\node_sample\sample  ← ローカルパッケージの配置場所(npm installコマンドを実行した場所)
+-- cookie-parser@1.4.4
| +-- cookie@0.3.1
| `-- cookie-signature@1.0.6
+-- debug@2.6.9
| `-- ms@2.0.0
+-- express@4.16.4
| +-- accepts@1.3.7
| | +-- mime-types@2.1.26
| | | `-- mime-db@1.43.0
| | `-- negotiator@0.6.2
 :
 :
  • 「-g」オプションを付けてインストールしたパッケージ(グローバルパッケージ)の確認
> npm ls -g
C:\nodejs\node_modules_global    ← グローバルパッケージの配置場所
+-- express@4.17.1
| +-- accepts@1.3.7
| | +-- mime-types@2.1.26
| | | `-- mime-db@1.43.0
| | `-- negotiator@0.6.2
| +-- array-flatten@1.1.1
| +-- body-parser@1.19.0
| | +-- bytes@3.1.0
| | +-- content-type@1.0.4 deduped
 :
 :

6. npm installでインストールされたパッケージの削除方法

npm uninstallコマンドで削除可能。
npm ls --depth=0でインストールパッケージを表示し、表示されたパッケージの@より前の文字列を指定することで削除ができる。

実行例)ローカルパッケージのexpressを削除する場合

> npm ls --depth=0
sample@0.0.0 C:\node_sample\sample
+-- cookie-parser@1.4.4
+-- debug@2.6.9
+-- express@4.16.4
+-- http-errors@1.6.3
+-- jade@1.11.0
`-- morgan@1.9.1

> npm uninstall express

参考にさせていただいたページ

いまさら聞けない!npmのこれだけは知っておきたい基礎知識
https://www.webprofessional.jp/beginners-guide-node-package-manager/

Qiita npmコマンドの使い方
https://qiita.com/yoh-nak/items/8446bf12094c729d00fe

npmコマンドの直列処理、並列処理の簡単な記述

$
0
0

npmコマンドを簡単に記述して、直列処理、並列処理を行います。

前提条件

  • npmがインストールされていること
  • package.jsonがあること

インストール

$ npm install-D npm-run-all

使い方

設定

package.json
run-sの後にコマンドを書くと直列、run-pの後にコマンドを書くと並列で処理を行ってくれます。
コマンドをhello:*と書くと、当てはまるすべてのコマンドを処理します。

{"scripts":{"hello:foo":"echo FOO","hello:bar":"echo BAR","hello-s":"run-s hello:foo hello:bar","hello-p":"run-p hello:*"}}

実行

npmコマンドでhello-sまたはhello-pを実行します。

$ npm run hello-s
$ npm run hello-p

出力結果

FOO
BAR

参考文献

この記事は以下の情報を参考にして執筆しました。

GETとPOST

$
0
0

GET

普通にwebサイトにアクセスするとき、Webブラウザは「GET」という方式でアクセスをしています。
GETはアクセスの基本と考えていいでしょう。

GETは「いつ、どこからどうアクセスしても同じ結果が返されされる」というようなものに使います。

POST

「POST」といのは、フォームなどを送信するときの基本となる送信方式です。

POSTは「その時、その状況でその表示」を行うような場合に使われます。

参考

node.js超入門 p130

node.js学習メモ

$
0
0

関数の引数のrequestには、ブラウザからアクセスがあった時に情報が入ってきて(url,methodなどの情報)、情報を取り出すときは、関数内で

request.メソッド

で情報を取り出せる。

Kabaneroプロジェクトアップデート(2020年2月25日)

$
0
0

昨年プロジェクトが発表されたKabaneroですが順調にいろんなコンポーネントが揃ってきたようです。今日は実際にどんな風に使えるのという観点でまとめてみました。
image.png
https://kabanero.io/

GitHub
Kabanero https://github.com/kabanero-io

Kabaneroとは?

昨年 @yamachan360が書かれている基本方針は変わらないです。参考にしてください。
Kabanero 公式ページをざっと翻訳してみた (2019年8月版)
https://qiita.com/yamachan360/items/7b4a53758ecdbe876a5f

-日本IBMの紹介サイト
BLOG IBM から始まった新しいオープン・ソース・プロジェクト Kabanero を使用して、Kubernetes 対応のクラウド・ネイティブ・アプリを迅速に構築する
https://developer.ibm.com/jp/2019/12/05/cloud-native-apps-kubernetes-kabanero/

Kabaneroは上にも書かれていますがある特定のオープンソースソフトというわけではなく、コンテナー化されたクラウド・ネイティブ・アプリケーションの作成から本番環境の Kubernetes 上でのライフサイクルまでのすべてを操作できることを目標にしたプロジェクトです。Kabanero全体のアーキテクチャーは以下のようになります。
image.png

この中でKabaneroプロジェクトがメインに開発を進めているのが以下の3つになります。

Appsody https://github.com/appsody
コンテナー内のクラウド・ネイティブ・アプリケーションを作成する際のタスクを単純化するオープン・ソース・プロジェクトです。Appsody を使用すると、開発者は組織の標準と要件に従ったマイクロサービスをものの数分で作成できます。

codewind https://github.com/eclipse/codewind
Codewind は VS Code、Eclipse、Eclipse Che (他にも予定されています) などのよく使われている統合開発環境 (IDE) の拡張機能を提供します。これらの拡張機能を使用すれば、使い慣れたワークフローと IDE でコンテナー内のアプリケーションを作成できます。

Razee https://github.com/razee-io/Razee
 Kubernetes 対応のマルチクラスター継続的デリバリー・ツール

それでは実際に上記オープンソースを体験できるワークショップのサイトを見てみましょう。(Appsody, Codewind)
まずは日本語のサイト
Kabanero、Appsody、Codewind を使用して、Kubernetes 上の Spring Boot アプリケーションを作成する
https://www.ibm.com/developerworks/jp/library/kabanero-introduction-to-modern-microservices-development-for-kubernetes/

Appsody の Node.js スタックと Spring スタックを使用して、フロントエンド Web アプリとバックエンド REST アプリを作成する
https://developer.ibm.com/jp/patterns/create-insurance-quote-application-appsody/

appsodyがNode-REDに対応したので使ってみた
https://qiita.com/motuo/items/2c59fd0f4410e8a7d014

英語だとたくさんあるようですが

Appsody

Developing microservice applications with the Appsody CLI
(Appsodyを使ってマイクロサービスのアプリを作ってみよう)
https://kabanero.io/guides/use-appsody-cli/#creating-an-application
Appsodyのコマンドをインストールしてnode-jsの雛形をベースに簡単なアプリケーションを作ってみるもの。所要時間は20分

Developing cloud native microservice applications with the Kabanero Node.js Collection and Appsody CLI.
https://kabanero.io/guides/collection-nodejs/#testing-locally-on-kubernetes
 上記とほぼ同じだがKubernetesにDeployするまでの手順がある

Working with Kabanero Collections
https://kabanero.io/guides/working-with-collections/#what-you-will-learn
Appsodyを使ってKabaneroコレクション(アプリケーションの雛形)を作成。

Developing cloud native microservices with the Kabanero Eclipse MicroProfile Collection and Appsody CLI
https://kabanero.io/guides/collection-microprofile/
 Appsody cliを使ってMicroProfileのアプリケーションを作成、Kubernetesの環境でDeploy

Developing cloud native microservices with the Kabanero Spring Boot Collection and Appsody CLI
https://kabanero.io/guides/collection-springboot2/
 Appsody cliを使ってSpring Bootのアプリケーションを作成、Kubernetesの環境でDeploy

codewind

Getting Started with Codewind and Kabanero
https://kabanero.io/guides/guide-codewind/#objectives
 codewindを使ってMicroProfileのアプリケーションを作ってみる。動作環境はローカル

Developing with Kabanero Collections in your Eclipse IDE
https://kabanero.io/guides/microprofile-eclipse-codewind/#what-you-will-learn
codewindを使ってMicroProfileのアプリケーションを作成、実行、テストまで行う。

Viewing all 8839 articles
Browse latest View live