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

How to build Rails5 API + Redux ToDo Application その3

プログラミング Ruby on Rails JavaScript React Redux

前回はReactでToDOクライアントを実装しました。

kogoto.hatenablog.com

で、これをRedux化してみます。

いきなりAPI叩くとややっこしいので、まずはクライアント単独で動作するものを作成してみます。

ソースコードはこちら。

github.com

create-react-app使ってるので、clientの下でnpm installしてから

$ npm start

ってやるとブラウザが起動して実行開始されます。

モジュールのインストール

$ npm install --save redux react-redux redux-actions

ディレクトリの再構成

%RAILS_ROOT%にclientフォルダを作成しそこにプロジェクトを配置します。
また、Reduxの作法にしたがったディレクトリ構成にします。

src
├── App.js
├── actions
│   └── index.js
├── components
│   └── Todo.js
├── constants
│   └── ActionTypes.js
├── containers
│   ├── TodoForm.js
│   └── TodoList.js
├── index.js
├── reducers
│   ├── form.js
│   ├── index.js
│   └── todos.js
└── store
    └── index.js

Actionの洗い出し

今回のToDOで発生するActionをリストアップします。

  • 優先順位変更
  • タイトル入力
  • 登録処理
  • 削除処理
  • 完了⇔未完了の切り替え

これを元にActionを生成します。

// constants/ActionTypes.js

export const CHANGE_ORDER = 'CHANGE_ORDER'
export const CHANGE_TITLE = 'CHANGE_TITLE'
export const ADD_TODO = 'ADD_TODO'
export const DELETE_TODO = 'DELETE_TODO'
export const TOGGLE_TODO = 'TOGGLE_TODO'
// actions/index.js

import { createAction } from 'redux-actions'
import * as types from '../constants/ActionTypes'

export const changeOrder = createAction(types.CHANGE_ORDER)
export const changeTitle = createAction(types.CHANGE_TITLE)
export const addTodo = createAction(types.ADD_TODO)
export const deleteTodo = createAction(types.DELETE_TODO)
export const toggleTodo = createAction(types.TOGGLE_TODO)

Stateの洗い出し

各コンテナ・コンポーネントがもつStateをリストアップします。

TodoForm

  • 入力中のタイトル
  • 入力中の優先順位
  • 登録ボタンの有効/無効状態

TodoList

  • 登録されたToDO一覧

Todo

  • タイトル
  • 優先順位
  • 完了/未完了状態

で、これを元にReducerを作成します。

// reducers/form.js

const initialState = {
  title: '',
  order: 1,
  completed: false,
  disabled: true
}

const form = handleActions({
  /* ... */
}, initialState)

export default form
// reducers/todos.js

const initialState = {
  title: '',
  order: 1,
  completed: false
}

const todo = handleActions({
  /* ... */
}, initialState)

const todos = handleActions({
  /* ... */
}, [])

export default todos

これらをcombineReducersでくっつけてstoreとします。

と書いてるとだんだんややこしくなってくるので、図にしてみました。

f:id:ktdk:20160728182352p:plain

ユーザーからの操作でReducerに対してActionが発行され、Stateが更新されます。
更新されたStateで再描画される、ってなわけです。

ひとまずRedux方式になったので、次回はこれにAPIを叩かせたいと思います。

How to build Rails5 API + Redux ToDo Application その2

プログラミング Ruby on Rails JavaScript React

前回はRails5でToDOリストのREST APIを実装しました。

kogoto.hatenablog.com

今回はクライアント側の実装をしてみます。

イメージ

f:id:ktdk:20160126104936g:plain

言語・ツール

  • ES6 (ES2015)
  • React
  • gulp
  • webpack
.
├── components
│   ├── todo-box.js
│   ├── todo-form.js
│   ├── todo-list.js
│   └── todo.js
└── main.js

コンポーネントの構成

f:id:ktdk:20160126094819p:plain

各パーツをコンポーネント化していきます。

