早くエンジニアになりたい

masatany's memorandum

Nuxt.jsで作成したページにログイン機構を作る

クリスマスなんて関係ない!!

この記事はFusic Advent Calendar 2017 25日目の記事です。

Fusicでは二ヶ月に一回くらいの頻度で「エンジニア開発合宿」と称して丸1日泊まり込みで自分の興味のある技術やガジェットに関する開発を行うイベントがあります(2017年12月時点)。

自分は、フロントエンドに苦手意識があるので(というか苦手)、javascriptフレームワークを使ってアプリを作成したいと思い、
今回はNuxt.jsでログイン認証ができるところまでを実装しました。

プロジェクトの作成

1. vue-cliをインストール

Vue.jsを使う環境をいい感じに構築してくれるコマンドラインインタフェースをインストールします。

$ npm install -g vue-cli

2. vue init

プロジェクトの初期化を行います。

$ vue init nuxt-community/starter-template hello_nuxt

? Project name hello_nuxt
? Project description Nuxt.js project
? Author k-masatany <masatani@fusic.co.jp>

   vue-cli · Generated "hello_nuxt".

   To get started:

     cd hello_nuxt
     npm install # Or yarn
     npm run dev

作成されたプロジェクトに必要なnodeモジュールをインストールします。

$ cd hello_nuxt/
$ yarn

Hello, Nuxt.js

$ yarn dev

を実行して、 http://localhost:3000 にアクセスします。 下記のようなページが表示されるはずです。 f:id:k_masatany:20171224224022p:plain

認証機構のベースを作成する

基本的な部分は公式ページを参考にして、認証機構を構築していきます。 (細かい説明は公式ページに書いてあるので、作業内容を箇条書きしています。)

1. 依存パッケージをインストール

$ yarn add express express-session body-parser whatwg-fetch

2. server.jsファイルを作成

プロジェクトのルートディレクトリにserver.jsを作成します。

const { Nuxt, Builder } = require('nuxt')
const bodyParser = require('body-parser')
const session = require('express-session')
const app = require('express')()

// req.body へアクセスするために body-parser を使う
app.use(bodyParser.json())

// req.session を作成します
app.use(session({
  secret: 'super-secret-key',
  resave: false,
  saveUninitialized: false,
  cookie: { maxAge: 60000 }
}))

// POST /api/login してログインし、認証されたユーザーを req.session.authUser に追加
app.post('/api/login', function (req, res) {
  if (req.body.username === 'k-masatany' && req.body.password === 'demo') {
    req.session.authUser = { username: 'k-masatany' }
    return res.json({ username: 'k-masatany' })
  }
  res.status(401).json({ error: 'Bad credentials' })
})

// POST /api/logout してログアウトし、ログアウトしたユーザーを req.session から削除
app.post('/api/logout', function (req, res) {
  delete req.session.authUser
  res.json({ ok: true })
})

// オプションとともに Nuxt.js をインスタンス化
const isProd = process.env.NODE_ENV === 'production'
const nuxt = new Nuxt({ dev: !isProd })
// プロダクション環境ではビルドしない
if (!isProd) {
  const builder = new Builder(nuxt)
  builder.build()
}
app.use(nuxt.render)
app.listen(3000)
console.log('Server is listening on http://localhost:3000')

このサンプルコードではk-masatany/demoでしかログインできません。

3. package.jsonを更新

先ほど作成したserver.jsを読み込むようにします。

