読者です 読者をやめる 読者になる 読者になる

にわとりプログラマーの備忘録

覚えたことをすぐ忘れてしまう、自分のための備忘録ブログです。

DockerでMongoDBをインストールして試してみる

DockerでMongoDBをインストールして試してみる

MongoDBとはドキュメント指向でJSONライクな形式でデータを格納するデータベースである
簡単に触ってみようと思いDockerでインストールしたので、メモを残しておく

イメージの取得

Docker Hubにイメージが置いてあるので、そこからイメージを取得する

$ docker pull mongo

コンテナの生成

取得したイメージからコンテナを生成する。
--name some-mongoはコンテナのエイリアスを指定している。
-dオプションでは、コンテナのバックグランド実行を指定している。
最後のmongoで生成元のイメージを指定している。

$ docker run --name some-mongo -d mongo

コンテナを確認してみると、無事に生成されているのが確認できる。

$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
61c8b5f2f4af        mongo               "/entrypoint.sh mongo"   24 minutes ago      Up 24 minutes       27017/tcp           some-mongo

コンテナに接続してMongoDBを試してみる

コンテナの生成が完了したので、後はコンテナの中に入り、mongoコマンドを実行することで、MongoDBを色々試すことが出来る状態になります

$ docker exec -it some-mongo bash
root@61c8b5f2f4af:/# mongo
MongoDB shell version v3.4.2
...
> 

MongoDBの簡単な使い方はMongoDB超入門などを参照すると分かりやすいです

Elixir + Phoenix でお手軽にJSONを返すWebAPIを構築

この記事はHamee Advent Calendar 2016の15日目の記事です。

今回は、ElixirのWebフレームワークPhoenixを使って、QiitaのAdvent Calendar 2016ランキングJSONで返すWebAPIを構築してみます。

調べているとDBを利用する場合の記事が多く、DBを利用しない場合に上手く動作しなかったので、DBを使わない場合のの実装方法をまとめておきます。

やること

前提条件

  • Elixirの環境構築済み

インストール

詳細は公式のインストールガイドをご参照ください。

Phoenixのインストール

以下のコマンドでPhoenixをインストールします。

$ mix archive.install https://github.com/phoenixframework/archives/raw/master/phoenix_new.ez

node.jsのインストール

Phoenixではjavascript, cssのcompileにbrunch.ioを利用しているため、node.js(>= 5.0.0)が必要になります。
macの場合はhomebrew経由でインストールできます。

$ brew install node

プロジェクトの作成

プロジェクトを作成していきましょう。
今回はスクレイピング結果をそのままJSON形式で返すAPIサーバーで、DBとViewは不要なので、オプションでその旨を指定しておきます。

$ mix phoenix.new --no-ecto --no-brunch --no-html web_api

ルーティングの設定

web/router.exに、ルーティングの設定を追記していきます。
get "/ranking", CalendarController, :indexが新しく追記した部分です。
/api/rankingにGETリクエストがあった際に、CalendarControllerindexアクションを実行します。

web/router.ex

defmodule WebApi.Router do
  use WebApi.Web, :router

  pipeline :api do
    plug :accepts, ["json"]
  end

  scope "/api", WebApi do
    pipe_through :api

    get "/ranking", CalendarController, :index
  end

end

ライブラリのインストール

コントローラーの実装に入る前に、スクレイピングで使用するライブラリの依存関係をmix.exsに追記していきます。

mix.exs

defmodule WebApi.Mixfile do
  use Mix.Project

  def project do
    [app: :web_api,
     version: "0.0.1",
     elixir: "~> 1.2",
     elixirc_paths: elixirc_paths(Mix.env),
     compilers: [:phoenix, :gettext] ++ Mix.compilers,
     build_embedded: Mix.env == :prod,
     start_permanent: Mix.env == :prod,
     deps: deps()]
  end

  # Configuration for the OTP application.
  #
  # Type `mix help compile.app` for more information.
  def application do
    [mod: {WebApi, []},
     applications: [:phoenix, :phoenix_pubsub, :cowboy, :logger, :gettext, :httpoison, :floki]]
  end

  # Specifies which paths to compile per environment.
  defp elixirc_paths(:test), do: ["lib", "web", "test/support"]
  defp elixirc_paths(_),     do: ["lib", "web"]

  # Specifies your project dependencies.
  #
  # Type `mix help deps` for examples and options.
  defp deps do
    [
     {:phoenix, "~> 1.2.1"},
     {:phoenix_pubsub, "~> 1.0"},
     {:gettext, "~> 0.11"},
     {:cowboy, "~> 1.0"},
     {:httpoison, "~> 0.10.0"},
     {:floki, "~> 0.11.0"}
    ]
  end