実装してみる

まずTodoから。StateとしてCompleted(完了/未完了)状態を持っています。
完了時にバックカラーが変更され削除時には列ごと消えます。

// components/todo.js

import React, { Component } from 'react'

export default class Todo extends Component {
  constructor(props) {
    super(props)
    this.state = { completed: props.completed }
  }

  rawMarkup() {
    var rawMarkup = marked(this.props.children.toString(), { sanitize : true })
    return { __html: rawMarkup }
  }

  render() {
    return (
      <tr className={this.state.completed ? "success": ""}>
        <td>
          <input
            type="checkbox"
            onChange={(e) => this.props.onTodoCompleted(this, e.target.checked)}
            checked={this.state.completed ? "checked": ""}
          />
        </td>
        <td>{this.props.order}</td>
        <td><span dangerouslySetInnerHTML={this.rawMarkup()} /></td>
        <td>
          <input
            className="btn btn-danger"
            type="button"
            onClick={() => this.props.onTodoDelete(this.props.id)}
            value="削除"
          />
        </td>
      </tr>
    )
  }
}

Todoを管理するTodoListを作成します。

// components/todo-list.js

import React, { Component } from 'react'
import Todo from './todo'

export default class TodoList extends Component {
  constructor(props) {
    super(props)
  }

  render() {
    let todoNodes = this.props.data.map(todo => {
      return (
        <Todo
          url={this.props.url}
          completed={todo.completed}
          order={todo.order}
          id={todo.id}
          key={todo.id}
          onTodoDelete={this.props.onTodoDelete}
          onTodoCompleted={this.props.onTodoCompleted}
        >
          {todo.title}
        </Todo>
      )
    })

    return (
      <div className="row">
        <table className="todoList table table-striped">
          <thead>
            <tr>
              <th width="10%">完了</th>
              <th width="20%">優先順位</th>
              <th>やること</th>
              <th width="20%"></th>
            </tr>
          </thead>
          <tbody>
            {todoNodes}
          </tbody>
        </table>
      </div>
    )
  }
}

次にTodoFormです。優先順位とTodoを入力して登録する普通のフォームです。

// components/todo-form.js

import React, { Component } from 'react'

export default class TodoForm extends Component {
  constructor(props) {
    super(props)
    this.state = { order: 1, title: '' }
  }

  handleOrderChange(e) {
    this.setState({ order: e.target.value })
  }

  handleTitleChange(e) {
    this.setState({ title: e.target.value })
  }

  handleSubmit(e) {
    e.preventDefault()
    let order = this.state.order
    let title = this.state.title.trim()

    if (!order || !title) {
      return
    }

    this.props.onTodoSubmit({ order: order, title: title, completed: false })
    this.setState({ order: 1, title: '' })
  }

  render() {
    return (
      <div className="row">
        <form className="todoForm form-horizontal" onSubmit={this.handleSubmit.bind(this)}>
          <div className="form-group col-md-2">
            <input
              id="todo-order"
              className="form-control"
              type="number"
              value={this.state.order}
              onChange={this.handleOrderChange.bind(this)}
            />
          </div>
          <div className="form-group col-md-8">
            <input
              id="todo-title"
              className="form-control"
              type="text"
              value={this.state.title}
              onChange={this.handleTitleChange.bind(this)}
            />
          </div>
          <div className="col-md-2">
            <input className="btn btn-primary" type="submit" value="登録" />
          </div>
        </form>
      </div>
    )
  }
}

んでこれらを管理するTodoBoxを作成します。
GET, POST, PATCH, DELETE処理はすべてここに実装しています。
これらを下位のコンポーネントに引き渡して状態を更新していく流れです。

import React, { Component } from 'react'
import TodoList from './todo-list'
import TodoForm from  './todo-form'

export default class TodoBox extends Component {
  constructor(props) {
    super(props)
    this.state = { data: [] }
  }

