旧gaaamiiのブログ

間違ったことを書いている時があります。コメントやTwitter、ブコメなどでご指摘ください

Storybookを使って気付くCSSの粗さ

がっと作った大きなOrganismがあって、それを構成するMolelculeやらAtomやらのレベルのコンポーネントをすべてStorybookに置いた段階で、「あれ、なんかこれいい感じに表示できない...」ということに気付く。CSS Modulesを使ってスタイルのスコープをそのコンポーネントに閉じさせているものの、それだけでは期待する見た目にならない。正しい見た目が上の要素に依存していて、それがないとちゃんとした見た目にならない。直さなくては...ということになる。

Storybookに細かい単位でコンポーネントを置かなければこういうのをごまかせてしまうので、いざ細かいコンポーネントを使い回すときになるまで「なんだこれ」ということに気づかなかったりする。Storybookの使いみちはいろいろあるけど、こういうスタイルのスコープ確認につかえて便利だと思う。

fetch-mockでStorybookからモックデータを利用する

新たに開発している画面で、Storybookを利用してデザイナーさんに見た目のレビューしてもらいやすくしようとしている。この画面はAPIと並行して開発しているので、モックデータを用意してそれを見るようにする必要がある。

アプリケーション本体の方ではjson-serverを使っていて、Storybookもローカルで確認するにはそれで十分だったのだけど、確認のために用意した環境はS3なので、別でモックサーバーを立てるというのはそこまでするの感がある。

ferch-mockを使うと、APIへのリクエスト部分をモックのpromiseに差し替えられる。setTimeoutの値を変えれば遅いAPIをシミュレーションできるし、エラーを返せばエラー時の挙動を確認できる。便利。

json-serverのために用意したモックデータをそのまま使えてよかった。

Reduxのreducerはなんでreducerと呼ばれているのか

Reduxのreducerはなんでreducerなのか、そもそもreducerってなんだ、みたいなことを調べたので書きます。間違ったこと書いてたら直します。

背景

Reduxでは、reducerってやつが状態の変更方法を知っています。reducerはReduxのドキュメントに以下のように書いてある通り、

状態とアクションを受け取って、次の状態を返すやつです。

しかし、reducerという言葉はどういう意味なのでしょう。「状態とアクションを受け取って、次の状態を返すやつ」を、なぜreducerと呼ぶのでしょう。

Weblioの辞書で調べると、

〔+目的語+to+(代)名詞〕〈ものを〉(整理して)〔簡単な形に〕変える,まとめる.

という意味が出てきます。上述の通りReduxのreducerはstateを変えたものを返すやつなので、これが適切なのではないでしょうか。

ここで納得して終わりにしてもいいのですが、Reduxのreducerの意味がこれでよしとするには少し早いような気がします。Reduxの文脈で、なぜreducerという言葉が使われているのかをもう少し調べてみます。

Why is it called a reducer?

Google検索で、雑に why reducerと検索すると、先程のページが出てきます。

疑問に対する答えに当たる部分はここでしょうか。

It's called a reducer because it's the type of function you would pass to Array.prototype.reduce(reducer, ?initialValue).

ふむ。Array.prototype.reduce(reducer, ?initialValue)として渡せるような関数だからreducerと呼ばれている…?

なるほどわからん

Array.prototype.reduceの仕様

なるほどわからんなのは、私がArray.prototype.reduce関数を普段使っていないからのような気がします。reduceという関数に慣れていれば、上記の説明で一発理解ということになるのかもしれません。reduceは使ったこともあるし他の言語でもだいたいあるような一般的なものですが、ちゃんと仕様を読んで理解しているわけではありませんでした。

そこで、このreduce関数の仕様を見てみます。

さすがMDN、説明が丁寧です。これもじっくり読んでいきます。

reduce() はアキュムレータと配列の各要素に対して(左から右へ)関数を適用し、単一の値にします。

なるほどわからん。今度はまた新たななるほどわからんです。

今度のなるほどわからんは、「アキュムレータ」ってなんだ、という話です。こういうことを正直に書いてしまうとコンピュータのこと知らんやつだなというのが露呈するのであれなんですが、わからないものは仕方ない。

アキュムレータ(英: Accumulator)は、コンピュータにおいて、演算装置による演算結果を累積する、すなわち総和を得るといったような計算に使うレジスタや変数のことである。

accumulateというのが累積するという意味の英単語なので、そのままAccumulatorは累積するやつのようです。コンピュータアーキテクチャのことを学びたい気持ちも湧いてきてしまいますが、MDNのreduceのドキュメントを理解する上では、accumulatorという言葉の理解は上の説明で十分でしょう。

