Qiita投稿一覧サイトの見直し

前回宣言したようにnuxtを使ったアプリケーションの1つである
Qiita投稿一覧サイトを見直したいと思います。

具体的に何をするか

以下の4点に取り組もうと思います。

  1. 各依存関係のバージョンアップ
  2. トップへ移動の処理をvue-scrolltoに代替
  3. スタイルをscssで書き直す
  4. 処理の見直し、機能追加

各依存関係のバージョンアップ

依存関係の(マイナー)バージョンアップを行います。

name before after
nuxt ^1.1.1 ^1.4.2
axios ^0.17.1 ^0.18.0
element-ui ^2.0.11 ^2.4.6
babel-eslint ^7.2.3 ^7.2.3
eslint ^4.3.0 ^4.19.1
eslint-config-standard ^10.2.1 ^10.2.1
eslint-loader ^1.9.0 ^1.9.0
eslint-plugin-html ^2.7.0 ^3.2.2
eslint-plugin-import ^2.0.11 ^2.14.0
eslint-plugin-node ^5.1.1 ^5.2.1
eslint-plugin-promise ^3.5.0 ^3.8.0
eslint-plugin-standard ^3.0.1 ^3.0.1
続きを読む

Google Kubernetes Engine 上で Spring Boot を動かしてみる

高幡不動のあじさい

先日、珈琲仲間に誘われて高幡不動のあじさいを観に行ってきました。
あいにくの雨でしたが、色んな種類のあじさいが綺麗に咲いていて心が洗われました。