{
  "name": "hello_nuxt",
  "version": "1.0.0",
  "description": "Nuxt.js project",
  "author": "k-masatany <masatani@fusic.co.jp>",
  "private": true,
  "scripts": {
    "dev": "node server.js",  // ここと
    "build": "nuxt build",  // ここと
    "start": "cross-env NODE_ENV=production node server.js",  // ここ
    "generate": "nuxt generate",
    "lint": "eslint --ext .js,.vue --ignore-path .gitignore .",
    "precommit": "npm run lint"
  },
  "dependencies": {
    "body-parser": "^1.18.2",
    "express": "^4.16.2",
    "express-session": "^1.15.6",
    "nuxt": "^1.0.0-rc11",
    "whatwg-fetch": "^2.0.3"
  },
  "devDependencies": {
    "babel-eslint": "^7.2.3",
    "eslint": "^4.3.0",
    "eslint-config-standard": "^10.2.1",
    "eslint-loader": "^1.9.0",
    "eslint-plugin-html": "^3.1.1",
    "eslint-plugin-import": "^2.7.0",
    "eslint-plugin-node": "^5.1.1",
    "eslint-plugin-promise": "^3.5.0",
    "eslint-plugin-standard": "^3.0.1"
  }
}

その後、

$ yarn add cross-env
$ yarn dev

を実行してデバッグ環境を再起動します。

4. ストアを作成する

ユーザーの情報を保持するためのstore/user.jsを作成します。

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

// window.fetch() のためのポリフィル
require('whatwg-fetch')

const store = () => new Vuex.Store({

  state: {
    authUser: null
  },

  mutations: {
    SET_USER: function (state, user) {
      state.authUser = user
    }
  },

  actions: {
    nuxtServerInit ({
      commit
    }, {
      req
    }) {
      if (req.session && req.session.authUser) {
        commit('SET_USER', req.session.authUser)
      }
    },
    login ({
      commit
    }, {
      username,
      password
    }) {
      return fetch('/api/login', {
        // クライアントのクッキーをサーバーに送信
        credentials: 'same-origin',
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          username,
          password
        })
      })
        .then((res) => {
          if (res.status === 401) {
            throw new Error('Bad credentials')
          } else {
            return res.json()
          }
        })
        .then((authUser) => {
          commit('SET_USER', authUser)
        })
    },
    logout ({
      commit
    }) {
      return fetch('/api/logout', {
        // クライアントのクッキーをサーバーに送信
        credentials: 'same-origin',
        method: 'POST'
      })
        .then(() => {
          commit('SET_USER', null)
        })
    }
  }
})

export default store

5. 認証が必要なページを作成

/helloルートを作成するため、pages/hello.vueを作成します。 今回はindex.vueをコピーして、文面だけ変えています。

<template>
  <!-- 中身は自由に作成 -->
  <section class="container">
    <div>
      <h2 class="subtitle">
        Hello!!
      </h2>
    </div>
  </section>
</template>

<script>
export default {
  // データをこのコンポーネントにセットする必要がないため fetch() を使う
  fetch ({ store, redirect }) {
    if (!store.state.authUser) {
      return redirect('/auth')
    }
  }
}

デフォルトのindex.vueに手を加えて、/helloへのリンクを作成します。

<template>
  <section class="container">
    <div>
      <logo/>
      <h1 class="title">
        hello_nuxt
      </h1>
      <h2 class="subtitle">
        Nuxt.js project
      </h2>
      <div class="links">
        <a href="/hello" class="button--green"> go to hello page</a>  // ここ
      </div>
    </div>
  </section>
</template>

f:id:k_masatany:20171224233331p:plain

今はまだ認証がおこなわれていないので、ボタンをクリックすると/authにリダイレクトされます。 /authはまだ作成されていないので、404になります。 f:id:k_masatany:20171224233950p:plain

6. ログインページを作成

認証用の/authページを作成するために、pages/auth.vueを作成します。 CSSなどは適当に当ててください。