もう一度MDNのドキュメントに戻って、reduceが何をしているのかを見ていきます。

const array1 = [1, 2, 3, 4];
const reducer = (accumulator, currentValue) => accumulator + currentValue;

// 1 + 2 + 3 + 4
console.log(array1.reduce(reducer));
// expected output: 10

// 5 + 1 + 2 + 3 + 4
console.log(array1.reduce(reducer, 5));
// expected output: 15

Reduceの使い方として、上のコードが例示されています。配列内の数の総和を出しているだけです。

やっていること、使い方はわかりますが、どう動作しているのでしょう。それについても書かれています。

なるほどわかりました。accumulatorがそれまでの返り値を保持していて、currentValueが配列内の現在の要素です。

let array = [1,2,3,4];
let accumulator = 0;
for (let i = 0; i < array.length(); i++) {
  let currentValue = array[i];
  accumulator += currentValue;
}

for文で書くとこんなかんじでしょうか。外で宣言した変数に詰めるのではなく、引数として渡ってくるのでreduceのほうがすっきり書けます。

Array.prototype.reduce?

Array.prototype.reduceがやっていることはわかったものの、

It's called a reducer because it's the type of function you would pass to Array.prototype.reduce(reducer, ?initialValue).

これは結局どういうことでしょうか。ReduxのreducerがArray.prototype.reduce(reducer)として呼べるのであれば、reduceのレシーバに当たる配列は何になるんでしょうか?

Stack Overflowのある回答がとてもわかりやすかったです。

['INCREMENT', 'DECREMENT', 'INCREMENT'].reduce(reducer, 0)

つまりアクションの配列がレシーバになる。なるほど〜〜〜、そういうことを言っていたのか!0が最初のstate(accumulator)になり、reducerで定義された変更が3つ行われた結果が返ってくる。

雑感

Reducerというのが「stateを変えるためのあれ」くらいの認識だったけど、今回の調べで言葉の意味から理解できた。

ところで、reduceを調べているとfoldとか折り畳み関数みたいな言葉も出てきていた。次はここらへんについてもまとめてみたい。

参考リンクなど

今さらReduxのSSOT原則とか状態管理について考える

Reduxを使ってウェブアプリケーションの開発をしていて、割と早い段階で、どうしてもReactのsetStateを呼びたい場面に遭遇しました。しかしReduxといえば、Single Source of Truthという原則があります。状態はすべて、Redux側で一つの状態ツリーで管理するものだと思っていました。

とすると、Reactコンポーネントたちは状態をpropsとして受け取って描画するだけ、ただのビューの関数であり、Reduxでの状態管理とReactでの要素表示の役割がしっかり分けられてきれいな世界になる...!!これがいまどきのきれいなウェッブアプリケーションなんじゃ!!!実装を始める前まではそんなものを想像していました。

ところが、実装を始めるとすぐにReactのsetStateを呼びたくなりました。それはもうすぐに呼びたくなりました。何かと言うと、いわゆるドロップダウンメニューの開閉表示状態です。今私が利用しているはてなブログの編集画面の、ここの部分みたいなものです。

クリックしたら開いたり閉じたりします。これ系の挙動をするコンポーネントは、だいたい何を作るにしても必要なものだと思います。

要は、この開閉状態を、Reduxで管理せず、Reactのそのコンポーネントのstateに持たせたくなったのです。

Reduxで管理する場合、きっと以下のようなコードが必要になります。

// components/DropdownComponent.jsx
const mapStateToProps = ...
const mapDispatchToProps = ...
export default connect(mapStateToProps, mapDispatchToProps)(DropdownComponent)

// actions/ToggleDropdown.js
export default {
  type: 'TOGGLE_DROPDOWN'
}
// reducers/toggleDropdown.js
const toggleDropdown = (state, action) => {
  switch(action.type) {
    case 'TOGGLE_DROPDOWN' :
    default:
      return !state
  }
}
export default toggleDropdown 

ふむ...ずいぶんと多くのファイルを編集しないといけない。

対して、ReactのsetStateを使う場合。

export default class DropdownComponent {
  constructor(props) {
    super(props)
    this.state = {
      visible: false
    }
  }

  handleClick = (e) => {
    this.setState({ visible: !this.state.visible })
  }

  render() {
    <div>
      <button onClick={this.handleClick}>
      {this.renderContent()}
    </div>
  }

  renderContent() {
    if (this.state.visible) { 
      return <DropdownContent />
    } else {
      return null
    }
  }
}

こりゃいいや!!!このコンポーネントのなかで完結していてわかりやすい!