end

コントローラーの実装

最後にQiitaのAdvent Calendar 2016ランキングからデータを抽出して、JSON形式で返す処理を実装していきます。

JSON形式で返す処理はjson conn, calendarsの部分です。
このように記述することで、PhoenixJSON形式に変換してレスポンスを投げてくれます。

web/controllers/calendar_controller.ex

defmodule WebApi.CalendarController do
  use WebApi.Web, :controller

  def index(conn, _params) do
    calendars = ranking()
    json conn, calendars
  end

  # Advent Calendar 2016ランキングからランキング情報を抽出
  defp ranking() do
    url = "http://qiita.com/advent-calendar/2016/ranking/subscriptions"
    HTTPoison.start
    res = HTTPoison.get! url
    %HTTPoison.Response{status_code: 200, body: body} = res
    body
    |> Floki.find(".adventCalendarRankingListItem-top, .adventCalendarRankingListItem")
    |> Enum.map(&parse_item/1)
  end

  # DOM要素からランキング、タイトル、ページリンクを抽出
  defp parse_item(item) do
    rank  = Floki.find(item, ".adventCalendarRankingListItem_rank") |> Floki.text
    title_link = Floki.find(item, ".adventCalendarRankingListItem_name > a")
    title = Floki.text(title_link)
    url = "http://qiita.com#{Floki.attribute(title_link, "href")}"
    %{rank: rank, title: title, url: url}
  end

end

レスポンスを取得してみる

サーバーを起動して、レスポンスを取得してみます。

$ mix compile
$ mix phoenix.server
$ curl "http://localhost:4000/api/ranking" | jq
[
  {
    "url": "http://qiita.com/advent-calendar/2016/docker",
    "title": "Docker",
    "rank": "1"
  },
  {
    "url": "http://qiita.com/advent-calendar/2016/git",
    "title": "Git",
    "rank": "2"
  },
  {
    "url": "http://qiita.com/advent-calendar/2016/go",
    "title": "Go",
    "rank": "3"
  },
  {
    "url": "http://qiita.com/advent-calendar/2016/job",
    "title": "転職",
    "rank": "4"
  },
  {
    "url": "http://qiita.com/advent-calendar/2016/deeplearning",
    "title": "DeepLearning",
    "rank": "5"
  },
  {
    "url": "http://qiita.com/advent-calendar/2016/vim",
    "title": "Vim",
    "rank": "6"
  },
  {
    "url": "http://qiita.com/advent-calendar/2016/ruby",
    "title": "Ruby",
    "rank": "7"
  },
  {
    "url": "http://qiita.com/advent-calendar/2016/python",
    "title": "Python",
    "rank": "8"
  },
  {
    "url": "http://qiita.com/advent-calendar/2016/muscle",
    "title": "筋肉",
    "rank": "9"
  },
  {
    "url": "http://qiita.com/advent-calendar/2016/nodejs",
    "title": "Node.js",
    "rank": "10"
  },
  {
    "url": "http://qiita.com/advent-calendar/2016/go2",
    "title": "Go (その2)",
    "rank": "11"
  },
  {
    "url": "http://qiita.com/advent-calendar/2016/ouch-hack",
    "title": "おうちハック",
    "rank": "12"
  },
  {
    "url": "http://qiita.com/advent-calendar/2016/go3",
    "title": "Go (その3)",
    "rank": "13"
  },
  {
    "url": "http://qiita.com/advent-calendar/2016/vim2",
    "title": "Vim (その2)",
    "rank": "14"
  },
  {
    "url": "http://qiita.com/advent-calendar/2016/se",
    "title": "システムエンジニア",
    "rank": "15"
  },
  {
    "url": "http://qiita.com/advent-calendar/2016/python_python",
    "title": "Python",
    "rank": "16"
  },
  {
    "url": "http://qiita.com/advent-calendar/2016/docker2",
    "title": "Docker2",
    "rank": "17"
  },
  {
    "url": "http://qiita.com/advent-calendar/2016/tensorflow",
    "title": "TensorFlow",
    "rank": "18"
  },
  {
    "url": "http://qiita.com/advent-calendar/2016/swift",
    "title": "Swift",
    "rank": "19"
  },
  {
    "url": "http://qiita.com/advent-calendar/2016/job2",
    "title": "転職(その2)",
    "rank": "20"
  }
]