<template>
  <section class="container">
    <div>
      <h2 class="subtitle">
        Login
      </h2>
      <form v-if="!$store.state.authUser" @submit.prevent="login">
        <p class="error" v-if="formError">{{ formError }}</p>
        <div>
          <input type="text" class="form-control" v-model="formUsername" name="username" placeholder="Username" />
          <input type="password" class="form-control" v-model="formPassword" name="password"  placeholder="Password" />
          <button type="submit" class="button--green block">Login</button>
        </div>
      </form>
      <div v-else>
        <h2>Hello {{ $store.state.authUser.username }}!</h2>
        <div class="links">
          <a href="/hello" class="button--green">go to hello page</a>
          <button class="button--grey" @click="logout">Logout</button>
        </div>
      </div>
    </div>
  </section>
</template>

<script>
export default {
  data() {
    return {
      formError: null,
      formUsername: '',
      formPassword: ''
    }
  },
  methods: {
    async login() {
      try {
        await this.$store.dispatch('login', {
          username: this.formUsername,
          password: this.formPassword
        })
        this.formUsername = ''
        this.formPassword = ''
        this.formError = null
      } catch (e) {
        this.formError = e.message
      }
    },
    async logout() {
      try {
        await this.$store.dispatch('logout')
      } catch (e) {
        this.formError = e.message
      }
    }
  }
}
</script>

f:id:k_masatany:20171225001452p:plain

ログインしていない状態では、フォームが表示され、認証が通ったらユーザー名と/helloへのリンクが表示されます。

ログインしてみる

k-masatany/demoでログインできるので、入力します。 f:id:k_masatany:20171225002115p:plain

きちんとk-masatanyと表示されています。
それでは、改めて/helloへ移動してみます。ログイン後のボタンをクリックしてみましょう。 f:id:k_masatany:20171225002204p:plain ※画像はいらすとや様よりお借りしました。

無事に表示されました。(hello.vueのコードは書き換えました) この状態であれば、/に戻って/helloへのリンクをクリックしても/authへ飛ばされることはありません。

終わりに

今回は公式ページを参考にして、Nuxt.jsにログイン機構を作成しました。
今後は、AWS Cognitoなどを使ったログインの仕組みをつけたいと思います。

nc(netcat)コマンドでHTTPサーバーの気持ちを知る

この記事はFusic Advent Calendar 2017 12日目の記事です。

WEBエンジニアとしていつもお世話になっているApacheやNginxと言った「HTTPサーバーの気持ち」を知ろうと思い立ったので、ncコマンドを使ってHTTPリクエストを受けてHTTPレスポンスを返す一連の流れを体験してみました。

まがいなりにもネットワークスペシャリストなのでネットワーク関連の記事を書こうと思っていましたが、良いネタが思いつかなかったので、今日はレイヤー7で遊ぼうと思います。

ncコマンドで簡易サーバーを立てる

何はともあれ、HTTPを受けるためのサーバーが必要です。 8000番ポートで待ち受けるTCPサーバーをncコマンドで構築します。

while : ; do  nc -l 127.0.0.1 8000; done

これでオレオレHTTPサーバ(文字通り)の構築は終わりです。簡単ですね。 もちろんこのサーバーは、同時に1つのリクエストしか処理できません。

ncコマンドはコネクションがクローズすると、プロセスが終了してしまいます。 一々コマンドを再実行してもいいですが、面倒臭いのでwhileで永続化しています。

HTTPリクエストを受けてみる

おもむろにブラウザ(Chromeがおすすめ)を立ち上げて、http://localhost:8000にアクセスしてみます。 おそらく、下記のようなHTTPリクエストが飛んでくるはずです。

$ while : ; do  nc -l 127.0.0.1 8000; done
GET / HTTP/1.1
Host: localhost:8000
Connection: keep-alive
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36
Upgrade-Insecure-Requests: 1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: ja,en-US;q=0.9,en;q=0.8
Cookie: username-localhost-50080="2|1:0|10:1511168650|24:username-localhost-50080|44:Nzc3OWJmNjE0NTI5NDEzZDk2Y2I2MzU5ZmEwMmE1MDQ=|4279df9a2de706e91212b77f6ef85216f0b7f2f9fb8211e6430c53b5f283b952"

