r-yanyoのブログ

アイコン画像

CSSの実装でポイントを競い合う「CSSBattle」をやってみた

最近、Tech 系のニュースは、はてなブログのテクノロジータグとかTechfeedで見ているんだけれど、そこでCSSBattleなるものを見つけたのでやってみた。

CSSBattle は、その名の通り、画像で与えられた TARGET デザインを参考に、CSS でデザインを作成し、それを提出すると評価ポイントが与えられる。競技プログラミングの CSS バージョンみたいなものだ。画像右の TARGET を目標に、画像左のエディタで HTML と CSS を書いていき、OUTPUT で出力結果が見れる。

評価基準は OUTPUT と TARGET の類似度と、ファイル文字数の少なさが関係しているっぽい。まだ簡単な問題しかなく、類似度は大抵 100%を狙えるため、以下に記述量を少なく実装できるかでポイントに差が出る。記述量の少なさを目指すという意味では競プロというより code golf に近い感じになるんだろうか。

具体的にはこんな感じのデザインが TARGET になっている。現在はまだ 12 問しか無いが、普段作らないような形ばかりなので、やってみると意外にオモシロイ。6 問目は円を 90° ごとに異なる色を割り当てる問題。最初は div を 4 つ組み合わせて作ろうと思ったけど、conic-gradientというのを使うと、1つの div タグで作れると初めて知った。普段使わない CSS プロパティを知れるのは良い点。

但し、HTML+CSS で実際に求められる能力って、レスポンシブで崩れないデザインであったり、変更がしやすいという意味での拡張性な訳で、CSSBattle ではその辺が評価に含まれていない。今の所はあくまで暇つぶしにやる程度なのかなと思います。問題数が増えてきて、評価方法が改良されればもう少し流行るかも。

Nuxt.jsでブログを作り直した。

Nuxt.js でブログを作り直している途中。

Next.js から Nuxt.js へ。

heroku から netlify へ。

道のりというWebサービスをVue.js+Ruby on Railsで作った。

「道のり」というサービスを作りました。 どういうサービスかというと、これまでの人生で努力してきた体験をシェアしようというものです。

実は「storys」という似たサービスが既にあったんですね。

作り終えた後にこのサービスを知ったので、やはり  世の中には同じようなアイデアを思いつく人はいるんだなと思いました(完成度は私のものとは全然違いますが)。

アプリを作ったのはほぼ初めてなので、これからもどんどん作っていけたらいいなぁ。

Vue.js + Ruby on RailsでTodoリストアプリを作る。

完成図

コンセプト

フロントエンドとサーバサイドを完全に分ける。よって Rails の Webpacker を用いた Vue.js の使用はしない。

環境

Mac なので基本 homebrew でインストールしている。 環境でこけるのが一番萎えるので慎重に。

  • Mac OS X
  • node 10.0.0
  • npm 5.6.0
  • Vue 2.9.3
  • Rails 5.1.6

早速 Todo リストアプリ作ろう。

サーバーサイドとフロントエンドを分けるので、それぞれフォルダも分かれます。

まずはサーバサイドから

まずはサーバサイドから作ります。単純な CRUD 機能(Create Read Update Delete)が出来ることを目指しましょう。但し今回は Update は省略しています。

APi モードで rails new します。

$ rails new server --api

Todo リストアプリを作るので、「task」というモデルを作り、タスクを保存したり削除したり出来るようにします。タスクが持つ column は string 型の「text」だけとします。DB のマイグレーションも忘れず行う。

$ rails generate model task text:string
$ rails db:migrate

次にコントローラーを作ります。

$ rails g controller api::tasks

tasks_controller.rb の中身を以下のようにします。

class Api::TasksController < ApplicationController
  def index
    @tasks = Task.all
    render json: @tasks
  end

  def create
    @task = Task.new(task_params)
    if @task.save
      render json: "create new task.\n", status: 200
    else
      render json: "fail to create.\n", status: 500
    end
  end

  def destroy
    @task = Task.find(params[:id])
    @task.destroy
    render json: "destroy a task.\n"
  end

  private
    def task_params
      params.require(:task).permit(:text)
    end
end

最後にルーティングを設定します。

Rails.application.routes.draw do
  namespace :api, { format: 'json' } do
    resources :tasks
  end
end

これによりルーティングは以下のようになります。

$ rake routes
       Prefix Verb   URI Pattern                   Controller#Action
    api_tasks GET    /api/tasks(.:format)          api/tasks#index {:format=>/json/}
              POST   /api/tasks(.:format)          api/tasks#create {:format=>/json/}
 new_api_task GET    /api/tasks/new(.:format)      api/tasks#new {:format=>/json/}
edit_api_task GET    /api/tasks/:id/edit(.:format) api/tasks#edit {:format=>/json/}
     api_task GET    /api/tasks/:id(.:format)      api/tasks#show {:format=>/json/}
              PATCH  /api/tasks/:id(.:format)      api/tasks#update {:format=>/json/}
              PUT    /api/tasks/:id(.:format)      api/tasks#update {:format=>/json/}
              DELETE /api/tasks/:id(.:format)      api/tasks#destroy {:format=>/json/}

ターミナルから curl でテストしてみましょう。

まず Rails サーバを立てます。

$ rails s

別ターミナルで、curl から rails サーバへリクエストを送ります。 まずは新しいタスクを作ってみましょう。

$ curl http://localhost:3000/api/tasks -X POST -d 'task[text]=洗濯する'
create new task.

次はそれを GET してみます。

$ curl http://localhost:3000/api/tasks
[{"id":1,"text":"洗濯する","created_at":"2018-05-31T15:05:34.862Z","updated_at":"2018-05-31T15:05:34.862Z"}]