しかし、そんなことをして怒られないのでしょうか。この軽はずみな実装によってReduxの怒りを買ってしまいプロダクトが破綻してしまっては困ります。

調べてみると、ちゃんとReduxにこんなFAQがありました。

(質問)

Do I have to put all my state into Redux? Should I ever use React's setState()?

まさしく自分が知りたいことです。setState呼んでいいのでしょうか?

(回答)

There is no “right” answer for this. Some users prefer to keep every single piece of data in Redux, to maintain a fully serializable and controlled version of their application at all times. Others prefer to keep non-critical or UI state, such as “is this dropdown currently open”, inside a component's internal state.

なるほど、、全部Reduxで管理するやつもいるし、ドロップダウンの開閉状態みたいなものに関してはコンポーネントに持たせる人もいるよね、と。答えになってないじゃないか!

Using local component state is fine. As a developer, it is your job to determine what kinds of state make up your application, and where each piece of state should live. Find a balance that works for you, and go with it.

それはお前が考えることだ、と。ふぉ〜〜〜。そうですよね〜〜。

改めて考えてみると、もともとはReduxなんかなくてもアプリケーションは作れる。じゃあなんでRedux使うのかというと、コンポーネントの中ではなくアプリケーション全体で持ち回したい状態を一箇所でちゃんと管理したいから、ということになる。それだってRedux使わなくてもできるわけだけど、そうするとバケツリレーと呼ばれるあの、状態とコールバックを受け渡し受け渡しする見通しの悪い地獄みたいなコードを書かないといけなくなる。それは嫌だ。それだったらReduxを使う、となります。

まとめ

ReduxのSingle Source of Truthを厳密に守ろうとするのはきついと感じることもあり、しかしReduxみたい状態管理ライブラリは何かしら必要です。自分の精進が足りず、まだベストな方法を見つけられていない感がありますが、Reduxを完璧なフレームワークと思い込まず、問題があったらそれについて考えながら使いこなしていければと思います。

YomomoのAjaxな部分をReactコンポーネント化した

http://i.gyazo.com/ab5a678ee2776ac5e013f9c9ede44524.gif

覚えることがたくさんあって、十分に使いこなせてはいないかもしれない。けれどもこれまでがjQueryべたべただったため、ちゃんとコンポーネントとして管理できるだけでもすごい良い。各コンポーネントは、公式のチュートリアルを参考にしながらこんな感じになりました。

  • RecommendedBooks
    • ItemList
      • Item
        • EditLink
    • LoadButton

サーバーとのやり取りをして表示を変えるような部分は積極的にコンポーネント化していきたいです。

gulpとBackbone.jsとBootstrapでSPA作ってGithub Pagesで公開するためのテンプレート

こんな感じになった。

Gruntよりgulpの方がよいというのをどっかの何かで目にしたので、Yeomanを使っていない。そのため、自分でテンプレを作る必要があった。まだかなり不完全なのでちょこちょこと使いやすくしていきたい。

使っているもの

何が嬉しいのか

自分はクズ人間なので、酒を飲まないと何もやる気がおきない。酒を飲むと人生どうでもよくなるので目の前の作りたいものに集中できる。

けれど、酒を飲むとちまちまとしたこと(ディレクトリ構成を考えたり、使うライブラリのファイル群をダウンロードしたり)ができなくなる。酒を飲んだ時に、ささっとファイルを生成してホスティングまで持っていければ、自然にくだらないものを作れるようになる。

これを使ってやりたいこと

「15分でジョークサイトを作る」みたいな動画を収録したい

課題

  • browserifyあんまよくわかってない
  • gulp使ってるのにstreamってなにかわかってない
  • Backboneはちゃんとしたものを作るためのものって気がするので、別のフレームワークのテンプレートも作っていきたい

参考

Backbone.jsを使ってブラウザベースのMarkdownエディタを作りました

f:id:shgam:20141016173701j:plain

というわけで、Nekobitoという名前のテキストエディタを作りました。何が目的でどうやって作ったのかとかそういうことを書きます。タイトルは2ちゃん風にしたので、2ちゃんまとめサイトに取り上げられることを願うばかりです。

背景

ブログの下書きを書くためのテキストエディタ、今まではKobitoを使ってたけど、できればブラウザ上で使いたい。でもQiitaの投稿ページがKobitoの代替になるとは思えず、他のテキストエディタとしてwri.peなども検討したけどテキストエリアがデフォルトすぎて気持よくなかったので自分で作りました。開発時には麒麟のどごし生を服用しました。

名前はNeo Kobitoとしようとしたんですが、思いっきりKobitoって言っちゃっていいのかと思ってoを抜いてnekobitoにしました。固有名詞っぽければなんでもよかった感じです。