f:id:k_masatany:20171208233945p:plain

ブラウザにはまだ何も表示されません。 レスポンスを返していないのだから当然ですね。

HTTPレスポンスを返す

いつまでもブラウザを待たせているのもかわいそうなので、そろそろレスポンスを返してあげます。 コンソールに、下記の内容のレスポンスヘッダを返します。

HTTP/1.1 200 OK
Server: k-masatany 28.5 (nightly)
Content-Type: text/html

<!DOCTYPE html>
<html>
<title>nc server</title>
</head>
<body>

ここまで入力すると、Chromeを使っていれば、開いているページが真っ白になり、ページタイトルが「nc server」になっているはずです。 Safariだと、Ctrl+DでEOFを送信するまでレンダリングは行われませんでした。 f:id:k_masatany:20171208234624p:plain

bodyの内容も送る

真っ白なページでは面白くないのでbodyの内容を送ってみましょう。 先ほどの<body>に続いて、下記の内容を書いて行きます。

<div>Hello, HTTP World!</div>
<h1>H1 Message</h1>
<h2 style="color: red">Red Color Message<h2>
<h3>hogehoge</h3>

Chromeでは、タグを閉じるたび、レンダリングが行われます。 f:id:k_masatany:20171208235246p:plain

404 NotFoundを返してみる

http://localhost:8000/notfoundにアクセスしてみます。 ここにはコンテンツがないので、404を返してみます。 (オレオレHTTPサーバにはそもそもコンテンツがないので全部404なのですが・・・)

HTTP/1.1 404 Not Found
Server: k-masatany 28.5 (nightly)
Content-Type: text/html

<!DOCTYPE html>
<html>
<title>nc server</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css" integrity="sha384-PsH8R72JQ3SOdhVi3uxftmaW6Vc51MKb0q5P2rRUpPvrszuE4W1povHYgTpBfshb" crossorigin="anonymous">
</head>
<body>
<h1 class="display-1">404 Not Found</h1>
<p class="h2">ご指定のページは見つかりませんでした<p>

f:id:k_masatany:20171209002214p:plain おっと、文字化けしてますね(しれっとBootstrap適用)。

ページをリロードして、metaタグを追加したレスポンスbodyを再送信します。

HTTP/1.1 404 Not Found
Server: k-masatany 28.5 (nightly)
Content-Type: text/html

<!DOCTYPE html>
<html>
<meta charset="utf-8">
<title>nc server</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css" integrity="sha384-PsH8R72JQ3SOdhVi3uxftmaW6Vc51MKb0q5P2rRUpPvrszuE4W1povHYgTpBfshb" crossorigin="anonymous">
</head>
<body>
<h1 class="display-1">404 Not Found</h1>
<p class="h2">ご指定のページは見つかりませんでした<p>

f:id:k_masatany:20171209002222p:plain 日本語も無事表示されました。

404ステータスコードを返しても、bodyにコンテンツを入れていると、描画されてしまうので、 レスポンスボディを消してみます。

HTTP/1.1 404 Not Found
Server: k-masatany 28.5 (nightly)
Content-Type: text/html
^D # Ctrl+D

ChromeのHTTP ERROR 404ページになりました。 きちんとステータスコードが影響しています。f:id:k_masatany:20171209002839p:plain

HTTPで遊んでみて

色々HTTPで遊んでみましたが、各種HTTPヘッダに対する応答をはじめ、オレオレおHTTPサーバーでは様々な機能が使えません。

こんな面倒なことをいつも一瞬でやってくれているHTTPサーバーに感謝を感じる遊びでした。

最後に

忙しくてレスポンスが返せない時は、きちんと503エラーを返しましょう。

f:id:k_masatany:20171209003745p:plain

HTTP/1.1 503 Service Unavailable
Server: k-masatany 28.5 (nightly)
Content-Type: text/html
^D # Ctrl+D