最後にそれを削除してみます。

$ curl http://localhost:3000/api/tasks/1 -X DELETE
destroy a task.

これでサーバ側は(だいたい)完成です。後からちょっと修正します。

次はフロントエンド

次はフロント側を Vue.js で作っていきます。 vue-cli で雛形を作っていきましょう。今回はテンプレとして webpack-simple を使います。

$ vue init webpack-simple client
$ cd client
$ npm install
$ npm run dev

まずは GET メソッドを送ってタスクを入手できるようにしましょう。 src/app.js を以下のように変更します。

<template>
  <div id="app">
    <ul id="task-list">
      <li class="task" v-for="task in tasks"><p>{{ task.text }}</p></li>
    </ul>
  </div>
</template>

<script>
import axios from 'axios';

const hostName = 'localhost:3000';
const path = '/api/tasks'

export default {
  name: 'app',
  data () {
    return {
      tasks: [],
    }
  },
  methods: {
    getTasks: function() {
      axios.get(`http://${hostName}${path}`)
        .then((response) => {
          this.tasks = response.data;
        })
        .catch(function(error) {
          console.log(error);
        });
    }
  },
  mounted: function() {
    this.getTasks();
  }
}
</script>

axios を使ってサーバにリクエストを送るので axios をインストールします。

npm install axios

axios で異なるオリジンに対してリクエストを送ると、クロスドメイン対応と pre-flight の対応が必要になります。OPTIONS メソッドで Pre-flight というリクエストが送られます。(あまり詳しくないのでここを参考にしました。これに対応するためにサーバ側を少し変更します。

まずクロスドメインに対応するために、 rack-cors モジュールをインストールし、

gem 'rack-cors'

config/initializers/cors.rb を以下にようにします。

Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins '*'

    resource '*',
      headers: :any,
      methods: [:get, :post, :put, :patch, :delete, :options, :head]
  end
end

config/routes.rb に options リクエストが来た時のルーティングを追加します。

match '*path' => 'options_request#preflight', via: :options

controller/options_request_controller.rb に以下を追加します。

class OptionsRequestController < ApplicationController
  ACCESS_CONTROL_ALLOW_METHODS = %w(GET OPTIONS).freeze
  ACCESS_CONTROL_ALLOW_HEADERS = %w(Accept Origin Content-Type Authorization).freeze

  def preflight
    set_preflight_headers!
    head :ok
  end

  private

  def set_preflight_headers!
    response.headers['Access-Control-Max-Age'] = ACCESS_CONTROL_MAX_AGE
    response.headers['Access-Control-Allow-Headers'] = ACCESS_CONTROL_ALLOW_HEADERS.join(',')
    response.headers['Access-Control-Allow-Methods'] = ACCESS_CONTROL_ALLOW_METHODS.join(',')
  end
end

さて、これで task をサーバから GET できるようになったと思います。 データは予め curl で post して下さい。

次にデータを POST するために form を追加しましょう。 form の値を newTask というデータに bind したので、data に追加します。 form に入力した値を axios でリクエストするメソッドを methods の中に追加します。

まとめると以下のコードになります。

<template>
  <div id="app">
    <form v-on:submit.prevent="postTask">
      <input id="new-task-form" type="text" v-model="newTask" placeholder="やりたいことは...">
    </form>
    <ul id="task-list">
      <li class="task" v-for="task in tasks"><p>{{ task.text }}</p></li>
    </ul>
  </div>
</template>

<script>
import axios from 'axios';

const hostName = 'localhost:3000';
const path = '/api/tasks'

export default {
  name: 'app',
  data () {
    return {
      tasks: [],
      newTask: '',
    }
  },
  methods: {
    getTasks: function() {
      axios.get(`http://${hostName}${path}`)
        .then((response) => {
          this.tasks = response.data;
        })
        .catch(function(error) {
          console.log(error);
        });
    },
    postTask: function() {
      axios.post(`http://${hostName}${path}`,
          `task[text]=${this.newTask}`
        )
        .then((response) => {
          this.getTasks();
          this.newTask = '';
        })
        .catch(function(error) {
          console.log(error);
        });
    },
    deleteTask: function(id) {
      axios.delete(`http://${hostName}${path}/${id}`)
        .then((response) => {
          this.getTasks();
        })
        .catch(function(error) {
          console.log(error);
        });
    }
  },
  mounted: function() {
    this.getTasks();
  }
}
</script>

最後に task を DELETE するためのボタンを追加し、ボタンが押された時に axios で DELETE を送るメソッドを追加します。ついでに scss でスタイリングを行うと以下のようなコードになります。

<template>
  <div id="app">
    <form v-on:submit.prevent="postTask">
      <input id="new-task-form" type="text" v-model="newTask" placeholder="やりたいことは...">
    </form>
    <ul id="task-list">
      <li class="task" v-for="task in tasks"><p>{{ task.text }}</p><button class="delete-button" v-on:click="deleteTask(task.id)">×</button></li>
    </ul>
  </div>
</template>

<script>
import axios from 'axios';

const hostName = 'localhost:3000';
const path = '/api/tasks'

export default {
  name: 'app',
  data () {
    return {
      tasks: [],
      newTask: '',
    }
  },
  methods: {
    getTasks: function() {
      axios.get(`http://${hostName}${path}`)
        .then((response) => {
          this.tasks = response.data;
        })
        .catch(function(error) {
          console.log(error);
        });
    },
    postTask: function() {
      axios.post(`http://${hostName}${path}`,
          `task[text]=${this.newTask}`
        )
        .then((response) => {
          this.getTasks();
          this.newTask = '';
        })
        .catch(function(error) {
          console.log(error);
        });
    },
    deleteTask: function(id) {
      axios.delete(`http://${hostName}${path}/${id}`)
        .then((response) => {
          this.getTasks();
        })
        .catch(function(error) {
          console.log(error);
        });
    },
  },
  mounted: function() {
    this.getTasks();
  }
}
</script>

<style lang="scss">

$list-item-height:   30px;

html {
  height: 100%;
}

body {
  height: 100%;
  margin: 0;
}

#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  color: #2c3e50;
}

