How to build Rails5 API + Redux ToDo Application その3
前回はReactでToDOクライアントを実装しました。
で、これをRedux化してみます。
いきなりAPI叩くとややっこしいので、まずはクライアント単独で動作するものを作成してみます。
ソースコードはこちら。
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とします。
と書いてるとだんだんややこしくなってくるので、図にしてみました。
ユーザーからの操作でReducerに対してActionが発行され、Stateが更新されます。
更新されたStateで再描画される、ってなわけです。
ひとまずRedux方式になったので、次回はこれにAPIを叩かせたいと思います。
- メディア:
- この商品を含むブログを見る
How to build Rails5 API + Redux ToDo Application その2
前回はRails5でToDOリストのREST APIを実装しました。
今回はクライアント側の実装をしてみます。
イメージ
言語・ツール等
- ES6 (ES2015)
- React
- gulp
- webpack
. ├── components │ ├── todo-box.js │ ├── todo-form.js │ ├── todo-list.js │ └── todo.js └── main.js
コンポーネントの構成
各パーツをコンポーネント化していきます。
実装してみる
まず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フロントエンド開発
- 作者: Frankie Bagnardi,Jonathan Beebe,Richard Feldman,Tom Hallett,Simon HØjberg,Karl Mikkelsen,宮崎空
- 出版社/メーカー: オライリージャパン
- 発売日: 2015/04/03
- メディア: 大型本
- この商品を含むブログ (2件) を見る
Hello, 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
- 作者: Dave Thomas
- 出版社/メーカー: Pragmatic Bookshelf
- 発売日: 2014/10/19
- メディア: ペーパーバック
- この商品を含むブログを見る
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
- 作者: 若山史郎
- 発売日: 2014/07/30
- メディア: Kindle版
- この商品を含むブログ (2件) を見る
Visual Studio CodeをES6に対応させる
プロジェクトフォルダ直下に設定ファイルを作成する
// 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
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 -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)