  loadTodosFromServer() {
    $.ajax({
      url: this.props.url,
      dataType: 'json',
      cache: false,
      success: (data) => {
        this.setState({ data: data })
      },
      error: (xhr, status, err) => {
        console.error(this.props.url, status, err.toString())
      }
    })
  }

  handleTodoSubmit(todo) {
    todo.id = Date.now()

    let todos = this.state.data
    let newTodos = todos.concat([todo])
    this.setState({ data: newTodos })

    $.ajax({
      url: this.props.url,
      dataType: 'json',
      type: 'POST',
      data: { todo: todo },
      success: (data) => {
        this.loadTodosFromServer()
      },
      error: (xhr, status, err) => {
        this.setState({ data: todos })
        console.error(this.props.url, status, err.toString())
      }
    })
  }

  handleTodoCompleted(todo, completed) {
    $.ajax({
      url: this.props.url + '/' + todo.props.id,
      dataType: 'json',
      method: 'PATCH',
      data: { todo: { completed: completed } },
      success: (data) => {
        todo.setState({ completed: data.completed })
      },
      error: (xhr, status, err) => {
        console.error(this.props.url, status, err.toString())
      }
    })
  }

  handleTodoDelete(id) {
    $.ajax({
      url: this.props.url + '/' + id,
      dataType: 'json',
      method: 'DELETE',
      success: (data) => {
        this.loadTodosFromServer()
      },
      error: (xhr, status, err) => {
        console.error(this.props.url, status, err.toString())
      }
    })
  }

  componentDidMount() {
    this.loadTodosFromServer()
    // setInterval(this.loadTodosFromServer.bind(this), this.props.pollInterval)
  }

  render() {
    return (
      <div className="todoBox">
        <h1>Todoリスト</h1>
        <TodoForm onTodoSubmit={this.handleTodoSubmit.bind(this)} />
        <TodoList
          data={this.state.data}
          url="/todos"
          onTodoCompleted={this.handleTodoCompleted.bind(this)}
          onTodoDelete={this.handleTodoDelete.bind(this)}
        />
      </div>
    )
  }
}

エントリポイントでこれらのコンポーネントを読み込みます。

import React      from 'react'
import { render } from 'react-dom'
import TodoBox    from './components/todo-box'

let content = document.getElementById('todos')

render(
  <TodoBox url="/todos" pollInterval={2000} />,
  content
)

webpackしたものをHTMLに組み込んで完了です。

<!-- index.html -->
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Rail5 + React Todo</title>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
  </head>
  <body>
    <div id="todos" class="container"></div>
    <script type="text/javascript" src="dist/bundle.js"></script>
  </body>
</html>

ReactはあくまでUIに特化したライブラリなので、正直Ajax周りの処理をどこに書いてどうしたらいいかってのが全然わかりませんでした。

これをRedux化するとどうなるんでしょうかね。続きは次回。

入門 React ―コンポーネントベースのWebフロントエンド開発

入門 React ―コンポーネントベースのWebフロントエンド開発

Hello, Mix!

Elixir Mix

Mixとは

Elixirのプロジェクト管理ツールです。

プロジェクトの作成

$ mix new hello_world

* creating README.md
* creating .gitignore
* creating mix.exs
* creating config
* creating config/config.exs
* creating lib
* creating lib/hello_world.ex
* creating test
* creating test/test_helper.exs
* creating test/hello_world_test.exs

Your Mix project was created successfully.
You can use "mix" to compile it, test it, and more:

    cd hello_world
    mix test