PlayFrameworkをlocalhostとして起動する

PlayFrameworkでローカルのMySQLに接続する時に、接続元のhostがlocalhostになっておらず、MySQL側で接続で拒否が発生する問題が起きました。

my.cnfにてMySQL側で外部ホストからの接続を許可しても良いのですが、今回はPlayFrameworkをloclahostとして実行する方法で解決しました。

$ activator run -Dhttp.adress=127.0.0.1

解決方法としては、オプションの-Dhttp.addressにてIPを指定してあげるだけで、大丈夫でした。

React + Reduxでカウントアップタイマーを作ってみる

はじめに

この記事はHamee Advent Calendar 2016の8日目の記事です。

最近、Reactが熱いですね!
今回は、前から気になっていたReactと相性が良いと言われるfluxフレームワークのReduxを使って、カウントアップタイマーを作ってみようと思います。

t-yng/countup-timer

デモ

f:id:t-yng:20161207231006p:plain

開発環境の構築

ビルドツールとしてwebpackを使っていきます。
またES6とReactのJSXは、webpackのbabel-loaderでコンパイルします。

$ npm i --save-dev webpack babel-core babel-loader babel-preset-es2015 babel-preset-react

webpackの設定をwebpack.config.jsに記述していきます。

module.exports = {
  entry: './js/main.js',
  output: {
    path: 'dist',
    filename: 'bundle.js'
  },
  module: {
    loaders: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
        query: {
          presets: ['es2015', 'react']
        }
      }
    ]
  }
}

続いて、react, reduxのインストールです。

$ npm i --save react react-dom redux react-redux

インストールしたライブラリの概要は以下の通りです。

react, react-dom: Reactのライブラリ
redux: Fluxフレームワーク
react-redux: ReactとReduxを連携させるためのライブラリ

packge.jsonのタスクコマンドにwebpackのbuildとwatchを追加します。

package.json

  (省略),
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack",
    "watch": "webpack --watch"
  },
  (省略)

ディレクトリ構成

.
├── README.md
├── css
│   └── style.css
├── dist
│   └── bundle.js
├── index.html
├── js
│   ├── action.js
│   ├── components
│   │   ├── App.js
│   │   ├── Timer.js
│   │   └── timer-button.js
│   ├── container.js
│   ├── main.js
│   ├── reducer.js
│   └── timer-model.js
├── node_modules
├── package.json
└── webpack.config.js

画面モックを作る

最初にHTMLとcssだけで画面のモックを作っていきます。

style.css

body {
  margin: 0;
  background-color: black;
}

.center-container {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}

.inline-block {
  display: inline-block;
}

.text-center {
  text-align: center;
}

.timer-number,
.timer-semicolon {
  color: white;
  font-size: 15vw;
}

.timer-button-container {
  width: 50vw;
  display: flex;
  justify-content: space-around;
}

.timer-button {
  font-size: 50px;
}

index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <link rel="stylesheet" href="css/style.css">
</head>
<body>
  <div class="center-container" style="margin-top: 100px;">
    <div>
      <span class="timer-number" role="hour">00</span>
      <span class="timer-semicolon">:</span>
      <span class="timer-number" role="minute">00</span>
      <span class="timer-semicolon">:</span>
      <span class="timer-number" role="second">00</span>
    </div>
    <div class="timer-button-container">
      <button class="timer-button" type="button" name="button" >START</button>
      <button class="timer-button" type="button" name="button" >STOP</button>
      <button class="timer-button" type="button" name="button" >RESET</button>
    </div>
  </div>
</body>
</html>

Reactだけを使って実装

画面が完成したので、実装に入っていきます。
いきなりReact+Reduxでは難しいので、最初はReactだけで実装を行いそれをベースにReduxを導入していきたいと思います。