使い方

  1. アクセス
  2. 使う

これだけです。以下詳細です。

どこに保存されるの?

ブラウザのローカルストレージという場所です。そのため、異なるデバイス間で同期!みたいなのは今のところできないです。ブラウザの履歴を小まめに全削除している・シークレットウィンドウを使っているような人は、下書きも消えてしまうので要注意です。

(追記)Dropbox Datastore APIを使ってクラウドへの保存を可能にしました(2014/12/20)

キーバインド

以下、今のところ設定してあるキーバインドです。

Ctrl + n : 新規の下書きを開く
Ctrl + s : 編集中の下書きを保存
Ctrl + d : 編集中の下書きを削除
Ctrl + l : リストを表示/非表示

追記(10/24)

Vimライクな上下移動を可能にしました。

(サイドバーを開いた状態で)
k: 上
j: 下
o: 下書きを開く

どれくらい保存できる?

ブラウザによって異なります。下書きのためのテキストエディタなので、あまり多く保存することは想定していません(想定して作るとwri.peのようにサーバーサイドのいろいろが必要、もしくはDropboxのような外部ストレージと同期するようにしなければいけないので)。

技術

技術面は妥協と挫折が多くてあまり語れませんが、はじめてのBackboneということで勉強に時間をかけました。

Backbone.jsでSPA

jQueryべたべただとキモいのでまともに管理できるコードにしないといけない。そのためには構造が必要だ。じゃあBackbone.jsだ。ということでBackbone.jsを使いました。SPAについてですが、テキストエディタなのでページ遷移が起きないのは当たり前といえば当たり前です。SPAと言ってみたかっただけです。

MVC

とりあえずアプリとしては使えるけど、MVCなどの設計がろくに出来ていない気がします。どうにかしないとです。本読みます。

おわりに

タイトルは酒を飲んで1日で作ったような風に書いてしまいましたが、実際にはけっこう時間がかかりました。最初は、「Markdownのパーサーを一から書くわけじゃない(marked.jsというのを使ってる)し、いったい何に時間がかかるのさww」と思ってたけど普通に時間かかった。できたと思っても気持ち悪さは残るし、ちょっと機能足そうとするとどこにコード書くべきか考えなくてはいけないし、プログラミング難しいです。

作り始めるときに酒飲むのはかなりいいなと思いました。

追記

一応Githubの個別リポジトリを用意しました。

(更新情報)

日記エディタを少しずつ作っています

f:id:shgam:20140610014028j:plain

使っている技術はHTMLとCSSjQueryです。

なんとなく書いて、古い日記は勝手に消えます。保存先は手元のブラウザのローカルストレージなので何を書いても大丈夫です。自分の中に抱えている想いをその時のテンションで書き散らすのに使えます。

100回もコミットしてる割には、なかなか進まない。(ローカルストレージの扱いでちょっとハマったのもあるけども)テキストエディタ侮れないです。

技術面で新しめなものを強いて挙げるならHTML5のHTML Editing APIsというものです。 以下の記事が詳しい。

これで何ができるかというと要はMediumっぽいUIのテキストエディタを実現できるということ。テキストをWYSIWYGな感じで編集できるのはかなり気持ちいいものです。

日本だとnoteがこれを使っているっぽくて、感動しました。noteを初めて見て「Mediumっぽくて素敵」と感じた人もいるのではないでしょうか。 3年後くらいには、「枠に囲まれたテキストエリアとか古いしキモいね。マークダウンプレビューとかもあったよね。懐かしいね」と言われているかもしれません。

ちなみに最近だと「Write」というMacアプリが出たみたいですが、スクリーンショットだけ見て普通にKobitoの方が良いなと感じました。個人的に、ボタンが多いテキストエディタは好きじゃありません。

だらだら書いています。何が書きたかったんだっけ。まあこういうことをだらだら書くのに使えるとお思います。だらだら書いて、読み返してもつまんなかったら勝手に消えるし、面白いなと思ったらブログで公開すればいい。そんな感覚で使えるはずです。たぶん。

SinatraとjQueryでおよそ100行で作るAjax掲示板アプリケーション

Web2.0と言われ始めた時代に、Ajaxという手法が広まりました。今ではAjaxを使っていないウェブアプリケーションを探すのが難しいくらい、Web開発の現場には浸透していると思います。もちろん、Ajaxを知らなくてもウェブアプリケーションは作れますが、これは使い心地を意識すれば自然と必要になってくるものです。