Run "mix help" for more commands.
  • README.md
    • プロジェクトの説明
  • .gitignore
    • gitの管理下に置かないファイルの定義
  • mix.exs
    • プロジェクトの設定
  • config/*
    • アプリケーションの設定
  • lib/*
    • ライブラリ
  • test/*

メインモジュールの作成

プロジェクトが作成されたので、プロジェクトのディレクリに移動してビルドしてみます。

$ cd hello_world
$ mix escript.build

Compiled lib/hello_world.ex
Generated hello_world app
Consolidated List.Chars
Consolidated Collectable
Consolidated String.Chars
Consolidated Enumerable
Consolidated IEx.Info
Consolidated Inspect
** (Mix) Could not generate escript, please set :main_module in your project configuration (under :escript option) to a module that implements main/1

「プロジェクト設定の:escriptオプションにmain関数を実装している:main_moduleを指定しろ」 とのことなので、そのようにします。

# ./mix.exs

defmodule HelloWorld.Mixfile do
  use Mix.Project

  def project do
    [app: :hello_world,
     version: "0.0.1",
     elixir: "~> 1.2-rc",
     build_embedded: Mix.env == :prod,
     start_permanent: Mix.env == :prod,
     deps: deps]
  end

  # ... 以下略
end
# ./lib/hello_world.exs

defmodule HelloWorld do

  def main(args) do
    IO.puts "Hello, World!!"
  end

end

ビルド&実行

メインモジュールを追記後、再度ビルドします。

$ cd hello_world
$ mix escript.build

Compiled lib/hello_world.ex
Generated hello_world app
Consolidated List.Chars
Consolidated Collectable
Consolidated String.Chars
Consolidated Enumerable
Consolidated IEx.Info
Consolidated Inspect
Generated escript hello_world with MIX_ENV=dev

ビルドに成功すると直下にバイナリが作成されるので実行してみます。

$ ./hello_world

Hello, world!

Programming Elixir: Functional, Concurrent, Pragmatic, Fun

Programming Elixir: Functional, Concurrent, Pragmatic, Fun

Ansible入門

Ansible

Ansibleとは

Chef, Puppetと同様の構成管理ツール

インストール

$ git clone git://github.com/ansible/ansible.git --recursive
$ cd ./ansible
$ source ./hacking/env-setup
$ ansible
Usage: ansible <host-pattern> [options]

Options:
  -a MODULE_ARGS, --args=MODULE_ARGS
                        module arguments
  --ask-become-pass     ask for privilege escalation password
...

リモートホストの管理

※事前にVagrant等でSSHログインできるホストを用意しておく

Inventoryの作成

$ vim ~/ansible_hosts

[webservers]
192.168.33.100 ansible_user=vagrant ansible_ssh_pass=vagrant

疎通確認

$ ansible all -m ping -i ~/ansible_hosts

192.168.33.100 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

しゃべらせてみる

$ ansible all -a "/bin/echo hello" -i ~/ansible_hosts

192.168.33.100 | SUCCESS | rc=0 >>
hello

リモートホストの構成

ex.) Apache

Playbook

# httpd.yml

---
- hosts: webservers
  vars:
    http_port: 80
    max_clients: 200
  become: true
  tasks:
  - name: ensure apache is at the latest version
    yum:
      name: httpd
      state: latest
  - name: ensure apache is running (and enable it at boot)
    service:
      name: httpd
      state: started
      enabled: yes
  handlers:
    - name: restart apache
      service:
        name: httpd
        state: restarted

実行

$ ansible-playbook -i ~/ansible_hosts httpd.yml

PLAY ***************************************************************************

TASK [setup] *******************************************************************
ok: [192.168.33.100]

TASK [ensure apache is at the latest version] **********************************
changed: [192.168.33.100]

TASK [ensure apache is running (and enable it at boot)] ************************
changed: [192.168.33.100]

PLAY RECAP *********************************************************************
192.168.33.100             : ok=3    changed=2    unreachable=0    failed=0

入門Ansible

入門Ansible

Visual Studio CodeをES6に対応させる

VS Code

プロジェクトフォルダ直下に設定ファイルを作成する

// jsconfig.json
{
    "compilerOptions": {
        "target": "ES6"
    }
}

平成28年度診療報酬改定 基本指針

診療報酬改定 医療

基本方針(中医協協議会資料より)

1. 改定にあたっての基本認識

超高齢社会における医療政策の基本方向

  • 団塊の世代が75歳以上となる平成37年(2025年)に向けた安全・安心、質の高い効率的な医療の提供
  • 疾病構造の変化に伴い「治す医療」から「治し、支える医療」、予防医学
  • 人口減少に伴う医療保険制度の持続可能性の確保

地域包括ケアシステムと効果的・効率的で質の高い医療提供体制の構築

  • 医療機能の分化・強化、連携や医療・介護の一体的な基盤整備
  • そのための質の高い人材の確保、医療従事者の負担軽減

経済成長や財政健全化との調和

  • 経済・財政との調和
  • 無駄の排除や医療資源の効率的な配分、医療分野におけるイノベーションの評価等を通じた経済成長への貢献

2. 改定の基本的視点と具体的方向性

1. 【重点課題】地域包括ケアシステムの推進と医療機能の分化・強化、連携に関する視点

(基本的視点)

  • 必要に応じた質の高い医療・介護サービスへのニーズ
  • そのためへの機能分化・基盤整備を含めた地域包括ケアシステムの構築

(具体的方向性の例)

  • 入院医療の評価
  • チーム医療の推進、勤務環境の改善、業務効率化の取組等を通じた医療従事者の負担軽減・人材確保
  • 地域包括ケアシステム推進のための取組の強化
    • 患者の状態に応じた診療が行われるよう、かかりつけ医の機能を評価
    • 同様に、適切な服薬管理が行われるよう、かかりつけ薬剤師・薬局の機能を評価
    • 多職種連携の強化(医師、歯科医師、薬剤師、看護師等)
    • 患者の早期退院、住み慣れた地域での療養・生活が継続できるための取組を推進
  • 質の高い在宅医療・訪問看護の確保
    • 患者の状態を考慮した効率的・効果的で質の高い在宅医療・訪問看護の提供体制
  • 医療保険制度改革法も踏まえた外来医療の機能分化
    • 医療制度改革法
    • 大病院・中小病院・診療所の機能分化
    • 外来医療の機能分化・連携の推進

2. 患者にとって安心・安全で納得できる効果的・効率的で質が高い医療を実現する視点

(基本的視点)

  • 患者自身による主体的な医療の選択
  • 「生活の質」を高める「治し、支える医療」の実現

(具体的方向性の例)

  • かかりつけ医の評価、かかりつけ歯科医の評価、かかりつけ薬剤師・薬局の評価
  • 情報通信技術(ICT)を活用した医療連携や医療に関するデータの収集・利活用の推進
  • 質の高いリハビリテーションの評価等、患者の早期の機能回復の推進

3. 重点的な対応が求められる医療分野を充実する視点

(基本的視点)

  • がん、心疾患、肺炎、脳卒中に加え増加が見込まれる認知症、救急医療への重点的な対応

(具体的方向性の例)

  • 緩和ケアを含む質の高いがん医療の評価
  • 認知症患者への適切な医療の評価
  • 地域移行・地域生活支援の充実を含めた質の高い精神医療の評価
  • 難病法の施行を踏まえた難病患者への適切な医療の評価
  • 小児医療、周産期医療の充実、高齢者の増加を踏まえた救急医療の充実
  • 口腔疾患の重症化予防・口腔機能低下への対応、生活の質に配慮した歯科医療の推進
  • かかりつけ薬剤師・薬局による薬学管理や在宅医療等への貢献度による評価・適正化
  • 医薬品、医療機器、検査等におけるイノベーションや医療技術の適切な評価

4. 効率化・適正化を通じて制度の持続可能性を高める視点

(基本的視点)

  • 医療費増大に対していかに国民皆保険の維持するか
  • 医療サービスの維持・向上、医療費の効率化・適正化

(具体的方向性の例)

  • 後発医薬品の使用促進・価格適正化、長期収載品の評価の仕組みの検討
    • 価格算定ルールの見直し
    • 長期収載品の価格引下げルールの見直し
  • 退院支援等の取込による在宅復帰の推進
  • 残薬や重複投薬、不適切な多剤投薬・長期投薬を減らすための取組など医薬品の適正使用の推進
  • 患者本位の医薬分業を実現するための調剤報酬の見直し
  • 重症化予防の取組の推進
    • 疾患の進展の阻止や合併症の予防、早期治療の取組を推進
  • 医薬品、医療機器、検査等の適正な評価

改定率

ネット改定率(全体) ▲0.84%

診療報酬本体 +0.49%

  • 医科 +0.56%
  • 歯科 +0.61%
  • 調剤 +0.17%

薬価等

  • 薬価 ▲1.22%
  • 材料価格 ▲0.11%

How to build Rails5 API + Redux ToDo Application その1

Ruby on Rails プログラミング JavaScript React Redux

Rails5から実装されるAPI専用アプリケーションの作成手順です。

プロジェクトの生成からAPI経由でのアクセスまで試してみます。

Rails5の取得

githubからRails5を取得するため適当なディレクトリに以下のGemfileを作成します。

Gemfile

source 'https://rubygems.org'

gem 'rails', github: 'rails/rails'

Gemfileを作成後、以下のコマンドを実行し直下にrailsを取得します。

$ bundle install --path vendor/bundle

Rails5 APIアプリケーションの作成

同ディレクトリで以下のコマンドを実行します。

rails newコマンドにapiオプションを指定するとAPIのみのアプリケーションが生成されます。

$ bundle exec rails new <application-name> --api

雛形の作成

生成されたアプリケーションのディレクトリに移動し、APIの雛形となるscaffoldを生成します。

$ bin/rails g scaffold todo title completed:boolean order:integer

Running via Spring preloader in process 3582
      invoke  active_record
      create    db/migrate/20160120044614_create_todos.rb
      create    app/models/todo.rb
      invoke    test_unit
      create      test/models/todo_test.rb
      create      test/fixtures/todos.yml
      invoke  resource_route
       route    resources :todos
      invoke  scaffold_controller
      create    app/controllers/todos_controller.rb
      invoke    test_unit
      create      test/controllers/todos_controller_test.rb

雛形を作成したらDBのmigrationをします。

$ bin/rails db:migrate

== 20160120044614 CreateTodos: migrating ======================================
-- create_table(:todos)
   -> 0.0016s
== 20160120044614 CreateTodos: migrated (0.0016s) =============================

サーバーの起動

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

$ bin/rails s

=> Booting Puma
=> Rails 5.0.0.beta1 application starting in development on http://localhost:3000
=> Run `rails server -h` for more startup options
=> Ctrl-C to shutdown server
I, [2016-01-20T13:57:13.149636 #10181]  INFO -- : Celluloid 0.17.3 is running in BACKPORTED mode. [ http://git.io/vJf3J ]
Puma 2.15.3 starting...
* Min threads: 0, max threads: 16
* Environment: development
* Listening on tcp://localhost:3000

※2016/01/20 サーバー起動時にactionpackがエラーを吐き出します。取り急ぎ実行したい場合は以下のファイルを編集して下さい。

config/initializers/per_form_csrf_tokens.rb

# Be sure to restart your server when you modify this file.

# Enable per-form CSRF tokens.
# 下記の行をコメントアウト
# Rails.application.config.action_controller.per_form_csrf_tokens = true

APIへのアクセス

ToDOの作成

curlを利用してJSON形式でPOSTします。

クライアント

$ curl -H "Content-Type:application/json; charset=utf-8" -X POST -d '{ "title":"単2電池を買いに行く", "order":1, "completed":false }' http://localhost:3000/todos
{"id":1,"title":"単2電池を買いに行く","completed":false,"order":1,"created_at":"2016-01-20T05:12:28.395Z","updated_at":"2016-01-20T05:12:28.395Z"}%

curl -H "Content-Type:application/json; charset=utf-8" -X POST -d '{ "title":"お土産を買いに行く", "order":1, "completed":false }' http://localhost:3000/todos
{"id":2,"title":"お土産を買いに行く","completed":false,"order":1,"created_at":"2016-01-20T05:13:50.865Z","updated_at":"2016-01-20T05:13:50.865Z"}%

サーバー

Started POST "/todos" for ::1 at 2016-01-20 14:04:17 +0900
  ActiveRecord::SchemaMigration Load (0.2ms)  SELECT "schema_migrations".* FROM "schema_migrations"
Processing by TodosController#create as */*
  Parameters: {"title"=>"単2電池を買いに行く", "order"=>1, "completed"=>false, "todo"=>{"title"=>"単2電池を買いに行く", "completed"=>false, "order"=>1}}
   (0.1ms)  begin transaction
  SQL (7.8ms)  INSERT INTO "todos" ("title", "completed", "order", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?)  [["title", "単2電池を買いに行く"], ["completed", false], ["order", 1], ["created_at", 2016-01-20 05:04:17 UTC], ["updated_at", 2016-01-20 05:04:17 UTC]]
   (1.1ms)  commit transaction