Reactを使う際に重要になってくるのがコンポーネントをどのように分割するかだと思います。
今回は時間表示、タイマーボタンを部品コンポーネントとして切り出し、全体部分をアプリケーションコンポーネントとします。

f:id:t-yng:20161207231207p:plain

main.js

import {render} from 'react-dom'
import {App} from './components/App.js'

render(
  <App />,
  document.getElementById('timer-container')
)

components/app.js

import React from 'react'
import {Timer} from './timer.js'
import {TimerButton} from './timer-button.js'

export class App extends React.Component {

  render () {
    return (
      <div className='center-container' style={{marginTop: '100px'}} >
        <Timer hours={'00'} minutes={'00'} seconds={'00'} ref='timer' />
        <div className='timer-button-container'>
          <TimerButton text={'START'} handleClick={() => this.refs.timer.start()} />
          <TimerButton text={'STOP'} handleClick={() => this.refs.timer.stop()} />
          <TimerButton text={'RESET'} handleClick={() => this.refs.timer.reset()} />
        </div>
      </div>
    )
  }
}

components/timer.js

import React from 'react'

export class Timer extends React.Component {
  constructor () {
    super()
    this.state = {hours: '00', minutes: '00', seconds: '00', time: 0}
  }

  start () {
    this.intervalTimer = setInterval(() => this.update(), 1000)
  }

  stop () {
    clearInterval(this.intervalTimer)
  }

  reset () {
    this.setState({
      hours: '00',
      minutes: '00',
      seconds: '00',
      time: 0
    })
  }

  update () {
    const time = this.state.time + 1
    const hours = this.toHours(time)
    const minutes = this.toMinutes(time)
    const seconds = this.toSeconds(time)

    this.setState({
      hours: this.toText(hours),
      minutes: this.toText(minutes),
      seconds: this.toText(seconds),
      time: time
    })
  }

  toHours (time) {
    return parseInt(time / 60 / 60)
  }

  toMinutes (time) {
    return parseInt(time / 60 % 60)
  }

  toSeconds (time) {
    return time % 60
  }

  toText (time) {
    return ('00' + time).slice(-2)
  }

  render () {
    return (
      <div>
        <span className='timer-number' role='hour'>{this.state.hours}</span>
        <span className='timer-semicolon'>:</span>
        <span className='timer-number' role='minute'>{this.state.minutes}</span>
        <span className='timer-semicolon'>:</span>
        <span className='timer-number' role='second'>{this.state.seconds}</span>
      </div>
    )
  }
}

components/timer-button.js

import React from 'react'

export class TimerButton extends React.Component {
  render () {
    return (
      <button className='timer-button' type='button' name='button' onClick={() => this.props.handleClick()}>{this.props.text}</button>
    )
  }
}

Reactだけで実装した場合、Timerコンポーネントに状態管理の処理が記述されているため、少し複雑になってしまいました。
理想としては、コンポーネントはViewの記述のみにしたいです。

Redux + React で実装

それでは、今回の目的であるReduxの導入をしていきたいと思います。

Reduxの概念に関しては、次の記事が大変参考になりました。
Redux入門【ダイジェスト版】10分で理解するReduxの基礎

Reduxで登場する要素について簡単に説明します。

  • state
    • アプリケーションの状態
    • Reduxでは一つのアプリケーションに対して一つのstateオブジェクトを持つ
  • sotre
    • アプリケーション全体の状態を管理
  • reducer
    • 状態遷移を処理する役割を持つ
    • 状態とアクションを受け取り、新たな状態を返す
  • dispatcher
    • reducerに対して、ユーザーアクションを通知
  • action
    • ボタンクリック等のアクションを表す
    • typeプロパティとアクションに応じたデータを持つオブジェクト
  • actionCreator
    • Actionを生成するための関数
  • container

reducerの実装

reducerではactionに応じた、タイマーの状態遷移の処理を記述しています。

reducer.js

import * as timerModel from './timer-model.js'

/**
 * Reduxのreducer タイマーの時間の状態遷移を処理する
 * @param state Reduxのstoreで管理されている状態
 * @param action アクションオブジェクト
 * @return actionに応じて変化させた新しい状態
 */