写真の右側のようなあじさいを「がくあじさい」と呼ぶそうです。
これまで中央部分が単に咲いていない(これから開花する)んだと思ってた… (´▽`*)

meet K8s

その内、GCPがAWSのシェアを塗り替えるんじゃないかと思っているのと
DockerやKubernetesとうワードを見かけない日がないので
GCP上でKubernetesを触って理解を深めたいと思います。


2018/8/1 追記
世界的にシェアを見ればAWS、Microsoft、Googleの順なんですね!
Alibabaの勢いもすごい…


Dockerとは

Docker enables true independence between applications and infrastructure and developers and IT ops to unlock their potential and creates a model for better collaboration and innovation.

Containers are an abstraction at the app layer that packages code and dependencies together. Multiple containers can run on the same machine and share the OS kernel with other containers, each running as isolated processes in user space. Containers take up less space than VMs (container images are typically tens of MBs in size), and start almost instantly.

Docker 社が開発しているコンテナ型の仮想環境を作成/配布/実行するためのプラットフォーム。
「コンテナ」と呼ばれる技術によりホストマシンのカーネルを利用しプロセスを隔離することで、あたかも別のマシン上のように動かすことができる。
ゲスト OS をインストールした上でミドルウェアやライブラリ、アプリケーションを動かすハイパーバイザ型やホスト型の仮想実行環境と比べリソースを効率的に利用できるのが特徴。

開発環境の準備/統一に便利だな〜くらいの認識でしたが、最近は運用にも使われているよう。
まさしく DevOps ですね。

ハイパーバイザ型やホスト型の仮想実行環境を使うことが多いのですが
確かに時間もリソースもかかるので、そこが簡略できるのはとても魅力的。

Kubernetes とは

It has a large, rapidly growing ecosystem.
Kubernetes provides a container-centric management environment. It orchestrates computing, networking, and storage infrastructure on behalf of user workloads. This provides much of the simplicity of Platform as a Service (PaaS) with the flexibility of Infrastructure as a Service (IaaS), and enables portability across infrastructure providers.

コンテナオーケストレーションツールと称される Kubernetes。
コンテナ化されたアプリケーションのデプロイ、スケーリング、管理を行うために用いる。
コンテナオーケストレーションツールは Kubernetes の他に、Docker Swarm や Apache Mesos などがある。

Kubernetes はクラスターが強力なのだそう。
GUI が用意されているし、オートスケーリングやロードバランシング、ヘルスチェック以外で自己修復もしてくれる。

オーケストレーション、賑やかな印象を受けます。

Kubernetes Clusters とは

Kubernetes のアーキテクチャについてはTHENEWSTACKさんの図がわかりやすいです。
#ぱっと見た中では公式サイトのドキュメントから概要図を見つけられませんでした。

a Kubernetes cluster consists of at least one master and multiple compute nodes. The master is responsible for exposing the application program interface (API), scheduling the deployments and managing the overall cluster. Each node runs a container runtime, such as Docker or rkt, along with an agent that communicates with the master. The node also runs additional components for logging, monitoring, service discovery and optional add-ons. Nodes are the workhorses of a Kubernetes cluster. They expose compute, networking and storage resources to applications. Nodes can be virtual machines (VMs) running in a cloud or bare metal servers running within the data center.
A pod is a collection of one or more containers. The pod serves as Kubernetes’ core unit of management. Pods act as the logical boundary for containers sharing the same context and resources. The grouping mechanism of pods make up for the differences between containerization and virtualization by making it possible to run multiple dependent processes together. At runtime, pods can be scaled by creating replica sets, which ensure that the deployment always runs the desired number of pods.

Kubernetes のクラスターは少なくとも 1 つのマスターと複数のノードから構成されており、マスターは API の公開、デプロイのスケジューリング、すべてのクラスタの管理を行います。
各ノードは Docker などのコンテナとマスターと通信するエージェントを実行します。加えて、ロギング、モニタリング、サービス検出やオプションのアドオンなど追加のコンポーネントも実行します。
ポッドは 1 つまたは複数のコンテナをグループ化します。このグループ化におけるメカニズムは、複数の依存プロセスを一緒に実行できるようにすることで、コンテナ化と仮想化の違いを補っています。実行時、ポッドはレプリカセットを作成することでスケールを可能にします。

…大雑把に言えば、マスターの配下にポッド、レプリカセット、サービス等 Kubernetes オブジェクトがありロードバランシングを可能にしてしてくれているんですね。

実際に触ってみる

なんとなく概要をみたところで GCP のチュートリアルにトライします。
書いてあることを実行する中で 2 点エラーが発生したのでそこを中心に明記します。

Dockerizing your application

Creating a Dockerfile

ローカルでビルドした際にはうまくいったのですが、GCP でビルドを行った際に gradlew に権限がないとエラーが発生しました。

そこで実行権限を付与することで対応しました。

1
2
3
4
5
6
7
8
9
10
// Dockerfile
FROM openjdk:8-jdk-alpine
VOLUME /tmp
RUN mkdir /work
COPY . /work
WORKDIR /work
RUN chmod +x /work/gradlew
RUN /work/gradlew build
RUN mv /work/build/libs/*.jar /work/app.jar
ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/.urandom", "-jar", "/work/app.jar"]

Create a cluster

クラスターを作成する際にzoneregionを指定してと言われました。

こちらは--zone=asia-northeast1をつけることで対応。
参照 => gcloud container clusters create

Deploy to the cluster

ポッドをデプロイ、デプロイしたポッドをロードバランシングで公開します。

EXTERNAL-IP に割り当てられたアドレスにアクセス、動作していることが確認できました。

Scaling and updating your application

Set the replica count

レプリカセットの値を 3 から 2 へ変更します。
スケールされていることが確認できました。

まとめ

難しいと思っていた K8s ですが、触るだけなら簡単にできました。GCP のおかげですね!
動かすアプリケーションの規模が小さいこともあり、スケーリングの恩恵はあまり感じられませんでしたが、それでもお手軽さは感じることができました。

クラウド化による Paas、IaaS でもすごいと感じるのに
さらにコンテナ化したものをオーケストレーションする技術まで出てきて
クラウドの技術革新には目を見張ります。

そもそも開発体制として Docker を導入していないので
Kubernetes を業務で扱うことはないだろうけど
社内ツール類をコンテナ化してまとめられたらすっきりしそう。
弊社は(お客様の)鶴の一声がないと新しいツールの導入はまずないので難しいかな…。
少しでも便利に、安定に、なサイクルを模索していきたいところ。

The DevOps ハンドブック、読み始めました。

参考

avaでテスト&GitLab CIに触れてみる

今日は前回作成した体重可視化サイトでavaによるテスト作成と
作成したテストをGitLab CIで走らせたいと思います。

テスト作成

公式の説明に従い、設定やテストケースを作成します。

ava、jsdomのインストール

1
yarn add --dev ava jsdom

package.jsonにava、jsdomを追加

1
2
3
4
5
6
7
8
9
10
11
12
13
"scripts": {
"test": "ava",
},
"ava": {
"require": [
"babel-register"
]
},
"babel": {
"presets": [
"es2015"
]
}

テストケースの作成

testというフォルダを作成し、index.test.jsを作成します。
テストケースはほぼ公式そのままです。

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
import test from 'ava'
import { Nuxt, Builder } from 'nuxt'
import { resolve } from 'path'

// We keep a reference to Nuxt so we can close
// the server at the end of the test
let nuxt = null

// Init Nuxt.js and start listening on localhost:4000
test.before('Init Nuxt.js', async t => {
const rootDir = resolve(__dirname, '..')
let config = {}
try { config = require(resolve(rootDir, 'nuxt.config.js')) } catch (e) {}
config.rootDir = rootDir // project folder
config.dev = false // production build
nuxt = new Nuxt(config)
await new Builder(nuxt).build()
nuxt.listen(4000, 'localhost')
})

// Example of testing only generated html
test('Route / exits and render HTML', async t => {
let context = {}
const { html } = await nuxt.renderRoute('/', context)
t.true(html.includes('<h1 class="title">Weight Loss Progress</h1>'))
})

// Example of testing via DOM checking
test('Route / exits and render HTML with CSS applied', async t => {
const window = await nuxt.renderAndGetWindow('http://localhost:4000/')
const element = window.document.querySelector('.title')
t.not(element, null)
t.is(element.textContent, 'Weight Loss Progress')
t.is(element.className, 'title')
})

// Close the Nuxt server
test.after('Closing server', t => {
nuxt.close()
})

動作確認

ローカルで動作確認を行います。

1
yarn run test

GitLab CIの設定

.gitlab-ci.yml(設定ファイル)の作成

行うのはテストのみなので、以下のようにしました。

1
2
3
4
5
6
7
8
9
10
11
image: node:latest

job:
before_script:
- yarn install
stage: test
script: yarn run test

cache:
paths:
- node_modules/

#GitLabのコンソール上では特に設定せずデフォルトのまま行いました。

GitLab CIで走らせる

コミットすると自動で走ります。


どきどき・・・


成功!


成功すると一覧にも✓マークが表示されます。

まとめ

思っていた以上に簡単にGitLab CIを走らせることが出来ました。

あとはテストケースの設計ですね。
コンポーネントのテストはどう行うのか?など
avaの使い方についてはもっと調べないと、と思います。

とりあえずは一歩踏み出せたので今日はこれまで:)

ソースコード

https://github.com/aytdm/weight-record

参考

その他

OSSのコミッターへの足掛かりとしてNuxt.jsの翻訳に携わり始めました。
英語力向上にも繋げたいと思いつつ、
まずはguideの翻訳率100%を目指したいところです。

nuxtとbuefyを使って日々の体重の推移をグラフで可視化する

ダイエットには日々の体重を測定し、記録する方法があります。
もちろん それだけで減量はしないのですが、
今の体重がどれくらいかという意識を働かせることが大事だそうです。

自分一人で継続するには鉄の意志が必要なため
第三者に自分の体重を報告し、モチベーションを維持する人もいます。

今回はそんな人がさらにやる気を出せるよう
日々の体重の推移をグラフで可視化してみました。

要件

  • 計測開始時から現在までの体重の推移を折れ線グラフで表示する
  • 計測開始時から最新日の体重の増減量を明示する
  • 各月ごとの体重の推移を折れ線グラフで表示する
  • 各月ごとの体重の増減量を明示する
  • 各月のy軸最小値は目標値である70kg、y軸最大値は95kgとする
  • 各月の目標減量(-2kg)を達成していたら「おめでとう」と表示する
  • 体重を登録または更新するためのフォームを用意する
  • とりあえず1年は使われる想定

アーキテクチャ

AWSのAPI Gateway、Lambda、RDS(MySQL)を使用し
サーバレスにトライしました。

※実運用ではLamdaから直接RDSを触る方法は推奨されないので留意ください。
あくまで勉強用の構成です。

#フロント周り
フレームワークはNuxt.js、UIライブラリはBulma
グラフ描画にはvue-chartjsを用い、Netlifyにデプロイしました。

使用したモジュールのバーション

1
2
3
4
5
6
7
8
"axios": "^0.17.1",
"buefy": "^0.6.3",
"chart.js": "^2.7.1",
"moment": "^2.20.1",
"node-sass": "^4.7.2",
"nuxt": "^1.0.0",
"sass-loader": "^6.0.6",
"vue-chartjs": "^3.1.1"

スクリーンショット

計測開始時から現在までの体重の推移

体重を登録または更新するためのフォーム

各月ごとの体重の推移

最後に

Netlifyについて

GitHub Pagesと違い自分でファイルを生成しないで済む点
Netlifyは簡単で感動しました。

フロント周りについて

初めてmomentを使ったのですが便利ですね!
これは手放せないライブラリになりそうです。

UIライブラリにはbuefyもとい、bulmaを使いました。
レスポンシブな点が魅力的だと感じました。
element-uiはレスポンシブではないものの、色合いや
フォームのバリデーションルールを定義できる点が個人的に好きです。

今回はグラフ部分をコンポーネント化し
1ページに処理をずらずら書いたのですが、
もう少しすっきりできないかと思っています。

アーキテクチャについて

自分で一からAWSの環境を用意するのは初めてで新鮮でした。

最初はLambdaと相性がいいだろうとDynamoDBで考えていたのですが
時系列に値を保持できないため、MySQLに変更しました。
後々調べたら、NoSQLでもGridDBやRiak TSなど時系列に強いDBがあるんですね。

ちょいちょいエラーが発生しますが、今回は目をつむってもらおう。。。

ひとりごと

最近は忙しいらしく結果が芳しくありませんが
気に入ってもらえたようで大成功です!
個人的にもIoTで使えそうなことが出来たので満足です:)

それにしても、自分で登録できるようにフォームを用意したのに全く使ってもらえない・・・。

ソースコード

https://github.com/aytdm/weight-record

参考

nuxtを使ってqiitaの投稿一覧サイトを作る


2018/10/27 追記
store を使うよう更新しました。
詳細はこちらを参照してください。


これまで 2 回に渡って nuxt と element-ui の使い方をみてきました。

これらを踏まえ、今日は実際に Qiita 投稿一覧サイトを作っていきたいと思います。

最初に考えた要件では以下の 3 点を挙げていました。

  • トップページから Qiita の投稿一覧ページへ移行させる
  • Qiita の投稿一覧ページではヘッダ有りとする
  • UI としては Element を利用する

これに以下 2 点を加えます。

  • 投稿検索機能
  • ページネーション機能

モジュールのバージョン

使用するモジュールのバーションは以下の通りです。

1
2
3
"nuxt": "^1.0.0-rc11",
"axios": "^0.17.1",
"element-ui": "^2.0.8"

プロジェクトの作成

新たにプロジェクトを作成します。
プロジェクト名は「hello-nuxt」としました。

1
2
3
4
$ vue init nuxt-community/starter-template hello-nuxt
$ cd hello-nuxt
$ yarn install
$ yarn run dev

axios、element-ui のインストール

axios、element-ui をインストールし、設定を行います。

1
$ yarn add axios element-ui

nuxt.config.js の build 内に以下の設定を追加します。

1
2
build: {
vendor: ['axios', 'element-ui'],

nuxt.config.js の plugins、css に以下の設定を追加します。

1
2
3
4
plugins: ['~plugins/element-ui', { src: '~plugins/element-ui', ssr: false }],
css: [
'element-ui/lib/theme-chalk/index.css'
],

/plugins/element-ui.js を作成します。

トップページの作成

/pages/index.vue の内容を変更します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template>
<section class="container">
<div>
<h1 class="title">
Qiita
</h1>
<h4 class="subtitle">
Qiita is a technical knowledge sharing and collaboration platform for programmers.
</h4>
<div class="links">
<nuxt-link to="/search" class="button--white"><i class="el-icon-search"></i> Search</nuxt-link>
</div>
</div>
</section>
</template>....以下略

Qiita の投稿一覧ページ用のヘッダー、フッター作成

components/Header.vue を作成します。

1
2
3
4
5
<template>
<div class="header">
<b><nuxt-link to="/">Hello Qiita with Nuxt.js \\\\ ٩(*'ω'*)و ////</nuxt-link></b>
</div>
</template>....以下略

components/Footer.vue を作成します。

1
2
3
4
<template>
<div class="footer">
</div>
</template>....以下略

続いて layouts/navbar.vue を追加します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<template>
<div>
<my-header />
<nuxt />
<my-footer />
</div>
</template>

<script>
import MyHeader from '~/components/Header.vue'
import MyFooter from '~/components/Footer.vue'
export default {
name: 'navbar',
components: {
MyHeader,
MyFooter
}
}
</script>

Qiita の投稿一覧ページの作成

検索部の作成

pages/search.vue を作成します。

ここではページ表示時に Qiita の API をコールする機能と
検索画面に入力されたキーワードを元に Qiita の API をコールする機能、
入力値のバリデーション機能を持ちます。
エンターキー押下で検索が走るようにもします。

投稿(Qiita API のレスポンスデータ)を描画する部分は別途作ります(コード上では my-list が当該箇所)。

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
<template>
<div>
<el-container>
<el-main>
<!-- 検索フォーム -->
<el-form :inline="true" :model="searchForm" ref="searchForm" :rules="rules" @submit.native.prevent>
<el-form-item prop="keyword">
<el-input placeholder="search by keyword" prefix-icon="el-icon-search" v-model="searchForm.keyword"@keyup.enter.native="search('searchForm')" />
</el-form-item>
<el-form-item>
<el-button @click="search('searchForm')">search</el-button>
</el-form-item>
</el-form>
<!-- 投稿一覧 -->
<my-list :lists="mylist" :hasData="hasData" />
</el-main>
</el-container>
</div>
</template>

<script lang="babel">
import axios from 'axios'
import MyList from '~/components/List.vue'
const BASE_URL = 'https://qiita.com/api/v2/'
export default {
layout: 'navbar',
components: {
// 投稿一覧を表示するコンポーネント
MyList
},
data () {
return {
// 検索フォーム
searchForm: {
keyword: ''
},
// バリデーションルール
rules: {
keyword: [
{ required: true, message: 'Please input the keyword', trigger: 'blur' }
]
},
mylist: [],
hasData: true
}
},
created () {
// ページ描画時にキーワード「nuxt.js」でQiitaのAPIをコール
this.searchForm.keyword = 'nuxt.js'
this.sendRequest()
this.searchForm.keyword = ''
},
methods: {
// キーワード検索時に呼ばれるメソッド。バリデーション含む
search (form) {
this.$refs[form].validate((valid) => {
if (!valid) {
return false
}
this.sendRequest()
})
},
// リクエスト送信
sendRequest () {
axios.get(BASE_URL+ 'items', {
headers: {'Content-Type': 'application/json'},
params: {
page: 1,
per_page: 20,
query: this.searchForm.keyword
}
})
.then(response => {
if (response.data.length === 0) {
this.hasData = false
}
this.mylist = response.data
})
.catch(e => {
console.error('error:', e)
})
}
}
}
</script>....以下略

投稿一覧部の作成

components/List.vue を作成します。

ここでは search.vue で取得した Qiita API のレスポンスデータを受け取り描画する機能と
トップへスクロールする機能を持ちます。

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
<template>
<div>
<div v-if="lists.length === 0 && !hasData">
<i class="el-icon-warning">&nbsp;No results found for your keyword.</i>
</div>
<div v-else>
<!-- 投稿一覧 -->
<el-col :span="6" v-for="(element, index) in lists" :key="index" class="col-style">
<el-card :body-style="{ padding: '15px' }" class="box-card">
<div slot="header" class="clearfix">
<a :href="element.url" target="_blank">{{ element.title }}</a>
</div>
<div class="bottom clearfix content-style text">
<div>{{ element.created_at }}</div>
<span>
<img :src="element.user.profile_image_url" width="15" height="15" />
<template v-if="element.user.description">
<el-popover slot="description" placement="top-start" width="300" trigger="hover" :content="element.user.description">
<span slot="reference">&nbsp;{{ element.user.id }}</span>
</el-popover>
</template>
<template v-else>
<span>&nbsp;{{ element.user.id }}</span>
</template>
</span>
&nbsp;
<span>
<i class="el-icon-star-off">{{ element.likes_count }}</i>
</span>
<div>{{ getDescription(element.body) }}</div>
<el-tag size="mini" type="info" class="tab-style" v-for="(tag, index) in element.tags" :key="index">{{ tag.name }}</el-tag>
</div>
</el-card>
</el-col>
<!-- スクロール用のボタン -->
<div v-if="250 < scrollY" class="page-component-up">
<transition name="fade">
<i class="el-icon-caret-top" @click="scrollTop" />
</transition>
</div>
</div>
</div>
</template>

<script lang="babel">
export default {
// search.vueで取得したQiita APIのレスポンスデータの受け取り
props: ['lists', 'hasData'],
data () {
return {
scrollY: 0
}
},
mounted () {
window.addEventListener('scroll', this.handleScroll)
},
methods: {
// ボディ部のトリミング
getDescription: function (body) {
return body.slice(0, 100) + '...'
},
// 現在の上部からのスクロール量取得
handleScroll: function () {
this.scrollY = window.scrollY
},
// トップへスクロール
scrollTop: function () {
document.body.scrollTop = 0
document.documentElement.scrollTop = 0
}
}
}
</script>....以下略

ページネーションの作成

続いて、ページネーションを作成しようと思いした…が
レスポンスヘッダーに含まれている「Total-Count」が取得できないため今回は断念しました。

デモ

https://aytdm.github.io/hello-nuxt/
今回は GitHub Pages にデプロイしました。
※PC での閲覧推奨です。

まとめ

コンポーネント間のデータの受け渡しやリスト、イベントハンドリングなども行うことができ、
基本的な操作は体験できたように思います:)
#ストアやトランジションはまたの機会に…!

デモで分かるようにレスポンシブではなく、スマホだととても見れないので
別の形式で表示するか、element-ui 以外の UI ライブラリを選定した方が良さそうです。
この点を考慮していませんでした。。。

テスト周りは追々勉強&追加していこうと思います。

vue.js は画面で行われる処理が分かりやすくていいですね。
使える機会がある際は積極的に使っていきたいと思います。

ソースコード

v1.0

参考

nuxtを使ってqiitaの投稿一覧サイトを作る(準備2)


2018/10/27 追記
store を使うよう更新しました。
詳細はこちらを参照してください。


ここのところ先日作成した LINE BOTに【cotogoto】や【repl-ai】を組み込んで遊んでいました
いやぁ・・・面白いですね!
microsoft のコグニートも使ってみたいなぁ…なんて夢が広がります

本題

だいぶ間が空きましたが、今回はページを読み込んだ際に
axios で取得したデータを描画したいと思います

前回はページを読み込んだ際に静的なデータをテーブルに描画しました

モジュールのバージョン

使用しているモジュールのバーションは以下の通りです
element-ui は 2.0 系がリリースされていますが、今回は 1.0 系で行きます

1
2
3
4
5
6
7
8
"dependencies": {
"axios": "^0.17.1",
"element-ui": "1.4.12",
"nuxt": "1.0.0-rc10",
"vue": "2.3.0",
"vue-server-renderer": "2.3.0",
"vue-template-compiler": "2.3.0"
}

使用する API

Qiita の公開 APIを使用します
ドキュメントによると、未認証ユーザーでも
1 時間に 60 回までリクエストできるそうなので未認証でいきます

コールする API は【投稿の一覧取得】です

axios のインストール

まずは axios をインストールします

1
$ yarn add axios

続いて nuxt.config.js の build 内に以下の設定を追加します

1
2
build: {
vendor: ['axios'], ...

これで axios を使う準備ができました

ページの作成

新たに pages/axiosList.vue を追加し、script 部分から作成します
axios をインポートしてレイアウトを指定します

よくよく考えると作っているデザインは 2 カラムではないのですが、気にしたらダメです。

1
2
3
4
5
6
7
8
<script lang="babel">
// axiosをインポート
import axios from 'axios'
export default {
// レイアウトを指定
layout: 'twocolumns'
}
</script>

ここまでは前回とあまり変わらないですね
次に axios で通信する部分を記述します

今回は vue インスタンスが作成された後に呼ばれる【created】で axios を呼びたいと思います

created で取得した Qiita 投稿データを lists に格納します
取得件数は 6 件、検索クエリは一先ず「nuxt.js」としました

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
export default {
layout: "twocolumns",
data() {
return {
lists: []
};
},
created() {
axios
.get("https://qiita.com/api/v2/items", {
params: {
page: 1,
per_page: 6,
query: "nuxt.js"
}
})
.then(response => {
this.lists = response.data;
})
.catch(e => {
console.error("error:", e);
});
}
};

通信部分ができたので次は描画部分に取り掛かります
今回は element-ui の【カード】、【タグ】を使って描画していきます
まずは記事タイトル、作成日、投稿者のアイコン、投稿者の id、記事タグの一覧を表示します

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
<template>
<div>
<el-row :gutter="10">
<el-col :xs="8" :sm="6" :md="4" :lg="3" :xl="1"><div class="grid-content bg-purple"></div></el-col>
<el-col :xs="8" :sm="8" :md="8" :lg="8" :xl="8" v-for="(element, index) in lists" :key="index" class="col-style">
<el-card class="box-card">
<div slot="header" class="clearfix">
<a :href="element.url" target="_blank">
<i class="el-icon-edit"></i>
{{ element.title }}
</a>
</div>
<div class="bottom clearfix content-style">
<div>{{ element.created_at }}</div>
<div>
<img :src="element.user.profile_image_url" width="20" height="20" />
<span>@{{ element.user.id }}</span>
</div>
<el-tag size="mini" type="info" class="tab-style" v-for="(tag, index) in element.tags" :key="index">{{ tag.name }}</el-tag>
</div>
</el-card>
</el-col>
</el-row>
</div>
</template>

動作を見てみると・・・ちゃんと表示されました:)

ちょっと欲張ってみる

当初の目的は達成できました
記事の内容も多少覗けるともっといいですね!

…とは言え、qiita の GET /api/v2/items レスポンスにアブストラクトはありません
body を指定すると本文がまるっと表示されてしまうので、ちょっとだけ表示させるよう javascript で工夫します

script 部分に body トリミング用の関数を作成します

1
2
3
4
5
methods: {
getDescription: function (body) {
return body.slice(0, 100) + '...'
}
}

この関数を描画部分に指定します

1
<div>{{ getDescription(element.body) }}</div>

動作を見てみると・・・いい感じです ♪

もうちょっと欲張ってみる

投稿者の自己紹介が見れると更にいいと思うので、次は element-ui の【ポップオーバー】を使って
id 名にカーソルを持っていった場合に投稿者の方が自己紹介を登録していたら表示させたいと思います

描画部分に分岐処理を入れます

1
2
3
4
5
6
7
8
<template v-if="element.user.description">
<el-popover slot="description" placement="top-start" width="300" trigger="hover" :content="element.user.description">
<span slot="reference">@{{ element.user.id }}</span>
</el-popover>
</template>
<template v-else>
<span>@{{ element.user.id }}</span>
</template>

完成です!

axiosList.vue の最終形態

デモ

まとめ

だいぶ形になってきました
準備編は今回でおしまいです
次回はこれまで作成したものを流用しつつ
element-ui2.0 へのバージョンアップやページネーションや検索機能をつけたいと思います

参考

ごみ収集日前にアラートを出すLINEbot

meet line-bot

最近 引越をした友人から「ごみ出しリマインダーを作って」と言われ模索することに。

ポイントは既存かつよく使用するアプリでリマンドできること。
APIもSDKも用意されているLINEがぴったりだと思い、挑戦しました。

構想

  • Springのスケジュール機能(cron)を使って、ごみ収集日前に該当者へリマインドをプッシュ
  • 該当者から何かメッセージが来たらレスポンス
  • ごみ捨てが終わっていたら褒め、まだだったら捨てようねと励ます(大事)

レスポンスに応対する箇所は「Webhook」、リマインド箇所は「Messaging API」で実装します。

環境

  • jdk1.8
  • Spring Boot
  • Heroku

herokuでアプリケーションの作成、LINEでチャンネルの作成、Webhookの有効化、Webhookのurlの登録が必要です。

SDKの使い方に慣れる

まずLINEでチャンネルを解説し
SDKのページにあるサンプル「echo」をherokuにデプロイし、エコーバックを楽しみました。
続いてサンプル「kitchensink」をherokuにデプロイしどのように作ればいいかを理解。
機能が網羅されているので、掴みやすかったです。

プロジェクト構成

依存関係

1
2
3
4
5
<dependency>
<groupId>com.linecorp.bot</groupId>
<artifactId>line-bot-spring-boot</artifactId>
<version>1.11.0</version>
</dependency>

LINE Messaging API SDKを追加します。

プロパティ

1
2
3
line.bot.channelSecret = XXXXX
line.bot.channelToken = XXXXX
line.bot.handler.path = /callback

LINEで開設したチャンネルの情報を設定します。

プッシュの実装

confirm.png
ごみ出しが完了したか確認するため、確認テンプレートを使用します。

コントローラ化する必要はありませんが、GETで呼び出せるようにすると確認する時に楽です。

余談ですが作っている当初、この部分ははSDKを使わずに地道実装していました…w
#こちらでも動作はします。

レスポンスの実装

Webhookはメッセージやスタンプなど何か送られた際に応答を返すことができます。
確認で「はい」もしくは「いいえ」が返却されるようにしたので、この2つのワードを拾い
スタンプとメッセージを返すようにします。

replyにreplyTokenとメッセージを設定します。
メッセージはコードに記述しているようにリストで複数設定することができます。

スケジューリング

Springのスケジュール機能@Scheduledを利用します。

※@EnableSchedulingアノテーションをmain関数のあるクラスにつけるのを忘れずに

備考

環境変数

1
2
3
4
line.bot.channelSecret = ${LINE_BOT_CHANNEL_SECRET}
line.bot.channelToken= ${LINE_BOT_CHANNEL_TOKEN}
cron.setting = ${LINE_CRON}
garbage.reminder.id = ${LINE_ID}

環境変数は全てherokuに持たせるようにしました。
コンソールで変更できるのでらくちんですね!

botのアイコン

アイコンはtabagotchiから拝借。
お気に入りの拡張機能です

動作確認

まとめ

SDKを使用すると、すっきり書けて楽ですね。

自前実装ではOkHttp3ClientHttpRequestFactoryを用いたRestTemplateの実装ができました。
こういう小さな達成感は大事(笑

楽しく書けました X)
あとは気に入ってくれるといいなぁ

ソースコード

https://github.com/aytdm/garbage-reminder-bot

参考

nuxtを使ってqiitaの投稿一覧サイトを作る(準備)


2018/10/27 追記
store を使うよう更新しました。
詳細はこちらを参照してください。


前回の続きで今日から実装編

vue に慣れるためにも、qiita の投稿リストは一先ず置いておき
まずは静的な 2 カラムのページを作りたいと思います

インストール

ガイダンスに従いスターターテンプレートを用いて nuxt をインストールします
プロジェクト名は「helloNuxt」としました

1
2
3
4
$ vue init nuxt-community/starter-template helloNuxt
$ cd helloNuxt
$ yarn install
$ yarn run dev

http://localhost:3000/にアクセスするとページが表示されます
これだけで出来上がった気分になります ♪

スタイルの指定

Element を使用したいので設定を行います

1
$ yarn add element-ui

/plugins/element-ui.js を作成します

nuxt.config.js に以下の設定を行います

これで Element を使う準備ができました

レイアウトの作成

ヘッダーを追加しようと思います
まず、components/Header.vue を作成します

続いて layouts/twocolumns.vue を追加します

ここで怒られました・・・

1
2
3
4
5
This dependency was not found:

* ~components/Header.vue in ./node_modules/babel-loader/lib?{"babelrc":false,"cacheDirectory":true,"presets":["C://vue//helloNuxt//node_modules//babel-preset-vue-app//dist//index.common.js"]}!./node_modules/vue-loader/lib/selector.js?type=script&index=0!./layouts/twocolumns.vue

To install it, you can run: npm install --save ~components/Header.vue

何でかなぁと思ったいてら「~/componentsを使ってね」とのこと
変更するとエラーが解消されました
https://github.com/nuxt/nuxt.js/issues/1183#issuecomment-318457804/

続いて pages/list.vue を追加します
layout に先ほど作成した twocolumns を指定します

ブラウザで確認してみると…

ヘッダーが追加されました

今日はここまで

ガイダンスを読んだ時よりも概要が掴めた気がします
peges に配置したファイルを自動でルーティングしてくれるのは便利ですね

次は axios を使ってもう少し動的なページにしたいと思います

それにしてもインデントについてまでエラーが出ると思わなかった@w@

1
2
3
4
5
6
C:\vue\helloNuxt\components\Header.vue
37:32errorExtra semicolon semi
39:1 errorExpected indentation of 2 spaces but found 0indent

? 2 problems (2 errors, 0 warnings)
2 errors, 0 warnings potentially fixable with the `--fix` option.

参考

ファイルのMD5ハッシュ値を比較する

業務中にファイルのハッシュ値を比較する機会がありました。
ハッシュ値の算出はコマンドでできるけれど
せっかくなのでツールとして作成します。

使用したIDEはVisual Studio Community 2017
WPFアプリケーションにしました。
デスクトップアプリケーションを作るのは久しぶり。
VisualStudioを使うのは前職以来なのでしみじみ。
GUIアプリケーションを作る時はMFCを使っていたなぁ。。。

構想

  1. 2つのファイルのハッシュ値を比較する
  2. ハッシュアルゴリズムはMD5を使用する
  3. ファイル指定はファイルチューザー、ドラッグ&ドロップ両方から行えるようにする
  4. 算出したハッシュ値を明示する
  5. UIイメージ図

実装

xamlファイル等、未知のファイルやC#のお作法に戸惑いつつも
UIを作ってそれに対するイベントを作っていく工程はMFCと変わらないですね:)
参照の追加が分かっておらず、「いろんなサイトのコード動かない!ムキー」ってなってました・・・。
他にはドラッグ&ドロップの実装にてこずりました。 

参考

総括

目的は達成できました。
今後もGUIアプリを作りたい時にWPFで作ろうと思います。
もう少し規模の大きくすればファイル構成にも目が向けられそう。

それにしてもエラーはともかく、ハッシュ値はメッセージボックスで出さずに
ウィンドウ内のテキストボックスに出力したほうがいいかも
イケテナイよね・・・
→ 2017/12 テキストボックスに出力するよう修正しました。

ソースコード

https://github.com/aytdm/compareMD5Hash

nuxtも完成はしていないものの少し前進したので
次はそのことについて書こうと思います。

nuxtを使ってqiitaの投稿一覧サイトを作る (構想)

前回はnuxtのサンプルそのままで満足したので今回から実践編。

せっかくだから使えるものにしたいなぁと思い
Qiitaに投稿された記事の一覧を作ってみようと思います。

どんなページを作成するか

・使用するAPI:Qiita - API
・トップページから投稿一覧ページへ移行させる(ページ遷移)
・投稿一覧ページではヘッダ有りとする
 (コンポーネントやレイアウト、その他もろもろの理解)
・UIとしてはElementを利用する
 (デザイン(とは呼べないかもしれないけど)理解)

投稿検索機能とか欲張り機能は追々・・・
まずは一覧を表示するところまで作りたいと思います。

わくわくしてきた(´ω`*)

