ましめも

技術系メモ

JSONをHTML/CSSで表現する

突然JSONをHTML/CSSでいい感じに表現したくなったのでやってみた。

<div class="json">
    <div class="js-value js-object">
      <div class="element">
        <div class="js-key">key1</div>
        <div class="js-value js-string">value1</div>
      </div>
      <div class="element">
        <div class="js-key">key2</div>
        <div class="js-value js-object">
          <div class="element">
            <div class="js-key">key3</div>
            <div class="js-value js-number">12345</div>
          </div>
          <div class="element">
            <div class="js-key">key4</div>
            <div class="js-value js-array">
              <div class="element">
                <div class="js-value js-string">A</div>
              </div>
              <div class="element">
                <div class="js-value js-string">B</div>
              </div>
              <div class="element">
                <div class="js-value js-number">3</div>
              </div>
              <div class="element">
                <div class="js-value js-object">
                  <div class="element">
                    <div class="js-key">fuga</div>
                    <div class="js-value js-number">12345</div>
                  </div>
                  <div class="element">
                    <div class="js-key">hoge</div>
                    <div class="js-value js-number">12345</div>
                  </div>
                </div>
              </div>
              <div class="element">
                <div class="js-value js-string">4</div>
              </div>
            </div>
          </div>
          <div class="element">
            <div class="js-key">key6</div>
            <div class="js-value js-string">めっちゃ長い文字列ABCDEFGめっちゃ長い文字列ABCDEFGめっちゃ長い文字列ABCDEFGめっちゃ長い文字列ABCDEFG</div>
          </div>
        </div>
      </div>
    </div>
  </div>

表示結果

f:id:mashijp:20131017234559p:plain

解説

箇条書きで簡潔に

  • 1つの要素は<div class="element">で表現している
  • キーは<div class="js-key">, 値は<div class="js-value 型の名前">で表現。型の名前は以下の通り
    • js-string ... 文字列型
    • js-number ... 数値型
    • js-boolean ... 真偽型
    • js-array ... 配列
    • js-object ... オブジェクト
    • js-nullを忘れてた…(あとでやる)

ここがすごいの

CSSのafter, before要素を使ってるので「"」や「,」「{」「[」をつけなくてもいい

誰が得するの?

...

感想