export function timer (state = timerModel.initialState(), action) {
  console.log(state)
  switch (action.type) {
    case 'START_TIMER':
      return timerModel.start(state, action.intervalID)
    case 'STOP_TIMER':
      return timerModel.stop(state)
    case 'UPDATE_TIMER':
      return timerModel.update(state)
    case 'RESET_TIMER':
      return timerModel.reset(state)
    default:
      return state
  }
}

ここで登場している、timer-model.jsはTimerコンポーネントから、タイマーの状態遷移ロジックを抽出した関数群になります。

Reduxでは状態遷移の関数は参照透過である必要があります。
そのため、Object.assign()では新しい空オブジェクトに対して、既存の値をコピーすることで、新たな状態を生成しています。

timer-model.js

/**
 * タイマーの状態を開始状態に変更する
 * @param state タイマーの状態
 * @param {number} intervalID setInterval()で得られたID
 * @return タイマーの開始状態
 */
export function start (state, intervalID) {
  return Object.assign({}, state, {
    started: true,
    intervalID: intervalID
  })
}

/**
 * タイマーの状態を停止状態に変更する
 * @param state タイマーの状態
 * @return タイマーの停止状態
 */
export function stop (state) {
  clearInterval(state.intervalID)

  return Object.assign({}, state, {
    started: false,
    intervalID: -1
  })
}

/**
 * タイマーの時間を1秒進める
 * @param state タイマーの時間の状態
 * @return 時間を1秒進めた新しい状態
 */
export function update (state) {
  const time = state.time + 1
  const hours = toHours(time)
  const minutes = toMinutes(time)
  const seconds = toSeconds(time)

  return Object.assign({}, state, {
    hours: toText(hours),
    minutes: toText(minutes),
    seconds: toText(seconds),
    time: time
  })
}

/**
 * タイマーの時間をリセットする
 * @return タイマーの初期状態
 */
export function reset (state) {
  return Object.assign({}, initialState(), {
    started: state.started,
    intervalID: state.intervalID
  })
}

/**
 * タイマーの初期状態
 */
export function initialState () {
  return {
    hours: '00',
    minutes: '00',
    seconds: '00',
    time: 0,
    started: false,
    intervalID: -1
  }
}

function toHours (time) {
  return parseInt(time / 60 / 60)
}

function toMinutes (time) {
  return parseInt(time / 60 % 60)
}

function toSeconds (time) {
  return time % 60
}

function toText (time) {
  return ('00' + time).slice(-2)
}

actionCreatorの実装

actionオブジェクトを返す、関数を定義していきます。
startTimerAction()では停止処理を実行するために、setInterval()で取得したIDを受け取り、storeに渡しています。

action.js

export function startTimerAction (intervalID) {
  return {type: 'START_TIMER', intervalID: intervalID}
}

export function stopTimerAction () {
  return {type: 'STOP_TIMER'}
}

export function updateTimerAction () {
  return {type: 'UPDATE_TIMER'}
}

export function resetTimerAction () {
  return {type: 'RESET_TIMER'}
}

containerの実装

ReactとReduxの連携部分の実装をして、Redux側の実装は完了です。

mapStateToProprs(state)関数ではReduxのStoreで管理しているstateをReactのpropsに紐付けています。 今回は簡単なアプリケーションなので、そのまま返していますが、複雑になってきた場合はここで加工して、必要な状態をReact側に渡すことができます。

mapDispatchToProps(dispatch)関数にてReduxのdispatch処理をpropsに紐付けることで、Reactのコンポーネントがクリック等のイベントを受け取った際にdispatch処理を実行させることが出来るようになります。

connect()関数の詳細な仕様に関しては、githubリポジトリAPIドキュメントがあるのでそちらを参考にしてください。

react-redux/api.md at master · reactjs/react-redux · GitHub

container.js

import {connect} from 'react-redux'

import {App} from './components/app.js'
import * as action from './action.js'

/**
 * reduxで管理しているstateをreact側に渡す
 * @param state reduxのstoreで管理されている状態
 * @return react側で使う状態 this.prorpsで参照
 */
function mapStateToProps (state) {
  return state
}

/**
 * reactで受け取るユーザーアクションとreduxのアクションを連携させる
 * @param dispatch reduxのreducerにアクションを渡す関数
 */