YAPC::Fukuoka 2017に行ってきました

ブログを書くまでがYAPCです。

というわけで、「弊社の参加者はブログを書かないのかしら」と社内Slackで実行委員長さんに突っつかれたので、久々の更新です。
そもそも、YAPCに限らず、こういったイベントに参加すること自体が初めて(だと思う)なので、期待半分、不安半分でした。

拝見したセッション一覧(講演順)

  • レガシーPerlと「今」を組み合わせ、開発を継続し続ける方法(山下 和彦さん)
  • 稼働中の Web サービスの Perl 処理系バージョンアップをしていく話(astjさん)
  • ウェブセキュリティの最近の話題早分かり(徳丸 浩さん)
  • コンテナを「守る」仕組みから、中身を理解しよう!(近藤 宇智朗さん)
  • システム障害をめぐる冒険 (タケタニヒロトさん)
  • Web application good error messages and bad error messages(moznionさん)
  • cpm(鍛治 匠一さん)
  • 本編スペシャルセッション: 福岡のIT企業、開発現場の未来
  • Inside Evalpark - the evolution of sandboxing(Dan Kogaiさん)
  • 巨大Perlプロジェクトに、Dockerが出会った(acidlemonさん)
  • Lightning Talk
  • Sponsor Session
  • 福岡からニューヨークへ転勤になったエンジニアの話(山本 竜三さん)

印象に残ってるセッション

コンテナを「守る」仕組みから、中身を理解しよう!

このセッションが見たくて参加したこともあり、とても勉強になりました。
コンテナと呼ばれる技術の基本的な考え方が理解でき、好奇心から、帰ってすぐにコンテナ風プロセスを実装して遊びました。

ウェブセキュリティの最近の話題早分かり

「時間がないのでサクッと攻撃しちゃいますね」というパワーワードが今でも頭から離れない。そんなセッション。
登壇用に用意されたサイトだとしても、徳丸さんがいとも簡単に脆弱性をついている様子を見て、「今までよりももっとセキュリティに意識を向けないといけない」と強く感じました。

割と嬉しかったこと

帰宅後、@udzuraさんのセッションで出てきたコンテナ(風)プロセスを、スライドを見ながらC言語で実装してみました。
(とりあえずchrootとか試すだけのクソコードなのでコードは割愛します)
思っていたよりずっと簡単に、コンテナみたいなプロセスができてしまいました。
↑の話を呟いたら、@udzuraさん本人がいいねしてくれたのが地味に嬉しかったです。

まとめ

前は割と「イベントってスライドも大体ネットに上がるし、行くほどでもないかな」と思って参加はあまりしていませんでした。
今回も、社内の参加予定の方が仕事の都合で行けなくなったため、チケットを譲っていただき参加しています。

ただ、今回の参加をきっかけに、少し考えを改めようと思いました。
行かないと得られない情報もありますし、何よりあの空気感は向上心を刺激してくれるので、今後も定期的にイベントには参加したいと思いました。

運営の皆様、登壇者の皆様、ありがとうございました。

Rubyでもフィボナッチ数を計算してみる

先日、ElixirからRustのプログラムを呼び出してフィボナッチ数の計算を高速化してみたので、今度はRubyを高速化してみます。

環境

  • マシン

    • Microsoft Windows 10 Home
    • Intel64 Family 6 Model 78 Stepping 3 GenuineIntel ~2300 Mhz
    • 3,935 MB
  • Ruby

    • ruby 2.3.1p112 (2016-04-26 revision 54768) [x64-mingw32]
  • Rust

    • rustc 1.9.0 (e4e8b6668 2016-05-18)
    • cargo 0.10.0-nightly (10ddd7d 2016-04-08)

検証したソースコード

Elixirの時同様、どちらの言語もまだまだ初学者レベルなので、細かいところがおかしい場合はご指摘願います。 また、「こうした方が早くなる」等のアドバイスも非常に助かりますので、お時間があればご教示願います。