#new-task-form {
  width: 100%;
  height: $list-item-height;
}

#task-list {
  width: 100%;
  list-style-type: none;
  margin: 0;
  padding: 0;
}

.task {
  display: flex;
  justify-content: space-between;
  align-items: center;
  width: 100%;
  height: $list-item-height;
  border-bottom: dashed 1px gray;

  p {
    margin: 0;
    padding-left: 10px;
  }
}

.delete-button {
  width: 20px;
  height: 20px;
  margin: 0 8px;
  background-color: gray;
  color: white;
  border: none;
  border-radius: 50%;
}

</style>

これで、完成です!UPDATE 機能はまだ実装していないので、実装してみると勉強になると思います。

Vue.js + Onsen UI でInstagramっぽいUIを作った。

Vue.js と Onsen UI の勉強のために、Instagram っぽい UI を目指して作ってみました。

作ったやつ

一番の強みは PC からでもスマホからでも綺麗なデザインのまま使用できるところ。(ちなみにこのブログはあんまりスマホ向けを意識していなかった。今後改善していきたい)

  • スマホから見た Web アプリ

決して本家の Instagram アプリでは無い(笑)

Onsen UI、楽しい!超便利だ!!

Onsen UI とはモバイルアプリ開発用 UI フレームワークで、PC、スマホ両方から使えるハイブリッドSPAな Web アプリを作ることが出来ます。Onsen UI は Vue.js だけでなく、React、Angular、jQuery などからも使用できるとのこと。コンポーネントが色々用意されているので、それを利用するだけで今風のアプリの UI が作れてしまう。しかもすぐにハイレベルなアクションが作れてしまうので楽しい。

まず、Vue.js の紹介

Vue.js は最近流行りの JavaScript の UI フレームワーク。他のフレームワークよりも簡単、軽量で使いやすい印象。このブログを React を使って書いた経験があったので、Vue を使い始めるのは React 程は苦労しなかったなという感想。Vue は React と違って CDN からも使えるので、初めは CDN で使って試してみるのがいいと思います。そうでないと Vue を使う前にWebpackの設定などの環境構築で断念してしまう可能性大です。

Vue.js はこんな風に.vue ファイルに html,js,css を全て書き込むことが出来て、コンポーネント(Web ページの部品)としてまとめて書けます。

//HTML
<template>
  <ul id='favorite-post' class='posts'>
    <li v-for="i in 7">
      <a href='#' class='post'>
        <img v-bind:src='"/public/img/food"+i+".jpg"' class="post-image">
      </a>
    </li>
  </ul>

  ...

</template>

