-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathatom.xml
494 lines (302 loc) · 258 KB
/
atom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Today's Commit</title>
<link href="/atom.xml" rel="self"/>
<link href="https://t-kojima.github.io/"/>
<updated>2019-07-12T07:03:13.101Z</updated>
<id>https://t-kojima.github.io/</id>
<author>
<name>t-kojima</name>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title>直近のとある月を得たい</title>
<link href="https://t-kojima.github.io/2019/07/10/0056-cloest-month/"/>
<id>https://t-kojima.github.io/2019/07/10/0056-cloest-month/</id>
<published>2019-07-10T12:59:32.000Z</published>
<updated>2019-07-12T07:03:13.101Z</updated>
<content type="html"><![CDATA[<p>小ネタ<br>直近の n 月を取得したい。例えば 10 月なら、今日(7 月 10 日)から一番近いのは 2018 年 10 月、という感じ。</p><a id="more"></a><p>お題は単純に思えるけど、意外とスキッと書けない。<br>Rails の<a href="http://railsdoc.com/references/Date" target="_blank" rel="noopener">Date</a>をみても、翌月とか前月とか、n ヶ月前とか取得するメソッドはあれど、直近 n 月を取得するメソッドはない。</p><h2><span id="思いついたままやる">思いついたままやる</span></h2><p>とりあえず何も考えず愚直に書いてみる</p><figure class="highlight ruby"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">closest_month</span><span class="params">(target_month, date)</span></span></span><br><span class="line"> year = date.month >= target_month ? date.year : date.prev_year.year</span><br><span class="line"> Date.new(year, target_month, <span class="number">1</span>)</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 今日から見て直近の10月を取得(したい)</span></span><br><span class="line">closest_month(<span class="number">10</span>, Date.current)</span><br></pre></td></tr></table></figure><p>この長さならワンライナーでもいい気がするな…</p><figure class="highlight ruby"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">closest_month</span><span class="params">(target_month, date)</span></span></span><br><span class="line"> Date.new(date.month >= target_month ? date.year : date.prev_year.year, target_month, <span class="number">1</span>)</span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure><h2><span id="if文を排除">if文を排除</span></h2><p>if文というか、三項演算子で条件判定が入っているのがなんか気になる。<br>対象の月が前だろうと後だろうと同じ式から導き出せないだろうか?と思って考えた。</p><figure class="highlight ruby"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">closest_month</span><span class="params">(target_month, date)</span></span></span><br><span class="line"> month_array = (target_month..<span class="number">12</span>).to_a + (<span class="number">1</span>...target_month).to_a</span><br><span class="line"> date.prev_month(month_array.index(date.month)).beginning_of_month</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 今日から見て直近の10月を取得(したい)</span></span><br><span class="line">closest_month(<span class="number">10</span>, Date.current)</span><br></pre></td></tr></table></figure><p>ちょっと複雑かな?と思うので解説</p><p><code>month_array = (target_month..12).to_a + (1...target_month).to_a</code>ここで、例えば<code>target_month</code>が<code>10</code>とすると<br><code>[10, 11, 12, 1, 2, 3, 4, 5, 6, 7, 8, 9]</code>という配列が得られる。</p><p>この配列から、当月のindexを引くと対象月との差分が得られる。<br>つまり当月が7月なら、<code>index=9</code>になるので、9ヶ月前が対象月の10月になる。</p><p>なので、<code>date.prev_month(month_array.index(date.month)).beginning_of_month</code>で差分だけ前の月を取得するという形だ。</p><p>うーん、なんかうまいことやってる感は出たけど<br>最初のワンライナーがシンプルでわかりやすいかも。</p><h2><span id="もう少し簡素に">もう少し簡素に</span></h2><p><code>[対象月から始まる配列].index(現在月) = 差分</code>という式で上の計算式は成り立っていてが<br><code>[0〜11の配列][現在月 - 対象月] = 差分</code>という式でもいける。こっちのほうがちょっと短いかな</p><figure class="highlight ruby"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">closest_month</span><span class="params">(target_month, date)</span></span></span><br><span class="line"> date.prev_month((<span class="number">0</span>...<span class="number">12</span>).to_a[date.month - target_month]).beginning_of_month</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 今日から見て直近の10月を取得(したい)</span></span><br><span class="line">closest_month(<span class="number">10</span>, Date.current)</span><br></pre></td></tr></table></figure><p>少しシンプルになった。。。気がする。</p><p>でも最初のワンライナーがわかりやすいかなぁ。</p>]]></content>
<summary type="html">
<p>小ネタ<br>直近の n 月を取得したい。例えば 10 月なら、今日(7 月 10 日)から一番近いのは 2018 年 10 月、という感じ。</p>
</summary>
<category term="Ruby" scheme="https://t-kojima.github.io/categories/Ruby/"/>
<category term="ruby" scheme="https://t-kojima.github.io/tags/ruby/"/>
</entry>
<entry>
<title>[AWS Amplify] ポーカーつくろうぜ!(プロジェクト初期設定)</title>
<link href="https://t-kojima.github.io/2019/04/30/0055-aws-amplify-auth/"/>
<id>https://t-kojima.github.io/2019/04/30/0055-aws-amplify-auth/</id>
<published>2019-04-30T00:23:10.000Z</published>
<updated>2019-04-30T02:51:07.000Z</updated>
<content type="html"><![CDATA[<p>GW の 10 連休で時間があるので AWS Amplify をやてみる。</p><a id="more"></a><h2><span id="題材">題材</span></h2><p>特にアイディアがない問題なので、またカードゲームでも作ってみよう。<br>以前はブラックジャックだったので、今度はポーカーあたりでどうだろうか?</p><h2><span id="aws-amplify-cli-アカウント設定">aws-amplify CLI - アカウント設定</span></h2><p>アカウントとの紐付けなどの初期セットアップは以下の手順が非常に詳しくそのまま使えるので、これを参考にやる。</p><p>参考:<a href="https://qiita.com/Junpei_Takagi/items/f2bc567761880471fd54" target="_blank" rel="noopener">AWS Amplify CLI の使い方〜インストールから初期セットアップまで〜 - Qiita</a></p><p>以下コンソールダイジェスト</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ yarn global add @aws-amplify/cli</span><br><span class="line">$ amplify configure</span><br><span class="line">Follow these steps to <span class="built_in">set</span> up access to your AWS account:</span><br><span class="line"></span><br><span class="line">Sign <span class="keyword">in</span> to your AWS administrator account:</span><br><span class="line">https://console.aws.amazon.com/</span><br><span class="line">Press Enter to <span class="built_in">continue</span></span><br><span class="line"></span><br><span class="line">Specify the AWS Region</span><br><span class="line">? region: us-east-1</span><br><span class="line">Specify the username of the new IAM user:</span><br><span class="line">? user name: amplify-SCCTT</span><br><span class="line">Complete the user creation using the AWS console</span><br><span class="line">https://console.aws.amazon.com/iam/home?region=undefined<span class="comment">#/users$new?step=final&accessKey&userNames=amplify-SCCTT&permissionType=policies&policies=arn:a</span></span><br><span class="line">ws:iam::aws:policy%2FAdministratorAccess</span><br><span class="line">Press Enter to <span class="built_in">continue</span></span><br><span class="line"></span><br><span class="line">Enter the access key of the newly created user:</span><br><span class="line">? accessKeyId: ******************************</span><br><span class="line">? secretAccessKey: ****************************************</span><br><span class="line">This would update/create the AWS Profile <span class="keyword">in</span> your <span class="built_in">local</span> machine</span><br><span class="line">? Profile Name: practice-poker</span><br><span class="line"></span><br><span class="line">Successfully <span class="built_in">set</span> up the new user.</span><br></pre></td></tr></table></figure><h3><span id="amplify-の初期設定">Amplify の初期設定</span></h3><p><code>amplify init</code>で Amplify の初期設定をする。</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ amplify init</span><br></pre></td></tr></table></figure><p>対話式で色々質問されるので回答してく</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">? Enter a name for the project</span><br><span class="line">> practice-poker</span><br><span class="line"></span><br><span class="line">? Enter a name for the environment</span><br><span class="line"># environmentってなんだろう?とりあえずdevelopとかにしとく</span><br><span class="line">> develop</span><br><span class="line"></span><br><span class="line">? Choose your default editor:</span><br><span class="line">> Visual Studio Code</span><br><span class="line"></span><br><span class="line">? Choose the type of app that you're building</span><br><span class="line">> javascript</span><br><span class="line"></span><br><span class="line">Please tell us about your project</span><br><span class="line">? What javascript framework are you using</span><br><span class="line">> react</span><br><span class="line"></span><br><span class="line">? Source Directory Path:</span><br><span class="line">> src</span><br><span class="line"></span><br><span class="line">? Distribution Directory Path:</span><br><span class="line">> build</span><br><span class="line"></span><br><span class="line">? Build Command:</span><br><span class="line"># これでいいのか?自信なし。。。</span><br><span class="line">> yarn build</span><br><span class="line"></span><br><span class="line">? Start Command:</span><br><span class="line">> yarn start</span><br><span class="line"></span><br><span class="line">? Do you want to use an AWS profile?</span><br><span class="line">> Yes</span><br><span class="line">? Please choose the profile you want to use</span><br><span class="line">> practice-poker</span><br></pre></td></tr></table></figure><p>全ログは以下の通り</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">$ amplify init</span><br><span class="line"></span><br><span class="line">Note: It is recommended to run this command from the root of your app directory</span><br><span class="line">? Enter a name for the project practice-poker</span><br><span class="line">? Enter a name for the environment develop</span><br><span class="line">? Choose your default editor: Visual Studio Code</span><br><span class="line">? Choose the type of app that you're building javascript</span><br><span class="line">Please tell us about your project</span><br><span class="line">? What javascript framework are you using react</span><br><span class="line">? Source Directory Path: src</span><br><span class="line">? Distribution Directory Path: build</span><br><span class="line">? Build Command: yarn build</span><br><span class="line">? Start Command: yarn start</span><br><span class="line">Using default provider awscloudformation</span><br><span class="line"></span><br><span class="line">For more information on AWS Profiles, see:</span><br><span class="line">https://docs.aws.amazon.com/cli/latest/userguide/cli-multiple-profiles.html</span><br><span class="line"></span><br><span class="line">? Do you want to use an AWS profile? Yes</span><br><span class="line">? Please choose the profile you want to use practice-poker</span><br><span class="line">⠙ Initializing project in the cloud...</span><br><span class="line"></span><br><span class="line">CREATE_IN_PROGRESS practice-poker-20190430105032 AWS::CloudFormation::Stack Tue Apr 30 2019 10:50:34 GMT+0900 (JST) User Initiated</span><br><span class="line">CREATE_IN_PROGRESS DeploymentBucket AWS::S3::Bucket Tue Apr 30 2019 10:50:39 GMT+0900 (JST)</span><br><span class="line">CREATE_IN_PROGRESS AuthRole AWS::IAM::Role Tue Apr 30 2019 10:50:39 GMT+0900 (JST)</span><br><span class="line">CREATE_IN_PROGRESS UnauthRole AWS::IAM::Role Tue Apr 30 2019 10:50:39 GMT+0900 (JST)</span><br><span class="line">⠹ Initializing project in the cloud...</span><br><span class="line"></span><br><span class="line">CREATE_IN_PROGRESS AuthRole AWS::IAM::Role Tue Apr 30 2019 10:50:40 GMT+0900 (JST) Resource creation Initiated</span><br><span class="line">CREATE_IN_PROGRESS DeploymentBucket AWS::S3::Bucket Tue Apr 30 2019 10:50:40 GMT+0900 (JST) Resource creation Initiated</span><br><span class="line">CREATE_IN_PROGRESS UnauthRole AWS::IAM::Role Tue Apr 30 2019 10:50:40 GMT+0900 (JST) Resource creation Initiated</span><br><span class="line">⠙ Initializing project in the cloud...</span><br><span class="line"></span><br><span class="line">CREATE_COMPLETE AuthRole AWS::IAM::Role Tue Apr 30 2019 10:50:51 GMT+0900 (JST)</span><br><span class="line">CREATE_COMPLETE UnauthRole AWS::IAM::Role Tue Apr 30 2019 10:50:52 GMT+0900 (JST)</span><br><span class="line">⠹ Initializing project in the cloud...</span><br><span class="line"></span><br><span class="line">CREATE_COMPLETE DeploymentBucket AWS::S3::Bucket Tue Apr 30 2019 10:51:01 GMT+0900 (JST)</span><br><span class="line">CREATE_COMPLETE practice-poker-20190430105032 AWS::CloudFormation::Stack Tue Apr 30 2019 10:51:05 GMT+0900 (JST)</span><br><span class="line">✔ Successfully created initial AWS cloud resources for deployments.</span><br><span class="line">✔ Initialized provider successfully.</span><br><span class="line">Initialized your environment successfully.</span><br><span class="line"></span><br><span class="line">Your project has been successfully initialized and connected to the cloud!</span><br><span class="line"></span><br><span class="line">Some next steps:</span><br><span class="line">"amplify status" will show you what you've added already and if it's locally configured or deployed</span><br><span class="line">"amplify <category> add" will allow you to add features like user login or a backend API</span><br><span class="line">"amplify push" will build all your local backend resources and provision it in the cloud</span><br><span class="line">"amplify publish" will build all your local backend and frontend resources (if you have hosting category added) and provision it in the cloud</span><br><span class="line"></span><br><span class="line">Pro tip:</span><br><span class="line">Try "amplify add api" to create a backend API and then "amplify publish" to deploy everything</span><br></pre></td></tr></table></figure><p>ここまで完了すると、AWS の CloudFormation にスタックが作成されている。</p><p><img src="/images/55-01.png" alt="CloudFormationに自動作成されたスタック"></p><h2><span id="create-react-app-をホスティング">create-react-app をホスティング</span></h2><p>やはり何かしらホスティングして、「できた!」ってところを確認したい。<br>なので create-react-app のデフォルトアプリをホスティングする。</p><p>しかしカレントディレクトリにアプリを作成しようとするとコンクリフトする。</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">$ create-react-app .</span><br><span class="line"></span><br><span class="line">The directory . contains files that could conflict:</span><br><span class="line"></span><br><span class="line"> __src</span><br><span class="line"> amplify</span><br><span class="line"> build</span><br><span class="line"> node_modules</span><br><span class="line"> package.json</span><br><span class="line"> public</span><br><span class="line"> src</span><br><span class="line"> yarn.lock</span><br><span class="line"></span><br><span class="line">Either try using a new directory name, or remove the files listed above.</span><br></pre></td></tr></table></figure><p>そういえば create-react-app はそうだった。<br>しょうがないのでサブディレクトリに一旦展開してカレントに持ってくる。</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">$ create-react-app temp</span><br><span class="line"># 生成されたファイルをルートに移動</span><br></pre></td></tr></table></figure><p>create-react-app 使う場合、git clone 直後にやっといたほうがよかったなとちょっと後悔。</p><h3><span id="amplify-js">amplify-js</span></h3><p>aws-amplify のプロジェクトを作るには、amplify-js を導入する必要がある。<br>firebase でいう firebase-tools みたいなもの(?)だ。たぶん</p><p><a href="https://github.com/aws-amplify/amplify-js" class="embedly-card" data-card-image="0" data-card-controls="0" data-card-align="left" target="_blank" rel="noopener"></a></p><p>yarn で入れる。React も使うので<code>aws-amplify-react</code>も入れる。</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">yarn add aws-amplify</span><br><span class="line">yarn add aws-amplify-react</span><br></pre></td></tr></table></figure><h3><span id="ホスティング有効化">ホスティング有効化</span></h3><p>ホスティングを有効化する。</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">amplify add hosting</span><br></pre></td></tr></table></figure><p>こんな感じで使いたいサービスを追加していくらしい。</p><h3><span id="デプロイ">デプロイ</span></h3><p><code>amplicy publish</code>でデプロイ!続けるかどうかだけ聞かれるので YesYes</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ amplify publish</span><br><span class="line"></span><br><span class="line">Current Environment: develop</span><br><span class="line"></span><br><span class="line">| Category | Resource name | Operation | Provider plugin |</span><br><span class="line">| -------- | --------------- | --------- | ----------------- |</span><br><span class="line">| Hosting | S3AndCloudFront | Create | awscloudformation |</span><br><span class="line">? Are you sure you want to <span class="built_in">continue</span>? Yes</span><br><span class="line">⠸ Updating resources <span class="keyword">in</span> the cloud. This may take a few minutes...</span><br><span class="line"></span><br><span class="line">UPDATE_IN_PROGRESS practice-poker-20190430110238 AWS::CloudFormation::Stack Tue Apr 30 2019 11:33:09 GMT+0900 (JST) User Initiated</span><br><span class="line">⠸ Updating resources <span class="keyword">in</span> the cloud. This may take a few minutes...</span><br><span class="line"></span><br><span class="line">CREATE_IN_PROGRESS hostingS3AndCloudFront AWS::CloudFormation::Stack Tue Apr 30 2019 11:33:16 GMT+0900 (JST)</span><br><span class="line">CREATE_IN_PROGRESS hostingS3AndCloudFront AWS::CloudFormation::Stack Tue Apr 30 2019 11:33:18 GMT+0900 (JST) Resource creation Initiated</span><br><span class="line">⠼ Updating resources <span class="keyword">in</span> the cloud. This may take a few minutes...</span><br><span class="line"></span><br><span class="line">CREATE_IN_PROGRESS practice-poker-20190430110238-hostingS3AndCloudFront-105QEPY9AH53T AWS::CloudFormation::Stack Tue Apr 30 2019 11:33:17 GMT+0900 (JST) User Initiated</span><br><span class="line">⠴ Updating resources <span class="keyword">in</span> the cloud. This may take a few minutes...</span><br><span class="line"></span><br><span class="line">CREATE_IN_PROGRESS S3Bucket AWS::S3::Bucket Tue Apr 30 2019 11:33:23 GMT+0900 (JST)</span><br><span class="line">⠦ Updating resources <span class="keyword">in</span> the cloud. This may take a few minutes...</span><br><span class="line"></span><br><span class="line">CREATE_IN_PROGRESS S3Bucket AWS::S3::Bucket Tue Apr 30 2019 11:33:26 GMT+0900 (JST) Resource creation Initiated</span><br><span class="line">⠇ Updating resources <span class="keyword">in</span> the cloud. This may take a few minutes...</span><br><span class="line"></span><br><span class="line">CREATE_COMPLETE S3Bucket AWS::S3::Bucket Tue Apr 30 2019 11:33:47 GMT+0900 (JST)</span><br><span class="line">⠦ Updating resources <span class="keyword">in</span> the cloud. This may take a few minutes...</span><br><span class="line"></span><br><span class="line">CREATE_COMPLETE hostingS3AndCloudFront AWS::CloudFormation::Stack Tue Apr 30 2019 11:33:52 GMT+0900 (JST)</span><br><span class="line">⠦ Updating resources <span class="keyword">in</span> the cloud. This may take a few minutes...</span><br><span class="line"></span><br><span class="line">UPDATE_COMPLETE_CLEANUP_IN_PROGRESS practice-poker-20190430110238 AWS::CloudFormation::Stack Tue Apr 30 2019 11:33:56 GMT+0900 (JST)</span><br><span class="line">UPDATE_COMPLETE practice-poker-20190430110238 AWS::CloudFormation::Stack Tue Apr 30 2019 11:33:57 GMT+0900 (JST)</span><br><span class="line">✔ All resources are updated <span class="keyword">in</span> the cloud</span><br><span class="line"></span><br><span class="line">Hosting endpoint: http://practice-poker-20190430112522-hostingbucket-develop.s3-website-ap-northeast-1.amazonaws.com</span><br><span class="line"></span><br><span class="line">yarn run v1.12.3</span><br><span class="line">$ react-scripts build</span><br><span class="line">Creating an optimized production build...</span><br><span class="line">Compiled successfully.</span><br><span class="line"></span><br><span class="line">File sizes after gzip:</span><br><span class="line"></span><br><span class="line"> 36.44 KB build/static/js/2.b41502e9.chunk.js</span><br><span class="line"> 762 B build/static/js/runtime~main.a8a9905a.js</span><br><span class="line"> 602 B build/static/js/main.28647029.chunk.js</span><br><span class="line"> 523 B build/static/css/main.584f321a.chunk.css</span><br><span class="line"></span><br><span class="line">The project was built assuming it is hosted at the server root.</span><br><span class="line">You can control this with the homepage field <span class="keyword">in</span> your package.json.</span><br><span class="line">For example, add this to build it <span class="keyword">for</span> GitHub Pages:</span><br><span class="line"></span><br><span class="line"> <span class="string">"homepage"</span> : <span class="string">"http://myname.github.io/myapp"</span>,</span><br><span class="line"></span><br><span class="line">The build folder is ready to be deployed.</span><br><span class="line">You may serve it with a static server:</span><br><span class="line"></span><br><span class="line"> yarn global add serve</span><br><span class="line"> serve -s build</span><br><span class="line"></span><br><span class="line">Find out more about deployment here:</span><br><span class="line"></span><br><span class="line"> https://bit.ly/CRA-deploy</span><br><span class="line"></span><br><span class="line">✨ Done <span class="keyword">in</span> 5.26s.</span><br><span class="line">frontend build <span class="built_in">command</span> exited with code 0</span><br><span class="line">✔ Uploaded files successfully.</span><br><span class="line">Your app is published successfully.</span><br><span class="line">http://practice-poker-20190430112522-hostingbucket-develop.s3-website-ap-northeast-1.amazonaws.com</span><br></pre></td></tr></table></figure><p>いった!</p><p><img src="/images/55-02.png" alt="いつもの"></p><p><a href="http://practice-poker-20190430112522-hostingbucket-develop.s3-website-ap-northeast-1.amazonaws.com/" target="_blank" rel="noopener">http://practice-poker-20190430112522-hostingbucket-develop.s3-website-ap-northeast-1.amazonaws.com/</a></p><h2><span id="参考">参考</span></h2><ul><li><a href="https://qiita.com/Junpei_Takagi/items/f2bc567761880471fd54" target="_blank" rel="noopener">AWS Amplify CLI の使い方〜インストールから初期セットアップまで〜 - Qiita</a></li><li><a href="https://qiita.com/oliverSI_/items/efc1a1e90584baac9e61" target="_blank" rel="noopener">AmplifyとReactを使用して爆速でWEBアプリを作成する - Qiita</a></li></ul>]]></content>
<summary type="html">
<p>GW の 10 連休で時間があるので AWS Amplify をやてみる。</p>
</summary>
<category term="AWS" scheme="https://t-kojima.github.io/categories/AWS/"/>
<category term="aws-amplify" scheme="https://t-kojima.github.io/tags/aws-amplify/"/>
</entry>
<entry>
<title>MacでVSCodeでPowerline</title>
<link href="https://t-kojima.github.io/2019/01/12/0054-mac-vscode-powerline/"/>
<id>https://t-kojima.github.io/2019/01/12/0054-mac-vscode-powerline/</id>
<published>2019-01-11T23:45:48.000Z</published>
<updated>2019-01-12T13:14:38.000Z</updated>
<content type="html"><![CDATA[<p>Mac にも powerline を入れたい!</p><p>過去に Windows と Linux ではやったので、今度は Mac に入れてみる。</p><ul><li><a href="https://t-kojima.github.io/2018/09/24/0041-windows-cmder-vscode-powerline/">Windows で Cmder で VSCode で Powerline</a></li><li><a href="https://t-kojima.github.io/2018/09/26/0042-linux-vscode-powerline/">Linux で VSCode で Powerline</a></li></ul><a id="more"></a><p><a href="https://powerline.readthedocs.io/en/2.4/installation/osx.html" target="_blank" rel="noopener">Powerline 公式の方法</a>でやってみる。</p><h1><span id="pip-をインストール">pip をインストール</span></h1><p>まずインストールには Python と pip が必要になるので、バージョンを確認してみる。</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ python --version</span><br><span class="line">Python 2.7.10</span><br><span class="line">$ pip --version</span><br><span class="line">bash: pip: <span class="built_in">command</span> not found</span><br></pre></td></tr></table></figure><p>なんてこった pip がない</p><p>というか python 環境が全くない(3 系とか)ので、pyenv から 3 系を入れてみよう。pyenv からインストールすれば pip も一緒にインストールされる。</p><h2><span id="pyenv-のインストール">pyenv のインストール</span></h2><p><a href="https://qiita.com/crankcube@github/items/15f06b32ec56736fc43a" target="_blank" rel="noopener">MacOS と Homebrew と pyenv で快適 python 環境を。 - Qiita</a> を参考に入れる。</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ brew install pyenv</span><br><span class="line">==> Installing dependencies <span class="keyword">for</span> pyenv: autoconf, openssl, pkg-config and readline</span><br><span class="line">==> Installing pyenv dependency: autoconf</span><br><span class="line">==> Downloading https://homebrew.bintray.com/bottles/autoconf-2.69.mojave.bottle.4.tar.gz</span><br><span class="line"><span class="comment">######################################################################## 100.0%</span></span><br><span class="line">==> Pouring autoconf-2.69.mojave.bottle.4.tar.gz</span><br><span class="line">==> Caveats</span><br><span class="line">Emacs Lisp files have been installed to:</span><br><span class="line"> /usr/<span class="built_in">local</span>/share/emacs/site-lisp/autoconf</span><br><span class="line">==> Summary</span><br><span class="line">🍺 /usr/<span class="built_in">local</span>/Cellar/autoconf/2.69: 71 files, 3.0MB</span><br><span class="line">==> Installing pyenv dependency: openssl</span><br><span class="line">==> Downloading https://homebrew.bintray.com/bottles/openssl-1.0.2q.mojave.bottle.tar.gz</span><br><span class="line"><span class="comment">######################################################################## 100.0%</span></span><br><span class="line">~~ 省略 ~~</span><br><span class="line"></span><br><span class="line">$ pyenv --version</span><br><span class="line">pyenv 1.2.9</span><br></pre></td></tr></table></figure><p>1.2.9 が入った。</p><p>.bash_profile に追記してパスを通す</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ <span class="built_in">echo</span> <span class="string">'export PYENV_ROOT="$HOME/.pyenv"'</span> >> ~/.bash_profile</span><br><span class="line">$ <span class="built_in">echo</span> <span class="string">'export PATH="$PYENV_ROOT/bin:$PATH"'</span> >> ~/.bash_profile</span><br><span class="line">$ <span class="built_in">echo</span> <span class="string">'eval "$(pyenv init -)"'</span> >> ~/.bash_profile</span><br><span class="line">$ <span class="built_in">source</span> ~/.bash_profile</span><br></pre></td></tr></table></figure><p><code>pyenv install --list</code> でインストール可能な一覧が見れる。今回は<code>3.7.2</code>を入れてみる。</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ pyenv install 3.7.2</span><br></pre></td></tr></table></figure><p>入ったらグローバルに設定して完了だ</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ pyenv global 3.7.2</span><br><span class="line">$ pyenv versions</span><br><span class="line"> system</span><br><span class="line">* 3.7.2 (<span class="built_in">set</span> by /Users/********/.pyenv/version)</span><br><span class="line">$ python --version</span><br><span class="line">Python 3.7.2</span><br><span class="line">$ pip --version</span><br><span class="line">pip 18.1 from /Users/********/.pyenv/versions/3.7.2/lib/python3.7/site-packages/pip (python 3.7)</span><br></pre></td></tr></table></figure><p>pip も無事使えるようになっている。</p><h3><span id="zlib-エラー">zlib エラー</span></h3><p>macos Mojave ではビルド時に以下のエラーになってしまう。</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">zipimport.ZipImportError: can<span class="string">'t decompress data; zlib not available</span></span><br><span class="line"><span class="string">make: *** [install] Error 1</span></span><br></pre></td></tr></table></figure><p>参考:<a href="https://qiita.com/zreactor/items/c3fd04417e0d61af0afe" target="_blank" rel="noopener">[MacOS Mojave]pyenv で python のインストールが zlib エラーで失敗した時の対応 - Qiita</a></p><p>以下のコマンドで Mojave 用の macOS SDK header を入れる</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">sudo installer -pkg /Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pkg -target /</span><br></pre></td></tr></table></figure><p>再度 <code>pyenv install 3.7.2</code> でインストールして成功すれば OK だ</p><h1><span id="powerline-のインストール">powerline のインストール</span></h1><p>前置き(?)が長くなってしまったが、powerline を入れる。<br>改めて<a href="https://powerline.readthedocs.io/en/2.4/installation/osx.html" target="_blank" rel="noopener">Powerline 公式の方法</a>でやろう。</p><p><a href="https://qiita.com/tkhr/items/8cc17c02dea1803be9c6" target="_blank" rel="noopener">Powerline 導入例 - Qiita</a>も参考にしながらやる。</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ pip install powerline-status</span><br><span class="line">Collecting powerline-status</span><br><span class="line"> Using cached https://files.pythonhosted.org/packages/9c/30/8bd3c62642778af9ad813a526c6ff7dd2f98144d6580ad6fab94ca389265/powerline-status-2.7.tar.gz</span><br><span class="line">Installing collected packages: powerline-status</span><br><span class="line"> Running setup.py install <span class="keyword">for</span> powerline-status ... <span class="keyword">done</span></span><br><span class="line">Successfully installed powerline-status-2.7</span><br></pre></td></tr></table></figure><p>即入った。簡単</p><p>ちなみに公式だと<code>--user</code>オプションをつけるようあるが、python を homebrew からインストールした際は付けないよう注意書きがしてある。<br><code>pyenv</code>から入れた場合もオプションをつけたら動かなかった。<code>pyenv</code>から入れた場合も同様にオプション無しが正解のようだ。</p><p><code>powerline-daemon -h</code> でヘルプが表示されれば OK</p><h2><span id="bash-に適用">bash に適用</span></h2><p><a href="https://powerline.readthedocs.io/en/2.4/usage/shell-prompts.html#bash-prompt" target="_blank" rel="noopener">Shell prompts — Powerline beta documentation</a>を参考に shell に powerline 適用する。</p><p>まずリポジトリのルートを確認</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ pip --version</span><br><span class="line">pip 18.1 from /Users/********/.pyenv/versions/3.7.2/lib/python3.7/site-packages/pip (python 3.7)</span><br></pre></td></tr></table></figure><p>ここの<code>/Users/********/.pyenv/versions/3.7.2/lib/python3.7/site-packages</code>がルートになる。</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ <span class="built_in">echo</span> <span class="string">'powerline-daemon -q'</span> >> ~/.bash_profile</span><br><span class="line">$ <span class="built_in">echo</span> <span class="string">'. {repository_root}/powerline/bindings/bash/powerline.sh'</span> >> ~/.bash_profile</span><br><span class="line">$ <span class="built_in">source</span> ~/.bash_profile</span><br></pre></td></tr></table></figure><p>これで bash を再起動すると shell に powerline が適用される。</p><p>しかしこのままだと文字化けしてしまうので、powerline 用のフォントを入れる。</p><h2><span id="フォントをインストール">フォントをインストール</span></h2><p>参考元のままやる</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">cd</span> ~/Desktop</span><br><span class="line">git <span class="built_in">clone</span> [email protected]:powerline/fonts.git</span><br><span class="line">./fonts/install.sh</span><br></pre></td></tr></table></figure><p>このように powerline 用のフォントが追加された。</p><p><img src="/images/54-01.png" alt="FontBook.app"></p><h3><span id="vscode-のフォントを変更">VSCode のフォントを変更</span></h3><p>VSCode でユーザー設定を開き、フォントを変更する。</p><p>ついでのサイズを 12 にする。ちょうどいいと思う。</p><figure class="highlight"><table><tr><td class="code"><pre><span class="line">"terminal.integrated.fontFamily": "Source Code Pro for Powerline",</span><br><span class="line">"terminal.integrated.fontSize": 12,</span><br></pre></td></tr></table></figure><p>これで VSCode のターミナルに powerline が適用された!</p><p><img src="/images/54-02.png" alt="Powerlineが適用されたターミナル"></p><h1><span id="configuration">Configuration</span></h1><p>デフォルト表記だと git のブランチが表示されなかったりするので、設定を変更する。</p><p>参考:<a href="https://powerline.readthedocs.io/en/2.4/configuration.html" target="_blank" rel="noopener">Configuration and customization — Powerline beta documentation</a></p><p>デフォルトの設定はリポジトリルートの奥底にあるが、<code>~/.config/powerline</code>配下に設定ファイルを置くと、記述した分だけデフォルト設定を上書きしてくれる。</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">mkdir ~/.config/powerline</span><br><span class="line">touch ~/.config/powerline/config.json</span><br><span class="line">code ~/.config/powerline/config.json</span><br></pre></td></tr></table></figure><p>VSCode で編集</p><figure class="highlight json"><table><tr><td class="code"><pre><span class="line">{</span><br><span class="line"> <span class="attr">"ext"</span>: {</span><br><span class="line"> <span class="attr">"shell"</span>: {</span><br><span class="line"> <span class="attr">"theme"</span>: <span class="string">"custom"</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>themes/custom.json も作る</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">touch ~/.config/powerline/themes/shell/custom.json</span><br><span class="line">code ~/.config/powerline/themes/shell/custom.json</span><br></pre></td></tr></table></figure><p>こちらも VSCode で編集</p><figure class="highlight json"><table><tr><td class="code"><pre><span class="line">{</span><br><span class="line"> <span class="attr">"segments"</span>: {</span><br><span class="line"> <span class="attr">"left"</span>: [</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"function"</span>: <span class="string">"powerline.segments.shell.mode"</span></span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"function"</span>: <span class="string">"powerline.segments.common.net.hostname"</span>,</span><br><span class="line"> <span class="attr">"priority"</span>: <span class="number">10</span></span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"function"</span>: <span class="string">"powerline.segments.common.env.user"</span>,</span><br><span class="line"> <span class="attr">"priority"</span>: <span class="number">30</span></span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"function"</span>: <span class="string">"powerline.segments.common.env.virtualenv"</span>,</span><br><span class="line"> <span class="attr">"priority"</span>: <span class="number">50</span></span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"function"</span>: <span class="string">"powerline.segments.common.vcs.branch"</span>,</span><br><span class="line"> <span class="attr">"priority"</span>: <span class="number">40</span>,</span><br><span class="line"> <span class="attr">"args"</span>: {</span><br><span class="line"> <span class="attr">"status_colors"</span>: <span class="literal">true</span></span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"function"</span>: <span class="string">"powerline.segments.shell.cwd"</span>,</span><br><span class="line"> <span class="attr">"priority"</span>: <span class="number">10</span>,</span><br><span class="line"> <span class="attr">"args"</span>: {</span><br><span class="line"> <span class="attr">"dir_limit_depth"</span>: <span class="number">1</span></span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"function"</span>: <span class="string">"powerline.segments.shell.jobnum"</span>,</span><br><span class="line"> <span class="attr">"priority"</span>: <span class="number">20</span></span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"function"</span>: <span class="string">"powerline.segments.shell.last_pipe_status"</span>,</span><br><span class="line"> <span class="attr">"priority"</span>: <span class="number">10</span></span><br><span class="line"> }</span><br><span class="line"> ]</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><code>powerline-daemon --refresh</code> すると適用される。</p><p><img src="/images/54-03.png" alt="コンフィグを修正した後のターミナル"></p><p>ここまでがんばったが、設定のやり方が分かり辛すぎる…<br>powerline の issue で 「documentation is confusing」 とか言われてて笑える w…</p><h1><span id="さいごに">さいごに</span></h1><p>結構めんどかった。インストール方法を調べると色々な方法が出てきてどれが適当かよくわからない。</p><p>今回公式の方法に沿って行ったが、Linux の時と同様に<code>powerline-shell</code>を使ったほうがサクっといけたような気がしてならない。</p><p>…ま、いいか</p><h2><span id="環境">環境</span></h2><ul><li>Mac OSX 10.14.1 Mojave</li><li>VSCode 1.30.2</li><li>powerline-status 2.7</li></ul>]]></content>
<summary type="html">
<p>Mac にも powerline を入れたい!</p>
<p>過去に Windows と Linux ではやったので、今度は Mac に入れてみる。</p>
<ul>
<li><a href="https://t-kojima.github.io/2018/09/24/0041-windows-cmder-vscode-powerline/">Windows で Cmder で VSCode で Powerline</a></li>
<li><a href="https://t-kojima.github.io/2018/09/26/0042-linux-vscode-powerline/">Linux で VSCode で Powerline</a></li>
</ul>
</summary>
<category term="VSCode" scheme="https://t-kojima.github.io/categories/VSCode/"/>
<category term="vscode" scheme="https://t-kojima.github.io/tags/vscode/"/>
<category term="powerline" scheme="https://t-kojima.github.io/tags/powerline/"/>
</entry>
<entry>
<title>[QNAP] TS-451PのNIC設定が変更できない</title>
<link href="https://t-kojima.github.io/2018/12/30/0053-qnap-network-and-virtual-switch/"/>
<id>https://t-kojima.github.io/2018/12/30/0053-qnap-network-and-virtual-switch/</id>
<published>2018-12-30T05:41:16.000Z</published>
<updated>2018-12-30T00:31:41.000Z</updated>
<content type="html"><![CDATA[<p>QNAPはネットワーク周りがちょっとイケてない部分が多い。昔はそうでもなかったけど、設定が「ネットワークと仮想スイッチ」に外出しされてからどうもトラブルが目に付くようになった。<br>今回またうまく動かない現象があったが、解決したのでメモっとく。</p><a id="more"></a><h1><span id="いきさつ">いきさつ</span></h1><p>QNAP TS-451Pのファームを1年ほど更新していなかったので、今日 <code>QTS 4.2.5</code> から <code>4.3.5</code> にアップデートを行った。</p><p>更新自体は正常に行われた(…と思われる)が、どうもネットワーク越しに接続できない。</p><ul><li><a href="https://www.qnap.com/ja-jp/utilities/essentials" target="_blank" rel="noopener">Qfinder</a>で検索をしても見つからず。</li><li>デフォルトのIP(169.254.100.100)に戻ったかと思い、IPを変えて接続しようとしても見つからず。</li></ul><p>そこでもう一つのNICなら?と思い、TS-451Pの空いていたNICで接続したら、そちらは接続することができた。<br>元々使っていたNIC1(接続できないほう)は静的IPを振っていたが、NIC2のほうは「DHCP経由でIPを取得」設定になっていたので、DHCPサーバからIPが振られた形になっていた。</p><h2><span id="不可解な動き">不可解な動き</span></h2><p>接続できないNIC1は以下のような状態になっていた。</p><ul><li>状態は<code>connected(接続済み)</code></li><li>アダプターリストのIPは<code>--</code>だが、詳細は<code>192.168.11.12</code>になっている。</li><li>Qfinderからは<code>192.168.11.12</code>として見えている。</li><li>「ネットワークと仮想スイッチ」のサマリ画面でも<code>192.168.11.12</code>として見える。</li><li>NICの設定から、設定を変更しようとしても反映されない。</li></ul><p>特に一番最後が致命的で、以下の画面で「静的IPアドレスを使用する」から「DHCP経由でIPアドレス設定を自動取得する」に変更して「適用」ボタンを押しても、設定が変わらないのだ。</p><p><img src="/images/53-01.png" alt="設定を変更しても反映されない"></p><h2><span id="迷子の仮想アダプタ">迷子の仮想アダプタ</span></h2><p>なんとなく仮想スイッチの画面を見たところ、仮想スイッチは無いのに仮想アダプタだけがあるように見えた。</p><p>ここでなんとなくピンと来た。(というか思い出した)</p><p>TS-451PのVirtualization Stationで仮想マシンを1台動かしていたが、その仮想マシンが外部接続するための仮想スイッチがファームアップデートで消えてしまったのではないだろうか。そしてその消えた仮想スイッチがNIC1を裏で掴んでいるので設定を変更できないのではないかと考えた。</p><h1><span id="修復する">修復する</span></h1><p>NIC1に仮想スイッチを適用したらどうも直りそうだが、上記のようにおかしくなったNIC1はそのまま仮想スイッチに割り当てることができなかった。なので以下の手順で修復を試みる。</p><ol><li>正常に動作しているNIC2で仮想スイッチを作成し、仮想アダプタを割り当てる。</li><li>NIC1の表示が正常に戻る(設定もできるようになる)</li><li>NIC2の仮想スイッチを削除し、NIC1で仮想スイッチを作り直す。そして仮想アダプタを割り当て直す。</li></ol><p>これで表示も以下のようになり、正常に接続できるようになった。</p><p><img src="/images/53-02.png" alt="正常に接続できるようになった"></p><h1><span id="さいごに">さいごに</span></h1><p>一応解決はしたが、再起動やファーム更新のタイミングでQNAPのネットワーク系はトラブルになりやすい。<br>次回は注意したい…と思いつつも、覚えてられるだろうかなぁ</p><h2><span id="環境">環境</span></h2><ul><li>TS-451P(QTS 4.3.5.0760)</li><li>Network & Virtual Switch 2.0.0</li></ul>]]></content>
<summary type="html">
<p>QNAPはネットワーク周りがちょっとイケてない部分が多い。昔はそうでもなかったけど、設定が「ネットワークと仮想スイッチ」に外出しされてからどうもトラブルが目に付くようになった。<br>今回またうまく動かない現象があったが、解決したのでメモっとく。</p>
</summary>
<category term="QNAP" scheme="https://t-kojima.github.io/categories/QNAP/"/>
<category term="qnap" scheme="https://t-kojima.github.io/tags/qnap/"/>
</entry>
<entry>
<title>停電とNASのシャットダウン</title>
<link href="https://t-kojima.github.io/2018/12/29/0052-power-cuts-and-qnap/"/>
<id>https://t-kojima.github.io/2018/12/29/0052-power-cuts-and-qnap/</id>
<published>2018-12-28T23:40:03.000Z</published>
<updated>2018-12-29T05:41:34.000Z</updated>
<content type="html"><![CDATA[<p>今朝停電があって、NAS(QNAP)のシャットダウンでやっちまった….</p><a id="more"></a><h2><span id="何があったか">何があったか</span></h2><p>今朝の7時前辺りから停電があり、NASの電源がUPSに切り替わっていた。</p><p>しかし僕はそれに30分?ほどしてから気付き、慌ててシャットダウンしたが<br>なんとシャットダウンシーケンス中にUPSが切れてしまった。</p><p>丁度スケジュールバックアップとカチあったのも悪かったかもしれない。</p><h3><span id="qnap-ts-451p">QNAP TS-451P</span></h3><p>NASは<a href="https://www.qnap.com/ja-jp/product/ts-451+" target="_blank" rel="noopener">QNAPのTS-451P</a>を使っている。</p><p><a href="https://www.qnap.com/ja-jp/product/ts-451+" class="embedly-card" data-card-image="0" data-card-controls="0" data-card-align="left" target="_blank" rel="noopener"></a></p><p>普段使う性能的には非常に満足していたが、電源周りにちょっと不安があった。具体的には起動・シャットダウンにやたらと時間がかかったり、リブート中にフリーズする…などだ。</p><h3><span id="omron-by50s">OMRON BY50S</span></h3><p>UPSは<a href="https://www.oss.omron.co.jp/ups/product/by35-120s/by35-120s.html" target="_blank" rel="noopener">OMRONのBY50S</a>を使ってる。<br>元々PC用として正弦波のUPSが必要で使っていたが、後にNAS用に流用していた。</p><p><a href="https://www.oss.omron.co.jp/ups/product/by35-120s/by35-120s.html" class="embedly-card" data-card-image="0" data-card-controls="0" data-card-align="left" target="_blank" rel="noopener"></a></p><h2><span id="その後">その後</span></h2><p>停電が復旧し、QNAPを立ち上げたところ正常に立ち上がり、HDDにも障害は無いようだ。</p><p>しかしUPSを繋げておきながら、自動シャットダウンの設定をしていなかったのは大いに反省。</p><h2><span id="自動シャットダウンの設定">自動シャットダウンの設定</span></h2><p>なので今のうちにやってしまう。鉄は熱いうちに打てというやつだ。</p><p>とはいえ、USBケーブルでUPSとNASを繋ぐだけでOKだった。もっと面倒臭い設定だったり、NASにUPS用のソフトをインストールしたり、そもそもNASで使えるソフト何てないんじゃね?とか思っててやっていなかった。</p><p>接続するとコントロールパネルでUPSを認識していることを確認することができる。</p><p><img src="/images/52-01.png" alt="コントロールパネル - 外部デバイス - UPS(上部)"></p><p><img src="/images/52-02.png" alt="コントロールパネル - 外部デバイス - UPS(下部)"></p><p>これで次回からはOKな…はず</p><p>電源抜いてテストでもしてみようかな</p>]]></content>
<summary type="html">
<p>今朝停電があって、NAS(QNAP)のシャットダウンでやっちまった….</p>
</summary>
<category term="QNAP" scheme="https://t-kojima.github.io/categories/QNAP/"/>
<category term="qnap" scheme="https://t-kojima.github.io/tags/qnap/"/>
</entry>
<entry>
<title>MacBookPro2018のセットアップ</title>
<link href="https://t-kojima.github.io/2018/12/20/0051-setup-macbook-pro-2018/"/>
<id>https://t-kojima.github.io/2018/12/20/0051-setup-macbook-pro-2018/</id>
<published>2018-12-20T01:09:54.000Z</published>
<updated>2018-12-26T10:29:17.000Z</updated>
<content type="html"><![CDATA[<p>MacBookPro 13インチ TouchBarモデルを手に入れたので、セットアップ手順を残しておく。</p><a id="more"></a><h1><span id="初期設定">初期設定</span></h1><p>開封後、MacBookを開くと勝手に初期セットアップのウィザードが開始される。</p><p>最近のOSはなんでもそうだけど、特に難しいことはない。むしろ「Hey!Siri!」が何度も言わされて若干恥ずい。。。</p><h1><span id="環境設定の変更">環境設定の変更</span></h1><p>細かいのが多いので箇条書きで</p><ul><li>システム環境設定 - 共有 からコンピュータ名を変更する。「○○のMacBook」とかになっているので。</li><li>システム環境設定 - セキュリティとプライバシー - ファイアウォール でファイアウォールを有効にする。</li><li>Finder - 環境設定 - 詳細 から「すべてのファイル名拡張子を表示」で拡張子を表示する。</li><li>日本語入力のライブ変換をOFF</li></ul><p>あんま多くなかった</p><h2><span id="ディレクトリ表示を英語に変更">ディレクトリ表示を英語に変更</span></h2><p>デフォルトだとホーム配下のディレクトリ名が日本語(ドキュメントとか)になっているので、英語(documents)に直したい。</p><p>10.11 El Capitanからはディレクトリ内にある<code>.localized</code>を削除すると良いらしい。<br>ちょっとやり方を調べたけど、個々に消すしかないっぽい(というか探す時間で消した方が早い)</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">rm ~/Downloads/.localized</span><br><span class="line">rm ~/Documents/.localized</span><br><span class="line">rm ~/Pictures/.localized</span><br><span class="line">rm ~/Applications/.localized</span><br><span class="line">rm ~/Music/.localized</span><br><span class="line">rm ~/Public/.localized</span><br><span class="line">rm ~/Desktop/.localized</span><br><span class="line">rm ~/Movies/.localized</span><br><span class="line">rm ~/Library/.localized</span><br></pre></td></tr></table></figure><h1><span id="パッケージマネージャの導入">パッケージマネージャの導入</span></h1><p>MACといえばHomebrew!</p><p>Homebrew(ホームブルー)にはビールを自家醸造的な意味があるらしい。自前でビルドするのが似ているのだとか。。。またどうでもいい知識が増えた。</p><p><a href="https://qiita.com/pypypyo14/items/4bf3b8bd511b6e93c9f9" target="_blank" rel="noopener">macOSにHomebrewをインストール - Qiita</a>を参考にインストールする。</p><h2><span id="xcode-select">xcode-select</span></h2><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ xcode-select --install</span><br><span class="line">xcode-select: note: install requested <span class="keyword">for</span> <span class="built_in">command</span> line developer tools</span><br></pre></td></tr></table></figure><p>ポップアップが表示されるので言われるがままにインストールする。</p><h2><span id="homebrew">homebrew</span></h2><p><a href="https://brew.sh/index_ja.html" target="_blank" rel="noopener">公式サイト</a>のスクリプトを貼り付けろということなので、張り付けて実行する。</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ /usr/bin/ruby -e <span class="string">"<span class="variable">$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)</span>"</span></span><br></pre></td></tr></table></figure><p>ダラララとコンソールが流れ、パスワードを求められたりするので入力したりするとインストールされる。</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ brew -v</span><br><span class="line">Homebrew 1.8.6</span><br><span class="line">Homebrew/homebrew-core (git revision ba68; last commit 2018-12-19)</span><br></pre></td></tr></table></figure><p>OKやね</p><h2><span id="homebrew-cask">homebrew cask</span></h2><p>どうもGUIツールをHomebrewで入れる場合はcaskなる拡張が必要のようだ。入れよう。</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ brew tap caskroom/cask</span><br></pre></td></tr></table></figure><p>たいしてコンソールも流れず、パスワードも求められずにインストールされる。</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ brew -v</span><br><span class="line">Homebrew 1.8.6</span><br><span class="line">Homebrew/homebrew-core (git revision ba68; last commit 2018-12-19)</span><br><span class="line">Homebrew/homebrew-cask (git revision bca0; last commit 2018-12-19)</span><br></pre></td></tr></table></figure><p>入った!</p><h1><span id="アプリケーションを入れる">アプリケーションを入れる</span></h1><p>brew caskで大体入る</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">brew cask install google-chrome</span><br><span class="line">brew cask install visual-studio-code</span><br><span class="line">brew cask install zoomus</span><br><span class="line">brew cask install dropbox</span><br></pre></td></tr></table></figure><h1><span id="sshのキーを作成">SSHのキーを作成</span></h1><p>Githubとかで使うので生成</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">ssh-keygen -t rsa -b 4096 -C <span class="string">"[email protected]"</span></span><br></pre></td></tr></table></figure><p>参考</p><ul><li><a href="https://qiita.com/suthio/items/2760e4cff0e185fe2db9" target="_blank" rel="noopener">お前らのSSH Keysの作り方は間違っている - Qiita</a></li></ul><h1><span id="nvmのインストール">nvmのインストール</span></h1><p>この記事を新しいMacで書いていて、いざデプロイしようとしたらyarn(+node)が未インストールということに気づいた。</p><p>なのでhomebrewでインストールする。<br>余談だけどデフォルトで.bash_profileがないのね…なのでこれも作る</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">brew install nvm</span><br><span class="line">mkdir ~/.nvm</span><br><span class="line">touch ~/.bash_profile</span><br><span class="line"><span class="built_in">echo</span> <span class="built_in">export</span> NVM_DIR=~/.nvm >> .bash_profile</span><br><span class="line"><span class="built_in">echo</span> <span class="built_in">source</span> $(brew --prefix nvm)/nvm.sh >> .bash_profile</span><br></pre></td></tr></table></figure><p>nvmが入ったら、LTSをインストールして、yarnをいれる</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">nvm install --lts</span><br><span class="line">npm -g install yarn</span><br></pre></td></tr></table></figure><p>これでブログをデプロイできる…</p><h1><span id="さいごに">さいごに</span></h1><p>他にもPowerlineとか入れたいけど、とりあえず普段使いに困らないレベルになったのでここまで</p><h2><span id="環境">環境</span></h2><ul><li>Mac OSX 10.14.1 Mojave</li></ul>]]></content>
<summary type="html">
<p>MacBookPro 13インチ TouchBarモデルを手に入れたので、セットアップ手順を残しておく。</p>
</summary>
<category term="Mac" scheme="https://t-kojima.github.io/categories/Mac/"/>
<category term="mac" scheme="https://t-kojima.github.io/tags/mac/"/>
</entry>
<entry>
<title>[Sinatra] config.ruとは</title>
<link href="https://t-kojima.github.io/2018/11/29/0050-about-config-ru/"/>
<id>https://t-kojima.github.io/2018/11/29/0050-about-config-ru/</id>
<published>2018-11-29T01:09:54.000Z</published>
<updated>2018-12-20T11:07:38.000Z</updated>
<content type="html"><![CDATA[<p>Sinatra のチュートリアルやブログなど参照していると、手順として「<code>config.ru</code>というファイルを作れ」とあるが<br>何故必要なのか説明できていなかったので、改めて調べてみた。</p><a id="more"></a><h1><span id="configru-を使わないで-sinatra-を動かす">config.ru を使わないで Sinatra を動かす</span></h1><p>例えば Sinatra のみを bundler でインストールする。</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ mkdir sinatra-test</span><br><span class="line">$ <span class="built_in">cd</span> sinatra-test</span><br><span class="line">$ bundle init</span><br><span class="line"><span class="comment"># Gemfileができるので、gem 'sinatra'を追記</span></span><br><span class="line">$ bundle install --path vendor/bundle</span><br></pre></td></tr></table></figure><p>そして以下のようなファイル index.rb を作成し</p><figure class="highlight ruby"><table><tr><td class="code"><pre><span class="line"><span class="keyword">require</span> <span class="string">'sinatra'</span></span><br><span class="line"><span class="keyword">require</span> <span class="string">'json'</span></span><br><span class="line"></span><br><span class="line">get <span class="string">'/'</span> <span class="keyword">do</span></span><br><span class="line"> content_type <span class="symbol">:json</span></span><br><span class="line"> response = {</span><br><span class="line"> <span class="symbol">body:</span> <span class="string">'welcome to sinatra'</span>,</span><br><span class="line"> }</span><br><span class="line"> response.to_json</span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure><p>以下のコマンドで立ち上げれば、Sinatra 自体は立ち上がる(WEBrick で)</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ bundle <span class="built_in">exec</span> ruby index.rb</span><br><span class="line">WARNING: If you plan to load any of ActiveSupport<span class="string">'s core extensions to Hash, be</span></span><br><span class="line"><span class="string">sure to do so *before* loading Sinatra::Application or Sinatra::Base. If not,</span></span><br><span class="line"><span class="string">you may disregard this warning.</span></span><br><span class="line"><span class="string">[2018-11-29 10:41:57] INFO WEBrick 1.4.2</span></span><br><span class="line"><span class="string">[2018-11-29 10:41:57] INFO ruby 2.5.1 (2018-03-29) [x86_64-linux]</span></span><br><span class="line"><span class="string">== Sinatra (v2.0.4) has taken the stage on 4567 for development with backup from WEBrick</span></span><br><span class="line"><span class="string">[2018-11-29 10:41:57] INFO WEBrick::HTTPServer#start: pid=23960 port=4567</span></span><br></pre></td></tr></table></figure><p>Sinatra 公式でも最小の起動方法はこれであると説明されている。</p><p>では config.ru は何に必要なんだろう?</p><h1><span id="自動生成した-configru-を読む">自動生成した config.ru を読む</span></h1><p>Rails の場合 config.ru が自動生成されるので、それを見てみよう。</p><p>まずテスト用のディレクトリを作り、<code>bundle init</code>する。</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ mkdir rails-test</span><br><span class="line">$ <span class="built_in">cd</span> rails-test</span><br><span class="line">$ bundle init</span><br></pre></td></tr></table></figure><p>生成された Gemfile の rails のコメントを外し…</p><p><code>rails new</code>まで実行する。</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ bundle install --path vendor/bundle</span><br><span class="line">$ bundle <span class="built_in">exec</span> rails new .</span><br></pre></td></tr></table></figure><p>するとデフォルトのファイル群が生成され、以下の内容の config.ru を得ることができる。</p><figure class="highlight ruby"><table><tr><td class="code"><pre><span class="line"><span class="comment"># This file is used by Rack-based servers to start the application.</span></span><br><span class="line"></span><br><span class="line">require_relative <span class="string">'config/environment'</span></span><br><span class="line"></span><br><span class="line">run Rails.application</span><br></pre></td></tr></table></figure><p>このコメントには、「Rack ベースのサーバーでアプリケーションを開始する時に使う」と書いてある。</p><h1><span id="rack-とは">Rack とは</span></h1><p>Rack について「Rack は Web サーバを立ち上げるためのインターフェース」とよく説明されるが、全然ピンとこない。</p><ul><li><a href="https://qiita.com/k0kubun/items/248395f68164b52aec4a" target="_blank" rel="noopener">Rack とは何か - Qiita</a></li><li><a href="https://doruby.jp/users/bibio/entries/Rack__" target="_blank" rel="noopener">Rack の話 | 初心者向け | DoRuby</a></li></ul><p>上記サイトにちょっとピンと来る説明があった。以下引用</p><blockquote><p>Rack は、指定したファイルを独自の Ruby DSL として読み込み、DSL で指定した様々なミドルウェア、アプリケーションを組み合わせて Web サーバを立ち上げることができる rackup というコマンドを提供するライブラリである。</p></blockquote><p>この「指定したファイル」というのが config.ru だろう。config.ru を DSL として読み込み、Web サーバを立ち上げる。までが Rack がやってくれることっぽい。</p><p>まず config.ru は Rack のファイルだったんだな、そこから分かってなかったわ。</p><h2><span id="configru-は必要">config.ru は必要?</span></h2><p>また、興味深い記述もあった。またまた引用</p><blockquote><p>require “sinatra”すると at_exit で Sinatra::Application.run!がフックされる。<br>Rails::Server と違って Sinatra::Application は Rack::Server のサブクラスではないので、new の際に Rack::Builder DSL を呼んだり、run!の際に Rack::Handler を探したりする実装が含まれている。</p></blockquote><p>つまり一番始めに config.ru 無しで動かした Sinatra サンプルでも、<code>require "sinatra"</code>としているので裏では Rack が動き WEBrick が動いていたということだ。</p><p>じゃあ config.ru はいらないんじゃないの?</p><h2><span id="rack-ミドルウェアを使う場合">Rack ミドルウェアを使う場合</span></h2><p>Rack ミドルウェアを使う場合、config.ru に設定することで Sinatra でそれらを使うことができる。</p><p>Rack ミドルウェアとはどんなものがあるだろう。と思って調べてみたがイマイチ要領を得ない。多分何かの目的で使おうと思っていた gem が Rack ミドルウェアだった!ということはあっても、逆に Rack ミドルウェアを探したら該当の gem がヒットした!ということは無さそうだ。</p><p>しかし Sinatra にはロギング・デバッグ・ルーティング・セッション等の標準的なミドルウェアは組み込まれているという。</p><h2><span id="いつ使うのか">いつ使うのか</span></h2><p>Sinatra 公式に「config.ru はいつ使うのか?」とドンピシャのセクションがある。</p><ul><li><a href="http://sinatrarb.com/intro-ja.html" target="_blank" rel="noopener">Sinatra: README (Japanese)</a></li></ul><p>それによると異なる Rack ハンドラ(Unicorn など)を使った時・Sinatra::Base の複数のサブクラスを使う時・Sinatra をミドルウェアとして利用する時、これらの場合は config.ru を使うとしている。</p><p>つまり普通に小規模な Web アプリ(クラシックスタイル)で使う場合は、気にしなくていいんじゃないの?</p><h1><span id="まとめ">まとめ</span></h1><p>クラシックスタイルで Unicorn/Heroku を使わないなら config.ru は無くても良い。</p><p>今の所この認識で問題無さそうだ。しかしクラシックスタイル・モジュラースタイルや Rack ミドルウェアの理解がまだあやふやな部分はかなりある。今後使いながらこの辺も徐々に固めていきたい。</p><h1><span id="参考">参考</span></h1><ul><li><a href="http://sinatrarb.com/intro-ja.html" target="_blank" rel="noopener">Sinatra: README (Japanese)</a></li><li><a href="https://qiita.com/k0kubun/items/248395f68164b52aec4a" target="_blank" rel="noopener">Rack とは何か - Qiita</a></li><li><a href="https://doruby.jp/users/bibio/entries/Rack__" target="_blank" rel="noopener">Rack の話 | 初心者向け | DoRuby</a></li></ul>]]></content>
<summary type="html">
<p>Sinatra のチュートリアルやブログなど参照していると、手順として「<code>config.ru</code>というファイルを作れ」とあるが<br>何故必要なのか説明できていなかったので、改めて調べてみた。</p>
</summary>
<category term="Sinatra" scheme="https://t-kojima.github.io/categories/Sinatra/"/>
<category term="ruby" scheme="https://t-kojima.github.io/tags/ruby/"/>
<category term="sinatra" scheme="https://t-kojima.github.io/tags/sinatra/"/>
</entry>
<entry>
<title>[MySQL] Unable to load authentication plugin 'caching_sha2_password'</title>
<link href="https://t-kojima.github.io/2018/11/14/0049-docker-mysql-error-caching-sha2-password/"/>
<id>https://t-kojima.github.io/2018/11/14/0049-docker-mysql-error-caching-sha2-password/</id>
<published>2018-11-14T01:52:38.000Z</published>
<updated>2018-12-20T11:07:38.000Z</updated>
<content type="html"><![CDATA[<p>Docker で MySQL を起動して接続しようとしたら<code>Unable to load authentication plugin 'caching_sha2_password</code>というエラーに遭遇した。</p><a id="more"></a><h1><span id="動作環境">動作環境</span></h1><ul><li>Linux Mint 19</li><li>docker 18.06.0-ce</li><li>mysql 8.0.4</li></ul><h1><span id="現象">現象</span></h1><p>まずいつものように Docker で MySQL を起動する。</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ docker run --name mysql -e MYSQL_ROOT_PASSWORD=mysql -p 3306:3306 -d mysql</span><br></pre></td></tr></table></figure><p>もしくは既にコンテナ作成済みなら以下のように start させる。</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ docker start mysql</span><br></pre></td></tr></table></figure><p>すると Docker 上で MySQL が起動する。ここは問題ない。</p><p>その後 DB クライアント(今回は<a href="https://dbeaver.io/" target="_blank" rel="noopener">DBeaver</a>を使う)からアクセスしようとすると、以下のエラーになる。</p><p><img src="/images/49-01.png" alt="通信テストでエラー"></p><h1><span id="原因">原因</span></h1><p>まあ Docker が原因ではないと思っていたが、MySQL のバージョン 8 からは<a href="https://yoku0825.blogspot.com/2018/01/mysql-804.html" target="_blank" rel="noopener">デフォルトの認証方式が変わったらしい。</a></p><p>MySQL の設定(<code>mysql.userテーブルのpluginカラム</code>)を直せば良いようだが、Docker で MySQL コンテナを作成する度に設定を直すのは大変なので、簡単に回避できる策を探す。</p><p>……</p><p>と、スタックオーバーフローで<a href="https://stackoverflow.com/questions/49019652/not-able-to-connect-to-mysql-docker-from-local" target="_blank" rel="noopener">それっぽいのを発見した。</a></p><p>これによると<code>--default-authentication-plugin=mysql_native_password</code>オプションをつけて起動すると良いみたいだ。</p><p>一度コンテナを捨てて、下記コマンドで再作成してみる。</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">docker run --name mysql -e MYSQL_ROOT_PASSWORD=mysql -p 3306:3306 -d mysql --default-authentication-plugin=mysql_native_password</span><br></pre></td></tr></table></figure><p>そして再度アクセスすると…</p><p><img src="/images/49-02.png" alt="接続成功"></p><p>いけた!</p><p>いやあ、久しぶりに使おうとしたらアクセスできなかったので、焦ったぜ。</p><h1><span id="参考">参考</span></h1><ul><li><a href="https://yoku0825.blogspot.com/2018/01/mysql-804.html" target="_blank" rel="noopener">日々の覚書: MySQL 8.0.4におけるデフォルト認証形式の変更</a></li><li><a href="https://stackoverflow.com/questions/49019652/not-able-to-connect-to-mysql-docker-from-local" target="_blank" rel="noopener">not able to connect to mysql docker from local - Stack Overflow</a></li></ul>]]></content>
<summary type="html">
<p>Docker で MySQL を起動して接続しようとしたら<code>Unable to load authentication plugin &#39;caching_sha2_password</code>というエラーに遭遇した。</p>
</summary>
<category term="MySQL" scheme="https://t-kojima.github.io/categories/MySQL/"/>
<category term="mysql" scheme="https://t-kojima.github.io/tags/mysql/"/>
<category term="docker" scheme="https://t-kojima.github.io/tags/docker/"/>
</entry>
<entry>
<title>[Node] ExpressでAPIを作ったらログも吐く</title>
<link href="https://t-kojima.github.io/2018/11/05/0048-express-with-log4js/"/>
<id>https://t-kojima.github.io/2018/11/05/0048-express-with-log4js/</id>
<published>2018-11-05T02:45:55.000Z</published>
<updated>2018-12-20T11:07:38.000Z</updated>
<content type="html"><![CDATA[<p><a href="https://t-kojima.github.io/2018/11/02/0047-helloworld-express-with-json/">[Node] Express を使って最速で JSON を返す</a>の続き</p><p>API が動いたらログを吐かなきゃ(使命感)</p><a id="more"></a><h1><span id="サンプルリポジトリ">サンプルリポジトリ</span></h1><p><a href="https://github.com/t-kojima/example-express-api" class="embedly-card" data-card-image="0" data-card-controls="0" data-card-align="left" target="_blank" rel="noopener"></a></p><h1><span id="ログを出力する">ログを出力する</span></h1><p><a href="https://github.com/log4js-node/log4js-node" target="_blank" rel="noopener">log4js</a>というモジュールを使ってログを吐くようにする。</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">yarn add log4js</span><br></pre></td></tr></table></figure><p>まずは最小の構成でログを吐いてみる。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> express = <span class="built_in">require</span>(<span class="string">'express'</span>);</span><br><span class="line"><span class="keyword">const</span> app = express();</span><br><span class="line"><span class="keyword">const</span> log4js = <span class="built_in">require</span>(<span class="string">'log4js'</span>); <span class="comment">// 追加</span></span><br><span class="line"><span class="keyword">const</span> logger = log4js.getLogger(); <span class="comment">// 追加</span></span><br><span class="line"></span><br><span class="line">logger.level = <span class="string">'info'</span>; <span class="comment">// 追加</span></span><br><span class="line"></span><br><span class="line">app.get(<span class="string">'/'</span>, (req, res) => res.send(<span class="string">'hello world!'</span>));</span><br><span class="line"></span><br><span class="line">app.get(<span class="string">'/:name'</span>, (req, res) => {</span><br><span class="line"> res.header(<span class="string">'Content-Type'</span>, <span class="string">'application/json; charset=utf-8'</span>);</span><br><span class="line"> res.send({ <span class="attr">message</span>: <span class="string">`hello <span class="subst">${req.params.name}</span>`</span> });</span><br><span class="line"> logger.info(<span class="string">`hello <span class="subst">${req.params.name}</span>`</span>); <span class="comment">// 追加</span></span><br><span class="line">});</span><br><span class="line"></span><br><span class="line">app.listen(<span class="number">3000</span>, () => <span class="built_in">console</span>.log(<span class="string">'http://localhost:3000'</span>));</span><br></pre></td></tr></table></figure><p>これで<code>http://localhost:3000/fuga</code>にアクセスすると、以下のログがコンソールに表示される。</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">[2018-11-05T09:05:17.473] [INFO] default - hello fuga</span><br><span class="line">[2018-11-05T09:05:17.519] [INFO] default - hello favicon.ico</span><br></pre></td></tr></table></figure><p><code>favicon.ico</code>…?リクエスト毎に<code>http://localhost:3000/favicon.ico</code>は必ずリクエストが走るから、かな?</p><h2><span id="ファイルに出力">ファイルに出力</span></h2><p>コンソールに吐くだけだと<code>console.log</code>と変わらないので、ファイルに吐くようにしよう。</p><p><code>log4js.configure</code>で appender と category を設定し、<code>./app.log</code>に info ログを出力するようにする。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> express = <span class="built_in">require</span>(<span class="string">'express'</span>);</span><br><span class="line"><span class="keyword">const</span> app = express();</span><br><span class="line"><span class="keyword">const</span> log4js = <span class="built_in">require</span>(<span class="string">'log4js'</span>);</span><br><span class="line"></span><br><span class="line">log4js.configure({</span><br><span class="line"> appenders: {</span><br><span class="line"> app: {</span><br><span class="line"> type: <span class="string">'file'</span>,</span><br><span class="line"> filename: <span class="string">'./app.log'</span>,</span><br><span class="line"> },</span><br><span class="line"> },</span><br><span class="line"> categories: {</span><br><span class="line"> <span class="keyword">default</span>: { <span class="attr">appenders</span>: [<span class="string">'app'</span>], <span class="attr">level</span>: <span class="string">'info'</span> },</span><br><span class="line"> },</span><br><span class="line">});</span><br><span class="line"><span class="keyword">const</span> logger = log4js.getLogger();</span><br><span class="line"></span><br><span class="line">app.get(<span class="string">'/'</span>, (req, res) => res.send(<span class="string">'hello world!'</span>));</span><br><span class="line"></span><br><span class="line">app.get(<span class="string">'/:name'</span>, (req, res) => {</span><br><span class="line"> res.header(<span class="string">'Content-Type'</span>, <span class="string">'application/json; charset=utf-8'</span>);</span><br><span class="line"> res.send({ <span class="attr">message</span>: <span class="string">`hello <span class="subst">${req.params.name}</span>`</span> });</span><br><span class="line"> logger.info(<span class="string">`hello <span class="subst">${req.params.name}</span>`</span>);</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line">app.listen(<span class="number">3000</span>, () => <span class="built_in">console</span>.log(<span class="string">'http://localhost:3000'</span>));</span><br></pre></td></tr></table></figure><p><code>log4js.getLogger()</code>を呼んだとき、引数なしならば categories の default が呼ばれる。</p><p>また、categories で level も指定するようにしたので、<code>logger.level = 'info';</code>の行は不要になる。</p><p>これで実行し、アクセスすると。。。</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ node app</span><br><span class="line">http://localhost:3000</span><br><span class="line"></span><br><span class="line">$ curl http://localhost:3000/fuga</span><br></pre></td></tr></table></figure><p>app.log ファイルに以下のログが出力される。</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">[2018-11-05T10:30:34.829] [INFO] default - hello fuga</span><br><span class="line">[2018-11-05T10:30:34.866] [INFO] default - hello favicon.ico</span><br></pre></td></tr></table></figure><h2><span id="express-と連携">Express と連携</span></h2><p>イイ感じになってきたが、もうちょっと行ってみよう。Express が出力するログを log4js に連携させてみる。</p><p>完成形は以下の形</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> express = <span class="built_in">require</span>(<span class="string">'express'</span>);</span><br><span class="line"><span class="keyword">const</span> app = express();</span><br><span class="line"><span class="keyword">const</span> log4js = <span class="built_in">require</span>(<span class="string">'log4js'</span>);</span><br><span class="line"></span><br><span class="line">log4js.configure(<span class="string">'./log4js.json'</span>); <span class="comment">// 修正</span></span><br><span class="line"><span class="keyword">const</span> logger = log4js.getLogger();</span><br><span class="line"></span><br><span class="line">app.use(log4js.connectLogger(log4js.getLogger(<span class="string">'express'</span>))); <span class="comment">// 追加</span></span><br><span class="line"></span><br><span class="line">app.get(<span class="string">'/'</span>, (req, res) => res.send(<span class="string">'hello world!'</span>));</span><br><span class="line"></span><br><span class="line">app.get(<span class="string">'/:name'</span>, (req, res) => {</span><br><span class="line"> res.header(<span class="string">'Content-Type'</span>, <span class="string">'application/json; charset=utf-8'</span>);</span><br><span class="line"> res.send({ <span class="attr">message</span>: <span class="string">`hello <span class="subst">${req.params.name}</span>`</span> });</span><br><span class="line"> logger.info(<span class="string">`hello <span class="subst">${req.params.name}</span>`</span>);</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line">app.listen(<span class="number">3000</span>, () => <span class="built_in">console</span>.log(<span class="string">'http://localhost:3000'</span>));</span><br></pre></td></tr></table></figure><p>まず<code>log4js.configure</code>の設定だが内容が多くなってくると見通しが悪くなるので、設定を json を読み込む形にする。</p><p>以下の内容で log4js.json を用意して</p><figure class="highlight json"><table><tr><td class="code"><pre><span class="line">{</span><br><span class="line"> <span class="attr">"appenders"</span>: {</span><br><span class="line"> <span class="attr">"console"</span>: { <span class="attr">"type"</span>: <span class="string">"console"</span> },</span><br><span class="line"> <span class="attr">"app"</span>: { <span class="attr">"type"</span>: <span class="string">"file"</span>, <span class="attr">"filename"</span>: <span class="string">"app.log"</span> },</span><br><span class="line"> <span class="attr">"express"</span>: { <span class="attr">"type"</span>: <span class="string">"file"</span>, <span class="attr">"filename"</span>: <span class="string">"express.log"</span> }</span><br><span class="line"> },</span><br><span class="line"> <span class="attr">"categories"</span>: {</span><br><span class="line"> <span class="attr">"default"</span>: { <span class="attr">"appenders"</span>: [<span class="string">"console"</span>, <span class="string">"app"</span>], <span class="attr">"level"</span>: <span class="string">"debug"</span> },</span><br><span class="line"> <span class="attr">"express"</span>: { <span class="attr">"appenders"</span>: [<span class="string">"console"</span>, <span class="string">"express"</span>], <span class="attr">"level"</span>: <span class="string">"info"</span> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><code>log4js.configure('./log4js.json');</code>で読み込むようにする。そして express の出力を連携する為に、<code>app.use(log4js.connectLogger(log4js.getLogger('express')));</code>の一行を追加する。</p><p>ちなみに<code>"console": { "type": "console" }</code>という設定も追加し、default,express の両方の category の appenders に追加しているので、ファイル出力すると共にコンソールにも出力される。</p><p>これもまた実行すると。。。</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ node app</span><br><span class="line">http://localhost:3000</span><br><span class="line"></span><br><span class="line">$ curl http://localhost:3000</span><br><span class="line">[2018-11-05T10:37:39.798] [INFO] express - ::1 - - <span class="string">"GET / HTTP/1.1"</span> 304 - <span class="string">""</span> <span class="string">"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:62.0) Gecko/20100101 Firefox/62.0"</span></span><br><span class="line">[2018-11-05T10:37:40.026] [INFO] default - hello favicon.ico</span><br><span class="line">[2018-11-05T10:37:40.029] [INFO] express - ::1 - - <span class="string">"GET /favicon.ico HTTP/1.1"</span> 200 31 <span class="string">""</span> <span class="string">"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:62.0) Gecko/20100101 Firefox/62.0"</span></span><br></pre></td></tr></table></figure><p>このようにコンソールにも出力されて</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">[2018-11-05T10:37:39.798] [INFO] express - ::1 - - "GET / HTTP/1.1" 304 - "" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:62.0) Gecko/20100101 Firefox/62.0"</span><br><span class="line">[2018-11-05T10:37:40.029] [INFO] express - ::1 - - "GET /favicon.ico HTTP/1.1" 200 31 "" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:62.0) Gecko/20100101 Firefox/62.0"</span><br></pre></td></tr></table></figure><p>express.log としてログファイルにも出力される!</p><h1><span id="tips">Tips</span></h1><h2><span id="日付でログをローテーション">日付でログをローテーション</span></h2><p>実運用する場合、日付でログをローテーションする運用が非常に多いので、設定のやり方を残しておく。</p><figure class="highlight json"><table><tr><td class="code"><pre><span class="line">{</span><br><span class="line"> <span class="attr">"appenders"</span>: {</span><br><span class="line"> <span class="attr">"app"</span>: {</span><br><span class="line"> <span class="attr">"type"</span>: <span class="string">"dateFile"</span>,</span><br><span class="line"> <span class="attr">"filename"</span>: <span class="string">"app.log"</span></span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> <span class="attr">"categories"</span>: {</span><br><span class="line"> <span class="attr">"default"</span>: { <span class="attr">"appenders"</span>: [<span class="string">"app"</span>], <span class="attr">"level"</span>: <span class="string">"debug"</span> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>各パラメータの意味は以下の通り</p><table><thead><tr><th>パラメータ</th><th>必須</th><th>デフォルト値</th><th>備考</th></tr></thead><tbody><tr><td>type</td><td>〇</td><td>-</td><td><strong>dateFile</strong>にする。しないと日付にならない。</td></tr><tr><td>filename</td><td>〇</td><td>-</td><td>ベースとなるファイル名</td></tr><tr><td>pattern</td><td></td><td>.yyyy-MM-dd</td><td>ファイル名に付く日付フォーマット、省略するとデフォルト値になる。</td></tr><tr><td>encoding</td><td></td><td>utf-8</td><td>ファイルエンコーディング</td></tr><tr><td>mode</td><td></td><td>0644</td><td>ファイルモード</td></tr><tr><td>flags</td><td></td><td>a</td><td>恐らくファイルオープンのモード、”a”は”append”と思われる。。。</td></tr><tr><td>compress</td><td></td><td>false</td><td>ローテーションのタイミングで、<code>.gz</code>で圧縮する。</td></tr><tr><td>alwaysIncludePattern</td><td></td><td>false</td><td>ローテーション前のログファイルにも日付フォーマットを適用する。</td></tr><tr><td>daysToKeep</td><td></td><td>0</td><td>0 以上の場合、ローテーションのタイミングで(値)日以前のログファイルは削除する。</td></tr><tr><td>keepFileExt</td><td></td><td>false</td><td>ローテーションした時にファイル拡張子を保持する。<code>app.log.2018-11-02</code>ではなく<code>app.2018-11-02.log</code>になる。</td></tr></tbody></table><p><strong>alwaysIncludePattern</strong>の補足:false の場合ログは<code>app.log</code>(カレント)に書き込まれていき、ローテーションのタイミングで<code>app.log.yyyy-MM-dd</code>にバックアップされる。そしてカレントのログは<code>app.log</code>のままリフレッシュされ、当日分のログはまたここに書き込まれていく。true にすると最初から<code>app.log.yyyy-MM-dd</code>(カレント)としてログが作成され、ローテーションされると翌日の日付のファイルがカレントになり、書き込まれていく。</p><h1><span id="実行環境">実行環境</span></h1><ul><li>Node 8.11.3</li><li>Express 4.16.4</li><li>log4js 3.0.6</li></ul><h1><span id="参考">参考</span></h1><ul><li>[express というか node というか・・でログを取る(log4js 編) - Qiita]h(ttps://qiita.com/zaburo/items/eaac8df099455f7367f7)</li><li><a href="https://log4js-node.github.io/log4js-node/" target="_blank" rel="noopener">log4js-node by log4js-node</a></li><li><a href="https://log4js-node.github.io/log4js-node/dateFile" target="_blank" rel="noopener">Date Rolling File Appender - log4js-node</a></li></ul>]]></content>
<summary type="html">
<p><a href="https://t-kojima.github.io/2018/11/02/0047-helloworld-express-with-json/">[Node] Express を使って最速で JSON を返す</a>の続き</p>
<p>API が動いたらログを吐かなきゃ(使命感)</p>
</summary>
<category term="Node" scheme="https://t-kojima.github.io/categories/Node/"/>
<category term="Express" scheme="https://t-kojima.github.io/categories/Node/Express/"/>
<category term="nodejs" scheme="https://t-kojima.github.io/tags/nodejs/"/>
<category term="express" scheme="https://t-kojima.github.io/tags/express/"/>
<category term="log4js" scheme="https://t-kojima.github.io/tags/log4js/"/>
</entry>
<entry>
<title>[Node] Expressを使って最速でJSONを返す</title>
<link href="https://t-kojima.github.io/2018/11/02/0047-helloworld-express-with-json/"/>
<id>https://t-kojima.github.io/2018/11/02/0047-helloworld-express-with-json/</id>
<published>2018-11-02T08:13:38.000Z</published>
<updated>2018-12-20T11:07:38.000Z</updated>
<content type="html"><![CDATA[<p>皆さん WebAPI で JSON を返したいですよね?Node.js 環境では<a href="https://github.com/typicode/json-server" target="_blank" rel="noopener">json-server</a>とかもあるけれど、ベーシックに Express でやってみたい。</p><p>ちなみに最速はレスポンスではなくて作業量的な意味。</p><a id="more"></a><h1><span id="express-でハロワ">Express でハロワ</span></h1><p>とりあえずハロワ</p><p>yarn で express をインストールする。</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">yarn init -y</span><br><span class="line">yarn add express</span><br></pre></td></tr></table></figure><p>そしたらエントリポイントとなるファイルを作成する。とりあえず<code>app.js</code>としとく。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> express = <span class="built_in">require</span>(<span class="string">'express'</span>)</span><br><span class="line"><span class="keyword">const</span> app = express()</span><br><span class="line"></span><br><span class="line">app.get(<span class="string">'/'</span>, (req, res) => res.send(<span class="string">'hello world!'</span>))</span><br><span class="line"></span><br><span class="line">app.listen(<span class="number">3000</span>, () => <span class="built_in">console</span>.log(<span class="string">'http://localhost:3000'</span>))</span><br></pre></td></tr></table></figure><p>うーん、4 行!かんたん</p><p>最後の<code>console.log</code>は無くてもいいが、アドレスを入れておくと起動後に即開けるので楽ちん</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ node app</span><br><span class="line">http://localhost:3000</span><br></pre></td></tr></table></figure><p>実行してアクセスすると。。。</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ curl http://localhost:3000</span><br><span class="line">hello world!</span><br></pre></td></tr></table></figure><p>ハロワでた!</p><h1><span id="json-で返す">JSON で返す</span></h1><p>次にレスポンスが json で返るようにしよう。</p><p>やり方は<code>response</code>オブジェクト(下記では<code>res</code>)に<code>Content-Type</code>を設定すれば良い。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">app.get(<span class="string">'/'</span>, (req, res) => {</span><br><span class="line"> res.header(<span class="string">'Content-Type'</span>, <span class="string">'application/json; charset=utf-8'</span>)</span><br><span class="line"> res.send(<span class="string">'{ "message": "hello world!" }'</span>)</span><br><span class="line">})</span><br></pre></td></tr></table></figure><p>これも実行すると。。。</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ node app</span><br><span class="line">http://localhost:3000</span><br><span class="line"></span><br><span class="line">$ curl http://localhost:3000</span><br><span class="line">{ <span class="string">"message"</span>: <span class="string">"hello world!"</span> }</span><br></pre></td></tr></table></figure><p>json キタ!</p><h1><span id="uri-にパラメータをつける">URI にパラメータをつける</span></h1><p>パラメータありのリクエストにも対応しよう</p><p><code>http://localhost:3000/<name></code>として name に入れた名前で<code>hello <name>!</code>と返るようにしてみる。</p><p>以下のコードを追加する。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">app.get(<span class="string">'/:name'</span>, (req, res) => {</span><br><span class="line"> res.header(<span class="string">'Content-Type'</span>, <span class="string">'application/json; charset=utf-8'</span>)</span><br><span class="line"> res.send({ <span class="attr">message</span>: <span class="string">`hello <span class="subst">${req.params.name}</span>`</span> })</span><br><span class="line">})</span><br></pre></td></tr></table></figure><p>get の URI 指定に<code>:<パラメータ></code>と記述することでパラメータと認識され、<code>req.params.<パラメータ></code>で取り出すことができる。</p><p>また、<code>res.send</code>にはオブジェクトをそのままぶち込める。これでもレスポンスは JSON にパースしてくれるので便利だ。</p><p>これを実行すると。。。</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ node app</span><br><span class="line">http://localhost:3000</span><br><span class="line"></span><br><span class="line">$ curl http://localhost:3000/hoge</span><br><span class="line">{ <span class="string">"message"</span>: <span class="string">"hello hoge"</span> }</span><br></pre></td></tr></table></figure><p>イケるね!</p><h1><span id="参考">参考</span></h1><ul><li><a href="https://qiita.com/tamura_CD/items/e3abdab9b8c5aa35fa6b" target="_blank" rel="noopener">Node.js + Express で REST API 開発を体験しよう[作成編] - Qiita</a></li></ul><h1><span id="実行環境">実行環境</span></h1><ul><li>Node 8.11.3</li><li>Express 4.16.4</li></ul>]]></content>
<summary type="html">
<p>皆さん WebAPI で JSON を返したいですよね?Node.js 環境では<a href="https://github.com/typicode/json-server" target="_blank" rel="noopener">json-server</a>とかもあるけれど、ベーシックに Express でやってみたい。</p>
<p>ちなみに最速はレスポンスではなくて作業量的な意味。</p>
</summary>
<category term="Node" scheme="https://t-kojima.github.io/categories/Node/"/>
<category term="Express" scheme="https://t-kojima.github.io/categories/Node/Express/"/>
<category term="nodejs" scheme="https://t-kojima.github.io/tags/nodejs/"/>
<category term="express" scheme="https://t-kojima.github.io/tags/express/"/>
</entry>
<entry>
<title>[Hexo] ソーシャルボタンを常に表示</title>
<link href="https://t-kojima.github.io/2018/10/17/0046-social-button-always-visible/"/>
<id>https://t-kojima.github.io/2018/10/17/0046-social-button-always-visible/</id>
<published>2018-10-17T02:07:07.000Z</published>
<updated>2018-12-20T11:07:38.000Z</updated>
<content type="html"><![CDATA[<p>このブログでは Hexo の <a href="https://github.com/hexojs/hexo-theme-landscape" target="_blank" rel="noopener">Landscape</a> テーマを使っている。デフォルトのやつだ。</p><p>エントリの右下に「共有」リンクがあり、それをクリックすることでソーシャルボタンがポップアップするのだが、なんだか分かり辛いような気がしたので常に表示するように変更した。</p><a id="more"></a><p>今の表示はこんな感じ</p><p><img src="/images/46-03.png" alt="わかりづらい"></p><h1><span id="ソーシャルボタンの常時表示">ソーシャルボタンの常時表示</span></h1><p>まずソーシャルボタンの表示/非表示を切り替えている個所を探す。</p><p>ソースを読み進めていくと、<a href="https://github.com/hexojs/hexo-theme-landscape/blob/master/layout/_partial/article.ejs#L25" target="_blank" rel="noopener">themes/landscape/layout/_partial/article.ejs</a>と<a href="https://github.com/hexojs/hexo-theme-landscape/blob/master/source/js/script.js#L34" target="_blank" rel="noopener">themes/landscape/source/js/script.js</a>辺りのようだ。</p><p>article.ejs</p><figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag"><<span class="name">footer</span> <span class="attr">class</span>=<span class="string">"article-footer"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">a</span> <span class="attr">data-url</span>=<span class="string">"<%- post.permalink %>"</span> <span class="attr">data-id</span>=<span class="string">"<%= post._id %>"</span> <span class="attr">class</span>=<span class="string">"article-share-link"</span>></span><span class="tag"><<span class="name">%=</span> <span class="attr">__</span>('<span class="attr">share</span>') %></span><span class="tag"></<span class="name">a</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">%</span> <span class="attr">if</span> (<span class="attr">post.comments</span> && <span class="attr">config.disqus_shortname</span>){ %></span></span><br><span class="line"> <span class="tag"><<span class="name">a</span> <span class="attr">href</span>=<span class="string">"<%- post.permalink %>#disqus_thread"</span> <span class="attr">class</span>=<span class="string">"article-comment-link"</span>></span><span class="tag"><<span class="name">%=</span> <span class="attr">__</span>('<span class="attr">comment</span>') %></span><span class="tag"></<span class="name">a</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">%</span> } %></span></span><br><span class="line"> <span class="tag"><<span class="name">%-</span> <span class="attr">partial</span>('<span class="attr">post</span>/<span class="attr">tag</span>') %></span></span><br><span class="line"><span class="tag"></<span class="name">footer</span>></span></span><br></pre></td></tr></table></figure><p>script.js</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> html = [</span><br><span class="line"> <span class="string">'<div id="'</span> + id + <span class="string">'" class="article-share-box">'</span>,</span><br><span class="line"> <span class="string">'<input class="article-share-input" value="'</span> + url + <span class="string">'">'</span>,</span><br><span class="line"> <span class="string">'<div class="article-share-links">'</span>,</span><br><span class="line"> <span class="string">'<a href="https://twitter.com/intent/tweet?url='</span> +</span><br><span class="line"> encodedUrl +</span><br><span class="line"> <span class="string">'" class="article-share-twitter" target="_blank" title="Twitter"></a>'</span>,</span><br><span class="line"> <span class="string">'<a href="https://www.facebook.com/sharer.php?u='</span> +</span><br><span class="line"> encodedUrl +</span><br><span class="line"> <span class="string">'" class="article-share-facebook" target="_blank" title="Facebook"></a>'</span>,</span><br><span class="line"> <span class="string">'<a href="http://pinterest.com/pin/create/button/?url='</span> +</span><br><span class="line"> encodedUrl +</span><br><span class="line"> <span class="string">'" class="article-share-pinterest" target="_blank" title="Pinterest"></a>'</span>,</span><br><span class="line"> <span class="string">'<a href="https://plus.google.com/share?url='</span> +</span><br><span class="line"> encodedUrl +</span><br><span class="line"> <span class="string">'" class="article-share-google" target="_blank" title="Google+"></a>'</span>,</span><br><span class="line"> <span class="string">'<a href="https://www.linkedin.com/shareArticle?mini=true&url='</span> +</span><br><span class="line"> encodedUrl +</span><br><span class="line"> <span class="string">'" class="article-share-linkedin" target="_blank" title="LinkedIn"></a>'</span>,</span><br><span class="line"> <span class="string">'</div>'</span>,</span><br><span class="line"> <span class="string">'</div>'</span>,</span><br><span class="line">].join(<span class="string">''</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> box = $(html);</span><br><span class="line"></span><br><span class="line">$(<span class="string">'body'</span>).append(box);</span><br></pre></td></tr></table></figure><p>雑に説明すると<code>.article-share-link</code>がクリックされた時、リンクの一覧を組み立てて body に追加している。</p><p>ということはここからクリックイベントを抜いてしまえばいい。というわけで以下のコードを同じ場所に追加する。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> $<span class="keyword">this</span> = $(<span class="string">'.article-share-items'</span>),</span><br><span class="line"> url = $<span class="keyword">this</span>.attr(<span class="string">'data-url'</span>),</span><br><span class="line"> encodedUrl = <span class="built_in">encodeURIComponent</span>(url),</span><br><span class="line"> id = <span class="string">'article-share-items-'</span> + $<span class="keyword">this</span>.attr(<span class="string">'data-id'</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> html = [</span><br><span class="line"> <span class="string">'<div id="'</span> + id + <span class="string">'" class="article-share-links">'</span>,</span><br><span class="line"> <span class="string">'<a href="https://twitter.com/intent/tweet?url='</span> +</span><br><span class="line"> encodedUrl +</span><br><span class="line"> <span class="string">'" class="article-share-twitter" target="_blank" title="Twitter"></a>'</span>,</span><br><span class="line"> <span class="string">'<a href="https://www.facebook.com/sharer.php?u='</span> +</span><br><span class="line"> encodedUrl +</span><br><span class="line"> <span class="string">'" class="article-share-facebook" target="_blank" title="Facebook"></a>'</span>,</span><br><span class="line"> <span class="string">'<a href="http://pinterest.com/pin/create/button/?url='</span> +</span><br><span class="line"> encodedUrl +</span><br><span class="line"> <span class="string">'" class="article-share-pinterest" target="_blank" title="Pinterest"></a>'</span>,</span><br><span class="line"> <span class="string">'<a href="https://plus.google.com/share?url='</span> +</span><br><span class="line"> encodedUrl +</span><br><span class="line"> <span class="string">'" class="article-share-google" target="_blank" title="Google+"></a>'</span>,</span><br><span class="line"> <span class="string">'</div>'</span>,</span><br><span class="line"> ].join(<span class="string">''</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> box = $(html);</span><br><span class="line"> $(<span class="string">'footer'</span>).append(box);</span><br><span class="line">})();</span><br></pre></td></tr></table></figure><p>article.ejs も以下のように変更する。</p><figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag"><<span class="name">footer</span> <span class="attr">class</span>=<span class="string">"article-footer"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">data-url</span>=<span class="string">"<%- post.permalink %>"</span> <span class="attr">data-id</span>=<span class="string">"<%= post._id %>"</span> <span class="attr">class</span>=<span class="string">"article-share-items"</span>></span><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="comment"><!-- <a data-url="<%- post.permalink %>" data-id="<%= post._id %>" class="article-share-link"><%= __('share') %></a> --></span></span><br><span class="line"> <span class="tag"><<span class="name">%</span> <span class="attr">if</span> (<span class="attr">post.comments</span> && <span class="attr">config.disqus_shortname</span>){ %></span></span><br><span class="line"> <span class="tag"><<span class="name">a</span> <span class="attr">href</span>=<span class="string">"<%- post.permalink %>#disqus_thread"</span> <span class="attr">class</span>=<span class="string">"article-comment-link"</span>></span><span class="tag"><<span class="name">%=</span> <span class="attr">__</span>('<span class="attr">comment</span>') %></span><span class="tag"></<span class="name">a</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">%</span> } %></span></span><br><span class="line"> <span class="tag"><<span class="name">%-</span> <span class="attr">partial</span>('<span class="attr">post</span>/<span class="attr">tag</span>') %></span></span><br><span class="line"><span class="tag"></<span class="name">footer</span>></span></span><br></pre></td></tr></table></figure><p>もともとあった a タグ(共有リンク)をコメントアウトし、その前に同じ属性を持った div タグを追加している。これは<code>data-url</code>と<code>data-id</code>を script.js に引き継ぐためだ。</p><p>酷く不格好な形だがしょうがない。僕は jQuery が得意では無いし、あまり時間も掛けたくない。。。!</p><h2><span id="css-も直す">css も直す</span></h2><p>このままだとボタン群が左に寄せられてしまうので、以下のように右寄せにする。</p><p>themes/landscape/source/css/_partial/article.styl</p><figure class="highlight stylus"><table><tr><td class="code"><pre><span class="line">.article-share-links</span><br><span class="line"> <span class="comment">// clearfix()</span></span><br><span class="line"> <span class="comment">// background: color-background</span></span><br><span class="line"> <span class="attribute">float</span>: right</span><br><span class="line"> <span class="attribute">margin-left</span>: <span class="number">20px</span></span><br></pre></td></tr></table></figure><p>もう一つ、footer にボタンを表示するようにすると、<code>.article-footer</code>の css の影響を受けてしまい、ボタンを hover してもアイコンの色が変わらない。</p><p>その為、以下のように<code>.article-share-link</code>の color を<code>!important</code>することで優先させる。</p><figure class="highlight stylus"><table><tr><td class="code"><pre><span class="line"><span class="variable">$article</span>-share-link</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">50px</span></span><br><span class="line"> <span class="attribute">height</span>: <span class="number">36px</span></span><br><span class="line"> <span class="attribute">display</span>: block</span><br><span class="line"> <span class="attribute">float</span>: left</span><br><span class="line"> <span class="attribute">position</span>: relative</span><br><span class="line"> <span class="attribute">color</span>: <span class="number">#999</span></span><br><span class="line"> <span class="attribute">text-shadow</span>: <span class="number">0</span> <span class="number">1px</span> <span class="number">#fff</span></span><br><span class="line"> &:before</span><br><span class="line"> <span class="attribute">font-size</span>: <span class="number">20px</span></span><br><span class="line"> <span class="attribute">font-family</span>: font-icon</span><br><span class="line"> absolute-center(@<span class="attribute">font-size</span>)</span><br><span class="line"> <span class="attribute">text-align</span>: center</span><br><span class="line"> &:hover</span><br><span class="line"> <span class="comment">// color: #fff</span></span><br><span class="line"> <span class="attribute">color</span>: <span class="number">#fff</span> !important</span><br></pre></td></tr></table></figure><p>ここまででボタンが常に表示されるようになった。</p><p><img src="/images/46-01.png" alt="ボタンを常に表示"></p><h1><span id="はてなブックマークボタンを追加">はてなブックマークボタンを追加</span></h1><p>続いてはてなブックマークボタンを追加してみたい。何故なら僕が他人のブログを見る時、一番よく使うボタンだからだ。</p><h2><span id="font-awesome-にアイコンが無い問題">font-awesome にアイコンが無い問題</span></h2><p>twitter だとか facebook だとか、これらのアイコンは font-awesome のものを使っている。css(stylus)の指定はこんな感じになっている。</p><p>themes\landscape\source\css_partial\article.styl</p><figure class="highlight stylus"><table><tr><td class="code"><pre><span class="line">.article-share-twitter</span><br><span class="line"> @extend <span class="variable">$article</span>-share-link</span><br><span class="line"> &:before</span><br><span class="line"> <span class="attribute">content</span>: <span class="string">"\f099"</span> // <- ココ</span><br><span class="line"> &:hover</span><br><span class="line"> <span class="attribute">background</span>: color-twitter</span><br><span class="line"> <span class="attribute">text-shadow</span>: <span class="number">0</span> <span class="number">1px</span> darken(color-twitter, <span class="number">20%</span>)</span><br></pre></td></tr></table></figure><p>しかし font-awesome にはてなブックマークのアイコンは無いので、何とかしなければならない…</p><p>で、色々探していたら<a href="https://hayashikejinan.com/webwork/css/913/" target="_blank" rel="noopener">普通のフォントで代用できるんじゃないの?</a>というサイトがあった。これは良さそう!</p><p>上記サイトを参考に、以下の定義を追加する。</p><figure class="highlight stylus"><table><tr><td class="code"><pre><span class="line">.article-share-hatebu</span><br><span class="line"> @extend <span class="variable">$article</span>-share-link</span><br><span class="line"> &:before</span><br><span class="line"> <span class="attribute">content</span>: <span class="string">"B!"</span></span><br><span class="line"> <span class="attribute">font-family</span>: Verdana</span><br><span class="line"> <span class="attribute">font-weight</span>: bold</span><br><span class="line"> &:hover</span><br><span class="line"> <span class="attribute">background</span>: color-hatebu</span><br><span class="line"> <span class="attribute">text-shadow</span>: <span class="number">0</span> <span class="number">1px</span> darken(color-hatebu, <span class="number">20%</span>)</span><br></pre></td></tr></table></figure><p>はてぶカラーも追加</p><p>themes\landscape\source\css_variables.styl</p><figure class="highlight stylus"><table><tr><td class="code"><pre><span class="line"><span class="comment">// Colors</span></span><br><span class="line"><span class="attribute">color</span>-default = <span class="number">#555</span></span><br><span class="line"><span class="attribute">color</span>-grey = <span class="number">#999</span></span><br><span class="line"><span class="attribute">color</span>-border = <span class="number">#ddd</span></span><br><span class="line"><span class="attribute">color</span>-link = <span class="number">#258fb8</span></span><br><span class="line"><span class="attribute">color</span>-background = <span class="number">#eee</span></span><br><span class="line"><span class="attribute">color</span>-sidebar-text = <span class="number">#777</span></span><br><span class="line"><span class="attribute">color</span>-widget-background = <span class="number">#ddd</span></span><br><span class="line"><span class="attribute">color</span>-widget-border = <span class="number">#ccc</span></span><br><span class="line"><span class="attribute">color</span>-footer-background = <span class="number">#262a30</span></span><br><span class="line"><span class="attribute">color</span>-mobile-nav-background = <span class="number">#191919</span></span><br><span class="line"><span class="attribute">color</span>-twitter = <span class="number">#00aced</span></span><br><span class="line"><span class="attribute">color</span>-facebook = <span class="number">#3b5998</span></span><br><span class="line"><span class="attribute">color</span>-pinterest = <span class="number">#cb2027</span></span><br><span class="line"><span class="attribute">color</span>-google = <span class="number">#dd4b39</span></span><br><span class="line"><span class="attribute">color</span>-hatebu = <span class="number">#00a4de</span> // <- 追加</span><br></pre></td></tr></table></figure><p>最後に、script.js にリンクを追加して完了</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> html = [</span><br><span class="line"> <span class="string">'<div id="'</span> + id + <span class="string">'" class="article-share-links">'</span>,</span><br><span class="line"> <span class="string">'<a href="https://twitter.com/intent/tweet?url='</span> +</span><br><span class="line"> encodedUrl +</span><br><span class="line"> <span class="string">'" class="article-share-twitter" target="_blank" title="Twitter"></a>'</span>,</span><br><span class="line"> <span class="string">'<a href="https://www.facebook.com/sharer.php?u='</span> +</span><br><span class="line"> encodedUrl +</span><br><span class="line"> <span class="string">'" class="article-share-facebook" target="_blank" title="Facebook"></a>'</span>,</span><br><span class="line"> <span class="string">'<a href="http://pinterest.com/pin/create/button/?url='</span> +</span><br><span class="line"> encodedUrl +</span><br><span class="line"> <span class="string">'" class="article-share-pinterest" target="_blank" title="Pinterest"></a>'</span>,</span><br><span class="line"> <span class="string">'<a href="https://plus.google.com/share?url='</span> +</span><br><span class="line"> encodedUrl +</span><br><span class="line"> <span class="string">'" class="article-share-google" target="_blank" title="Google+"></a>'</span>,</span><br><span class="line"> <span class="string">'<a href="http://b.hatena.ne.jp/entry/'</span> +</span><br><span class="line"> encodedUrl +</span><br><span class="line"> <span class="string">'" class="article-share-hatebu" target="_blank" title="Hatena Bookmark"></a>'</span>, <span class="comment">// <- 追加</span></span><br><span class="line"> <span class="string">'</div>'</span>,</span><br><span class="line">].join(<span class="string">''</span>);</span><br></pre></td></tr></table></figure><p>はてなブックマークボタンが追加された!</p><p><img src="/images/46-02.png" alt="はてなブックマークボタンの追加"></p><p>めでたしめでたし</p><h1><span id="参考">参考</span></h1><ul><li><a href="https://hayashikejinan.com/webwork/css/913/" target="_blank" rel="noopener">Font Awesome などアイコンフォントにないはてなブックマークを自力で追加する簡単な方法</a></li></ul>]]></content>
<summary type="html">
<p>このブログでは Hexo の <a href="https://github.com/hexojs/hexo-theme-landscape" target="_blank" rel="noopener">Landscape</a> テーマを使っている。デフォルトのやつだ。</p>
<p>エントリの右下に「共有」リンクがあり、それをクリックすることでソーシャルボタンがポップアップするのだが、なんだか分かり辛いような気がしたので常に表示するように変更した。</p>
</summary>
<category term="hexo" scheme="https://t-kojima.github.io/tags/hexo/"/>
</entry>
<entry>
<title>[JavaScript] Arrayのメソッドはまだまだ貧弱問題</title>
<link href="https://t-kojima.github.io/2018/10/17/0045-js-array-is-still-a-weak-problem/"/>
<id>https://t-kojima.github.io/2018/10/17/0045-js-array-is-still-a-weak-problem/</id>
<published>2018-10-17T01:43:31.000Z</published>
<updated>2018-12-20T11:07:38.000Z</updated>
<content type="html"><![CDATA[<p>JavaScript で配列を操作する機会は多いけど、目的にドンピシャなメソッドが無いことが多々ある。</p><p>今回は以下のことがやりたい。</p><ul><li>インデックスを指定して要素を追加</li><li>インデックスを指定して要素を削除</li><li>インデックスを指定して要素の書き換え</li></ul><a id="more"></a><h1><span id="insertdelete-ないの">Insert/Delete ないの?</span></h1><p>例えば Ruby だと<a href="https://docs.ruby-lang.org/ja/latest/method/Array/i/insert.html" target="_blank" rel="noopener">Array#insert</a>や<a href="https://docs.ruby-lang.org/ja/latest/method/Array/i/delete_at.html" target="_blank" rel="noopener">Array#delete_at</a>のようなメソッドが今回は欲しい。(これも破壊的なメソッドなのでドンピシャではないけど)</p><p>しかし JavaScript の Array に insert/delete はなく、「インデックスを指定して要素を追加」できそうなメソッドは<code>Array#splice</code>しかなさそうだ。</p><h2><span id="arraysplice-ではダメなのか">Array#splice ではダメなのか</span></h2><p>「要素の追加」と「要素の削除(取り出し)」を一度にやろうとしているメソッドなので、あまり好きではない。</p><p>一見して何をしているのか分かり辛いのもポイント低い、これ瞬時にどうなるか分かります?</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> array = [<span class="number">0</span>, <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>];</span><br><span class="line"><span class="keyword">const</span> p = array.splice(<span class="number">1</span>, <span class="number">2</span>, <span class="number">0</span>);</span><br></pre></td></tr></table></figure><p>正解は以下のようになる。…分かり辛い。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">array => [<span class="number">0</span>, <span class="number">0</span>, <span class="number">3</span>, <span class="number">4</span>];</span><br><span class="line">p => [<span class="number">1</span>, <span class="number">2</span>];</span><br></pre></td></tr></table></figure><p>ちなみに「インデックスを指定して要素を追加」と「インデックスを指定して要素を削除」ならこうなる。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// insert</span></span><br><span class="line">array.splice(index, <span class="number">0</span>, <span class="string">'value'</span>);</span><br><span class="line"><span class="comment">// delete</span></span><br><span class="line">array.splice(index, <span class="number">1</span>);</span><br></pre></td></tr></table></figure><p>慣れなのかもしれないけど、上が insert で下が delete だと瞬時に判断は難しいと感じる。</p><p>そもそも破壊的なメソッドなので、あまり良くは無いです。はい。</p><h2><span id="lodash-なら">lodash なら…?</span></h2><p>ネイティブ Array にないなら lodash だ!</p><p>と、思ったけど lodash にも insert/delete 相当の関数は無いみたいだ。<a href="https://github.com/lodash/lodash/issues/89" target="_blank" rel="noopener">過去に機能要求はあったよう</a>だが、reject されている。</p><p>しかし後半に<code>Array#slice</code>を利用した代替案が提示されている。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> newArray = [...array.slice(<span class="number">0</span>, index), <span class="string">'value'</span>, ...arr.slice(index)];</span><br></pre></td></tr></table></figure><p><code>Array#splice</code>より分かりやすいか?と言えば微妙な感じしかないが、非破壊的な insert/delete としてはこれくらいしか無いのかなとも思う。</p><p>ちなみに以下のようになる。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// insert</span></span><br><span class="line"><span class="keyword">const</span> insertedArray = [</span><br><span class="line"> ...array.slice(<span class="number">0</span>, index),</span><br><span class="line"> <span class="string">'value'</span>,</span><br><span class="line"> ...array.slice(index),</span><br><span class="line">];</span><br><span class="line"><span class="comment">// delete</span></span><br><span class="line"><span class="keyword">const</span> deletedArray = [...array.slice(<span class="number">0</span>, index), ...array.slice(index + <span class="number">1</span>)];</span><br></pre></td></tr></table></figure><h1><span id="インデックスを指定して要素の書き換え">インデックスを指定して要素の書き換え</span></h1><p><code>[0, 1, 2, 3, 4]</code>に対して、<code>2</code>を<code>hoge</code>に書き換えたい…としよう。</p><p>単純に考えると以下のようになる。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> array = [<span class="number">0</span>, <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>];</span><br><span class="line">array[<span class="number">2</span>] = <span class="string">'hoge'</span>;</span><br></pre></td></tr></table></figure><p>しかし要素を書き換えてしまっているので、イミュータブルにやってみよう。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> array = [<span class="number">0</span>, <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>];</span><br><span class="line"><span class="comment">// update</span></span><br><span class="line"><span class="keyword">const</span> updatedArray = [</span><br><span class="line"> ...array.slice(<span class="number">0</span>, index),</span><br><span class="line"> <span class="string">'hoge'</span>,</span><br><span class="line"> ...array.slice(index + <span class="number">1</span>),</span><br><span class="line">];</span><br></pre></td></tr></table></figure><p>前述の insert/delete と仕組みは同じだ。やっぱり冗長な感じはするけど…もっといい方法ないだろうか。</p><h1><span id="まとめ">まとめ</span></h1><p>思っていたよりもスッキリ書けなかったので若干モヤモヤが残るが、一応目的は達成できた。言語ごとの思想の違いではあると思うけど、もっと便利なメソッドがネイティブに追加されればいいなと思う。</p><p>あと「イミュータブルにする」ということについて、有意性は理解しているがそれを上手く言語化できないことに気付いたので、別途エントリにしたいなと思う。</p><h1><span id="環境">環境</span></h1><ul><li>Windows 10</li><li>Node v8.11.3</li></ul>]]></content>
<summary type="html">
<p>JavaScript で配列を操作する機会は多いけど、目的にドンピシャなメソッドが無いことが多々ある。</p>
<p>今回は以下のことがやりたい。</p>
<ul>
<li>インデックスを指定して要素を追加</li>
<li>インデックスを指定して要素を削除</li>
<li>インデックスを指定して要素の書き換え</li>
</ul>
</summary>
<category term="javascript" scheme="https://t-kojima.github.io/tags/javascript/"/>
</entry>
<entry>
<title>[WPF] Bindingに匿名クラスを使う</title>
<link href="https://t-kojima.github.io/2018/10/04/0044-wpf-anonymous-binding/"/>
<id>https://t-kojima.github.io/2018/10/04/0044-wpf-anonymous-binding/</id>
<published>2018-10-04T01:09:53.000Z</published>
<updated>2018-12-20T11:07:38.000Z</updated>
<content type="html"><![CDATA[<p>WPF の双方向バインディング、便利ですよね。ただ実際使っていると双方向が必要なケースってあまりなくて、大部分は単一方向で済む場合が多いはず。</p><p>この時プロパティには<code>getter</code>だけあればいいんだけど、匿名クラスを返せると表現力が上がるね!という話。</p><a id="more"></a><h1><span id="基本的なバインディングの使い方">基本的なバインディングの使い方</span></h1><p>ほんとにベーシックかどうかはわからないけど、僕は基本だと思っている使い方。</p><p>TextBlock にファイルの名前を表示させる。</p><h2><span id="双方向バインディングで表示">双方向バインディングで表示</span></h2><p>まずは双方向バインディングで表示する例</p><h4><span id="mainwindowxaml">MainWindow.xaml</span></h4><figure class="highlight xml"><table><tr><td class="code"><pre><span class="line"><span class="tag"><<span class="name">TextBlock</span> <span class="attr">Text</span>=<span class="string">"{Binding File.Name}"</span>></span></span><br></pre></td></tr></table></figure><h4><span id="viewmodelcs">ViewModel.cs</span></h4><figure class="highlight cs"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">ViewModel</span> : <span class="title">INotifyPropertyChanged</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">event</span> PropertyChangedEventHandler PropertyChanged;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// FileにFileInfoを詰めるなんらかの処理</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> FileInfo file;</span><br><span class="line"> <span class="keyword">public</span> FileInfo File {</span><br><span class="line"> <span class="keyword">get</span> { <span class="keyword">return</span> file; }</span><br><span class="line"> <span class="keyword">set</span> {</span><br><span class="line"> <span class="keyword">if</span> (file == <span class="keyword">value</span>)</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> file = <span class="keyword">value</span>;</span><br><span class="line"> PropertyChanged?.Invoke(<span class="keyword">this</span>, <span class="keyword">new</span> PropertyChangedEventArgs(<span class="string">"File"</span>));</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>特に問題ないと思うが、<code>File</code> が Public である必要がある。時には private な <code>file</code> しかない場合で、<code>file.Name</code> を表示したい場合もあるだろう。そういう場合は次のように getter のみを作って公開する。</p><h2><span id="単一方向バインディング">単一方向バインディング</span></h2><h4><span id="mainwindowxaml">MainWindow.xaml</span></h4><figure class="highlight xml"><table><tr><td class="code"><pre><span class="line"><span class="tag"><<span class="name">TextBlock</span> <span class="attr">Text</span>=<span class="string">"{Binding File.Name}"</span>></span></span><br></pre></td></tr></table></figure><h4><span id="viewmodelcs">ViewModel.cs</span></h4><figure class="highlight cs"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">ViewModel</span> : <span class="title">INotifyPropertyChanged</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">event</span> PropertyChangedEventHandler PropertyChanged;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// fileにFileInfoを詰めるなんらかの処理</span></span><br><span class="line"> <span class="comment">// 同時に PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("File"));を呼ぶ必要がある。</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> FileInfo file;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">string</span> File {</span><br><span class="line"> <span class="keyword">get</span> { <span class="keyword">return</span> file; }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>上記のように File プロパティとして getter のみを実装し、<code>file</code>を返している。表示のみの場合はこれで十分だが、<code>file</code>に FileInfo を設定する際、<code>PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("File"));</code>を実行する必要はある。(しないと View に変更が通知されない)</p><h2><span id="匿名クラスを返す">匿名クラスを返す</span></h2><p>前述の単一方向バインディングの例だが、匿名クラスを返すことでも実装することができる。</p><h4><span id="mainwindowxaml">MainWindow.xaml</span></h4><figure class="highlight xml"><table><tr><td class="code"><pre><span class="line"><span class="tag"><<span class="name">TextBlock</span> <span class="attr">Text</span>=<span class="string">"{Binding File.Name}"</span>></span></span><br></pre></td></tr></table></figure><h4><span id="viewmodelcs">ViewModel.cs</span></h4><figure class="highlight cs"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">ViewModel</span> : <span class="title">INotifyPropertyChanged</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">event</span> PropertyChangedEventHandler PropertyChanged;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// fileにFileInfoを詰めるなんらかの処理</span></span><br><span class="line"> <span class="comment">// 同時に PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("File"));を呼ぶ必要がある。</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> FileInfo file;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">object</span> File {</span><br><span class="line"> <span class="keyword">get</span> { <span class="keyword">return</span> <span class="keyword">new</span> { Name = file.Name }; }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>ポイントはプロパティの型を object とする点と、文字通り匿名クラスを返す点だ。</p><p>こうすることで、バインディング対象が FileInfo のインスタンスから、<code>Name</code>プロパティのみを持つ匿名クラスのインスタンスに変わった。</p><p>これだけだと特になんてことない例になってしまうので、具体的な使い方を見てみたい。</p><h1><span id="表示内容を動的に組み立てる例">表示内容を動的に組み立てる例</span></h1><p>とあるディレクトリに、TXT ファイルと JPEG ファイルがいくつか入っているとしよう。</p><p>まずは FileInfo のリストとして取得する。</p><figure class="highlight cs"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> fileInfoList = <span class="keyword">new</span> DirectoryInfo(<span class="string">"<とあるディレクトリのパス>"</span>).GetFiles();</span><br></pre></td></tr></table></figure><p>さて、このリストに TXT ファイルと JPEG ファイルがそれぞれ何個含まれているか、ListView に表示したいとする。以下のような形だ。</p><table><thead><tr><th>形式</th><th>個数</th></tr></thead><tbody><tr><td>TXT</td><td>12</td></tr><tr><td>JPEG</td><td>8</td></tr></tbody></table><p>ちなみに XAML はこんな感じになる。匿名クラスを使っても使わなくても同じ</p><figure class="highlight xml"><table><tr><td class="code"><pre><span class="line"><span class="tag"><<span class="name">ListView</span> <span class="attr">ItemsSource</span>=<span class="string">"{Binding Summaries}"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">ListView.View</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">GridView</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">GridViewColumn</span> <span class="attr">Header</span>=<span class="string">"形式"</span> <span class="attr">DisplayMemberBinding</span>=<span class="string">"{Binding Type}"</span> /></span></span><br><span class="line"> <span class="tag"><<span class="name">GridViewColumn</span> <span class="attr">Header</span>=<span class="string">"個数"</span> <span class="attr">DisplayMemberBinding</span>=<span class="string">"{Binding Count}"</span> /></span></span><br><span class="line"> <span class="tag"></<span class="name">GridView</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">ListView.View</span>></span></span><br><span class="line"><span class="tag"></<span class="name">ListView</span>></span></span><br></pre></td></tr></table></figure><h2><span id="匿名クラスを使わない場合">匿名クラスを使わない場合</span></h2><p>ListView にバインドするコレクションの要素として、クラスか構造体を定義する必要がある。</p><figure class="highlight cs"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">struct</span> Item {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">string</span> Type { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">int</span> Count { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> IEnumerable<Item> Summaries {</span><br><span class="line"> <span class="keyword">get</span> {</span><br><span class="line"> <span class="keyword">return</span> fileInfoList.GroupBy(f => f.Extension)</span><br><span class="line"> .Select(g => <span class="keyword">new</span> Item() { Type = g.Key.TrimStart(<span class="string">'.'</span>).ToUpper(), Count = g.Count() });</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2><span id="匿名クラスを使う場合">匿名クラスを使う場合</span></h2><p>対して匿名クラスを使う場合は、コレクションの要素となるクラスや構造体を用意する必要がない。</p><figure class="highlight cs"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> IEnumerable<<span class="keyword">object</span>> Summaries {</span><br><span class="line"> <span class="keyword">get</span> {</span><br><span class="line"> <span class="keyword">return</span> fileInfoList.GroupBy(f => f.Extension)</span><br><span class="line"> .Select(g => <span class="keyword">new</span> { Type = g.Key.TrimStart(<span class="string">'.'</span>).ToUpper(), Count = g.Count() });</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>こんな風に書いてもいい。こっちだと<code>Type</code>に自由な文字を入れることができる。</p><figure class="highlight cs"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> IEnumerable<<span class="keyword">object</span>> Summaries {</span><br><span class="line"> <span class="keyword">get</span> {</span><br><span class="line"> <span class="keyword">yield</span> <span class="keyword">return</span> <span class="keyword">new</span> { Type = <span class="string">"TXT"</span>, Count = fileInfoList.Count(f => f.Extension == <span class="string">'.txt'</span>) };</span><br><span class="line"> <span class="keyword">yield</span> <span class="keyword">return</span> <span class="keyword">new</span> { Type = <span class="string">"JPEG"</span>, Count = fileInfoList.Count(f => f.Extension == <span class="string">'.jpg'</span>) };</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1><span id="まとめ">まとめ</span></h1><p>表示のみに使うクラス(構造体)をわざわざ定義する必要はなく、匿名クラスで記述すると簡易に表現することができた。</p><h1><span id="環境">環境</span></h1><ul><li>Windows 10</li><li>.NetFramework 4.0</li></ul>]]></content>
<summary type="html">
<p>WPF の双方向バインディング、便利ですよね。ただ実際使っていると双方向が必要なケースってあまりなくて、大部分は単一方向で済む場合が多いはず。</p>
<p>この時プロパティには<code>getter</code>だけあればいいんだけど、匿名クラスを返せると表現力が上がるね!という話。</p>
</summary>
<category term="csharp" scheme="https://t-kojima.github.io/tags/csharp/"/>
</entry>
<entry>
<title>QiitaとBlogの使い分けについて思う事</title>
<link href="https://t-kojima.github.io/2018/10/04/0043-think-about-qiita-and-blog/"/>
<id>https://t-kojima.github.io/2018/10/04/0043-think-about-qiita-and-blog/</id>
<published>2018-10-04T00:30:22.000Z</published>
<updated>2018-12-20T11:07:38.000Z</updated>
<content type="html"><![CDATA[<p>最近自分の Qiita への投稿が減ってきたなぁ…と思ったので、Qiita と Blog の使い分けについて振り返ってみる。</p><a id="more"></a><h1><span id="最初は-qiita-から始めた">最初は Qiita から始めた</span></h1><p>Qiita への初めての投稿は 2017/10/19、その頃どこかで「アウトプットしない技術者に意味は無い」(だったかな?)というような言葉を目にし、「これはヤバい、取り合えず何か始めよう」と思って始めたのが Qiita だった。</p><h2><span id="なぜ-qiita">なぜ Qiita?</span></h2><p>Qiita を書こうと思った時、どんな記事を書きたいかはわりと最初から固まっていた。</p><p>良くある困りごととして、何かのエラーや障害に当たった時にググっても英語の記事しかヒットしないということは良くある。この時、このエラーの現象や解決策を英語の記事を参考として日本語で分かりやすく説明できたら、きっと役に立つのではないかと思った。それは Qiita の理念である「技術的な困りごとを解決したい」と素直に繋がると感じたのだ。</p><p>一番の懸念は三日坊主になることだったけど、幸い日々仕事をしているだけでもそこそこネタになる事は多くあったので、もうすぐ 1 年、悪くない感じで続けられているのではないかと思った。</p><h2><span id="上手くいかない点も">上手くいかない点も</span></h2><p>しかし書いていくうちに悪い点(?)も出てきた。</p><p>Qiita は Public な場なので、あまりに雑な記事や、明らかにダダ被りな記事は書けない。下書きはそこそこ書いても公開まで持っていけない場面が増えてきたのだ。</p><p>それ自体は完全に自分のせいなんだけど、自分の記事の質が低いと思う原因の一つに「文章力の低さ」があって、これはとにかく数をこなさなければ上がらない部類のものと思っている。なので文章力を上げる為にとにかく数をこなしたいとも思っていて、上手くいかないと感じていたのだ。</p><h1><span id="blog-も始める">Blog も始める</span></h1><p>Qiita では書けない(と自分では思ってる)ような事を書くため、ゆるーい制約のもと始めた。Qiita が Public なら Blog は Private、オレの庭だ。</p><p>具体的には以下のような違いを意識していた。</p><ul><li>何かのエラー・障害について書くとして、結果解決しなくてもいい。</li><li>長い内容なら「明日につづく」でもいい。</li><li>誰のメリットにもならない内容(ex: 〇〇の問題を解いてみた)でも OK。</li></ul><p>何十番煎じの内容でも OK だ、自分でやってみたらこうだったという、それこそ備忘録的な内容として残すようにした。</p><h2><span id="hexo-への移行">Hexo への移行</span></h2><p>最初 WordPress で書いていたが、サイトの管理が煩わしく感じていた。WordPress 自体やプラグインのバージョン管理も面倒だし、SEO 対策用のプラグインは別途ログインする必要があったりして見るだけでも面倒だし、記事を書いても SEO 用のタイトルや詳細なんかも設定しないといけないし。。。なんなら管理コンソールを開くこと(WordPress へのログイン)すら億劫だった。</p><p>「ブログ記事を書く」以外の作業がとても面倒に感じていた。</p><p>そこで他に良いプラットフォームが無いかを探して、見つけたのが Hexo。いわゆる静的サイトジェネレータだ。</p><p>丁度 JavaScript を触り始めていた時期なので、Node で動く Hexo は僕のニーズにぴったりだった。特に以下の点がイイと感じた。</p><ul><li>記事はマークダウンファイルとして独立しているので何処でも書ける。</li><li>公式のプラグインを使うと、GithubPages へのデプロイが 1 コマンドでできる。</li><li>実体はただのリポジトリなので管理が楽。</li><li>普段使いのエディタでそのまま記事が書ける!</li></ul><p>特に最後が大きい、WordPress と比べて書くのが圧倒的に楽になった。</p><p>大事なことなのでもう一回言う。<strong>圧倒的に楽になった!</strong></p><h2><span id="blog-を始めたことによる弊害">Blog を始めたことによる弊害</span></h2><p>弊害というほどでもないけど、最初のボヤキに戻る。Qiita への投稿が減ってしまったのだ。</p><p>つまり Qiita に書くか Blog に書くかどっちつかずなネタの記事は、Blog に書くほうが楽だから Blog に書いてしまう。結果、Qiita に書くネタの心理的ハードルが非常に高くなってしまった。</p><p>現在では以下のように使い分けようと考えている。</p><h3><span id="blog">Blog</span></h3><ul><li>日々の仕事などでやったこと、問題があったこと、思ったことを書いていく。</li><li>ネタかぶりは気にしない。</li><li>技術的な記事には限らない(けど実態は技術記事だけになってる。。。)</li><li>とにかく書くことが目的</li><li>深堀りできたり、体系的にまとめる余地がある記事は Qiita に書き直したい。</li></ul><h3><span id="qiita">Qiita</span></h3><ul><li>誰かの問題を解決できるような記事を書きたい。</li><li>技術的な記事に限る</li><li>分かりやすく説明することが目的</li></ul><p>Qiita で「技術的な記事に限る」というのは当たり前のように思うけど、個人的にはポエム・イベント告知/報告・備忘録なんかは技術的な記事かはちょっと微妙と感じる。(備忘録は技術的な記事かもしれないけど、書いた人しか分からない)</p><p>逆に HowTo・用語/概念の説明・トラブルシューティングなど「読んだ人の課題が解決した・何かを使えるようになった・コードの書き方が変わった」といった<strong>読み手の技術的な課題を解決する記事</strong>が良い記事と考えている。</p><p>ポエムとかも読む分には面白いし、トレンドに良くあがるのでイイネしたりするけど、自分で書くなら後者のような記事が書けるようにしていきたいと思っている。</p><h1><span id="おわりに">おわりに</span></h1><p>改めて書き出してみたけど、やっぱりどちらに書こうか悩む時は多いし、結局 Blog に書くことが多いように思う。</p><p>他の人を見てると Blog では技術記事以外を書くことで使い分けしている人が多い印象だ。しかしなんとなく Blog にプライベートなことだったり、思想的なことを書く気になれない…結果技術的な記事ばかり増えてしまっている。</p><p>Blog に書くべき内容の一つとして「問題に取り組んだが解決しなかった」というのを挙げていたが、この記事もそれに該当していることを改めて確認して、解決できなかった免罪符にしつつ終わりにしたい。</p>]]></content>
<summary type="html">
<p>最近自分の Qiita への投稿が減ってきたなぁ…と思ったので、Qiita と Blog の使い分けについて振り返ってみる。</p>
</summary>
</entry>
<entry>
<title>LinuxでVSCodeでPowerline</title>
<link href="https://t-kojima.github.io/2018/09/26/0042-linux-vscode-powerline/"/>
<id>https://t-kojima.github.io/2018/09/26/0042-linux-vscode-powerline/</id>
<published>2018-09-26T05:53:39.000Z</published>
<updated>2019-01-11T23:46:20.000Z</updated>
<content type="html"><![CDATA[<p>Linux にも powerline を入れたい。Windows ほど面倒ではなくサクっとできるのだけど、次回クリーンインストール時に多分手順を覚えていないので残しておく。</p><p>ちなみに Linux は Mint 19 “Tera” です。</p><a id="more"></a><h1><span id="powerline-shell">Powerline-shell</span></h1><p><a href="https://github.com/b-ryan/powerline-shell" class="embedly-card" data-card-image="0" data-card-controls="0" data-card-align="left" target="_blank" rel="noopener"></a></p><p>powerline-shell を使ってシェルに powerline を適用させる。Python で作られていて pip で簡単にインストールできる。</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">pip install powerline-shell</span><br></pre></td></tr></table></figure><p>bash を使っている場合は、.bashrc に以下を追記</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="function"><span class="title">_update_ps1</span></span>() {</span><br><span class="line"> PS1=$(powerline-shell $?)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> [[ <span class="variable">$TERM</span> != linux && ! <span class="variable">$PROMPT_COMMAND</span> =~ _update_ps1 ]]; <span class="keyword">then</span></span><br><span class="line"> PROMPT_COMMAND=<span class="string">"_update_ps1; <span class="variable">$PROMPT_COMMAND</span>"</span></span><br><span class="line"><span class="keyword">fi</span></span><br></pre></td></tr></table></figure><p>bash 以外は<a href="https://github.com/b-ryan/powerline-shell#setup" target="_blank" rel="noopener">README の Setup</a>を参考にして欲しい。zsh とか fish とかある。</p><p>これで powerline がシェルに適用されるが、まだフォントが無いので記号が表示されない。</p><p>続けて powerline 用のフォントをインストールしていく。</p><h1><span id="powerline-fonts">Powerline-fonts</span></h1><p><a href="https://github.com/powerline/fonts" class="embedly-card" data-card-image="0" data-card-controls="0" data-card-align="left" target="_blank" rel="noopener"></a></p><p><a href="https://github.com/powerline/fonts#quick-installation" target="_blank" rel="noopener">Quick Installation</a>に従ってやればいい、クローンしてシェル叩くだけだね。</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">git <span class="built_in">clone</span> https://github.com/powerline/fonts.git --depth=1</span><br><span class="line"></span><br><span class="line"><span class="built_in">cd</span> fonts</span><br><span class="line">./install.sh</span><br><span class="line"></span><br><span class="line"><span class="built_in">cd</span> ..</span><br><span class="line">rm -rf fonts</span><br></pre></td></tr></table></figure><p>これでフォントが追加された。</p><h1><span id="vscode-に適用">VSCode に適用</span></h1><p>VSCode の<code>settings.json</code>でフォントを設定する。</p><figure class="highlight"><table><tr><td class="code"><pre><span class="line">"terminal.integrated.fontFamily": "Source Code Pro for Powerline"</span><br></pre></td></tr></table></figure><p>カンタン!</p><p>ちょっとフォントの大きさが気になる場合は調整すると良い。デフォルトが 14 なので少し小さめの 12 あたりにしてみる。</p><figure class="highlight"><table><tr><td class="code"><pre><span class="line">"terminal.integrated.fontSize": 12</span><br></pre></td></tr></table></figure><p>以下のように VSCode のターミナルに Powerline が適用できた。</p><p><img src="/images/42-01.png" alt="Powerline適用後の画面"></p><h1><span id="powerline-shell-の設定">Powerline-shell の設定</span></h1><p>Powerline の適用はできたが、プロンプトにカレントのパスがズラズラ表示されるのは若干イケてない。なので設定を変更してみる。</p><p>powerline-shell では設定ファイルを生成し、カスタマイズすることができる。(設定ファイルを生成していない状態では、デフォルトの設定が適用される)</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">powerline-shell --generate-config > ~/.powerline-shell.json</span><br></pre></td></tr></table></figure><p>これで<code>.powerline-shell.json</code>が生成されるので、それを編集する。</p><p>今回はディレクトリを展開せずに現在のディレクトリのみを表示させたいので、以下のオプションを追加する。</p><figure class="highlight"><table><tr><td class="code"><pre><span class="line">"cwd": {</span><br><span class="line"> "mode": "dironly"</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>ちなみに json 全体は以下の通りだ。</p><figure class="highlight json"><table><tr><td class="code"><pre><span class="line">{</span><br><span class="line"> <span class="attr">"segments"</span>: [</span><br><span class="line"> <span class="string">"virtual_env"</span>,</span><br><span class="line"> <span class="string">"username"</span>,</span><br><span class="line"> <span class="string">"ssh"</span>,</span><br><span class="line"> <span class="string">"cwd"</span>,</span><br><span class="line"> <span class="string">"git"</span>,</span><br><span class="line"> <span class="string">"hg"</span>,</span><br><span class="line"> <span class="string">"jobs"</span>,</span><br><span class="line"> <span class="string">"root"</span>,</span><br><span class="line"> <span class="string">"read_only"</span></span><br><span class="line"> ],</span><br><span class="line"> <span class="attr">"cwd"</span>: {</span><br><span class="line"> <span class="attr">"mode"</span>: <span class="string">"dironly"</span></span><br><span class="line"> },</span><br><span class="line"> <span class="attr">"vcs"</span>: {</span><br><span class="line"> <span class="attr">"show_symbol"</span>: <span class="string">"true"</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>すると以下のように現在のディレクトリしか表示されなくなった。</p><p><img src="/images/42-02.png" alt="設定変更後の画面"></p><h1><span id="環境">環境</span></h1><ul><li>Linux Mint 19</li><li>powerline-shell 0.5.4</li></ul><h1><span id="参考">参考</span></h1><ul><li><a href="https://githubja.com/b-ryan/powerline-shell" target="_blank" rel="noopener">powerline-shell – 美しくて便利なシェルのプロンプト – GitHub じゃ!Python じゃ!</a></li></ul>]]></content>
<summary type="html">
<p>Linux にも powerline を入れたい。Windows ほど面倒ではなくサクっとできるのだけど、次回クリーンインストール時に多分手順を覚えていないので残しておく。</p>
<p>ちなみに Linux は Mint 19 “Tera” です。</p>
</summary>
<category term="VSCode" scheme="https://t-kojima.github.io/categories/VSCode/"/>
<category term="vscode" scheme="https://t-kojima.github.io/tags/vscode/"/>
<category term="powerline" scheme="https://t-kojima.github.io/tags/powerline/"/>
</entry>
<entry>
<title>WindowsでCmderでVSCodeでPowerline</title>
<link href="https://t-kojima.github.io/2018/09/24/0041-windows-cmder-vscode-powerline/"/>
<id>https://t-kojima.github.io/2018/09/24/0041-windows-cmder-vscode-powerline/</id>
<published>2018-09-24T02:41:19.000Z</published>
<updated>2018-12-20T11:07:38.000Z</updated>
<content type="html"><![CDATA[<p>唐突に windows で powerline が使いたくなり、powershell に入れようとしたら上手くいかなかった。<br>しかし cmder では上手く適用できたので、VSCode のターミナルとして使えるようになるまでの手順を残す。</p><a id="more"></a><h1><span id="cmder-のインストール">Cmder のインストール</span></h1><p><a href="http://cmder.net/" target="_blank" rel="noopener">Cmder | Console Emulator</a>から Mini 版をダウンロードする。VSCode のターミナルとして使う為、Full 版の充実したコンソールは必要ないという判断。</p><p>ダウンロードしたら解凍して適当な場所に配置する。今回は<code>C:\Program Files\cmder_mini</code>に配置した。</p><p>以下注意点が 2 点</p><ul><li><code>C:\Program Files (x86)</code>に配置しようとしたら、<code>\cmder_mini\vendor\lib\lib_base"" の使い方が誤っています。</code>という警告が出た。別の場所がいいのだろう。</li><li><code>cmder_mini</code>ディレクトリをコピーして配置したら起動に管理者権限を要求されたが、移動して配置したらそのまま起動できた。移動して配置しよう。</li></ul><p>初回起動でこのような画面が立ち上がる。</p><p><img src="/images/41-01.png" alt="初回起動時の画面"></p><h1><span id="powerline-のインストール">Powerline のインストール</span></h1><p><a href="https://github.com/AmrEldib/cmder-powerline-prompt" target="_blank" rel="noopener">AmrEldib/cmder-powerline-prompt: Custom prompt for Cmder on Windows</a>からリポジトリを zip でダウンロードしてくる。(clone でも良いよ)</p><p>そして以下のファイルを<code>C:\Program Files\cmder_mini\config\</code>へコピーする。</p><ul><li>powerline_core.lua</li><li>powerline_git.lua</li><li>powerline_prompt.lua</li></ul><p>再度起動すると、Powerline が適用されているのが分かる。</p><p><img src="/images/41-02.png" alt="Powerline適用後の画面"></p><p>ただまだフォントが化けているが、気にせず進める。</p><h2><span id="powerline-font-のインストール">Powerline-font のインストール</span></h2><p>Powerline のフォントは色々あるようだが、今回は<a href="https://github.com/powerline/fonts" target="_blank" rel="noopener">powerline/fonts: Patched fonts for Powerline users.</a>を使う。</p><p>これも zip でダウンロードし、PowerShell で<code>.\install.ps1</code>を実行する。そうするとダウンロードしたフォントが全て Windows にインストールされる。時間が掛かるのでしばし待とう。</p><h1><span id="vscode-のターミナルを-cmder-にする">VSCode のターミナルを Cmder にする。</span></h1><p>cmder のインストールディレクトリ(<code>C:\Program Files\cmder_mini</code>)に以下の内容で<code>vscode.bat</code>というバッチを作成する。</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">@echo off</span><br><span class="line">SET CMDER_ROOT=C:\Program Files\cmder_mini</span><br><span class="line">"%CMDER_ROOT%\vendor\init.bat"</span><br></pre></td></tr></table></figure><p>そして VSCode の<code>settings.json</code>に以下設定を追加する。</p><figure class="highlight"><table><tr><td class="code"><pre><span class="line">"terminal.integrated.shell.windows": "C:\\WINDOWS\\sysnative\\cmd.exe",</span><br><span class="line">"terminal.integrated.shellArgs.windows": ["/K", "C:\\Program Files\\cmder_mini\\vscode.bat"]</span><br></pre></td></tr></table></figure><h2><span id="ターミナルのフォント設定">ターミナルのフォント設定</span></h2><p>VSCode の<code>settings.json</code>で設定する。</p><figure class="highlight"><table><tr><td class="code"><pre><span class="line">"terminal.integrated.fontFamily": "Source Code Pro for Powerline"</span><br></pre></td></tr></table></figure><p>先にインストールしたフォントで<code>**** Powerline</code>という名前のものならなんでもいいだろう。ただ Cmder のコンソールで表示した時と VSCode のターミナルで表示した時では、同じフォントでも表示がかなり違ったので注意(?)が必要。</p><p><img src="/images/41-03.png" alt="VSCodeのターミナル"></p><p>イイ感じになってきた</p><h2><span id="カーソルの位置が一文字ずれる問題">カーソルの位置が一文字ずれる問題</span></h2><p>プロンプトが<code>λ</code>だと、入力の 1 文字目のカーソルと入力位置がずれる問題がある。なのでプロンプトを変えてしまえばいい。</p><p><code>powerline_core.lua</code>の 113 行目、<code>plc_prompt_lambSymbol</code>を修正することで変えられる。</p><figure class="highlight lua"><table><tr><td class="code"><pre><span class="line"><span class="keyword">if</span> <span class="keyword">not</span> plc_prompt_lambSymbol <span class="keyword">then</span></span><br><span class="line"> plc_prompt_lambSymbol = <span class="string">"λ"</span> <span class="comment">-- ここを別の文字に変える</span></span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure><p><img src="/images/41-04.png" alt="プロンプト修正後のターミナル"></p><p>OK!</p><hr><p>ちょっと追記:</p><p>プロンプトを <code>></code> に変更したら、node の REPL を実行した時にプロンプトが変わらず分かり辛かった。<code>></code> ではなく <code>$</code> のほうがいいかも</p><hr><h1><span id="環境">環境</span></h1><ul><li>Windows 10</li><li>VSCode 1.25.1</li><li>cmder_mini 1.3.6</li></ul><h1><span id="参考">参考</span></h1><ul><li><a href="https://qiita.com/hmcGit/items/98b80507406cf25c10b4" target="_blank" rel="noopener">2017 年お手軽なのに格好良くて最強の Windows ターミナル環境構築。「Windows8.1 に cmder + Powerline」にて - Qiita</a></li><li><a href="https://qiita.com/alchemist/items/f2f54cfe545c84af9430" target="_blank" rel="noopener">Windows に Cmder で Bash 環境を作って日本語が表示できるようになるまでの覚え書き - Qiita</a></li><li><a href="https://code.visualstudio.com/docs/editor/integrated-terminal" target="_blank" rel="noopener">Integrated Terminal in Visual Studio Code</a></li></ul>]]></content>
<summary type="html">
<p>唐突に windows で powerline が使いたくなり、powershell に入れようとしたら上手くいかなかった。<br>しかし cmder では上手く適用できたので、VSCode のターミナルとして使えるようになるまでの手順を残す。</p>
</summary>
<category term="vscode" scheme="https://t-kojima.github.io/tags/vscode/"/>
<category term="powerline" scheme="https://t-kojima.github.io/tags/powerline/"/>
</entry>
<entry>
<title>[Firebase x React] ReactをFirebaseでDatabase</title>
<link href="https://t-kojima.github.io/2018/08/13/0040-firebase-react-database/"/>
<id>https://t-kojima.github.io/2018/08/13/0040-firebase-react-database/</id>
<published>2018-08-12T15:00:56.000Z</published>
<updated>2018-12-20T11:07:38.000Z</updated>
<content type="html"><![CDATA[<p>Firebase で静的サイトのホスティングと ESLint・Prettier の適用までやったので、Firebase Database を使って定番の ToDo アプリを作ってみたい。</p><p>ただ実際に写したり動かしたり色々検討しながらなので、内容がかなり乱暴になっているのはご容赦いただきたい。</p><hr><ul><li>9/24 大幅に書き直し、ちゃんとしました。</li></ul><hr><a id="more"></a><h1><span id="前提作業">前提作業</span></h1><p>事前にアプリの雛形の作成と、ESLint・Prettier の設定を行っておく。</p><ul><li><a href="https://t-kojima.github.io/2018/08/12/0038-firebase-react-hosting/">[Firebase x React] React を Firebase で Hosting</a></li><li><a href="https://t-kojima.github.io/2018/08/12/0039-firebase-react-eslint-prettier/">[Firebase x React] ESLint と Prettier を React に適用</a></li></ul><p>まあ ESLint・Prettier の設定は必須では無いけど。。。</p><h2><span id="firebase-database-の初期化">Firebase Database の初期化</span></h2><p>まずは Firebase Database を扱う為に、設定ファイルの生成や初期化を行う。</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">firebase init database</span><br></pre></td></tr></table></figure><p>すると<code>database.rules.json</code>が作成され、<code>firebase.json</code>に database の設定が追記される。</p><h1><span id="todo-アプリの作成">ToDo アプリの作成</span></h1><h2><span id="構成">構成</span></h2><p>ToDo アプリは以下の構成にする。</p><ul><li>index.js : App.js を呼び出すエントリーポイント</li><li>App.js : アプリ全体のレイアウトを持つコンポーネント</li><li>TodoList.jsx : TodoList のコンポーネント</li><li>Todo.jsx : Todo 1 個に対するコンポーネント</li><li>InputForm.jsx : Todo を登録するフォームのコンポーネント</li></ul><h3><span id="indexjs">index.js</span></h3><p>index.js はエントリーポイントとなるファイルになる。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> React <span class="keyword">from</span> <span class="string">'react'</span>;</span><br><span class="line"><span class="keyword">import</span> ReactDOM <span class="keyword">from</span> <span class="string">'react-dom'</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="string">'./index.css'</span>;</span><br><span class="line"><span class="keyword">import</span> App <span class="keyword">from</span> <span class="string">'./App'</span>;</span><br><span class="line"><span class="keyword">import</span> registerServiceWorker <span class="keyword">from</span> <span class="string">'./registerServiceWorker'</span>;</span><br><span class="line"></span><br><span class="line">ReactDOM.render(<span class="xml"><span class="tag"><<span class="name">App</span> /></span>, document.getElementById('root'));</span></span><br><span class="line"><span class="xml">registerServiceWorker();</span></span><br></pre></td></tr></table></figure><p>処理としては<code>#root</code>の要素に App コンポーネントをレンダリングするのみだ。<br>ここはある程度お決まりの記述になる。いわゆる<em>おまじない</em>だ。</p><p>registerServiceWorker は create-react-app が自動生成したモジュールになる。ぶっちゃけ何をしているのか分かっていないので、近いうちに内容をよく確認したい。。。</p><h3><span id="appjs">App.js</span></h3><p>App.js は実質的なルートになるコンポーネントになる。<br>ここではタイトルを表示し、TodoList コンポーネントを読み込んでいる。</p><figure class="highlight jsx"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> React, { Component } <span class="keyword">from</span> <span class="string">'react'</span>;</span><br><span class="line"><span class="keyword">import</span> TodoList <span class="keyword">from</span> <span class="string">'./TodoList'</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="string">'./App.css'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="class"><span class="keyword">class</span> <span class="title">App</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>{</span><br><span class="line"> render() {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <div className=<span class="string">"app"</span>></span><br><span class="line"> <section className=<span class="string">"hero container is-info"</span>></span><br><span class="line"> <div className=<span class="string">"hero-body"</span>></span><br><span class="line"> <h1 className=<span class="string">"title"</span>>ToDo Application.<<span class="regexp">/h1></span></span><br><span class="line"><span class="regexp"> </</span>div></span><br><span class="line"> <<span class="regexp">/section></span></span><br><span class="line"><span class="regexp"> <section className="container"></span></span><br><span class="line"><span class="regexp"> <TodoList /</span>></span><br><span class="line"> <<span class="regexp">/section></span></span><br><span class="line"><span class="regexp"> </</span>div></span><br><span class="line"> );</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>ちなみに<code>hero</code>や<code>container</code>などのクラスは<a href="https://bulma.io/" target="_blank" rel="noopener">Bulma</a>を利用している。以下のように CDN で利用できるので非常にカンタンだ。</p><figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag"><<span class="name">link</span> <span class="attr">rel</span>=<span class="string">"stylesheet"</span> <span class="attr">href</span>=<span class="string">"https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.1/css/bulma.min.css"</span>></span></span><br></pre></td></tr></table></figure><p>最初は<a href="https://material-ui.com/" target="_blank" rel="noopener">Material-ui</a>などのコンポーネントセットの利用も検討したが、パラメータなどが多く、React 初心者にはちょっと辛そうだったので今回は CSS フレームワークにした。</p><h3><span id="todolistjsx">TodoList.jsx</span></h3><p>次に Todo アイテムをリスト保持するコンポーネントを作成する。</p><p>まだ Firebase database と繋いでいないのでとりあえず初期値をベタ書きしているが、このコンポーネントは Todo アイテムのリストを状態(state)として保持する。</p><figure class="highlight jsx"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> React, { Component } <span class="keyword">from</span> <span class="string">'react'</span>;</span><br><span class="line"><span class="keyword">import</span> Todo <span class="keyword">from</span> <span class="string">'./Todo'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="class"><span class="keyword">class</span> <span class="title">TodoList</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>{</span><br><span class="line"> <span class="keyword">constructor</span>() {</span><br><span class="line"> <span class="keyword">super</span>();</span><br><span class="line"> <span class="keyword">this</span>.state = {</span><br><span class="line"> todos: [</span><br><span class="line"> {</span><br><span class="line"> id: <span class="number">1</span>,</span><br><span class="line"> title: <span class="string">'Todo その1'</span>,</span><br><span class="line"> description: <span class="string">'Todo その1'</span>,</span><br><span class="line"> checked: <span class="literal">false</span>,</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> id: <span class="number">2</span>,</span><br><span class="line"> title: <span class="string">'Todo その2'</span>,</span><br><span class="line"> description: <span class="string">'Todo その2'</span>,</span><br><span class="line"> checked: <span class="literal">false</span>,</span><br><span class="line"> },</span><br><span class="line"> ],</span><br><span class="line"> };</span><br><span class="line"> }</span><br><span class="line"> render() {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <ul></span><br><span class="line"> {<span class="keyword">this</span>.state.todos.map(<span class="function"><span class="params">todo</span> =></span> (</span><br><span class="line"> <Todo key={todo.id} {...todo} /></span><br><span class="line"> ))}</span><br><span class="line"> <<span class="regexp">/ul></span></span><br><span class="line"><span class="regexp"> );</span></span><br><span class="line"><span class="regexp"> }</span></span><br><span class="line"><span class="regexp">}</span></span><br></pre></td></tr></table></figure><p>Todo コンポーネントを map で作成し、ul 配下に展開している。ちなみに<code>{...todo}</code>は要素を全て Todo コンポーネントの props に引き継ぐ記述だ。</p><h3><span id="todojs">Todo.js</span></h3><p>最後に Todo 1 個あたりのコンポーネントを作る。</p><figure class="highlight jsx"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> React, { Component } <span class="keyword">from</span> <span class="string">'react'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="class"><span class="keyword">class</span> <span class="title">Todo</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>{</span><br><span class="line"> render() {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <li className=<span class="string">"todo"</span>></span><br><span class="line"> <nav className=<span class="string">"panel"</span>></span><br><span class="line"> <div className=<span class="string">"panel-heading"</span>></span><br><span class="line"> <p>{<span class="keyword">this</span>.props.title}<<span class="regexp">/p></span></span><br><span class="line"><span class="regexp"> </</span>div></span><br><span class="line"> <div className=<span class="string">"panel-block"</span>></span><br><span class="line"> <p>{<span class="keyword">this</span>.props.description}<<span class="regexp">/p></span></span><br><span class="line"><span class="regexp"> </</span>div></span><br><span class="line"> <div className=<span class="string">"panel-block"</span>></span><br><span class="line"> <a href=<span class="string">""</span>>Completed<<span class="regexp">/a></span></span><br><span class="line"><span class="regexp"> </</span>div></span><br><span class="line"> <<span class="regexp">/nav></span></span><br><span class="line"><span class="regexp"> </</span>li></span><br><span class="line"> );</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>基本的には受け取った props の title や description を表示するだけ。それなりにタグが色々あるように見えるのは Bulma の panel を使っているからだ。</p><p>ここで一度実行(<code>yarn start</code>)すると以下のような画面が立ち上がる。</p><p><img src="/images/40-01.png" alt="実行画面"></p><p>このようにベタ書きではあるが Todo が表示されれば OK だ。</p><h1><span id="firebase-database-適用">Firebase Database 適用</span></h1><p>ここまでで静的な Todo アプリ(アプリと言ってよいのだろうか。。。?)が出来たが、ここに Firebase database との連携を追加して動きのあるページにしていく。</p><p>まず Firebase SDK をインストールする。前に入れた Firebase CLI とは別物で、Firebase Database 等へのアクセスが可能になる。</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">yarn add firebase</span><br></pre></td></tr></table></figure><h2><span id="database-アクセス用のモジュール作成">Database アクセス用のモジュール作成</span></h2><p>Firebase Database へのアクセスを簡易にするためモジュールを作成します。</p><ul><li>firebase/config.js</li><li>firebase/index.js</li></ul><p>まずは config.js に Firebase へアクセスする為の設定情報を持たせます。</p><p>Firebase Concole にアクセスし、以下のボタンから接続情報を確認する。</p><p><img src="/images/38-03.png" alt="ウェブアプリにFirebaseを追加"></p><p>確認できた接続情報を以下のように<code>firebase/config.js</code>に持たせる。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> firebaseConfig = {</span><br><span class="line"> apiKey: <span class="string">'**********'</span>,</span><br><span class="line"> authDomain: <span class="string">'**********.firebaseapp.com'</span>,</span><br><span class="line"> databaseURL: <span class="string">'https://**********.firebaseio.com'</span>,</span><br><span class="line"> projectId: <span class="string">'**********'</span>,</span><br><span class="line"> storageBucket: <span class="string">'**********.appspot.com'</span>,</span><br><span class="line"> messagingSenderId: <span class="string">'**********'</span>,</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>続いて<code>firebase/index.js</code>で config を読み出し、初期化したうえで export する。</p><p>今回は database のみなので、<code>firebaseDb</code>のみを export する。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> firebase <span class="keyword">from</span> <span class="string">'firebase'</span>;</span><br><span class="line"><span class="keyword">import</span> { firebaseConfig } <span class="keyword">from</span> <span class="string">'./config'</span>;</span><br><span class="line"></span><br><span class="line">firebase.initializeApp(firebaseConfig);</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> firebaseDb = firebase.database();</span><br></pre></td></tr></table></figure><p>これで各コンポーネントからは以下のように呼び出すことで、DB を利用することができるようになる。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> { firebaseDb } <span class="keyword">from</span> <span class="string">'./firebase'</span>;</span><br></pre></td></tr></table></figure><h2><span id="初期データのロード">初期データのロード</span></h2><p>初期データのロードということで、Firebase Database に登録されているデータを初回読み込み時にロードする。現時点ではデータを登録していないけど、データ登録処理を入れた時にスムーズにいくよう先にやっとく。</p><p>TodoList.jsx を以下のように書き換える。</p><figure class="highlight jsx"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> React, { Component } <span class="keyword">from</span> <span class="string">'react'</span>;</span><br><span class="line"><span class="keyword">import</span> Todo <span class="keyword">from</span> <span class="string">'./Todo'</span>;</span><br><span class="line"><span class="keyword">import</span> { firebaseDb } <span class="keyword">from</span> <span class="string">'./firebase'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="class"><span class="keyword">class</span> <span class="title">TodoList</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>{</span><br><span class="line"> <span class="keyword">constructor</span>() {</span><br><span class="line"> <span class="keyword">super</span>();</span><br><span class="line"> <span class="keyword">this</span>.state = {</span><br><span class="line"> todos: [],</span><br><span class="line"> };</span><br><span class="line"> }</span><br><span class="line"> componentDidMount() {</span><br><span class="line"> firebaseDb</span><br><span class="line"> .ref(<span class="string">'todos'</span>)</span><br><span class="line"> .on(<span class="string">'value'</span>, snapshot => <span class="keyword">this</span>.setState({ <span class="attr">todos</span>: snapshot.val() || [] }));</span><br><span class="line"> }</span><br><span class="line"> render() {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <ul></span><br><span class="line"> {<span class="built_in">Object</span>.entries(<span class="keyword">this</span>.state.todos).map(<span class="function">(<span class="params">[key, value]</span>) =></span> (</span><br><span class="line"> <Todo key={key} id={key} {...value} /></span><br><span class="line"> ))}</span><br><span class="line"> <<span class="regexp">/ul></span></span><br><span class="line"><span class="regexp"> );</span></span><br><span class="line"><span class="regexp"> }</span></span><br><span class="line"><span class="regexp">}</span></span><br></pre></td></tr></table></figure><p>state の todos をただの空配列にし、<code>componentDidMount</code>メソッドを追加した。このメソッドで Firebase Database のデータをロードし、state の todos に設定する。</p><p>constructor と componentDidMount で処理を分けているのはデータのロードが非同期だからで、コンポーネントマウント時に改めて setState している。</p><p>DB のデータを取得するメソッドとして、ここでは<code>on</code>を利用している。ざっくり言うとデータに変化がある度にロードする<code>on</code>と、一回限りロードする<code>once</code>がある。主な違いは以下の通りだ。</p><h3><span id="on">on</span></h3><p>on メソッドでは<code>'value'</code>とコールバック関数が引数として渡される。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">database.ref(<span class="string">'***'</span>).on(<span class="string">'value'</span>, callback);</span><br></pre></td></tr></table></figure><p>ここで渡された callback はリスナーに登録され、データの状態を監視するようになり、データに変化があり次第 callback をキックするようになる。</p><p>今回の用途では<code>snapshot => this.setState({ todos: snapshot.val() || [] })</code>というコールバックを登録しているので、データに変化がある度にこの関数が実行される。</p><p>ちなみに on メソッドは戻り値としてリスナーに登録された callback 自体を返す。リスナーの登録を解除したい場合は、この返された callback を利用して off メソッドを呼んでやればいい。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// onでリスナー登録</span></span><br><span class="line"><span class="keyword">const</span> callback = database</span><br><span class="line"> .ref(<span class="string">'***'</span>)</span><br><span class="line"> .on(<span class="string">'value'</span>, snapshot => <span class="keyword">this</span>.setState({ <span class="attr">todos</span>: snapshot.val() || [] }));</span><br><span class="line"><span class="comment">// 戻り値のcallbackを利用し、offでリスナー解除</span></span><br><span class="line">database.ref(<span class="string">'***'</span>).off(<span class="string">'value'</span>, callback);</span><br></pre></td></tr></table></figure><h3><span id="once">once</span></h3><p>once メソッドでも<code>'value'</code>とコールバック関数が引数として渡されるが、on と異なりリスナーを登録するわけでは無く一度のみデータを取得する。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">database.ref(<span class="string">'***'</span>).once(<span class="string">'value'</span>, callback);</span><br></pre></td></tr></table></figure><p>また、on ではコールバックとしてでしか関数を呼べなかったが、once は Promise を返すこともできる。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// これもOK</span></span><br><span class="line">database</span><br><span class="line"> .ref(<span class="string">'***'</span>)</span><br><span class="line"> .once(<span class="string">'value'</span>)</span><br><span class="line"> .then(callback);</span><br></pre></td></tr></table></figure><h3><span id="id-を-key-にする">id を key にする</span></h3><p>もう一つ変化点として、todos のデータの持ち方を変えている。</p><p>データをベタ書きしていた時は、todo 1 件のデータは以下のように id を持っているが</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">{</span><br><span class="line"> id: <span class="number">1</span>,</span><br><span class="line"> title: <span class="string">'Todo その1'</span>,</span><br><span class="line"> description: <span class="string">'Todo その1'</span>,</span><br><span class="line"> checked: <span class="literal">false</span>,</span><br><span class="line">},</span><br></pre></td></tr></table></figure><p>次のようなデータの持ち方に変更する。(次項参照)</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">id: {</span><br><span class="line"> title: <span class="string">'Todo その1'</span>,</span><br><span class="line"> description: <span class="string">'Todo その1'</span>,</span><br><span class="line"> checked: <span class="literal">false</span>,</span><br><span class="line">},</span><br></pre></td></tr></table></figure><p>何故かというと、id は Firebase Database が一意に作成するものを使用するようにする為、データの中に id というフィールドを持つのではなく、データのキーとして id を持つようになるからだ。(フィールドとして id を持ってもいいけど、冗長になるので排除する)</p><p>そのため、todos を map で回す際は以下のように書き換える。key と value に分離して、props の id には明示的に key を設定してあげるようにする。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">{</span><br><span class="line"> <span class="built_in">Object</span>.entries(<span class="keyword">this</span>.state.todos).map(<span class="function">(<span class="params">[key, value]</span>) =></span> (</span><br><span class="line"> <Todo key={key} id={key} {...value} /></span><br><span class="line"> ));</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2><span id="データ登録フォームの作成">データ登録フォームの作成</span></h2><p>次に新規の ToDo タスクを登録するための入力フォームをコンポーネントで作成する。</p><p>InputForm.jsx としよう</p><figure class="highlight jsx"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> React, { Component } <span class="keyword">from</span> <span class="string">'react'</span>;</span><br><span class="line"><span class="keyword">import</span> { firebaseDb } <span class="keyword">from</span> <span class="string">'./firebase'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="class"><span class="keyword">class</span> <span class="title">InputForm</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>{</span><br><span class="line"> <span class="keyword">constructor</span>(props) {</span><br><span class="line"> <span class="keyword">super</span>(props);</span><br><span class="line"> <span class="keyword">this</span>.state = {</span><br><span class="line"> title: <span class="string">''</span>,</span><br><span class="line"> desc: <span class="string">''</span>,</span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">this</span>.onClick = <span class="keyword">this</span>.onClick.bind(<span class="keyword">this</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> onClick() {</span><br><span class="line"> firebaseDb.ref(<span class="string">`todos`</span>).push({</span><br><span class="line"> title: <span class="keyword">this</span>.state.title,</span><br><span class="line"> description: <span class="keyword">this</span>.state.desc,</span><br><span class="line"> checked: <span class="literal">false</span>,</span><br><span class="line"> });</span><br><span class="line"> <span class="keyword">this</span>.setState({ <span class="attr">title</span>: <span class="string">''</span>, <span class="attr">desc</span>: <span class="string">''</span> });</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> render() {</span><br><span class="line"> <span class="keyword">const</span> { title, desc } = <span class="keyword">this</span>.state;</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <div className=<span class="string">"container"</span>></span><br><span class="line"> <div className=<span class="string">"field"</span>></span><br><span class="line"> <label className=<span class="string">"label"</span>>Title<<span class="regexp">/label></span></span><br><span class="line"><span class="regexp"> <input</span></span><br><span class="line"><span class="regexp"> className="input"</span></span><br><span class="line"><span class="regexp"> value={this.state.title}</span></span><br><span class="line"><span class="regexp"> onChange={e => this.setState({ title: e.target.value })}</span></span><br><span class="line"><span class="regexp"> /</span>></span><br><span class="line"> <<span class="regexp">/div></span></span><br><span class="line"><span class="regexp"> <div className="field"></span></span><br><span class="line"><span class="regexp"> <label className="label">Description</</span>label></span><br><span class="line"> <input</span><br><span class="line"> className=<span class="string">"input"</span></span><br><span class="line"> value={<span class="keyword">this</span>.state.desc}</span><br><span class="line"> onChange={e => <span class="keyword">this</span>.setState({ <span class="attr">desc</span>: e.target.value })}</span><br><span class="line"> /></span><br><span class="line"> <<span class="regexp">/div></span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp"> <div className="control"></span></span><br><span class="line"><span class="regexp"> {title &&</span></span><br><span class="line"><span class="regexp"> desc && (</span></span><br><span class="line"><span class="regexp"> <button className="button is-link" onClick={this.onClick}></span></span><br><span class="line"><span class="regexp"> Submit</span></span><br><span class="line"><span class="regexp"> </</span>button></span><br><span class="line"> )}</span><br><span class="line"> <<span class="regexp">/div></span></span><br><span class="line"><span class="regexp"> </</span>div></span><br><span class="line"> );</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>入力フォームではタスクの Title と Description を登録できるようにする。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">constructor</span>(props) {</span><br><span class="line"> <span class="keyword">super</span>(props)</span><br><span class="line"> <span class="keyword">this</span>.state = {</span><br><span class="line"> text: <span class="string">''</span>,</span><br><span class="line"> desc: <span class="string">''</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">this</span>.onClick = <span class="keyword">this</span>.onClick.bind(<span class="keyword">this</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>コンストラクタでは state の初期化と、イベントの bind を行う。イベントの bind は<a href="https://qiita.com/cubdesign/items/ee8bff7073ebe1979936" target="_blank" rel="noopener">React.createClass ではなく React.Component を使う場合に必要</a>なようだ。</p><p>submit の onClick で firebase の API を呼び出す。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">onClick() {</span><br><span class="line"> firebaseDb.ref(<span class="string">`todos`</span>).push({</span><br><span class="line"> title: <span class="keyword">this</span>.state.title,</span><br><span class="line"> description: <span class="keyword">this</span>.state.desc,</span><br><span class="line"> checked: <span class="literal">false</span>,</span><br><span class="line"> });</span><br><span class="line"> <span class="keyword">this</span>.setState({ <span class="attr">title</span>: <span class="string">''</span>, <span class="attr">desc</span>: <span class="string">''</span> });</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><code>.push()</code>を使ってデータ登録することにより、Todo に対して一意の ID を付与しつつ登録することができる。</p><p>そしてこのコンポーネントを App.js から呼びだす。</p><figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag"><<span class="name">section</span> <span class="attr">className</span>=<span class="string">"container"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">InputForm</span> /></span></span><br><span class="line"> <span class="tag"><<span class="name">TodoList</span> /></span></span><br><span class="line"><span class="tag"></<span class="name">section</span>></span></span><br></pre></td></tr></table></figure><p>以下のような画面になるはずだ</p><p><img src="/images/40-03.png" alt="フォームを追加した画面"></p><h2><span id="todo-を消せるようにする">Todo を消せるようにする</span></h2><p>最後に Todo を削除できるようにする。</p><p>完了のチェックボックスを付けて、チェックしたら削除ボタンが押せるようになる。だと Todo リストっぽい(よね?)</p><p>以下のように Todo コンポーネントに処理を追加する。</p><figure class="highlight jsx"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> React, { Component } <span class="keyword">from</span> <span class="string">'react'</span>;</span><br><span class="line"><span class="keyword">import</span> { firebaseDb } <span class="keyword">from</span> <span class="string">'./firebase'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="class"><span class="keyword">class</span> <span class="title">Todo</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>{</span><br><span class="line"> <span class="keyword">constructor</span>() {</span><br><span class="line"> <span class="keyword">super</span>();</span><br><span class="line"> <span class="keyword">this</span>.handleCheck = <span class="keyword">this</span>.handleCheck.bind(<span class="keyword">this</span>);</span><br><span class="line"> <span class="keyword">this</span>.handleDelete = <span class="keyword">this</span>.handleDelete.bind(<span class="keyword">this</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> handleCheck() {</span><br><span class="line"> firebaseDb.ref(<span class="string">`todos/<span class="subst">${<span class="keyword">this</span>.props.id}</span>`</span>).update({</span><br><span class="line"> checked: !<span class="keyword">this</span>.props.checked,</span><br><span class="line"> });</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> handleDelete() {</span><br><span class="line"> firebaseDb.ref(<span class="string">`todos/<span class="subst">${<span class="keyword">this</span>.props.id}</span>`</span>).remove();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> render() {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <li className=<span class="string">"todo"</span>></span><br><span class="line"> <nav className=<span class="string">"panel"</span>></span><br><span class="line"> <div className=<span class="string">"panel-heading"</span>></span><br><span class="line"> <p>{<span class="keyword">this</span>.props.title}<<span class="regexp">/p></span></span><br><span class="line"><span class="regexp"> </</span>div></span><br><span class="line"> <div className=<span class="string">"panel-block"</span>></span><br><span class="line"> <label className=<span class="string">"checkbox"</span>></span><br><span class="line"> <input</span><br><span class="line"> type=<span class="string">"checkbox"</span></span><br><span class="line"> onChange={<span class="keyword">this</span>.handleCheck}</span><br><span class="line"> checked={<span class="keyword">this</span>.props.checked}</span><br><span class="line"> /></span><br><span class="line"> {<span class="keyword">this</span>.props.description}</span><br><span class="line"> <<span class="regexp">/label></span></span><br><span class="line"><span class="regexp"> </</span>div></span><br><span class="line"> <div className=<span class="string">"panel-block"</span>></span><br><span class="line"> {<span class="keyword">this</span>.props.checked && (</span><br><span class="line"> <button className=<span class="string">"button is-link"</span> onClick={<span class="keyword">this</span>.handleDelete}></span><br><span class="line"> Delete</span><br><span class="line"> <<span class="regexp">/button></span></span><br><span class="line"><span class="regexp"> )}</span></span><br><span class="line"><span class="regexp"> </</span>div></span><br><span class="line"> <<span class="regexp">/nav></span></span><br><span class="line"><span class="regexp"> </</span>li></span><br><span class="line"> );</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3><span id="チェックボックスを付ける">チェックボックスを付ける</span></h3><p>チェックボックスを追加する。チェック状態は<code>this.props.checked</code>と同期させ、クリックすると<code>handleCheck</code>という関数を呼ぶようにする。</p><figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag"><<span class="name">input</span></span></span><br><span class="line"><span class="tag"> <span class="attr">type</span>=<span class="string">"checkbox"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">onChange</span>=<span class="string">{this.handleCheck}</span></span></span><br><span class="line"><span class="tag"> <span class="attr">checked</span>=<span class="string">{this.props.checked}</span></span></span><br><span class="line"><span class="tag">/></span></span><br></pre></td></tr></table></figure><p>呼ばれる関数は以下のものだ</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">handleCheck() {</span><br><span class="line"> firebaseDb.ref(<span class="string">`todos/<span class="subst">${<span class="keyword">this</span>.props.id}</span>`</span>).update({</span><br><span class="line"> checked: !<span class="keyword">this</span>.props.checked,</span><br><span class="line"> });</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>props の id をキーとして todos からデータを指定し、checked のみを update かける。ここで update を掛けると DB の変更を TodoList.jsx で検知するので、checked が変更されたデータが自動的にロードされる。</p><p>これでチェックボックスをクリックする度にチェックが切り替わる動作ができるようになる。</p><h3><span id="削除ボタンを付ける">削除ボタンを付ける</span></h3><p>次に、チェックボックスが ON になったら表示される削除ボタンを追加する。</p><figure class="highlight jsx"><table><tr><td class="code"><pre><span class="line">{</span><br><span class="line"> <span class="keyword">this</span>.props.checked && (</span><br><span class="line"> <button className=<span class="string">"button is-link"</span> onClick={<span class="keyword">this</span>.handleDelete}></span><br><span class="line"> Delete</span><br><span class="line"> <<span class="regexp">/button></span></span><br><span class="line"><span class="regexp"> );</span></span><br><span class="line"><span class="regexp">}</span></span><br></pre></td></tr></table></figure><p>チェックボックスと同様に、クリックすると<code>handleDelete</code>関数を呼ぶ。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">handleDelete() {</span><br><span class="line"> firebaseDb.ref(<span class="string">`todos/<span class="subst">${<span class="keyword">this</span>.props.id}</span>`</span>).remove();</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>props の id をキーとして DB からデータを削除する。これも update と同様に、データの変更を検知して、画面表示の一覧が自動的に更新される。</p><h1><span id="さいごに">さいごに</span></h1><p>Todo リストとして必要最小限の機能は実装することはできたが、React も Firebase も<strong>ギリギリ動かせた</strong>というのが正直なところ。</p><p>しかしこのお手軽さでリアルタイムな Web アプリを作れるのは非常に可能性を感じる。もっと色々いじり倒してみたいものだ。</p><h1><span id="環境">環境</span></h1><ul><li>Windows 10</li><li>React 16.4.2</li><li>create-react-app 1.5.2</li><li>Firebase 5.3.1</li></ul><h1><span id="参考">参考</span></h1><ul><li><a href="https://qiita.com/mikan3rd/items/20152cdd63a708264a9e" target="_blank" rel="noopener">【React】ToDo アプリを作ってみよう【前編】 - Qiita</a></li><li><a href="https://qiita.com/gonta616/items/278a7e81a8b624d9621e#firebase%E3%81%A7%E3%83%97%E3%83%AD%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B" target="_blank" rel="noopener">React + Redux + Firebase で作る Todo App - Qiita</a></li><li><a href="https://github.com/r-park/todo-react-redux" target="_blank" rel="noopener">r-park/todo-react-redux: Todo app with Create-React-App • React-Redux • Firebase • OAuth</a></li><li><a href="https://firebase.google.com/docs/database/web/read-and-write?hl=ja#listen_for_value_events" target="_blank" rel="noopener">ウェブでのデータの読み取りと書き込み | Firebase Realtime Database | Firebase</a></li></ul>]]></content>
<summary type="html">
<p>Firebase で静的サイトのホスティングと ESLint・Prettier の適用までやったので、Firebase Database を使って定番の ToDo アプリを作ってみたい。</p>
<p>ただ実際に写したり動かしたり色々検討しながらなので、内容がかなり乱暴になっているのはご容赦いただきたい。</p>
<hr>
<ul>
<li>9/24 大幅に書き直し、ちゃんとしました。</li>
</ul>
<hr>
</summary>
<category term="react" scheme="https://t-kojima.github.io/tags/react/"/>
<category term="firebase" scheme="https://t-kojima.github.io/tags/firebase/"/>
</entry>
<entry>
<title>[Firebase x React] ESLintとPrettierをReactに適用</title>
<link href="https://t-kojima.github.io/2018/08/12/0039-firebase-react-eslint-prettier/"/>
<id>https://t-kojima.github.io/2018/08/12/0039-firebase-react-eslint-prettier/</id>
<published>2018-08-12T01:17:55.000Z</published>
<updated>2018-12-20T11:07:38.000Z</updated>
<content type="html"><![CDATA[<p><a href="https://t-kojima.github.io/2018/08/12/0038-firebase-react-hosting/">前回</a>、create-react-app で React アプリを作成したが必要最小限の構成となっていた。</p><p>快適にコーディングするため、まずは ESLint と Prettier を適用させてみたい。</p><a id="more"></a><h1><span id="環境">環境</span></h1><ul><li>VSCode 1.25.1</li><li>Node 8.11.3</li></ul><p>エディタは VSCode を使用する。</p><h1><span id="eslint">ESLint</span></h1><p>yarn でガシガシいれていく。まず Standard スタイル</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">yarn add --dev eslint eslint-config-standard eslint-plugin-standard eslint-plugin-promise eslint-plugin-import eslint-plugin-node</span><br></pre></td></tr></table></figure><p>続いて React と JSX</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">yarn add --dev eslint-plugin-react eslint-plugin-jsx-a11y</span><br></pre></td></tr></table></figure><p>そして <code>.eslintrc.js</code> を作成する。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = {</span><br><span class="line"> env: {</span><br><span class="line"> browser: <span class="literal">true</span>,</span><br><span class="line"> es6: <span class="literal">true</span></span><br><span class="line"> },</span><br><span class="line"> extends: [<span class="string">'standard'</span>],</span><br><span class="line"> plugins: [<span class="string">'react'</span>, <span class="string">'jsx-a11y'</span>, <span class="string">'import'</span>],</span><br><span class="line"> rules: {}</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>もし VSCode で<code>ESLint</code>拡張機能を入れていない場合は入れておこう。</p><p>すると ESLint が効くようになるので、js ファイルを開くとエラーが表示されるようになる。</p><h1><span id="prettier">Prettier</span></h1><p>このままだとエラーは表示されるが自動的にフォーマットは掛けてくれないが、一個一個手動でエラーを潰すのは手間だ。</p><p>そこで Prettier を利用してフォーマットをかけることにする。 Prettier でフォーマットすることでいくつかのエラーは解決することができる。フォーマットに関わるエラーはこれで潰すと楽だ。</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">yarn add --dev prettier eslint-config-prettier eslint-plugin-prettier</span><br></pre></td></tr></table></figure><h2><span id="eslintrcjs">.eslintrc.js</span></h2><p>同時に<code>.eslintrc.js</code>も修正する。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = {</span><br><span class="line"> env: {</span><br><span class="line"> browser: <span class="literal">true</span>,</span><br><span class="line"> es6: <span class="literal">true</span></span><br><span class="line"> },</span><br><span class="line"> extends: [<span class="string">'standard'</span>, <span class="string">'prettier/react'</span>, <span class="string">'prettier/standard'</span>],</span><br><span class="line"> plugins: [<span class="string">'react'</span>, <span class="string">'jsx-a11y'</span>, <span class="string">'import'</span>, <span class="string">'prettier'</span>],</span><br><span class="line"> globals: {</span><br><span class="line"> it: <span class="literal">false</span></span><br><span class="line"> },</span><br><span class="line"> rules: {</span><br><span class="line"> <span class="string">'react/jsx-uses-vars'</span>: <span class="number">1</span>,</span><br><span class="line"> <span class="string">'react/jsx-uses-react'</span>: <span class="number">1</span>,</span><br><span class="line"> <span class="string">'space-before-function-paren'</span>: <span class="number">0</span>,</span><br><span class="line"> <span class="string">'comma-dangle'</span>: <span class="number">0</span>,</span><br><span class="line"> <span class="string">'prettier/prettier'</span>: [</span><br><span class="line"> <span class="string">'error'</span>,</span><br><span class="line"> {</span><br><span class="line"> semi: <span class="literal">false</span>,</span><br><span class="line"> singleQuote: <span class="literal">true</span>,</span><br><span class="line"> trailingComma: <span class="string">'es5'</span></span><br><span class="line"> }</span><br><span class="line"> ]</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>追加のルールには prettier のフォーマットを指定するルール他、4 つ追加している。</p><h3><span id="reactjsx-uses-vars">react/jsx-uses-vars</span></h3><p>このルールが適用されると、以下の JSX で<code>import App from './App'</code>が<code>no-unused-vars</code>でエラーとなってしまう。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> App <span class="keyword">from</span> <span class="string">'./App'</span></span><br><span class="line"></span><br><span class="line">ReactDOM.render(<span class="xml"><span class="tag"><<span class="name">App</span> /></span>, document.getElementById('root'))</span></span><br></pre></td></tr></table></figure><p><code>'react/jsx-uses-vars': 1</code>とすることでこのルールを回避する。</p><h3><span id="reactjsx-uses-react">react/jsx-uses-react</span></h3><p>JSX 自体を有効にするため、<code>import React from 'react'</code>としてインポートする必要があるが、このルールが適用されるとやはり<code>no-unused-vars</code>となる。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> React <span class="keyword">from</span> <span class="string">'react'</span></span><br></pre></td></tr></table></figure><p>これも同様に<code>'react/jsx-uses-react': 1</code>でルールを回避できる。</p><h3><span id="space-before-function-paren">space-before-function-paren</span></h3><p>Standard ルールでは<code>function ()</code>のように関数名と()の間にスペースが必要になる。</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">render () { // OK</span><br><span class="line">render() { // NG</span><br></pre></td></tr></table></figure><p>しかし Prettier のフォーマットではここにスペースを開けてはくれない。VSCode の設定を頑張ればスペースを入れれないこともないが、できるだけシンプルに解決したいし、そもそもスペースがあろうがなかろうがあまり気にしていないので、ルール自体を無効化する。</p><h2><span id="prettierrcjs">.prettierrc.js</span></h2><p>prettier の挙動をある程度設定するため、<code>.prettierrc.js</code>も作成する。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = {</span><br><span class="line"> semi: <span class="literal">false</span>,</span><br><span class="line"> singleQuote: <span class="literal">true</span>,</span><br><span class="line"> trailingComma: <span class="string">'es5'</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>作成すると言ったが、VSCode の場合は<code>"prettier.eslintIntegration": true,</code>という設定でも良い。</p><p>この設定を true にすると<code>.eslintrc.js</code>を参照して Prettier が挙動を決定する。つまり上記の<code>.eslintrc.js</code>のように Prettier の挙動をルールとして定義しておけば、<code>.prettierrc.js</code>不要で同様の挙動を取るようにすることができる。</p><p>こっちのほうがカンタンだ。</p><p>最後に VSCode で<code>Prettier</code>拡張機能を入れていない場合は入れておこう。これで Prettier が有効になり、フォーマットができるはずだ。</p><h1><span id="ついでに">ついでに</span></h1><p><code>create-react-app</code>で生成されるコードは以下のように Component を継承しているが、Stateless Functional Component (SFC) に書き直してみる。</p><figure class="highlight jsx"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">App</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>{</span><br><span class="line"> render() {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <div className=<span class="string">"App"</span>></span><br><span class="line"> <header className=<span class="string">"App-header"</span>></span><br><span class="line"> <img src={logo} className=<span class="string">"App-logo"</span> alt=<span class="string">"logo"</span> /></span><br><span class="line"> <h1 className=<span class="string">"App-title"</span>>Welcome to React<<span class="regexp">/h1></span></span><br><span class="line"><span class="regexp"> </</span>header></span><br><span class="line"> <p className=<span class="string">"App-intro"</span>></span><br><span class="line"> To get started, edit <code>src/App.js<<span class="regexp">/code> and save to reload.</span></span><br><span class="line"><span class="regexp"> </</span>p></span><br><span class="line"> <<span class="regexp">/div></span></span><br><span class="line"><span class="regexp"> )</span></span><br><span class="line"><span class="regexp"> }</span></span><br><span class="line"><span class="regexp">}</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">export default App</span></span><br></pre></td></tr></table></figure><p>とはいえ以下のように関数とするだけだ</p><figure class="highlight jsx"><table><tr><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> () => (</span><br><span class="line"> <div className=<span class="string">"App"</span>></span><br><span class="line"> <header className=<span class="string">"App-header"</span>></span><br><span class="line"> <img src={logo} className=<span class="string">"App-logo"</span> alt=<span class="string">"logo"</span> /></span><br><span class="line"> <h1 className=<span class="string">"App-title"</span>>Welcome to React<<span class="regexp">/h1></span></span><br><span class="line"><span class="regexp"> </</span>header></span><br><span class="line"> <p className=<span class="string">"App-intro"</span>></span><br><span class="line"> To get started, edit <code>src/App.js<<span class="regexp">/code> and save to reload.</span></span><br><span class="line"><span class="regexp"> </</span>p></span><br><span class="line"> <<span class="regexp">/div></span></span><br><span class="line"><span class="regexp">)</span></span><br></pre></td></tr></table></figure><p>それほど変わっていないが、<code>render</code>関数が不要になったり、<code>Component</code>を import しなくても良かったりするので、よりシンプルになったと思う。</p><h1><span id="さいごに">さいごに</span></h1><p>ESLint と Prettier を適用させたことで、スタイルチェックとフォーマットを使うことができるようになった。</p><p>これで安心(?)して React が書けるようになったネ!</p>]]></content>
<summary type="html">
<p><a href="https://t-kojima.github.io/2018/08/12/0038-firebase-react-hosting/">前回</a>、create-react-app で React アプリを作成したが必要最小限の構成となっていた。</p>
<p>快適にコーディングするため、まずは ESLint と Prettier を適用させてみたい。</p>
</summary>
<category term="react" scheme="https://t-kojima.github.io/tags/react/"/>
<category term="firebase" scheme="https://t-kojima.github.io/tags/firebase/"/>
<category term="eslint" scheme="https://t-kojima.github.io/tags/eslint/"/>
</entry>
<entry>
<title>[Firebase x React] ReactをFirebaseでHosting</title>
<link href="https://t-kojima.github.io/2018/08/12/0038-firebase-react-hosting/"/>
<id>https://t-kojima.github.io/2018/08/12/0038-firebase-react-hosting/</id>
<published>2018-08-12T01:00:21.000Z</published>
<updated>2018-12-20T11:07:38.000Z</updated>
<content type="html"><![CDATA[<p>2 ヶ月前ほどに Firebase で HelloWorld 的なチュートリアルはやったんだけど、2 か月経ったらほとんど忘れた&触れてない機能がまだまだあるので、ステップバイステップでミニマルに触ってみたいと思う。</p><p>全然関係無いけど StepByStep と minimal ってワードが好きです。</p><a id="more"></a><h1><span id="環境">環境</span></h1><ul><li>Node 8.11.3</li><li>Firebase CLI 4.0.3</li></ul><h1><span id="静的サイトをホスティング">静的サイトをホスティング</span></h1><p>まず一番基本的(?)な静的サイトのホスティングをしてみたい。アップするサイトは html 一つでもいいが、ここは react の HelloWorld も一緒にやってしまう。</p><h2><span id="react-で-helloworld">React で HelloWorld</span></h2><p><a href="https://github.com/facebook/create-react-app" target="_blank" rel="noopener">create-react-app</a>を使ってサクっと React プロジェクトを作成する。</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">mkdir tutorial-firebase-react</span><br><span class="line"><span class="built_in">cd</span> tutorial-firebase-react</span><br><span class="line"></span><br><span class="line">yarn init -y</span><br><span class="line">yarn global add create-react-app</span><br><span class="line">create-react-app .</span><br></pre></td></tr></table></figure><p>プロジェクトディレクトリを作成し、<code>create-react-app</code>を yarn でグローバルインストールし、<code>.</code>(カレントディレクトリ)を対象に React アプリを作成する。</p><p>いつもは<code>yarn add --dev</code>でこういったツールを入れる(なるべくグローバルに入れたくない)のだが、create-react-app はそうするとカレントディレクトリを対象にアプリを作成できない。create-react-app 自身がインストールされた node_modules や package.json を上書きできない(と、言われる)ためだ。</p><p>ちなみに<code>vue-cli</code>の場合はローカルインストールでも問題ない。<code>vue init webpack .</code>などやるとカレントの node_modules や package.json を上書きするからだ。個人的にはこっちの動きのほうがありがたい。しかしまあ好みの差かな。</p><p>インストールが完了したら<code>yarn start</code>でローカルサーバが立ち上がり、<a href="http://localhost:3000/" target="_blank" rel="noopener">http://localhost:3000/</a>からアプリを確認できる。(HelloWorld とは表示されないけど、まあそういう意味だ)</p><h2><span id="firebase-プロジェクトの準備">Firebase プロジェクトの準備</span></h2><p>まず<a href="https://firebase.google.com/" target="_blank" rel="noopener">Firebase</a>のコンソールにログインする。Google アカウントを持っていない場合は事前に作っておこう。</p><p><img src="/images/38-01.png" alt="右上のボタンからログイン"></p><p>「プロジェクトを追加」ボタンからプロジェクトの作成に進む。</p><p><img src="/images/38-02.png" alt="プロジェクトを追加を選択"></p><p>とりあえずプロジェクト名「tutorial-react」で作成</p><p>するとプロジェクトの管理画面に入れる。入れたらまずは OK</p><h2><span id="firebase-のインストール">Firebase のインストール</span></h2><h3><span id="ログイン">ログイン</span></h3><p>続いて Firebase 関係のツールをインストールする。まず CLI ツールから入れる。</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">yarn global add firebase-tools</span><br></pre></td></tr></table></figure><p>そして Firebase にログインする。</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">firebase login</span><br></pre></td></tr></table></figure><p>この時初回ログインだとブラウザが立ち上がり、google アカウントでのログインを求められる。ログインして「<code>Woohoo! Firebase CLI Login Successful</code>」というメッセージが表示されれば OK だ。コンソールにも以下のメッセージが表示され、ログインできたことが分かる。</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">✔ Success! Logged <span class="keyword">in</span> as **********@gmail.com</span><br></pre></td></tr></table></figure><p>二度目移行のログインだと前回のログイン情報を利用してログインする。アカウントを切り替えたい場合は<code>firebase login --reauth</code>とすることで、再度ブラウザからログイン処理を行う流れになる。</p><h3><span id="初期化">初期化</span></h3><p>続いてアプリに対し Firebase を初期化する。</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">firebase init</span><br></pre></td></tr></table></figure><p><img src="/images/38-04.png" alt="Hostingを選択"></p><p>今回はまず Hosting のみを選択。最初から色々入れると混乱してしまうので…</p><p>他にも色々聞かれるので基本 Enter で良いが、<code>? What do you want to use as your public directory?</code>のみは<code>build</code>を指定しよう。React アプリのビルド先が<code>build</code>ディレクトリだからだ。</p><h3><span id="ビルド-amp-デプロイ">ビルド & デプロイ</span></h3><p>最後に React を build して deploy する。</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">yarn build</span><br></pre></td></tr></table></figure><p>ビルドして…</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ firebase deploy</span><br><span class="line"></span><br><span class="line">=== Deploying to <span class="string">'tutorial-react-79436'</span>...</span><br><span class="line"></span><br><span class="line">i deploying hosting</span><br><span class="line">i hosting: preparing build directory <span class="keyword">for</span> upload...</span><br><span class="line">✔ hosting: 11 files uploaded successfully</span><br><span class="line"></span><br><span class="line">✔ Deploy complete!</span><br><span class="line"></span><br><span class="line">Project Console: https://console.firebase.google.com/project/tutorial-react-*****/overview</span><br><span class="line">Hosting URL: https://tutorial-react-*****.firebaseapp.com</span><br></pre></td></tr></table></figure><p>デプロイする。</p><p>最後にホスティングされた URL が表示されるので、アクセスして React の HelloWorld が表示されれば OK だ。</p><p>カンタン!</p><h1><span id="おわりに">おわりに</span></h1><p>React は create-react-app のおかげで煩雑な環境構築が必要なく、Firebase もまず Hosting だけなら難しいことは何も無かった。</p><p>ただ package.json を確認すると<code>react</code>、<code>react-dom</code>、<code>react-scripts</code>のみがインストールされているので、本当に最低限の構成ということが分かる。それなりに動かすアプリを作成する場合は明らかに機能は不足するので、ベースとして使うにしてもそこそこの肉付けは必要だろう。</p>]]></content>
<summary type="html">
<p>2 ヶ月前ほどに Firebase で HelloWorld 的なチュートリアルはやったんだけど、2 か月経ったらほとんど忘れた&触れてない機能がまだまだあるので、ステップバイステップでミニマルに触ってみたいと思う。</p>
<p>全然関係無いけど StepByStep と minimal ってワードが好きです。</p>
</summary>
<category term="react" scheme="https://t-kojima.github.io/tags/react/"/>
<category term="firebase" scheme="https://t-kojima.github.io/tags/firebase/"/>
</entry>
<entry>
<title>Node.js でも Jupyter Notebook が使いたい</title>
<link href="https://t-kojima.github.io/2018/08/09/0037-jupyter-notebook-for-nodejs/"/>
<id>https://t-kojima.github.io/2018/08/09/0037-jupyter-notebook-for-nodejs/</id>
<published>2018-08-09T06:27:32.000Z</published>
<updated>2018-12-20T11:07:38.000Z</updated>
<content type="html"><![CDATA[<p>Node.js でも Jupyter Notebook が使いたい!</p><p><img src="/images/37-jupyter.png" alt="jupyter logo"></p><a id="more"></a><h1><span id="環境">環境</span></h1><ul><li>Windows 10</li><li>Python 3.6.3</li><li>Node 8.11.3</li></ul><p><code>※Python と Node は既にインストール済み</code></p><h1><span id="jupyter-nodejs">jupyter-nodejs</span></h1><p>手始めに調べたところヒットしたのが以下のリポジトリだ。</p><p><a href="https://github.com/notablemind/jupyter-nodejs" class="embedly-card" data-card-image="0" data-card-controls="0" data-card-align="left" target="_blank" rel="noopener"></a></p><p>Installation に従って進めるが、Windows 環境だと <code>yarn install</code> のタイミングで以下のエラーになる。</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">info There appears to be trouble with your network connection. Retrying...</span><br><span class="line">[4/4] Building fresh packages...</span><br><span class="line">[1/1] ⠄ zmq: not ok</span><br><span class="line">[-/1] ⠄ waiting...</span><br><span class="line">[-/1] ⠄ waiting...</span><br><span class="line">[-/1] ⡀ waiting...</span><br><span class="line">error An unexpected error occurred: "C:\\Users\\********\\Documents\\Git\\JavaScript\\jupyter-nodejs\</span><br><span class="line">ode_modules\\zmq: Command failed.</span><br><span class="line">Exit code: 1</span><br><span class="line">Command: C:\\Windows\\system32\\cmd.exe</span><br><span class="line">Arguments: /d /s /c node-gyp rebuild</span><br><span class="line">Directory: C:\\Users\\********\\Documents\\Git\\JavaScript\\jupyter-nodejs\</span><br><span class="line">ode_modules\\zmq</span><br><span class="line">Output:</span><br><span class="line">C:\\Users\\********\\Documents\\Git\\JavaScript\\jupyter-nodejs\</span><br><span class="line">ode_modules\\zmq>if not defined npm_config_node_gyp (node \"C:\\Program Files\</span><br><span class="line">odejs\</span><br><span class="line">ode_modules\</span><br><span class="line">pm\\bin\</span><br><span class="line">ode-gyp-bin\\\\..\\..\</span><br><span class="line">ode_modules\</span><br><span class="line">ode-gyp\\bin\</span><br><span class="line">ode-gyp.js\" rebuild ) else (node \"\" rebuild )</span><br><span class="line">gyp info it worked if it ends with ok</span><br><span class="line">gyp info using [email protected]</span><br><span class="line">gyp info using [email protected] | win32 | x64</span><br><span class="line"></span><br><span class="line">...以下省略</span><br></pre></td></tr></table></figure><p>zmq がごにょごにょ…という感じで、ふんいきから感じ取ると恐らく <code>zeromq</code> のモジュールが無い感じだ。</p><p>探すと Windows 向けにも<a href="http://zeromq.org/distro:microsoft-windows" target="_blank" rel="noopener">ありそう</a>なんだけど、これを入れて解決するかは不明なので、WSL でやることにする。</p><p>ちなみに zeromq が無いとエラーになるので事前にインストールしておく。</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">sudo apt-get install libzmq-dev</span><br></pre></td></tr></table></figure><p>そしてインストール手順に従って進めていく。</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">git <span class="built_in">clone</span> https://github.com/notablemind/jupyter-nodejs.git</span><br><span class="line"><span class="built_in">cd</span> jupyter-nodejs</span><br><span class="line">mkdir -p ~/.ipython/kernels/nodejs/</span><br><span class="line">npm install && node install.js</span><br><span class="line">npm run build</span><br><span class="line">npm run build-ext</span><br></pre></td></tr></table></figure><p>インストール自体は問題なくいけた。</p><p>しかし、<code>ipython console --kernel nodejs</code> でやはりエラーになる。</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">$ ipython console --kernel nodejs</span><br><span class="line">Traceback (most recent call last):</span><br><span class="line"> File "/usr/bin/ipython", line 6, in <module></span><br><span class="line"> start_ipython()</span><br><span class="line"> File "/usr/lib/python2.7/dist-packages/IPython/__init__.py", line 118, in start_ipython</span><br><span class="line"> return launch_new_instance(argv=argv, **kwargs)</span><br><span class="line"> File "/usr/lib/python2.7/dist-packages/IPython/config/application.py", line 545, in launch_instance</span><br><span class="line"> app.initialize(argv)</span><br><span class="line"> File "<string>", line 2, in initialize</span><br><span class="line"> File "/usr/lib/python2.7/dist-packages/IPython/config/application.py", line 89, in catch_config_error</span><br><span class="line"> return method(app, *args, **kwargs)</span><br><span class="line"> File "/usr/lib/python2.7/dist-packages/IPython/terminal/ipapp.py", line 312, in initialize</span><br><span class="line"> super(TerminalIPythonApp, self).initialize(argv)</span><br><span class="line"> File "<string>", line 2, in initialize</span><br><span class="line"> File "/usr/lib/python2.7/dist-packages/IPython/config/application.py", line 89, in catch_config_error</span><br><span class="line"> return method(app, *args, **kwargs)</span><br><span class="line"> File "/usr/lib/python2.7/dist-packages/IPython/core/application.py", line 373, in initialize</span><br><span class="line"> self.parse_command_line(argv)</span><br><span class="line"> File "/usr/lib/python2.7/dist-packages/IPython/terminal/ipapp.py", line 307, in parse_command_line</span><br><span class="line"> return super(TerminalIPythonApp, self).parse_command_line(argv)</span><br><span class="line"> File "<string>", line 2, in parse_command_line</span><br><span class="line"> File "/usr/lib/python2.7/dist-packages/IPython/config/application.py", line 89, in catch_config_error</span><br><span class="line"> return method(app, *args, **kwargs)</span><br><span class="line"> File "/usr/lib/python2.7/dist-packages/IPython/config/application.py", line 474, in parse_command_line</span><br><span class="line"> return self.initialize_subcommand(subc, subargv)</span><br><span class="line"> File "<string>", line 2, in initialize_subcommand</span><br><span class="line"> File "/usr/lib/python2.7/dist-packages/IPython/config/application.py", line 89, in catch_config_error</span><br><span class="line"> return method(app, *args, **kwargs)</span><br><span class="line"> File "/usr/lib/python2.7/dist-packages/IPython/config/application.py", line 405, in initialize_subcommand</span><br><span class="line"> subapp = import_item(subapp)</span><br><span class="line"> File "/usr/lib/python2.7/dist-packages/IPython/utils/importstring.py", line 42, in import_item</span><br><span class="line"> module = __import__(package, fromlist=[obj])</span><br><span class="line"> File "/usr/lib/python2.7/dist-packages/IPython/terminal/console/app.py", line 25, in <module></span><br><span class="line"> from IPython.consoleapp import (</span><br><span class="line"> File "/usr/lib/python2.7/dist-packages/IPython/consoleapp.py", line 35, in <module></span><br><span class="line"> from IPython.kernel.blocking import BlockingKernelClient</span><br><span class="line"> File "/usr/lib/python2.7/dist-packages/IPython/kernel/__init__.py", line 4, in <module></span><br><span class="line"> from . import zmq</span><br><span class="line"> File "/usr/lib/python2.7/dist-packages/IPython/kernel/zmq/__init__.py", line 14, in <module></span><br><span class="line"> check_for_zmq('2.1.11', 'IPython.kernel.zmq')</span><br><span class="line"> File "/usr/lib/python2.7/dist-packages/IPython/utils/zmqrelated.py", line 37, in check_for_zmq</span><br><span class="line"> raise ImportError("%s requires python-zmq >= %s"%(required_by, minimum_version))</span><br><span class="line">ImportError: IPython.kernel.zmq requires python-zmq >= 2.1.11</span><br></pre></td></tr></table></figure><p><code>pip install pyzmq</code> してもこのエラーは解消しない。</p><p>というかこれは仮に動いても Github の README を見る限り、なんか ipython で nodejs が動くようなふんいきだ。</p><p>僕がやりたいのはブラウザで動くタイプの Jupyter Notebook なんです!</p><h1><span id="ijavascript">ijavascript</span></h1><p>次の選択肢はこれ、こちらはちゃんとローカルサーバーが立って Jupyter Notebook が使えるふんいきだ。</p><p><a href="https://github.com/n-riesco/ijavascript" class="embedly-card" data-card-image="0" data-card-controls="0" data-card-align="left" target="_blank" rel="noopener"></a></p><p>これも Installation に従って進める</p><p>まず pipenv を使って、venv の仮想環境に jupyter を入れる。</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">pipenv install jupyter</span><br><span class="line">pipenv shell</span><br></pre></td></tr></table></figure><p>つぎに ijavascript を yarn でインストール。</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">yarn init</span><br><span class="line">yarn add ijavascript</span><br><span class="line">yarn ijsinstall</span><br></pre></td></tr></table></figure><p>起動!</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">jupyter notebook</span><br></pre></td></tr></table></figure><p>するとローカルサーバーが立ちあがり、Node.js が追加された Jupyter Notebook を使うことができる。</p><p><img src="/images/37-01.png" alt="Node.jsが選択できるようになっている"></p><h2><span id="スクリプトの実行で-kernel-error">スクリプトの実行で Kernel Error</span></h2><p>しかし実際ノートブックを書いていると、JavaScript が動作せず、<code>Kernel Error</code>というエラーが出ていた。</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">[E 10:56:36.735 NotebookApp] Failed to run command:</span><br><span class="line"> ['ijskernel.cmd', '--hide-undefined', 'C:\\Users\\********\\AppData\\Roaming\\jupyter\\runtime\\kernel-0330c836-ab32-4e7b-bb1f-758b26fa4375.json', '--protocol=5.0']</span><br><span class="line"> PATH='C:\\Users\\********\\Documents\\Git\\JavaScript\\jupyter\\.venv\\Scripts;C:\\ProgramData\\Oracle\\Java\\javapath;C:\\Program Files\\Docker\\Docker\\Resources\\bin;C:\\Program Files\\Microsoft MPI\\Bin\\;C:\\Windows\\system32;C:\\Windows;C:\\Windows\\System32\\Wbem;C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\;C:\\Program Files (x86)\\Microsoft SQL Server\\140\\Tools\\Binn\\;C:\\Program Files\\Microsoft SQL Server\\140\\Tools\\Binn\\;C:\\Program Files (x86)\\Microsoft SQL Server\\140\\DTS\\Binn\\;C:\\Program Files\\Microsoft SQL Server\\140\\DTS\\Binn\\;C:\\Program Files\\Microsoft SQL Server\\Client SDK\\ODBC\\130\\Tools\\Binn\\;C:\\Program Files (x86)\\Microsoft SQL Server\\Client SDK\\ODBC\\130\\Tools\\Binn\\;C:\\Program Files (x86)\\Microsoft SQL Server\\140\\Tools\\Binn\\ManagementStudio\\;C:\\Program Files\\Microsoft SQL Server\\130\\Tools\\Binn\\;C:\\Users\\********\\AppData\\Local\\Programs\\Python\\Python36-32\\Scripts\\;C:\\Users\\********\\AppData\\Local\\Programs\\Python\\Python36-32\\;C:\\Users\\********\\AppData\\Local\\Microsoft\\WindowsApps;C:\\Program Files\\Microsoft VS Code\\bin;C:\\Ruby\\Tools\\uru;C:\\Ruby\\Ruby23\\bin;C:\\Users\\********\\AppData\\Roaming\\npm;C:\\Program Files\\OpenSSH;C:\\Program Files\\Git\\cmd;C:\\ProgramData\\chocolatey\\bin;C:\\Program Files\\nodejs\\;C:¥MinGW¥bin;C:\\Users\\********\\AppData\\Roaming\\local\\bin;C:\\Users\\********\\AppData\\Local\\Programs\\Python\\Python36-32\\Scripts\\;C:\\Users\\********\\AppData\\Local\\Programs\\Python\\Python36-32\\;C:\\Users\\********\\AppData\\Local\\Microsoft\\WindowsApps;C:\\Ruby\\Tools\\uru;C:\\Ruby\\Ruby23\\bin;C:\\Program Files\\cli-kintone;C:\\Program Files\\Microsoft VS Code\\bin;C:\\Program Files (x86)\\phantomjs-2.1.1-windows\\bin;C:\\Program Files (x86)\\chromedriver_win32;C:\\Program Files\\wkhtmltopdf\\bin;C:\\Program Files\\wkhtmltopdf\\bin\\wkhtmltoimage.exe;C:\\tools\\cmder;C:\\Users\\********\\AppData\\Roaming\\npm;c:\\users\\********\\appdata\\local\\programs\\python\\python36-32\\lib\\site-packages\\pywin32_system32'</span><br><span class="line"> with kwargs:</span><br><span class="line"> {'stdin': -1, 'stdout': None, 'stderr': None, 'cwd': 'C:\\Users\\********\\Documents\\Git\\JavaScript\\jupyter'}</span><br><span class="line"></span><br><span class="line">[E 10:56:36.737 NotebookApp] Uncaught exception POST /api/sessions (127.0.0.1)</span><br><span class="line"> HTTPServerRequest(protocol='http', host='localhost:8888', method='POST', uri='/api/sessions', version='HTTP/1.1', remote_ip='127.0.0.1')</span><br><span class="line"> Traceback (most recent call last):</span><br><span class="line"> File "c:\users\********\documents\git\javascript\jupyter\.venv\lib\site-packages\tornado\web.py", line 1592, in _execute</span><br><span class="line"> result = yield result</span><br><span class="line"> File "c:\users\********\documents\git\javascript\jupyter\.venv\lib\site-packages\tornado\gen.py", line 1133, in run</span><br><span class="line"> value = future.result()</span><br><span class="line"> File "c:\users\********\documents\git\javascript\jupyter\.venv\lib\site-packages\tornado\gen.py", line 1141, in run</span><br><span class="line"> yielded = self.gen.throw(*exc_info)</span><br><span class="line"> File "c:\users\********\documents\git\javascript\jupyter\.venv\lib\site-packages\notebook\services\sessions\handlers.py", line 73, in post</span><br><span class="line"> type=mtype))</span><br><span class="line"> File "c:\users\********\documents\git\javascript\jupyter\.venv\lib\site-packages\tornado\gen.py", line 1133, in run</span><br><span class="line"> value = future.result()</span><br><span class="line"> File "c:\users\********\documents\git\javascript\jupyter\.venv\lib\site-packages\tornado\gen.py", line 1141, in run</span><br><span class="line"> yielded = self.gen.throw(*exc_info)</span><br><span class="line"> File "c:\users\********\documents\git\javascript\jupyter\.venv\lib\site-packages\notebook\services\sessions\sessionmanager.py", line 79, in create_session</span><br><span class="line"> kernel_id = yield self.start_kernel_for_session(session_id, path, name, type, kernel_name)</span><br><span class="line"> File "c:\users\********\documents\git\javascript\jupyter\.venv\lib\site-packages\tornado\gen.py", line 1133, in run</span><br><span class="line"> value = future.result()</span><br><span class="line"> File "c:\users\********\documents\git\javascript\jupyter\.venv\lib\site-packages\tornado\gen.py", line 1141, in run</span><br><span class="line"> yielded = self.gen.throw(*exc_info)</span><br><span class="line"> File "c:\users\********\documents\git\javascript\jupyter\.venv\lib\site-packages\notebook\services\sessions\sessionmanager.py", line 92, in start_kernel_for_session</span><br><span class="line"> self.kernel_manager.start_kernel(path=kernel_path, kernel_name=kernel_name)</span><br><span class="line"> File "c:\users\********\documents\git\javascript\jupyter\.venv\lib\site-packages\tornado\gen.py", line 1133, in run</span><br><span class="line"> value = future.result()</span><br><span class="line"> File "c:\users\********\documents\git\javascript\jupyter\.venv\lib\site-packages\tornado\gen.py", line 326, in wrapper</span><br><span class="line"> yielded = next(result)</span><br><span class="line"> File "c:\users\********\documents\git\javascript\jupyter\.venv\lib\site-packages\notebook\services\kernels\kernelmanager.py", line 160, in start_kernel</span><br><span class="line"> super(MappingKernelManager, self).start_kernel(**kwargs)</span><br><span class="line"> File "c:\users\********\documents\git\javascript\jupyter\.venv\lib\site-packages\jupyter_client\multikernelmanager.py", line 110, in start_kernel</span><br><span class="line"> km.start_kernel(**kwargs)</span><br><span class="line"> File "c:\users\********\documents\git\javascript\jupyter\.venv\lib\site-packages\jupyter_client\manager.py", line 259, in start_kernel</span><br><span class="line"> **kw)</span><br><span class="line"> File "c:\users\********\documents\git\javascript\jupyter\.venv\lib\site-packages\jupyter_client\manager.py", line 204, in _launch_kernel</span><br><span class="line"> return launch_kernel(kernel_cmd, **kw)</span><br><span class="line"> File "c:\users\********\documents\git\javascript\jupyter\.venv\lib\site-packages\jupyter_client\launcher.py", line 128, in launch_kernel</span><br><span class="line"> proc = Popen(cmd, **kwargs)</span><br><span class="line"> File "c:\users\********\appdata\local\programs\python\python36-32\Lib\subprocess.py", line 709, in __init__</span><br><span class="line"> restore_signals, start_new_session)</span><br><span class="line"> File "c:\users\********\appdata\local\programs\python\python36-32\Lib\subprocess.py", line 997, in _execute_child</span><br><span class="line"> startupinfo)</span><br><span class="line"> FileNotFoundError: [WinError 2] 指定されたファイルが見つかりません。</span><br><span class="line">[W 10:56:36.750 NotebookApp] Unhandled error</span><br><span class="line">[E 10:56:36.751 NotebookApp] {</span><br><span class="line"> "Host": "localhost:8888",</span><br><span class="line"> "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:61.0) Gecko/20100101 Firefox/61.0",</span><br><span class="line"> "Accept": "application/json, text/javascript, */*; q=0.01",</span><br><span class="line"> "Accept-Language": "ja,en-US;q=0.7,en;q=0.3",</span><br><span class="line"> "Accept-Encoding": "gzip, deflate",</span><br><span class="line"> "Referer": "http://localhost:8888/notebooks/Tilde_and_IndexOf.ipynb",</span><br><span class="line"> "Content-Type": "application/json",</span><br><span class="line"> "X-Xsrftoken": "2|4dcc5b30|03fde516121f595f281c8c0fe4867c74|1533774838",</span><br><span class="line"> "X-Requested-With": "XMLHttpRequest",</span><br><span class="line"> "Content-Length": "103",</span><br><span class="line"> "Cookie": "username-localhost-8888=\"2|1:0|10:1533779787|23:username-localhost-8888|44:NDJlN2ExNGFkMzliNGY4YjlkNDNiMWYxODg5NTE1YWM=|0623f8e34931a73fea2928b271be35e69dea1d11d513d74d0b3b0b4a8a76275b\"; _xsrf=2|4dcc5b30|03fde516121f595f281c8c0fe4867c74|1533774838",</span><br><span class="line"> "Dnt": "1",</span><br><span class="line"> "Connection": "keep-alive"</span><br><span class="line"> }</span><br><span class="line">[E 10:56:36.751 NotebookApp] 500 POST /api/sessions (127.0.0.1) 63.00ms referer=http://localhost:8888/notebooks/Tilde_and_IndexOf.ipynb</span><br></pre></td></tr></table></figure><p>調べるとカーネルのパスに問題があると同様のエラーになるらしい。</p><p>以下が現在の状態のパスだ</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ jupyter kernelspec list</span><br><span class="line">Available kernels:</span><br><span class="line"> javascript C:\Users\********\AppData\Roaming\jupyter\kernels\javascript</span><br><span class="line"> python3 c:\users\********\documents\git\javascript\jupyter\.venv\share\jupyter\kernels\python3</span><br></pre></td></tr></table></figure><p>試しに <code>jupyter kernelspec remove python3</code> で python3 のパスを削除してみる。</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ jupyter kernelspec list</span><br><span class="line">Available kernels:</span><br><span class="line"> python3 c:\users\********\documents\git\javascript\jupyter\.venv\lib\site-packages\ipykernel\resources</span><br><span class="line"> javascript C:\Users\********\AppData\Roaming\jupyter\kernels\javascript</span><br></pre></td></tr></table></figure><p>すると以下のパスに変化した。remove するとデフォルト(?)のパスが設定されるのだろうか!?<br>そして相変わらずエラーは解消しない。</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ jupyter kernelspec list</span><br><span class="line">Available kernels:</span><br><span class="line"> javascript C:\Users\********\AppData\Roaming\jupyter\kernels\javascript</span><br><span class="line"> python3 c:\users\********\appdata\<span class="built_in">local</span>\programs\python\python36-32\share\jupyter\kernels\python3</span><br></pre></td></tr></table></figure><p>もしかしたら Python モジュールも Node モジュールも仮想環境だったり、プロジェクトローカルにインストールしていたのが悪かったのかもしれない。</p><p>なのでグローバルインストールしてみる。</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">npm install -g ijavascript</span><br></pre></td></tr></table></figure><p>すると…<strong>動いた</strong>。これが原因か?</p><p>しかし <code>jupyter kernelspec list</code> のパスは最後の状態のままだ。python3 のパスが <code>c:\users\********\documents\git\javascript\jupyter\.venv\share\jupyter\kernels\python3</code> から <code>c:\users\********\appdata\local\programs\python\python36-32\share\jupyter\kernels\python3</code> に変わったことは何か影響あるのだろうか?</p><p>ただ <code>jupyter kernelspec install</code> などで元の状態への復旧を試みたが、どうしても戻らなかったのでこのまま動かすことにする。</p><h2><span id="nodejs-の少し困った挙動">Node.js の少し困った(?)挙動</span></h2><p>Jupyter Notebook は一つのノートブックが一つの REPL プロセスのような挙動を取る。つまりあるセルで定義した変数は保持され別のセルで参照可能だし、全てのセルを一旦削除して、新たにセルを作り直したとしても変数は保持されたままになり参照できる。</p><p>以下は Python のノートブックだが、このようにセルを跨いで変数の参照ができるし</p><p><img src="/images/37-02.png" alt="次のセルでも参照可能"></p><p>以下のように一旦セルを全て削除しても変数は参照可能だ。</p><p><img src="/images/37-03.png" alt="一旦削除しても変数は残る"></p><p>これは Node.js でも全く同じように動作するのだが、<code>const</code> を使った変数を定義すると少し困ったこと(?)になる。</p><p>Node.js のノートブックでも以下のように変数を定義してみよう。</p><p><img src="/images/37-04.png" alt="最初は問題ないように見えるが..."></p><p>これは問題ない。しかし一旦書き直したくなってセルを全削除した場合はどうなるだろう</p><p><img src="/images/37-05.png" alt="二度目はエラーになる"></p><p>このようにエラーになってしまう。つまり 2 度目からは <code>test</code> という変数が定義できず、書き直しができないのだ。</p><p>前述の挙動を理解ていれば当たり前に思えるのだが、最初どうなっているか分からず詰まってしまった。</p><p>対策としては <code>const</code> や <code>let</code> ではなく <code>var</code> を使えば良いし、何なら省略してもいい。ただノートブックでそこまでスコープを意識しないにしても、意図しない挙動を避けるためスコープの違いは覚えておくべきだ。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">value = <span class="number">1</span> <span class="comment">// グローバルスコープ</span></span><br><span class="line"><span class="keyword">var</span> value2 = <span class="number">1</span> <span class="comment">// 関数スコープ</span></span><br><span class="line"><span class="keyword">let</span> value3 = <span class="number">1</span> <span class="comment">// ブロックスコープ</span></span><br><span class="line"><span class="keyword">const</span> value4 = <span class="number">1</span> <span class="comment">// ブロックスコープ</span></span><br></pre></td></tr></table></figure><p>とはいえチョロっと書くようなノートは宣言なしでやってしまってもいいだろう。スコープの問題は頭の隅にでも留めておけば問題ない。</p><h1><span id="github-でも見れる">Github でも見れる</span></h1><p>Github に <code>.ipynb</code> ファイルをアップすると Jupyter Notebook としてレンダリングされる。</p><p><a href="https://github.com/t-kojima/practice-black-python/blob/master/notebook/ghost_method.ipynb" class="embedly-card" data-card-image="0" data-card-controls="0" data-card-align="left" target="_blank" rel="noopener"></a></p><p>イイネ!</p><h1><span id="参考">参考</span></h1><ul><li><a href="http://kato-robotics.hatenablog.com/entry/2018/03/27/155434" target="_blank" rel="noopener">JavaScript の始め方 (Windows, Jupyter notebook) - YKpages</a></li><li><a href="https://github.com/n-riesco/ijavascript" target="_blank" rel="noopener">n-riesco/ijavascript: IJavascript is a javascript kernel for the Jupyter notebook</a></li><li><a href="http://www.geocities.jp/penguinitis2002/computer/programming/Python/jupyter_notebook_kernel_error.html" target="_blank" rel="noopener">PENGUINITIS - Jupyter Notebook の Kernel error メモ</a></li></ul>]]></content>
<summary type="html">
<p>Node.js でも Jupyter Notebook が使いたい!</p>
<p><img src="/images/37-jupyter.png" alt="jupyter logo"></p>
</summary>
<category term="nodejs" scheme="https://t-kojima.github.io/tags/nodejs/"/>
<category term="jupyter" scheme="https://t-kojima.github.io/tags/jupyter/"/>
</entry>
</feed>