herokuにnuxtをデプロイ

モジュールバンドラを勉強するにあたり
まずNode.jsを分かってないと始まらないと思い、Node.js入門を門戸を開きました
お手軽な印象

2、3サイトを回ってるとどこもherokuというPaaSを紹介している
ローカルでも十分だけどNode.jsを使ったサービスのデプロイが
どのような感じか知りたいので登録

公式のチュートリアルを見ながら真似っこ。
詰まることなく進む。充実してるなぁ

欲が出てvueを使ってみたくなったので
Vue.js をサーバーサイドレンダリングするNuxtにトライ

設定を行いデプロイ

1
2
3
4
5
6
$ git push heroku master
fatal: ‘heroku’ does not appear to be a git repository
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.

「herokuがないよ」と言われる
.git内のconfigを見ると「remote=origin」になっていました。

1
2
3
4
5
6
\[remote "heroku"\]
url = https://git.heroku.com/rocky-island-86467.git
fetch = +refs/heads/*:refs/remotes/orgin/*
\[branch "master"\]
remote = heroku
merge = refs/heads/maste:\\Users\\mn\\Desktop\\nodeTest\\rocky-island-86467\\.git

これを「heroku」に修正してトライ
・・・結果が変わらない

1
git config -l

gitの設定をみると「remote.heroku.url」、「remote.heroku.fetch」がない。
原因これだ。。。

1
2
heroku git:remote -a appName
git push heroku master

設定して再トライ

1
2
3
4
5
6
7
8
9
10
11
$ git push heroku master Counting objects: 30, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (25/25), done.
Writing objects: 100% (30/30), 43.70 KiB | 2.91 MiB/s, done.
Total 30 (delta 2), reused 0 (delta 0)
remote: Compressing source files… done.
remote: Building source:
remote:
remote: —–> Node.js app detected
remote:
remote: —–> Creating runtime environment :

成功!

サンプルそのままだけど動くの嬉しい:)
ちょっと前進した気分で今日はおしまい

==

これを覚えておこう

Follow this steps:
$ heroku login

Create a new Git repository
Initialize a git repository in a new or existing directory
$ cd my-project/
$ git init
$ heroku git:remote -a appname

Deploy your application
Commit your code to the repository and deploy it to Heroku using Git.
$ git add .
$ git commit -am “make it better”
$ git push heroku master

Existing Git repository
For existing repositories, simply add the heroku remote
$ heroku git:remote -a appname

PowerShellに触れてみる

APIを大量に検証する際、curlをバッチファイルに書いて半自動/半手動で行っています。
これをスマートに行えるようにPowerShellを使おうと思います。

使用しているpowershellのバージョンは以下の通り。

1
2
3
4
5
6
7
8
**Name**                       **Value**
PSVersion 5.0.10586.117
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...}
BuildVersion 10.0.10586.117
CLRVersion 4.0.30319.42000
WSManStackVersion 3.0
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1

Invoke-WebRequestを使用。PowerShell3.0から使えるとのこと。
#invoke-webrequest以外にもHttpWebRequestやWebClientを使う方法があるみたいだけれど省略。

基本的な書き方はこんな感じです。

1
2
3
4
5
6
7
$uri = "http://example.com"
$method = "POST"
$header = @{"hoge" = "fuga"}
$contentType = "application/json"
$body = "test"

Invoke-WebRequest -URI $uri -Method $method -Headers $header -ContentType $contentType -Body $body

結果を扱いたい場合はリクエストを変数にとって扱える。便利!

1
2
3
4
5
6
7
$response = Invoke-WebRequest -URI $uri -Method $method -Headers $header -ContentType $contentType -Body $body

//レスポンスヘッダー取得
$header = $response.Headers

//レスポンスボディー取得
$body = $response.Content

400以上のエラーが出た場合は[System.Net.WebException]が飛んでくるのでcatchしてあげる。
ヘッダやボディー情報をとることも、もちろん出来る。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
try {
$response = Invoke-WebRequest -URI $uri -Method $method -Headers $header -ContentType $contentType -Body $body
:
}
}
catch \[System.Net.WebException\] {
#write some code
}
catch {
#write some code
}
finally {
#write some code
}

本当に便利・・・。
今まで何て非効率なことをやっていたんだろう。

C#に触れるきっかけにツールを作ろうと思っていたけど、当座はこれで十分。
余裕が出来たらツールに挑戦しよう。

okHttpに触れてみる

先日、JJUG CCC 2017 Springへ行った際に
ANDRESさんの講演で紹介されたライブラリの1つokHttp

これまでHttpURLConnectionで
地道に頑張っていたので、これを機に触ってみようと思います
#2017残り半分のテーマは最近のライブラリに触れることだなぁ…

レシピを見ながら実装してみたところ
ごちゃーと書いていたものが、かなりすっきりする印象

RequestBodyを送らないPOSTで
詰まったけど、こうしたら大丈夫でした

1
2
3
4
5
6
Request request = new Request.Builder()
.url("https://hogehoge.com")
.header("Accept-Charset", "UTF-8")
.addHeader("Content-Type", "application/json; charset=UTF-8")
.post(RequestBody.create(null, new byte\[0\]))
.build();

可読性も上がるし、これは使えるようになりたい
あとは講演で紹介されていたRetrofitの組み合わせでできたら素敵
もうちょっと触って慣れたら挑戦してみようと思います

ビルド時にwebpack、webpackしたものをwarに含める

mavenでビルド時にwebpackしてwarに含める方法ないかなぁ・・・と
思っていたら良さげなプラグインを発見。

1
2
3
4
5
6
7
8
<plugin>
<groupId>com.github.eirslett</groupId>
<artifactId>frontend-maven-plugin</artifactId>
<!\-\- Use the latest released version:
https://repo1.maven.org/maven2/com/github/eirslett/frontend-maven-plugin/ -->
<version>1.4</version>


これでいけるのね、便利。
package.jsonで依存関係のあるモジュールのバージョン差異も起きない(`・ω・´)


2018/5/10 追記

使用例:
https://github.com/aytdm/study-springboot-mongodb-angular/blob/master/backend/pom.xml