この記事では、実際に簡素な掲示板アプリを作りながら、現在ほとんどのウェブアプリケーションで使われているAjaxという手法を紹介していきます。ちなみに、タイトルの「100行で」というのは今回書いたapp.rb、index.erb、schema.sqlの3つのファイルのトータルがおよそ100行だという意味です。数字をタイトルに入れると読んでもらいやすいという話をどこかで聞いたので入れてみました。

ソースコードGithubにも置いておきました。

対象者としては「Ajaxって言葉は耳にするけど、どういうものかいまいちわからない」という方を想定しています。すでにウェブアプリの作法がわかっているような方には物足りないかもしれません。また、わかりやすさを優先しているため大雑把な説明が多くなっています。AjaxJavaScript周りの正確な説明を必要とする方には、「入門 モダンJavaScript 」という本がおすすめです。

できるだけ役立つ記事にしたいので、明らかにおかしい説明からささいな打ち間違いまで、なんでもご指摘ください。ブコメでもTwitter(@gaaamii)でもけっこうです。

目次

  1. Ajaxとは?
  2. 作ってみよう
    • 完成品
    • 環境
    • ファイル構成
    • ルーティング
    • DB設計
    • ビュー
    • jQueryAjax
  3. まとめ

Ajaxとは?

Ajaxという名前が付いているとプログラミング言語のようなものを想像してしまいますが、それは違います。Ajaxはあくまでもウェブアプリケーションにおいてサーバーと通信してデータを表示する際の手法の一つです。

噛み砕いて表現すると、Ajaxとは「ページの読み込みを減らす工夫」です。

使う場面

なぜAjaxを使うのか。これは多くの場合、アプリケーションの使い心地を高めるためです。ウェブアプリケーションはざっくり言うと「サーバーと通信してデータを取得してHTMLに表示する」仕組みと言えますが、サーバーから毎回全てのHTMLをダウンロードするのは無駄が多くなります。今見ているレイアウトのまま、新しいデータを表示したり、自分が送信したデータをすぐに反映させたりできたら便利ではないでしょうか。

これを実現するのがAjaxなのです。

大手ウェブサービスに使われているAjax

実例を見てみましょう。

などなど。

探してみればたいていのウェブサービスAjaxが使われています。 どれもなんとなく使っているものですが、逆に言うとなんとなくでも使えるような快適さを提供するための工夫としてAjaxという手法が使われていることがわかります。

作ってみよう

では、実際に掲示板アプリを作りながらAjaxに触れていきます。

完成品

完成品は、このようになります。簡単な掲示板アプリです。

テキストを入力して送信すると、ページを読み込み直すことなくそのテキストがすぐにビューに反映されます。これと全く同じことをjQueryだけで実装することもできますが、今回作るアプリでは、

サーバーにテキストを送信 -> データベースに保存 -> データベースからそのテキストを取得 -> ブラウザに表示

というところまでを(ページを読み込み直すことなく)行っています。これがAjax的な動作です。

環境

RubyフレームワークSinatraと、JavaScriptのライブラリjQueryを使ってアプリケーションを作っていきます。

ファイル構成

  • ajax_bbs
    • app.rb
    • views
      • index.erb
    • db
      • schema.sql
      • bbs.db
    • public

ルーティング

Sinatraではルーティングにgetメソッド、postメソッドを使います。メソッド名がhttpのそれと同じなのでとてもわかりやすいですね。

require 'sinatra'

get '/' do
  # コメント一覧を取得
  erb :index
end

post '/comment' do
  # コメントをDBに保存する
end

get '/comments/last' do
  # 最新のコメントをクライアントに返す
end

データベースが絡んでくる諸々は置いといて、ビューを先に作りましょう。

ビュー

ビューは、とりあえずこんな感じにしましょう。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>掲示板アプリでAjax</title>
  <script type="text/javascript" src="/js/jquery-1.11.0.min.js"></script>
</head>
<body style="width:60%;margin:0 auto;text-align:center;">
  <h1>掲示板アプリでAjax</h1>
  <div class="comment-form">
    <input type="text" id="user-name” placeholder="名無し” /><br />
    <textarea id="comment-body" /></textarea><br />
    <input id="submit-comment" type="button" value="コメントする" />
  </div>
  <div class="comment-view">
    <div id="comments">
    </div>
  </div>
</body>

今回はAjaxがメインなのでCSSをしっかり整えたりはしないのですが、「デザインがキモいままだとやる気出ない」という方はしっかりレイアウトしてもいいかもしれません。

このビューを構成しているのはフォーム部分(comment-form)とコメントの表示部分(comment-view)です。

DB設計

掲示板アプリなので、データベースを使います。schema.sqlというファイルをdbディレクトリの下で作り、以下のように書きます。