function mapDispatchToProps (dispatch, props) {
  return {
    startTimer: () => {
      const intervalID = setInterval(() => dispatch(action.updateTimerAction()), 1000)
      dispatch(action.startTimerAction(intervalID))
    },
    stopTimer: () => dispatch(action.stopTimerAction()),
    resetTimer: () => dispatch(action.resetTimerAction())
  }
}

function mergeProps (stateProps, dispatchProps, ownProps) {
  return Object.assign({}, stateProps, dispatchProps, ownProps, {
    // タイマーが止まっていたら、カウントアップを開始する
    startTimer: () => {
      if (!stateProps.started) dispatchProps.startTimer()
    }
  })
}

// 上記で定義した関数を使って、ReduxとReactのコンポーネントを繋げる
export default connect(mapStateToProps, mapDispatchToProps, mergeProps)(App)

timer-model.jsでdispatch()関数を呼ぶ方法が思いつかなかったので、ここでsetInterval()関数にてタイマーの開始処理をしています。
理想としては、ここではactionをdispatchするだけにしたかったです。

React側のコンポーネントを修正

Reduxの導入が完了したので、それに合わせてReactのコンポーネントを修正します。
Redux側が状態管理をしてくれるため、Reactのコンポーネントは純粋に状態を受け取るだけで良くなりました。

components/app.js

import React from 'react'
import {Timer} from './timer.js'
import {TimerButton} from './timer-button.js'

export class App extends React.Component {

  render () {
    return (
      <div className='center-container' style={{marginTop: '100px'}}>
        <Timer hours={this.props.hours} minutes={this.props.minutes} seconds={this.props.seconds} ref='timer' />
        <div className='timer-button-container'>
          <TimerButton text={'START'} handleClick={this.props.startTimer} />
          <TimerButton text={'STOP'} handleClick={this.props.stopTimer} />
          <TimerButton text={'RESET'} handleClick={this.props.resetTimer} />
        </div>
      </div>
    )
  }
}

components/timer.js

import React from 'react'

export class Timer extends React.Component {
  render () {
    return (
      <div>
        <span className='timer-number' role='hour'>{this.props.hours}</span>
        <span className='timer-semicolon'>:</span>
        <span className='timer-number' role='minute'>{this.props.minutes}</span>
        <span className='timer-semicolon'>:</span>
        <span className='timer-number' role='second'>{this.props.seconds}</span>
      </div>
    )
  }
}

components/timer-button.js

import React from 'react'

export class TimerButton extends React.Component {
  render () {
    return (
      <button className='timer-button' type='button' name='button' onClick={this.props.handleClick}>{this.props.text}</button>
    )
  }
}

main処理を実装

最後にmainの処理を実装します。
const store = createStore(timer)にて、Reduxのstoreを生成します。

最後の<Provider store={store}>...</Proivder>にて、Reduxのstoreで管理している状態を子コンポーネントである<App />に対して状態を渡しています。 ここの記述はreact-reduxにて決められているため、必ずこのように書く必要があります。

main.js

import React from 'react'
import {Provider} from 'react-redux'
import {render} from 'react-dom'
import {createStore} from 'redux'

import App from './container.js'
import {timer} from './reducer.js'

const store = createStore(timer)

render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('timer-container')
)

さいごに

アプリケーションを作ってみての感想として、React, Redux 単体の場合は分かりやすかったのですが、ReactとReduxを連携させようとすると、難しくなるなと感じました。
ただ、状態管理の処理をReduxが担ってくれるので、Reactのコンポーネントからビジネスロジックが無くなり、純粋なViewに徹することが出来るようになるのは、シンプルになって扱いやすいなと思いました。

Reactを使っていて、状態管理に悩んでいる人はRedux(Fluxフレームワーク)を導入する価値は十分にあると思います。

ターミナルで実行した直前のコマンドをコマンドだけでクリップボードにコピーする方法

結論から言うと、以下のシェルコマンドで目的が達成できました。

fc -ln  | tail -n1 | pbcopy

上記の流れとしては、最初に【fc】コマンドにて実行コマンドの履歴を表示します。
その次に、【tail】コマンドで最後の1行のみを表示することで、直前の実行コマンドを標準出力に表示します。
最後に出力された内容を【pbcopy】コマンドでクリップボードにコピーしています。