//JavaScript
<script>
  import myPosts from './myPosts'

  export default {
    data() {
      return {
        toggle: true,
        tabIndex: 0,
        tabs: [
        {
          label: '投稿',
          page: myPosts,
        },
  ...

</script>

//CSS
<style scoped>
  .posts{
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-start;
    align-items: flex-start;
    align-self: flex-start;
  }

  ...

</style>

Onsen UI の紹介

Onsen UI は公式サイトにコンポーネントの一覧があり、サンプルコードと実際の動作がセットで置いてあるのでコピペするだけで自分のアプリのそのコンポーネントを取り入れられます。 上記の Instagram っぽいアプリでは Carousel(写真をスワイプして切り替えられる機能)なんかも Onsen UI のコンポーネントを使っただけで簡単に作れます。

Onsen UI Vue.js の API

また以下のサイトでは、Onsen UI のコンポーネントをまとめたデモが見れます。 https://onsenui.github.io/vue-onsenui-kitchensink/main.html?platform=ios

一番大変だったのは最初にする Webpack の設定。それさえ乗り越えられればとても便利なのではないかと。

まとめ

Vue.js + Onsen UI おすすめです!!

初めてオープンソースにPull Request出してみた

春休みから某地元企業でアルバイトをさせてもらっていて、その業務の中でLeafletというマップを簡単に描ける JS のライブラリを使用しています。

さらにそのライブラリの拡張としてLeaflet.markerclusterというライブラリがあります。これはズームアウトした時に地図上で重なってしまうオブジェクトを集約表示するためのライブラリです。

スター数は 2018 年 4 月現在で 2,128。ちなみに Leaflet 自体はスター数2万越えなので結構大きい。以下の図が Leaflet.markercluster の例です。

このライブラリを使っている時にたまたま簡単なバグを見つけたので人生で初めて OSS に Pull Request を送ってみました。

どんなバグかというと以下のように、変数に数値が入っていたらその値を使い、null や undefined だった場合は 1 を代入するというコードがあります。

しかし、これには問題があって変数の値が 0 だった場合は False と見なされて 0 ではなく 1 が代入されてしまいます

this.options.opacityWhenUnclustered = this.options.opacity || 1;

ということで以下のように変更します。つまり変数の中身が数字だったらそれを使用し、そうでない場合は 1 を代入します。

this.options.opacityWhenUnclustered = Number.isFinite(this.options.opacity) ? this.options.opacity : 1;

あんまり大きいバグでは無いですが、むしろ小さいバグの方がプルリク出しやすいので、せっかくだから Pull Request 出してみよう!という気持ちになりました。

ちょうど e-navigator(前の記事を参照)を行った時に Pull Request の正しい出し方を教わっていたのですが、実際やるとなると正直メンドくさい。

なので、「適当に Pull Request 出して、何か言われたら書き直すか〜」ぐらいの気持ちで書きました。実際の Pull Request

そしたらまさかのマージ。Pull Request を送った次の日には反応がありました。

初めてだったので良い経験できたな〜という感じです。ただ、今回は良かったものの、次 Pull Request 出す時はしっかりテストしてから出さなアカンなと思いました。

e-Navigatorを完走した!

e-Navigatorをやり終えたので、その感想を書きます。

e-Navigatorとはなんぞや

Ruby on Railsを業務で使用しているフィードフォース社員の方からオンラインでコードレビューをして頂きながら日程調整アプリを作成し、Ruby on Railsを学ぶことができる学習プログラム。社員の方とのコミュニケーションはLINEとGitHubで行いました。最後まで完走すると技術書を貰うことができます。Railsの勉強も出来て技術書も貰えて良いことづくめ。

詳細ページ

https://e-navigator.feedforce.jp/

e-Navigatorの流れ

基本的にこのGitHubのページに進め方が書いてあります。

  1. LINEでe-Navigatorを開始したい旨を連絡
  2. 連絡後テンプレートになるリポジトリをforkし開発を進める。
  3. 短く区切られたセクションに従い機能を追加する。
  4. プルリクエストを作成しレビューをお願いする。
  5. レビューに従いコードの修正を行う。
  6. 修正がすべて終了したら次のセクションへ進む。

3~6を繰り返す。

得られる技術

作成するアプリは日程調整アプリです。サンプルはこちら

最終的にはこのアプリを使って社員の方とアプリ作成に関するフィードバック面談を行います。アプリを作成する目的が明確なのでモチベーションも湧きやすい。

アプリに必要な要件

  • ユーザ登録・編集
  • ユーザの面談日程の表示
  • 面談日程を知らせるメール送信機能

などです。

アプリを作ることで得られる技術

  • Railsの基本知識(MVCモデル、データベースとか)
  • GitHubのプルリクエスト周り
  • Herokuへのアップロードの仕方

とかです。

一通り終了すると、ユーザ登録を行い、ユーザがテキストを投稿できるアプリを作れるようになります。なのでTwitterとかInstagramみたいなSNSとか作れるようになります、多分。

感想

Ruby on Rails勉強したいな〜と思い、定番のRuby on Rails Tutorialをやり始めたものの、モチベーションが続かずなかなか進まない。そんな時にe-Navigatorを偶然発見し、「おっ、Railsの勉強も出来てしかも技術書で貰えるのか!」ということで昨年の3月から開始しました。(実際はRails Tutorialを完了してから進める想定らしいですが気にしない)

e-Navigatorではプログラマーを職業としている方からコードレビューをして頂けます。これは学生時代にはなかなかできない体験。コードレビューは非常に細かいことまで指摘して頂けるな〜という印象。例えば、ファイルの最終行には改行を入れた方が良い!とか、変数名はもっとこういう具体的なものに変更した方が良いよとか、コミットメッセージをこうした方が分かりやすいよとか。一番の利点は一人で勉強するよりもモチベーションが維持できることですね。

Railsを勉強したいけどモチベーション湧かんな〜って人にオススメです。

gitで便利なコマンドのメモ

最近個人的にgitとかGitHubを使うことが多くなったのでメモ。

直前の「git add」の取り消し

git reset HEAD

使用方法: git addを間違えて打ってしまった時に使う

git reset "コミットのハッシュ値"

でそのコミットまで戻ることができ、HEADは直前のコミットを意味するので、直前にステージングしたものを戻せる。

ファイルを特定のコミットの状態まで戻す

git log #戻りたいコミットのハッシュ値の確認
git reset --hard "コミットのハッシュ値"

使用方法: 作業していた内容が大幅に間違っていた(バグった)時に前の状態に戻したい時に使う

git reset に --hardオプションを付けると、ファイルの内容とステージング状態の遡った分は消える。--softだと指定したコミットの状態に戻るが、遡った分は消えない。

[参考] git resetを使いこなす

特定のコミットの変更点を見る

git diff "コミットのハッシュ値"^ "コミットのハッシュ値"

"コミットのハッシュ値"^ は1個前のコミットを意味する。^^は2個前。

2個以上前のコミットメッセージを変更

git rebase -i HEAD~3

# 該当コミットのpickをeditに変更

git commit --amend
git rebase —continue

使用方法: gitアカウントを使い分けている時に、違うアカウントでコミットしまくってしまった。コミットのAuthorを書き換えたい時に使う。

1個前のコミットメッセージはgit commit --amendで変更可能だが、それより前のコミットメッセージを変更するにはこれを使う。

特定のファイルのコミットログを見る

git log “ファイルパス”

git log -p “ファイルパス” pを付けるとファイルの変更箇所が見れる。

色々操作をミスったら戻す

git reflog
git reset —hard HEAD@{reflogで見た番号}

使用方法: 前述のgit reset --hardで前の状態に戻したが、やっぱり戻すのを辞めたくなった時に使う。

git reflogでgit resetなどの操作履歴を表示することが出来る。git resetでその操作履歴を無かったことにする。

JavaScriptのクロージャについて

クロージャとは

クロージャは、独立した (自由な) 変数を参照する関数です。言い換えるとクロージャ内で定義された関数は、自身が作成された環境を覚えています

MDN web docより


関数を実行するたびに変数をincrementする関数を作るとすると、一般的には以下のようにグローバル変数が必要になる。

実行するたびcntを1増やす

let cnt = 0;

function g(){
  cnt ++;
}

console.log(cnt);
g();
console.log(cnt);

実行結果

0
1

グローバル変数を使わないで書こうとすると以下のように失敗する。

実行するたびcntを1増やしたい(失敗)

function h(){
  var cnt = 0;
  console.log(cnt);
  cnt++;
}

h();
h();

実行結果

0
0

しかし、クロージャを利用することでグローバル変数を使わずに実現出来る。当然変数nは外から参照出来ない。

クロージャを利用した例

function f(){
  let n = 0;
  return {
    show : function(){
      console.log('n is ' + n);
    },
    inc : function(){
      n ++;
    }
  }
}

const count = f();

count.show();
count.inc();
count.show();
console.log(count.n);

実行結果

n is 0
n is 1
undefined

クロージャを使う利点

オブジェクト指向で言う、外部から見えないプライベートな変数を作ることが出来る。これによりグローバル変数を使わなくては実現出来なかった処理を、グローバル変数無しで実現出来る。

Node.jsの標準モジュール(https)でAPIを叩く(GETのみ)

(結構前にQiitaに上げた記事です。)

Node.jsを始めたばかりでAPIの叩き方が分からなかったのでメモ。 一般的にはrequestモジュールを使用するようですが、モジュール無しでサクッと書きたいことがあったので。

APIを叩くテストとして「RANDOM USER GENERATOR」を使用。 (このサービスはアプリケーションのテスト用に、適当なダミーユーザを作成するものらしいです。)

基本型

以下のようにhttps.requestの引数に直接URIを渡すと、自動的にhttpメソッドは'GET'になる。responseデータはres.onイベントで受け取る。最後にreq.end()を呼ばないと動かないので注意。

const https = require('https');
const req = https.request('URI', (res) => {
        res.on('data', (chunk) => {

    ...

    });

    ....

})
req.end();

実装例

URI: https://randomuser.me/api/?inc=gender,name,nat&format=PrettyJSON

httpメソッド: 'GET'

query: inc=gender,name,nat&format=PrettyJSON

const https = require('https');

const req = https.request('https://randomuser.me/api/?inc=gender,name,nat&format=PrettyJSON', (res) => {
    res.on('data', (chunk) => {
        console.log(`BODY: ${chunk}`);
    });
    res.on('end', () => {
        console.log('No more data in response.');
    });
})

req.on('error', (e) => {
  console.error(`problem with request: ${e.message}`);
});

req.end();

結果

queryで指定した「gender」、「name」、「nat」プロパティだけを取得出来ている。

BODY: {
  "results": [
    {
      "gender": "female",
      "name": {
        "title": "miss",
        "first": "shivanie",
        "last": "van kekem"
      },
      "nat": "NL"
    }
  ],
  "info": {
    "seed": "cdac8f9044c4fe04",
    "results": 1,
    "page": 1,
    "version": "1.1"
  }
}
No more data in response.

Optionsを使って書く

先程のようにURIを直接指定するのではなく、optionsに各要素を書くことも出来る。queryはpathに書く。 optionsの書き方は公式リファレンスに書いてある。(httpモジュールのリファレンスですが、多分httpsでもだいたい同じだと思う。。。)

https://nodejs.org/api/http.html#http_http_request_options_callback

const https = require('https');

const options = {
  protocol: 'PROTOCOL',
  host: 'HOST',
  path: 'PATH'+'QUERY',
  method: 'METHOD',
    headers: {
    'Content-Type': 'CONTENT-TYPE'
  }
};

const req = https.request(options, (res) => {
        res.on('data', (chunk) => {

    ...

    });

    ....

})
req.end();

実装例

URI: https://randomuser.me/api/?inc=gender,name,nat&format=PrettyJSON

httpメソッド: 'GET'

query: inc=gender,name,nat&format=PrettyJSON

const https = require('https');

const options = {
  protocol: 'https:',
  host: 'www.randomuser.me',
  path: '/api'+'?inc=gender,name,nat&format=PrettyJSON',
  method: 'GET',
};

const req = https.request(options, (res) => {
    res.on('data', (chunk) => {
        console.log(`BODY: ${chunk}`);
    });
    res.on('end', () => {
        console.log('No more data in response.');
    });
})

req.on('error', (e) => {
  console.error(`problem with request: ${e.message}`);
});

req.end();

結果

BODY: {
  "results": [
    {
      "gender": "female",
      "name": {
        "title": "ms",
        "first": "patricia",
        "last": "evans"
      },
      "nat": "GB"
    }
  ],
  "info": {
    "seed": "795d48206f63ba29",
    "results": 1,
    "page": 1,
    "version": "1.1"
  }
}
No more data in response.

記事を目次ページに表示する方法

自分でも忘れかけているのでメモ。 フロントエンドからaxiosでリクエストが来た時に、サーバ側がどうやってmarkdownファイルの情報を送っているか見てみます。

以下に示すのは目次ページに必要な要素を供給する部分です。

server.get('/api/articles', (req,res) => {
    fs.readdir(`./markdowns/articles`,'utf-8',(err,files) => {
      if(err){
        res.status(400)
        res.send()
      }
      let metas = []
      files.forEach((file)=>{
        if(file.match('.md')){
          const data = fs.readFileSync(`./markdowns/articles/${file}`,'utf-8')
          const meta = frontMatter(data)
          const tags = Array.isArray(meta.attributes.tags) ? meta.attributes.tags : new Array(meta.attributes.tags); //配列ではない要素の場合も配列化する
          metas.push({
            filename: file.replace('.md',''),
            title: meta.attributes.title,
            subtitle: meta.attributes.subtitle,
            date: meta.attributes.date,
            tags: tags
          })
        }
      })
      metas.sort((a,b)=>{
        const aDate = parseInt(a.date.replace(/[^0-9]/g,""),10);
        const bDate = parseInt(b.date.replace(/[^0-9]/g,""),10);
        return bDate - aDate
      })
      const metasJSON = JSON.stringify(metas)   
      res.status(200)
      res.json(metasJSON)
    }) 
  })

フロント側が/api/articlesにアクセスして来たら、Markdownディレクトリをロードします。

server.get('/api/articles', (req,res) => {
    fs.readdir(`./markdowns/articles`,'utf-8',(err,files) => {
      if(err){
        res.status(400)
        res.send()
      }

    ・・・

  }

Markdonwディレクトリを読み込んだ後にMarkdownファイルを一つずつ処理します。ここで行うのは目次に表示するためのメタ情報「タイトル」「タグ」「日時」などを取り出す処理です。

・・・

  let metas = []
    files.forEach((file)=>{
      if(file.match('.md')){
        const data = fs.readFileSync(`./markdowns/articles/${file}`,'utf-8')
        const meta = frontMatter(data)
        const tags = Array.isArray(meta.attributes.tags) ? meta.attributes.tags : new Array(meta.attributes.tags); //配列ではない要素の場合も配列化する
        metas.push({
          filename: file.replace('.md',''),
          title: meta.attributes.title,
          subtitle: meta.attributes.subtitle,
          date: meta.attributes.date,
          tags: tags
        })
      }
    })

    ・・・

メタ情報の取得には「frontMatter」を使っています。 frontMatterはMarkdownファイルの先頭に以下のようにメタ情報を書くとそれを認識してくれます。 処理が終わったら変数metasにプッシュしていきます。

---
title: 記事を目次ページに表示する方法
date: 2018年01月29日
tags:
- JavaScript
- blog
---

このままだと記事が目次に表示される順番がバラバラなので書いた日時でMarkdownファイルをソートします。

・・・

metas.sort((a,b)=>{
      const aDate = parseInt(a.date.replace(/[^0-9]/g,""),10);
      const bDate = parseInt(b.date.replace(/[^0-9]/g,""),10);
      return bDate - aDate
    })
    const metasJSON = JSON.stringify(metas)   
    res.status(200)
    res.json(metasJSON)
  }) 
})

日時は「YYYY年MM月DD日」のようなフォーマットなので、単純に日時以外の要素を取り除き大きさを比較します。

例) 
2018年01月29日 -> 20180129
2017年12月30日 -> 20171230

20180129 > 20171230

最後にJSONにしてフロントに送って終了です。

「Reactビギナーズガイド」を読んだ

タイトル

Reactビギナーズガイド コンポーネントベースのフロントエンド開発入門

読んだ時間

13時間ぐらい

内容

第I部 基礎編で最初にExcelライクなコンポーネントを作成し、第Ⅱ部 実践編でそれを使ったワインについてのメモや評価を記録出来るアプリを作る。Reactアプリを作る際にはES6の文法をブラウザで使用するために、Babelとbrowserifyを使用。本の終盤ではコーディング環境を向上させる方法について学ぶ。例えば、ESLintとJestとFluxなど。

第I部 基礎

  • 1章 Hello world
  • 2章 コンポーネントのライフサイクル
  • 3章 : 高機能な表コンポーネントベース
  • 4章 JSX

第Ⅱ部 実践

  • 5章 開発環境のセットアップ
  • 6章 アプリケーションのビルド
  • 7章 品質チェック、型チェック、テスト、そして繰り返し
  • 8章 Flux

感想

この本を読んだ後に実際にReact+Fluxを使ってアプリケーションを作ったが、Reactは大規模なアプリケーションを作る際に便利であって、個人で使う分には冗長な部分があるなと感じた。そもそもReactが出来た背景には「jQueryで作っているアプリが大規模になってくるとコンポーネントの状態が管理できなくなる」問題があるので、大規模ではないアプリではReactは不要な気がする。最近はVue.jsが人気なようなので勉強してみようと思う。

courseraのmachine learningコースを修了した。

3ヶ月前から続けていた couseraの機械学習コースを修了しました。

このコースの流れは、

  • 教材ビデオを見る
  • 理解度チェッククイズを解く
  • プログラミング課題を提出する

です。(プログラミング課題が無いこともありました) これを11週間続けます。

このコースではMatlabOctave(受講期間内は無料で使えます)を使って機械学習の課題を行います。Pythonじゃないのかよ!っていうツッコミに対する回答はコースの最初でちゃんと説明してくれます。要約すると、Matlabの方が機械学習を勉強するには簡単だよと言っていました。私はMatlab初心者でしたが、特に困らず最後まで課題を提出できました。

機械学習の勉強方法について調べると必ずと言っていいほど出てくる超有名なコースなだけあって、内容は超分かり易いです。難しい数式は殆ど無く、どちらかというと図形的なイメージで理解する。逆に数式できっちりと理解したいという人には向いていないかも知れません。

内容はだいたい以下の通りです。

Week 1 導入と単回帰
Week 2 重回帰
Week 3 ロジスティック回帰
Week 4 ニューラルネットワーク
Week 5 ニューラルネットワーク
Week 6 機械学習を実装する時のtips
Week 7 SVM(サポートベクターマシン)
Week 8 教師なし学習
Week 9 異常検出
Week 10 大規模機械学習とアドバンストピック
Week 11 写真 OCR

私はこの授業を受けながら、ゼロから作るDeep Learningを読んでいました。この本の内容も図形やグラフが多く、数式はあまり無かったような気がします。機械学習を道具として使いたいが、1から10まで勉強するのはしんどいという人にはこの2つの教材はオススメだと思います。

一番最後の授業では3ヶ月間授業を教わったAndrew先生からの熱い激励。3ヶ月間コツコツ頑張っていたので、これには感動しました。

Stanford大学の授業を無料で受けられるなんて良い時代だなぁと思いました。機械学習に興味がある人にはオススメです。あ、あと英語の勉強にもなりました。勿論日本語字幕もあります。

「JavaScript: The Good Parts」を読んだ

タイトル

JavaScript: The Good Parts 「良いパーツ」によるベストプラクティス

ページ数と読んだ時間

170ページぐらいと5時間ぐらい

内容

- 1章 良いパーツ
- 2章 文法
- 3章 オブジェクト
- 4章 関数
- 5章 継承
- 6章 配列
- 7章 正規表現
- 8章 メソッド
- 9章 スタイル
- 10章 美しい機能たち
- 付録A ひどいパーツ
- 付録B 悪いパーツ
- 付録C JSLint
- 付録D 鉄道ダイアグラム
- 付録E JSON

感想

この本はJavaScript初級者向けの本であり初心者向けではない、とまえがきに書いてある。先にパーフェクトJavaScriptを読んでいてJavaScriptの基礎はだいたい知っていたので、この本の内容はちょうど良かったかなと思う。 JavaScriptの擬似クラスをあまり使ったことが無いので、5章は流し読みした。JavaScriptの良いパーツについても載っているが、最後の方にある付録にはJavaScriptのひどいパーツと悪いパーツが載っていて、それらは単純に使わないだけでコードが良くなるので、本編よりも付録の方が役に立つかもしれない。

「ハイパフォーマンスWebサイト」を読んだ

タイトル

ハイパフォーマンスWebサイト 高速サイトを実現する14のルール

発行日

  • 初版第1刷 2008年04月11日
  • 初版第10刷 2016年07月21日

ページ数と読んだ時間

150ページぐらいと5時間ぐらい

内容

yahooでサイトのパフォーマンスを改善するチームに所属し、実際にそのWebサイトのパフォーマンスを改善した著者が、Webサイトを高速化する14のルールを紹介する。

- ルール1:  HTTPリクエストを減らす
- ルール2:  CDN(Content Delivery Network)を使う
- ルール3:  Expiresヘッダを設定する
- ルール4:  コンポーネントをgzipする
- ルール5:  スタイルシートは先頭に置く
- ルール6:  スクリプトは最後に置く
- ルール7:  CSS expressionの使用を控える
- ルール8:  JavaScriptとCSSは外部ファイル化する
- ルール9:  DNSルックアップを減らす
- ルール10: JavaScriptを縮小化する
- ルール11: リダイレクトを避ける
- ルール12: スクリプトを重複させない
- ルール13: ETagの設定を変更する
- ルール14: Ajaxをキャッシュ可能にする

感想

この本は第1刷が2008年と結構古く、Web技術の進化は早いので古いものはあまり買わないようにしていたが、この本の内容は今でも役に立ちそうだったので読んでみた。HTMLを書くときに「JavaScriptファイルはBodyタグの最後に書くと良い」というのをネット上でよく見かけるのでなんとなくそれに従って書いていたが、この本を読むとそれがなぜ良いのかが分かる。

ブログを最適化する(1) の記事では「ルール4: コンポーネントをgzipする」を実際にやってみて、PageSpeed Insights(Webページの速度を評価するツール)の数値が劇的に変わったので、その効果を実感できた。

この本は150ページとページ数が少なく気軽に読めるのでオススメです。

ブログを最適化する

dev.to と阿部寛のホームページどっちが速いですか?というめっちゃ面白い記事を見ました。

その記事では Google のPageSpeed Insightsという Web ページを評価するためのツールを利用して、dev.to と阿部寛のホームページを比較しています。

ということでこのブログの評価を見てみます。

スコアは41と結構酷くなってしまいました。この記事を書くちょっと前に計測したときは 90 超えていたのに、記事やコードを増やしたら一気に悪くなってしまいました。(ちなみに阿部寛のホームページのスコアは 92 らしい)

で、このPageSpeed Insightsは Web ページが遅い原因も教えてくれて、このブログではコンテンツを gzip 圧縮していないよとのことでした。コンテンツっていうのは HTML とか JavaScript とか CSS とか画像とか、とにかく全部です。

このブログのサーバサイドはExpressで書かれていて、そのドキュメントを見ると、以下のようにして gzip 圧縮をしたコンテンツを送信してくれるそうです。

var compression = require('compression')
var express = require('express')
var app = express()
app.use(compression())

で、実際にやってみると、

となり、スコアは92まで上昇しました。他にも遅い原因としてキャッシュを利用していないことが分かったので、それは後で解決します。たぶん。

Next.jsでブログを作った。

Next.js でブログを作りました。

Next.js とは、Web ページを高速に表示する為に、SPA(シングルページアプリケーション)と SSR(サーバサイドレンダリング)の両方を生かそうというコンセプトで作られたフレームワークらしいです。

詳しくは

Next.js は SPA なので、ページを移動してもサーバとの通信が発生しません。よって高速にページ間の移動が出来ます。 以下は「Home」ページから「about」ページへ移動する時の通信を chrome の devtools で見た様子です。

1回だけ通信が発生していますが、これはブログ記事をサーバから取ってくる為に私が書いたものなので、それを無くせば Next.js 自体のルーティングでは通信は発生しません。 実際このブログでは記事を持ってくるのに 190ms もかかってしまっているので、ページ遷移はあんまり速くないです。

ブログ記事は markdown で書いています。 markdown ファイルを HTML に変換する為に、markdown-itを使用しています。markdown-it には拡張用のプラグインがあって、それによってコードハイライトをしています。例えば

function f(arg) {
  var n = 123 * Number(arg)
  return function() {
    console.log('n is ' + n)
    console.log('g is called')
  }
}
const num = f(5)
num()

のような感じ。Tex のプラグインもあるのそれを導入すれば数式も綺麗に表示できるはずです。

Next.js を使ったブログ作成はこちら https://github.com/ganow/nextjs-blog-sample を参考にさせて頂きました。

「実践Node.jsプログラミング」を読んだ

タイトル

実践Node.jsプログラミング

読んだ時間

25時間ぐらい

内容

Node.jsを使って実際にWebアプリを作りながら、Node.jsを学ぶのがメイン。チャットアプリ、ファイルサーバなどを作る。

第1部 Nodeの基礎

  • Node.jsの基本的な説明。イベント駆動、非同期処理、シングルスレッド、よって大量の接続を捌ける!
  • Socket.ioを使ってチャットアプリを作る。

第2部 NodeでWebアプリケーションを開発する

  • 静的ファイルサーバとして、Todoリストアプリを作る。
  • MySQLを使って作業日報アプリを作る。
  • Expressでフォトアップロードアプリを作る。
  • Expressでシャウトボックス(チャットみたいなもの)を作る。

第3部 Nodeの配置と応用

  • 作ったアプリをWebにアップロードする方法。GitHubへHerokuなど。
  • Node.jsでCLIツールを作る。

感想

初めて買ったNode.jsの本。Webアプリケーションフレームワークどころかサーバを書いたのも初めてだったので、最初はあまり理解せずにソースコードを写経していたが、読んでいるうちにチャットアプリとかサーバとかを作れるようになった。Webアプリを実際に作るので、ある程度はWebの知識が必要だと思った。私は「Webを支える技術」という本を読んでからこの本を読んだので理解しやすかった。

技科大の休講情報を自動でカレンダーに登録する。

Github にソースがあります

TUT の補講・休講・教室変更情報を Gmail から GoogleCalendar に追加する GoogleAppsScript です。

教務から送られてくるメールはだいたい以下のフォーマットになっています。

■ 開講学部:工学部
■ 時間割番号:B136*****
■ 時間割名:ほげほげ
■ 教室変更日:2017/06/08 のみ
■ 時限:2 限
■ 時間:10:30 ~ 12:00
■ 変更前教室:A2棟講義室 A2-301
■ 変更後教室:A1棟講義室 A1-201
■ 学生へのメッセージ

このメールをパースして、カレンダーのイベントに追加します。(カレンダーが無い場合は新しく作成します。)
カレンダーに追加したイベントはメールで通知されます。

実行方法

  • "tutAddCalendarFromGmail.gs"をダウンロードするかコピーして新しく GoogleAppsScript を作成して下さい。(自分の Google ドライブに保存して下さい。)
  • 教務から来るメールのラベルを"豊橋技科大"として下さい。(ラベル名は"createEvent"関数の引数で変更出来ます。)
  • スクリプトを実行すると Google アカウントの認証を求められるので、教務からメールを受け取っているアカウントで認証して下さい。
  • 実行が終わると、Google カレンダーに新しく"TUT 授業"というカレンダーが作成され、そこに補講・休講・教室変更情報が追加されます。
  • また、追加されたイベントは Gmail にも通知されます。

改善したい点

  • 補講・休講・教室変更の取り消しメールに対応していない。

カレンダーに休講情報を追加した様子

google カレンダーはあまり見やすくないので google カレンダーと同期できるジョルテというカレンダーアプリを使用しています。

マーク・ザッカーバーグのスピーチを聞いて超感動した話

たまたまこの記事(ザッカーバーグのハーバード卒業式スピーチが感動的だったので日本語訳した。)をネットで見かけて、元々著名人のスピーチを見るのが好きだったので見てみた。この記事と合わせて動画を見ると良いと思います。

ざっくりした感想は「ザッカーバーグめっちゃ良いやつやん・・・」です。スピーチの中で不法移民の子がそれを理由に大学に行けないかもしれないという話があって、その話をしているザッカーバーグが涙を堪えながら話しているのが印象的でした。

私が一番好きなフレーズは「Ideas don’t come out fully formed. They only become clear as you work on them. You just have to get started.」です。これは日本語にすると、「アイデアは完全な形では浮かばない。やっているうちにだんだんクリアになってくる。まずは始めることだ。」といった感じです。

スピーチは勿論素晴らしいものでしたが、ザッカーバーグは口だけではなくて、実際に多くの慈善事業を行っていて、有名な話だとフェイスブックの株式の99%を慈善事業に寄付するというのがニュースになっていました。で、そのニュースに対してそれは偽善だの相続税対策だの言っている記事をたくさん見かけますし、実際自分もその記事を見て「へー、ザッカーバーグって金持ちやなー」程度にしか思っていませんでしたが、このスピーチを聞くと、「ああ、この人本気で世界を変えようとしているんだなぁ」と思わせられます。

彼は本当に色々な慈善事業をしていて、例えばこの恵まれない地域を救おうとして失敗した時の話なんかも面白いです。