作ったあとに気づいたけど[や{等をCSSで表示するようにしてるのでコピペしたときに[や{がつかない!!!!!!!!CSSのcontentってコピペできないのか…。
↓コピペ結果例

key1 value1 
key2 
key3 12345 
key4 
A 
B 
3 
fuga 12345 
hoge 12345
 
4
 
key6 めっちゃ長い文字列ABCDEFGめっちゃ長い文字列ABCDEFGめっちゃ長い文字列ABCDEFGめっちゃ長い文字列ABCDEFG

まじで糞なものを作ってしまった感…

UnicornでUnix domain socketを使う場合は絶対パスで指定しなければならない

listen 8080 # TCP
listen "tmp/unicorn.sock" # Unix Domain Socketのつもり

と指定するとそんなListenの方法ないよー、と怒られた

I, [2013-06-26T09:22:21.178723 #11457]  INFO -- : listening on addr=0.0.0.0:8080 fd=8
F, [2013-06-26T09:22:21.179525 #11457] FATAL -- : error adding listener addr=tmp/unicorn.sock
/u/apps/..../gems/unicorn-4.6.2/lib/unicorn/socket_helper.rb:149:in `bind_listen': Don't know how to bind: tmp/unicorn.sock (ArgumentError)

Unicornソースコード
https://github.com/schneems/unicorn/blob/master/lib/unicorn/socket_helper.rb

    def bind_listen(address = '0.0.0.0:8080', opt = {})
      return address unless String === address

      sock = if address[0] == ?/
        if File.exist?(address)
          if File.socket?(address)
            begin
              UNIXSocket.new(address).close

1文字目がスラッシュからはじまってるかどうかで判定してるんですね...そうですか...

つまり、絶対パスじゃないと無理

Githubからやってくるhookの内容メモ

Githubのhook
https://help.github.com/articles/post-receive-hooks

タグが作成され、pushされたときは

{
  "ref": "refs/tags/testtag",
  "after": "a011d5d157f769b39b99d2c10333e936eeab6bed",
  "before": "0000000000000000000000000000000000000000",
  "created": true,
  "deleted": false,
  "forced": true,
  "base_ref": "refs/heads/master",
  "compare": "https://github.com/mashijp/gitstudy/compare/testtag",
  "commits": [

  ],
  "head_commit": {
    "id": "a011d5d157f769b39b99d2c10333e936eeab6bed",
    "distinct": true,
    "message": "delete",
    "timestamp": "2013-04-25T09:10:23-07:00",
    "url": "https://github.com/mashijp/gitstudy/commit/a011d5d157f769b39b99d2c10333e936eeab6bed",
    "author": {
      "name": "mashijp",
      "email": "(ry",
      "username": "mashijp"
    },
    "committer": {
      "name": "mashijp",
      "email": "(ry",
      "username": "mashijp"
    },
    "added": [

    ],
    "removed": [
      "fugahoge"
    ],
    "modified": [

    ]
  },
  "repository": {
    "id": 9674385,
    "name": "gitstudy",
    "url": "https://github.com/mashijp/gitstudy",
    "description": "",
    "watchers": 0,
    "stargazers": 0,
    "forks": 0,
    "fork": false,
    "size": 108,
    "owner": {
      "name": "mashijp",
      "email": "(ry"
    },
    "private": false,
    "open_issues": 0,
    "has_issues": true,
    "has_downloads": true,
    "has_wiki": true,
    "created_at": 1366902142,
    "pushed_at": 1366906300,
    "master_branch": "master"
  },
  "pusher": {
    "name": "mashijp",
    "email": "(ry"
  }
}

ブランチがきられ、pushされたときは

{
  "ref": "refs/heads/develop",
  "after": "0ce9e6e68576a18c073aaf6fadd5b93aef850811",
  "before": "0000000000000000000000000000000000000000",
  "created": true,
  "deleted": false,
  "forced": true,
  "compare": "https://github.com/mashijp/gitstudy/commit/0ce9e6e68576",
  "commits": [
    {
      "id": "0ce9e6e68576a18c073aaf6fadd5b93aef850811",
      "distinct": true,
      "message": "doyaa",
      "timestamp": "2013-04-25T09:13:55-07:00",
      "url": "https://github.com/mashijp/gitstudy/commit/0ce9e6e68576a18c073aaf6fadd5b93aef850811",
      "author": {
        "name": "mashijp",
        "email": "(ry",
        "username": "mashijp"
      },
      "committer": {
        "name": "mashijp",
        "email": "(ry",
        "username": "mashijp"
      },
      "added": [
        "pyo"
      ],
      "removed": [

      ],
      "modified": [

      ]
    }
  ],
  "head_commit": {
    "id": "0ce9e6e68576a18c073aaf6fadd5b93aef850811",
    "distinct": true,
    "message": "doyaa",
    "timestamp": "2013-04-25T09:13:55-07:00",
    "url": "https://github.com/mashijp/gitstudy/commit/0ce9e6e68576a18c073aaf6fadd5b93aef850811",
    "author": {
      "name": "mashijp",
      "email": "(ry",
      "username": "mashijp"
    },
    "committer": {
      "name": "mashijp",
      "email": "(ry",
      "username": "mashijp"
    },
    "added": [
      "pyo"
    ],
    "removed": [

    ],
    "modified": [

    ]
  },
  "repository": {
    "id": 9674385,
    "name": "gitstudy",
    "url": "https://github.com/mashijp/gitstudy",
    "description": "",
    "watchers": 0,
    "stargazers": 0,
    "forks": 0,
    "fork": false,
    "size": 108,
    "owner": {
      "name": "mashijp",
      "email": "(ry"
    },
    "private": false,
    "open_issues": 0,
    "has_issues": true,
    "has_downloads": true,
    "has_wiki": true,
    "created_at": 1366902142,
    "pushed_at": 1366906447,
    "master_branch": "master"
  },
  "pusher": {
    "name": "mashijp",
    "email": "(ry"
  }
}

こんな通知がきたよ。というメモ。

"created": true が来たら新しいタグ or ブランチ作成かな?

Railsのprotect_from_forgeryは必ずしも外部からのリクエストから守ってくれない

Railsお勉強中。ためしにログイン不要な掲示板作ってる
https://github.com/mashijp/railsboard

外部からトピック作成やレス作成をできないように対策することを「CSRF対策」っていうのかと思っていたが、ログイン前提じゃないとCSRFじゃないらしい…知らんかった…。Railsのprotect_from_forgeryはトークンのない更新系アクセスがあった場合セッションを消すだけなので、ログイン不要なサイトだと意味がない(警視庁等の通報ページも同様)。
※詳しくは» Rails3のprotect_from_forgeryはトークンの検証NGの場合にreset_sessionする TECHSCORE BLOGを参照


どうしようかなと頭を抱えていたけど、Railsソースコード(request_forgery_protection.rb)読んでたら簡単にオーバーライドできることが判明

      # This is the method that defines the application behavior when a request is found to be unverified.
      # By default, \Rails resets the session when it finds an unverified request.
      def handle_unverified_request
        reset_session
      end

ということでoverrideしました!

class ApplicationController < ActionController::Base
  protect_from_forgery

  protected
  def handle_unverified_request
    raise Exception("なんやこれ")
  end
end

https://github.com/mashijp/railsboard/commit/31d67c7803770219b8461a0266e46f76392f72b4
↑あとでまじめにやる

(タイトルは煽り)

Amazon CloudFrontを使ってみた

Amazon CloudFrontってなに

f:id:mashijp:20130114230342p:plain
http://aws.amazon.com/jp/cloudfront/
(語弊がありそうだけど)簡単に書くとキャッシュサーバ。
配信サーバ(Originと呼ばれる)のデータをキャッシュし、クライアントに効率的に配信する。日本やアメリカ、ヨーロッパなどにサーバがあり、クライアントの地理的条件に応じて使用するサーバを選択するので、レイテンシも小さい。

作成の仕方

アカウント登録した後は超カンタンだった!(といってもそのアカウント登録に時間がかかったんだけど…)

1. Account ConsoleのCloudFrontにアクセス
Create Distributionを押すと次の画面がでてくる
f:id:mashijp:20130114224803p:plain
今回は静的ファイルを配信したいのでDownloadを選択。

2. Originサーバの設定をする
f:id:mashijp:20130114224804p:plain
配信元のドメイン名をOrigin Domainに入力。

3. (必要なら)CNAMEの設定もする
標準だと"fugahogefuga.cloudfront.net"のようなドメイン名がふられるのだが、自分のドメイン名に設定することができる。
f:id:mashijp:20130114224800p:plain
やりたいならCNAMEに"example.mashijp.net"等を追加。

これで終わり。簡単。(CNAME設定したい場合は、作成後ドメイン名がふられるのでそれに対するCNAMEレコードを追加する)

料金は?

http://aws.amazon.com/jp/cloudfront/pricing/ にも書かれている通り、リクエスト数・転送量に応じた従量課金制。つまり、青天井です。DoSられるといくらでも料金がかかるのではという不安が常に押し寄せます。

何に使うの?

静的ファイル配信に便利なんじゃないかなあ、と思ったり。急激にアクセスが来た場合(or 見込まれる場合)は使うと良さそう。

(自分用メモ) playのevolutionファイルの番号飛ばしはダメ

1.sql, 2.sql, 999.sqlというファイルを作っても、999.sqlは見てくれない(2.sqlまでしか実行してくれない)
連番になってないと実行しない。(1.sql, 2.sql, 3.sql...)

ソース

Play Framework 2.1のちょっとした新機能

実はPlay Framework 2.0.xのFormでは同じフィールド名で複数値を受け取ることができない。

<input type="checkbox" name="name" value="value1">
<input type="checkbox" name="name" value="value2">
<input type="checkbox" name="name" value="value3">

例えば、上のようなフォームがあったとして、全てにチェックを入れても一つしか値は格納されない(多分name=value3しか入らない。もしかしたらvalue1かも)。

正確に書くと、request.body.asFormUrlEncodedの時点ではMap[String, Seq[String]]としてちゃんと格納されているのでこれを直接参照すれば"(name -> Seq(value1, value2, value3))"が得られるのだが、form.bindFromRequest()した瞬間に内部的にMap[String, String]に変換され、(name -> value3)となってしまう…。

POSTパラメータ name=value1&name=value2&name=value3
request.body.asFormUrlEncoded Map(name -> Seq(value1, value2, value3))
bindFromRequestした際の内部的なデータ Map(name -> value3)


一応、以下のようにフィールド名に"[番号]"をつけてあげれば複数の値を取得することができる。

<input type="checkbox" name="name[0]" value="value1">
<input type="checkbox" name="name[1]" value="value2">
<input type="checkbox" name="name[2]" value="value3">

コントローラ側の例

  case class AddForm(val name: List[String])

  val addForm = Form(
    mapping(
      "name" -> list(text)
    )(AddForm.apply)(AddForm.unapply)
  )

チェックボックスならこれで解決だが、複数選択可能なセレクトボックス(multiple要素をつけたselect)だとお手上げ。(どう転んでもnameは1つしか指定できないので)

しかし、Play Framework 2.1はこれが改善されており、末尾が"[]"で終わるフィールド名は自動的にname[0], name[1]に変換されるようになった!

POSTパラメータ name=value1&name=value2&name=value3
request.body.asFormUrlEncoded Map(name -> Seq(value1, value2, value3))
bindFromRequestした際の内部的なデータ Map(name[0] -> value1, name[1] -> value2, name[2] ->value3)

↑こんな感じに保存されてる

これで、mappingのlistが使い物になる!やったー (2.0.xでもやってほしい…)