create table comments(
  id integer primary key not null,
  user_name text,
  body text,
  created_at
);

見ての通りコメント本体とユーザーの名前、投稿日時の保存を期待しています。このスキーマをもとに実際にテーブルを作るには、データベースのファイル(bbs.db)を作成・指定して.readでsql文が書かれたファイル(schema.sql)を読み込んであげればOKです。

$ sqlite3 bbs.db
sqlite> .read schema.sql

次に、app.rbの方でActiveRecordの設定をします。

ActiveRecord::Base.establish_connection(
  "adapter" => "sqlite3",
  "database" => "./db/bbs.db"
)

class Comment < ActiveRecord::Base
end

after do
  ActiveRecord::Base.connection.close
end

これでDBの準備ができたので、Comment.メソッドという形でデータベースのデータにアクセスできるようになりました。インデックスページへアクセスした時にコメント一覧を返すようにします。

get '/' do
  @comments = Comment.order('id desc')
  index :erb
end

orderメソッドで、idの降順でデータを取ってきています。掲示板らしく、最新のコメントから表示するためにこうしています。

jQueryAjax

さて、ようやくAjaxです。Ajaxはサーバーと通信してクライアントにデータを表示する際の手法の一つなので、サーバーサイドとクライアントサイドのプログラミングを一緒に見ていきます。

必要なgemを読み込む
require 'sqlite3'
require 'active_record'
require 'json'
コメントをDBに保存する

今回はちょっと手抜きをしてindex.erbのbodyの閉じタグの直前に書いてしまいます。

<script type="text/javascript">
    $(function(){
      $("#submit-comment").click(function(){
        var comment = $("#comment-body").val();
        var userId = $("#user-id").val();
        if(userId.length == 0) userId = "名無し" ;
        var request = $.ajax({
          type: "POST",
          url: "/comment",
          data: {
            body: comment,
            user_id:  userId
          }
        });
      });
    });
</script>