Completed 201 Created in 32ms (Views: 1.5ms | ActiveRecord: 9.8ms)

Started POST "/todos" for ::1 at 2016-01-20 14:13:50 +0900
Processing by TodosController#create as */*
  Parameters: {"title"=>"お土産を買いに行く", "order"=>1, "completed"=>false, "todo"=>{"title"=>"お土産を買いに行く", "completed"=>false, "order"=>1}}
   (0.1ms)  begin transaction
  SQL (0.4ms)  INSERT INTO "todos" ("title", "completed", "order", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?)  [["title", "お土産を買いに行く"], ["completed", false], ["order", 1], ["created_at", 2016-01-20 05:13:50 UTC], ["updated_at", 2016-01-20 05:13:50 UTC]]
   (0.7ms)  commit transaction
Completed 201 Created in 4ms (Views: 0.6ms | ActiveRecord: 1.2ms)

ToDO一覧の取得

クライアント

$ curl http://localhost:3000/todos
[{"id":1,"title":"単2電池を買いに行く","completed":false,"order":1,"created_at":"2016-01-20T05:12:28.395Z","updated_at":"2016-01-20T05:12:28.395Z"},{"id":2,"title":"お土産を買いに行く","completed":false,"order":1,"created_at":"2016-01-20T05:13:50.865Z","updated_at":"2016-01-20T05:13:50.865Z"}]%