fibonacci.rb

require "ffi"
require 'benchmark'

module Fib
  extend FFI::Library
  ffi_lib 'lib/fib.dll'
  attach_function :fib, [:uint], :uint
end

def fibonacci(n)
  return n if n <= 1
  fibonacci(n - 1) + fibonacci(n - 2)
end

0.upto(ARGV[0].to_i) do |num|
  ruby_result = Benchmark.realtime do
    fibonacci(num)
  end
  rust_result = Benchmark.realtime do
    Fib.fib(num)
  end
  puts format("%2d, %.9f, %.9f", num, ruby_result, rust_result)
end

lib.rs

#[no_mangle]  //関数名をマングリングさせないようにする
pub extern fn fibonacci(n: u32) -> u32 {
    match n {
        0 => 0,
        1 => 1,
        _ => fibonacci(n-1) + fibonacci(n-2)
    }
}

Cargo.toml

[package]
name = "fib"
version = "0.1.0"
authors = ["k-masatany"]

[dependencies]

[lib]
name = "fib"
crate-type = ["dylib"]

rustはreleaseで最適化してビルドしています。

結果

f:id:k_masatany:20160801230259p:plain * 画像が大きすぎて文字がつぶれました。青い棒がRubyのみ、赤い棒(見にくい)がRust呼び出し版です。

まとめ

  • Rubyはとても書きやすい。Rustは少しレベルアップしました?(マッチング使ってみました)
  • やっぱりRustはやい。

Elixirでフィボナッチ数を計算してみる

このごろ流行のruby風味の関数型言語「Elixir」でフィボナッチ数を計算してみました。
そして、これまた、このごろ流行の関数型言語Rustで実装したフィボナッチ数を求めるプログラムを呼び出して、速度比較をしてみました。 とくに意味はありません。

環境

  • マシン

    • Microsoft Windows 10 Home
    • Intel64 Family 6 Model 78 Stepping 3 GenuineIntel ~2300 Mhz
    • 3,935 MB
  • Elixr

    • Erlang/OTP 18 [erts-7.2.1] [64-bit] [smp:4:4] [async-threads:10]
    • Elixir 1.3.1
  • Rust

    • rustc 1.9.0 (e4e8b6668 2016-05-18)
    • cargo 0.10.0-nightly (10ddd7d 2016-04-08)

検証したソースコード

どちらの言語もまだまだ初学者レベルなので、細かいところがおかしい場合はご指摘願います。 また、「こうした方が早くなる」等のアドバイスも非常に助かりますので、お時間があればご教示願います。

fibonacci.ex

defmodule Fibonacci do
  def start do
    [num_string|_] = System.argv
    num = String.to_integer(num_string)
    exec_cmd = "lib/rust_fib.exe " <> num_string
    IO.puts "Elixir fibonacci"
    {result_elixir, :ok} = :timer.tc(Fibonacci, :fibonacci_elixir, [num])
    IO.puts "#{result_elixir/1000}ms"
    IO.puts "Rust   fibonacci"
    {result_rust, :ok} = :timer.tc(Fibonacci, :fibonacci_rust, [exec_cmd])
    IO.puts "#{result_rust/1000}ms"
  end

  # Elixirのみで計算する場合
  def fibonacci_elixir(num) do
    IO.puts fibonacci(num)
  end
  
  def fibonacci 0 do 0 end
  def fibonacci 1 do 1 end
  def fibonacci(num) when (is_integer num) and (num > 0) do
    fibonacci(num - 1) + fibonacci(num - 2)
  end


  # Rustプログラムを呼び出す場合
  def fibonacci_rust(exec_cmd) do
    port = Port.open({:spawn, exec_cmd}, [:binary])
    Port.command(port, "")

    receive do
      {^port, {:data, result}} -> IO.puts(String.trim(result))
    end
  end
end