送信ボタン(#submit-comment)をクリックすると、サーバーにリクエストが送られます。ご覧の通り、リクエスト部分の記述はこちらです。

var request = $.ajax({
  type: "POST",
  url: "/comment",
  data: {
    body: comment,
    user_id:  userId
  }
});

jQueryajax関数を使って、引数にjsonオブジェクトを渡すことでリクエストのオブジェクトを作り、送信しています。この辺は深く掘り下げませんが、ブラウザのコンソール画面やJavaScrpt,jQueryの本を参照して理解を深めておきたいですね。

そして、このリクエストが送られた時にサーバー側で実行される処理がこちらです。

post '/comment' do

  Comment.create({
    body: params[:body],
    user_id: params[:user_id]
  })
  
end

もらった値を、そのままデータベースに保存しています。これでbbs.dbにデータが書き込まれます。

最新のコメントをクライアントに返す

さて、ここが今回のチュートリアルの肝です。コメントをpostして、ページを読み込み直すことなくそれを反映します。

先ほどのリクエストが成功したら、最新コメント(つまり今まさにpostしたコメント)を返してもらうように要求します。そしてそれも成功したら、そのデータをHTMLに整形して表示します。

request.done(function(){
    $.ajax({
        type: "GET",
        url: "/comments/last”,
        dataType: “json”
    })
    .done(function(res){
        $("#comments").prepend('<p>' + escapeHTML(res.body) + ' by ' + escapeHTML(res.user_id) + ‘</p>');
    });
});

ここで、escapeHTML()という関数が使われています。これはコメントを表示する際にscriptタグなどを実行しないように自前で定義したエスケープのための関数です。

function escapeHTML(s){
  $('<div>').text(s).html();
}

これに対応するサーバー側の処理はこうなります。

get '/comments/last' do
  comment = Comment.last
  {comment_body: comment.body, user_id: comment.user_id}.to_json
end
まとめ

いかがでしたか。細かいセキュリティ面での配慮やデザインが不十分なので公開するにはもう少し作業が必要そうですが、今回のチュートリアルAjaxがどういうものかがなんとなく感じていただければ幸いです。

最初に書いたとおり、Githubにもソースコードを上げてあります。

また、説明のために多少前後したところがあったので、書いてきたコードを全てまとめて表示しておきます。

  • app.rb
require 'sinatra'
require 'sqlite3'
require 'active_record'
require 'json'

ActiveRecord::Base.establish_connection(
  "adapter" => "sqlite3",
  "database" => "./db/bbs.db"
)

after do
  ActiveRecord::Base.connection.close
end

class Comment < ActiveRecord::Base
end

get '/' do
  erb :index
end

post '/comment' do

  user_id = "名無し"

  if params[:user_id]
    user_id = params[:user_id]
  end

  Comment.create({
    body: params[:body],
    user_id: user_id
  })
end

get '/comments/last' do
  comment = Comment.last
  {comment_body: comment.body, user_id: comment.user_id}.to_json
end
  • index.erb
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>掲示板アプリでAjax</title>
  <script type="text/javascript" src="/js/jquery-1.11.0.min.js"></script>
</head>
<body style="width:60%;margin:0 auto;text-align:center;">
  <h1>掲示板アプリでAjax</h1>
  <div class="comment-form">
    <input type="text" id="user_id" placeholder="名無し" /><br />
    <textarea id="comment-body" /></textarea><br />
    <input id="submit-comment" type="button" value="コメントする" />
  </div>
  <div class="comment-view">
    <div id="comments">
      <% @comments.each do |comment| %>
        <p><%= escape_html "#{comment.body} by #{comment.user_id}" %></p>
      <% end %>
    </div>
  </div>
  <script type="text/javascript">
    function escapeHTML(s) {
      return $('<div>').text(s).html();
    }
    $(function(){
      $("#submit-comment").click(function(){
        var comment = $("#comment-body").val();
        var userId = $("#user_id").val();
        if(userId.length == 0) userId = "名無し" ;
        var request = $.ajax({
          type: "POST",
          url: "/comment",
          data: {
            body: comment,
            user_id:  userId
          }
        });
        request.done(function(msg){
          console.log(this);
          $.ajax({
            type: "GET",
            url: "/comments/last"
          })
          .done(function( res ){
            $("#comments").prepend('<p>' + escapeHTML(res.body + ' by ' + escapeHTML(res.user_id) + '</p>');
          });
        });
      });
    });
  </script>
</body>
</html>
create table comments(
  id integer primary key not null,
  user_id text,
  body text,
  created_at,
  updated_at
);

参考書籍

CoffeeScript記法の雑な覚え方

このサイトを使ってます。書いたjsをcoffeeに変換するというよりは、間違いを確認しながらcoffeeを書いている感じです。慣れるまで使うのに丁度いいのではと。

こんなかんじです。

夏休み中に「入門 モダンJavaSciript」→「JavaScriptデザインパターン」→「JavaScript Ninjaの極意」を読む

学期末くらいに図書館に購入依頼していた書籍が届きました。大学の図書館ってほんとに最高です。購入依頼が可能なことを知ってから、学費(施設維持費)の元をとってやろうってくらいの意気込みです。

今回借りたのはこの3冊。全て2013年出版の新しい本です。それぞれ、要点まとめなどもブログで紹介していきます。

» 「入門 モダンJavaScript」の要点まとめ

» これから読みます

» これから読みます

せっかくの夏休みで時間はけっこうあるので、ガッっと読んじゃいたいと思います

function(e)のeってなんだ?

最近、週1くらいのペースでミニアプリを作りながらjQueryを教わってる。
教えていただいたことをメモしておく。

jQueryを書くとき、基本はこう書くってことは知ってた。

$(セレクタ).イベント(function(){
  // 処理
});

意味がしっかりわからなくてもこれが書ければなんとなくでできちゃうことがけっこうある。たとえば、ページの先頭に戻るボタンの実装とか。文字をすっと消したりとか。

んで、こうやって書くのも見かけることがある。

$(セレクタ).イベント(function(e){

こう書く場合の、(e)はなんなんだ?引数?
え?何を持ってるんだこのeは?

というわけで教えてもらったのが、「コンソールに出せばわかるよ」ということ。
こう書いて、

$("#sample").click(function(e){
  console.log(e);
});

コンソールを見ると

jQuery.event

というのが返ってきてる。これを開くと、

altKey: false
bubbles: true
button: 0
buttons: undefined
cancelable: true
clientX: 99
clientY: 47
ctrlKey: false
currentTarget: input#sample
data: null
delegateTarget: input#sample
...(省略)

て感じ。

ほほう、これがjQueryイベントの正体か。

たとえば先ほどのクリックイベントでこのeを使って、

alert(e.clientX);

とすれば、クリックした場所のx座標がアラートで返ってくる。

また、keypressイベントなら「どのキーが押されたか」というのを見ることもできる。

$("#sample").keypress(function (e){
  alert(e.keyCode);
});

うーん、面白い。こういうのを使えば、「エンターキーが押されたら」みたいな判定も実装できるようになる。そういうことをしたいときは、function(e)と(eじゃなくてもいいけど)書く必要があるみたい。

jQuery、まだそんなに深くやってるわけじゃないけど、UI周りの「あそこをあれされたらこれする」っていう機能の実装がすごい直感的で楽しい。

YouTube API, jQuery, TwitterBootstrapでYouTubeのジュークボックスをつくってみた

先日、ドットインストールのCTOであるFkojiさんがこんな記事を書かれていた。
YouTubeの検索結果を連続再生できるサービスいくつか (YouTube XLは終了) -F.Ko-jiの「一秒後は未来」

探せばこれだけサービスが見つかるように、この手のサービスは JavaScript から YouTube API を使って自分で作ることも可能です。

ということなので、作ってみた(とりあえずDropBoxで公開しています)。
f:id:shgam:20130526131544j:plain
Continuous YT
jQueryプラグインの「YTPlayer」とは無関係です。→タイトルを変えてスクリーンショットも撮り直しました

それにしても、APIってすごいですね。ビックデータうんぬんじゃないですが、大手サービスの力を借りて個人的に欲しいものを作れるってちょっと感動します。

このままだとまだ微妙ですが、動画のタイトルを表示したりGoogleFontsでいい感じにするとかしたらもっと使いやすくなりそうです。

HTML,CSS,jQueryの基礎知識があれば一晩で出来る感じなのでおすすめしておきます。

追記

先日紹介したWebサービスのつくり方の著者ゆーすけべーさん(@yusukebe)も、最近こういったものをつくったらしい。
YouTubeの再生リストをMetro UIで検索視聴出来る「Melodie」が出来た
こういうのをささっと書けていいかんじのフレームワークもちょろっと見つけてきちゃうあたりがもうなんかすげえっす。

GoogleFeedAPIで取ってきたブログ記事の日付を日本語で表示する

ちょっとメモ。

1. pタグを用意する

var hoge = document.createElement("p");

2. フィードの記事情報を変数に入れる

var entry = result.feed.entries;

3. 日付の情報を日付のデータとして変数に入れる

var entryTime = new Date(entry.publishedDate);

4. 日付の情報を年・月・日に分けて変数に入れる

var entryYear = entryTime.getFullYear();
var entryMonth = entryTime.getMonth() + 1;
var entryDate = entryTime.getDate();

5. hogeの下に日付のテキストを出力

hoge.appendChild(document.createTextNode(
  entryYear +"年 " +entryMonth + "月 " + entryDate +"日 "
));

参考:日付の表示形式の変更 - Google AJAX Feed API入門

ドットインストールでJavaScriptの添削コースを受講した

以前、プログラミングの第一歩としてドットインストールの添削コースに申し込んだ(リリース前にモニターとしてだったので無料だった)。

全4回のコースで、最後の課題としてJavaScriptで何かしらのツールを作るっていうのをやったので晒しとく。現時点でこのブログを書いている僕は以下のようなものしか作れないプログラミング童貞なので、それが伝われば良いな、と思ってこの記事を書きました。


70:25:5 配色ツール
f:id:shgam:20130206145950p:plain

function setColor() {
	var r = Math.floor(Math.random() * 256); // 0~255
	var g = Math.floor(Math.random() * 256);
	var b = Math.floor(Math.random() * 256);
	var a = 0.3

	var rAccent = Math.floor(Math.random() * 256); // 0~255
	var gAccent = Math.floor(Math.random() * 256);
	var bAccent = Math.floor(Math.random() * 256);

	var baseColor = 'rgba('+r+','+g+','+b+','+a+')';
	var subColor = 'rgb('+r+','+g+','+b+')';
	var accentColor = 'rgb('+rAccent+','+gAccent+','+bAccent+')';

	document.getElementById('base').style.background = baseColor; 
	document.getElementById('sub').style.background = subColor; 
	document.getElementById('accent').style.background = accentColor; 

	document.getElementById('baseNum').innerHTML = baseColor; 
	document.getElementById('subNum').innerHTML = subColor; 
	document.getElementById('accentNum').innerHTML = accentColor; 

}

「配色の基本は70:25:5だ」
みたいなことを聞いた事があったので、正方形に対して70:25:5でランダムに色を流し込むツールを作ってみた。

見た目もコードもあまりよろしい出来映えじゃないしそもそもツールとして使えるレベルじゃないけど、楽しかったので良しとする。

色を流し込む対象を単なる正方形じゃなくてちゃんとHTMLのタグに割り当てれば実用性が出てくるんだろうけど、だるくてやってない。
あとは、色の知識付ければいくらかましなものになる気がする。

JavaScriptはブラウザとテキストエディタがあればできるので僕みたいな童貞にはハードルが低くて魅力的だなぁ、というのが課題をやってみた感想です。

» 通信添削コース - プログラミングならドットインストール