ここで登場している【fc】コマンドはコマンドの再実行やリスト表示を行うことが可能なコマンドです。
詳細はfc - コマンドを再実行・コマンド履歴をリスト表示 - Linuxコマンドを確認してください。

Scalaの軽量フレームワークScalatra

ScalatraSinatraに影響を受けたScalaの軽量Webフレームワークです。

簡単に触ってみたので、導入手順をメモしておきます。

giter8のインストール

ScalatraはScalaのスキャフォールディングツールgiter8を使って、テンプレートからプロジェクトを生成します。 インストールはhomebrewを使って行います。

$ brew update && brew install giter8

プロジェクトの雛形生成

先ほどインストールしたgiter8を使って、Scalatraのプロジェクトを作成します。

$ g8 scalatra/scalatra-sbt 

生成されたプロジェクトのソースコードを確認してみます。 アプリケーションのメインとなるソースコードsrc/main/scala/com/example/app/MyScalatraServlet.scalaになります。 MyScalatraServlet.scalaの部分は、g8で雛形を作成する際に入力したファイル名になっています。

package com.example.app

import org.scalatra._

class MyScalatraServlet extends SimplescalatraStack {

  get("/") {
    <html>
      <body>
        <h1>Hello, world!</h1>
        Say <a href="hello-scalate">hello to Scalate</a>.
      </body>
    </html>
  }

}

このソースコードは、サーバーのルート(/)にアクセスした際に記述されたHTMLをレスポンスとして返す処理が書いてあります。

HTMLページを表示してみる

以下のコマンドでサーバーを起動します。

$ sbt
> ~jetty:start

上記コマンドの~を付けることでソースコードの変更時にサーバーを自動で再起動されるようになります。 毎回起動し直すのは、面倒なので~を付けることをお勧めします。

http://localhost:8080にアクセスして、HTMLページが表示されれば導入完了です。

さいごに

Scalatraは非常にシンプルに使うことができるので、簡単なREST API などを実装するのに向いているのかなと思います。

Scala.jsを触ってみた

Scala.jsというScalaで記述な可能なAltJSがあったので、興味本意で導入部分だけですが、触ってみました。

導入

早速、Scala.jsの導入を行っていきます。

プロジェクトの作成

activotr newコマンドでテンプレートからScalaのプロジェクトを作成します。

$ activator new

Fetching the latest list of templates...

Browse the list of templates: http://typesafe.com/activator/templates
Choose from these featured templates or enter a template name:
  1) minimal-akka-java-seed
  2) minimal-akka-scala-seed
  3) minimal-java
  4) minimal-scala
  5) play-java
  6) play-scala
(hit tab to see a list of all templates)
> 4

sbtの設定

project/plugins.sbtに以下を追記

addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.12")

build.sbtに以下を追記

enablePlugins(ScalaJSPlugin)

name := "Scala.js Tutorial"

scalaVersion := "2.11.7" // or any other Scala version >= 2.10.2

Hello Worldを表示するアプリケーションの作成

src/main/scala/com/example/Hello.scalaを作成

package com.example

import scala.scalajs.js.JSApp

object Hello extends JSApp {
  def main(): Unit = {
    println("Hello world")
  }
}

sbtを起動して上記のファイルを実行してみます。

$ sbt
> run
[info] Running com.example.Hello
Hello world
[success] Total time: 6 s, completed 2016/10/03 23:45:15

JavaScriptファイルに変換

fastOptJSコマンドでScalaのファイルをJavaScriptのファイルに変換します。

> fastOptJS
[success] Total time: 2 s, completed 2016/10/03 23:46:46

JavaScriptファイルを読み込む

実際にHTMLを作成して、先ほど生成したJavaScriptファイルを読み込んでみます。 index.htmlを以下の内容で作成します。

<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <title>Document</title>
   <script src="./target/scala-2.11/scalajs-fastopt.js"></script>
</head>
<body>
    <script type="text/javascript">
       com.example.TutorialApp().main()
   </script>
</body>
</html>

index.htmlでブラウザで読み込んでみます。

f:id:t-yng:20161003235930p:plain

無事に読み込まれ、コンソール上に"Hello World"と表示されています。

さいごに

正直、型安全をメリットとするならTypeScriptの方で良いなとは思いました。