Fibonacci.start

rust_fib.rs

use std::env;

fn calc_fibonacci(n: u32) -> u32 {
    if n <= 1 {
        n
    } else {
        calc_fibonacci(n - 1) + calc_fibonacci(n - 2)
    }
}

fn fibonacci(mut argv: env::Args) -> Result<u32, String> {
    let arg1 = try!(argv.nth(1).ok_or("数字を1つ指定してください。".to_owned()));
    let n = try!(arg1.parse::<u32>().map_err(|err| err.to_string()));
    Ok(calc_fibonacci(n))
}

fn main() {
    match fibonacci(env::args()) {
        Ok(n) => println!("{}", n),
        Err(err) => println!("{}", err),
    }
}

結果

f:id:k_masatany:20160731164719p:plain ※ ms単位にしたため、us単位の数値が消えて0msになっています。すみません。
※ 与える数値が低い範囲(0~25くらいまで)は速度の数値が安定しなかったので、何度か計測して頻度の高い数値を使っています。
※ 画像出力の際に表の縦線が消えました。

  • 0から26までは、Elixirの計算速度の方が早いようです。と言っても、Elixir+Rustが遅いのではなく、外部プログラムを呼び出して結果を受けるまでのオーバーヘッドが必ず入ってしまうので遅くなる感じですね。
  • 27で双方が肩を並べ、30から外部プログラムの恩恵を受けられるようになりました。
  • 45では、Elixir+Rustの方が10倍早い結果が出ています。

まとめ

  • Elixirは書きやすい。Rustは少し取っつきにくかったです。
  • Elixirが遅いときは、重い処理を簡潔な外部プログラムで実装してPortで呼び出すという手もある。
  • 外部プログラムの呼び出しに一定のコストがかかるので、Elixirの実行速度が比較的遅いからと言って、むやみやたらに外部プログラムを呼ぶものではないと感じました。

Raspberry PiでPython3を使う

仕事でPython3系を使うことになり、趣味で使っているRaspberry PiでもPython3が使いたいので、インストールしました。

環境

Raspberry Pi2 Model B + raspbian

$ cat /etc/debian_version
8.0

インストール手順

インストール手順といっても最新のソースを持ってきてMakeしてインストールするだけです。

$ cd /usr/local/src/
$ sudo wget https://www.python.org/ftp/python/3.5.2/Python-3.5.2.tgz
$ sudo tar xzvf Python-3.5.2.tgz
$ cd Python-3.5.2/
# ソースを落としてきます。/usr/local/src/で作業するとsudoが必要なので、
# 気になる方は/home/の下で作業するといいです。
# その場合はsudoは不要です。

$ ./configure
# デフォルトでは/usr/local/bin/にインストールされるので、
# 場所を変えたい場合はprefixオプションを付けます。
# 私は趣味なので気にせずデフォルトです。
# ./configure --prefix=/your/python/install/path

$ make
# スペックの問題でかなり時間がかかるので、気長に待ちます。私は10分以上かかりました。

$ sudo make install
# prefixオプションでroot権限が不要な場所を指定しているのならsudoは不要です。

pipもインストール

最近のPythonはpipも一緒にインストールしてくれるはずなのですが・・・。

Ignoring ensurepip failure: pip 8.1.1 requires SSL/TLS

はい、ごめんなさい。 OpenSSLをインストールして、改めてpipをインストール。

$ sudo apt-get install libssl-dev
$ sudo make install

動作確認

$ python -V
Python 2.7.9
$ pip -V
pip 1.5.6 from /usr/lib/python2.7/dist-packages (python 2.7)

$ python3 -V
Python 3.5.2
$ pip3 -V
pip 8.1.1 from /usr/local/lib/python3.5/site-packages (python 3.5)

Pythonもpipも問題なくインストールできました。 仕事も趣味も3系に揃えられて、仕様の違いに悩まされずに済みそうです。