サーバー

Started GET "/todos" for ::1 at 2016-01-20 14:15:23 +0900
Processing by TodosController#index as */*
  Todo Load (0.2ms)  SELECT "todos".* FROM "todos"
Completed 200 OK in 3ms (Views: 2.1ms | ActiveRecord: 0.2ms)

ToDOの更新

クライアント

curl -H "Content-Type:application/json; charset=utf-8" -X PATCH -d '{ "completed":true }' http://localhost:3000/todos/1
{"id":1,"completed":true,"title":"単2電池を買いに行く","order":1,"created_at":"2016-01-20T05:12:28.395Z","updated_at":"2016-01-20T05:17:05.151Z"}%

サーバー

Started PATCH "/todos/1" for ::1 at 2016-01-20 14:17:05 +0900
Processing by TodosController#update as */*
  Parameters: {"completed"=>true, "id"=>"1", "todo"=>{"completed"=>true}}
  Todo Load (0.3ms)  SELECT  "todos".* FROM "todos" WHERE "todos"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
   (0.1ms)  begin transaction
  SQL (0.4ms)  UPDATE "todos" SET "completed" = ?, "updated_at" = ? WHERE "todos"."id" = ?  [["completed", true], ["updated_at", 2016-01-20 05:17:05 UTC], ["id", 1]]
   (1.0ms)  commit transaction
Completed 200 OK in 75ms (Views: 0.5ms | ActiveRecord: 1.8ms)