ぽっちぽちにしてやんよ

技術ネタとかアプリに関する話とか

Backbone.jsでView.renderを書く場合のセオリー

こんにちは、ぽちです. みなさんBackbone.js使ってますか?Marionetteですか?Chaplinですか?それともngが付いちゃうアレですか?

さて今日は、Backbone.jsのお話です.

タイトルは「Backbone.jsでrenderした時に空divを無くす方法」とかにしようと思ったのですが,なんだかそれに限らない気もしたのでブチあげました.

時間がない人へのまとめ

忙しい人のために,一言でまとめるとこうです.

1
2
3
4
5
6
7
8
9
Class HogeView extends Backbone.View
  initialize: ->
    @listenTo @model, ‘change’, render
    do @render
  render:->
    $oldel = @$el
    $newel = $(<render HTML by template engine>)
    @setElement $newel
    $oldel.replaceWith $newel

次の段落からは簡単な形から流れに沿って最終系になるように説明していきます.

ベーシックなよくある書き方

1
2
3
4
5
6
7
8
9
10
11
class HogeView extends Backbone.View
  el: '.hoge'
  initialize: ->
    do @render
  render:->
    @$el.html ich.hoge_tmpl()

<hoge.jade>
.hoge
script#hoge_tmpl(type="text/html")
  p this is hoge

よくあるのだと、こういう書き方しますよね. この場合、elを指定しているので、既に存在する要素にViewを紐付けています.

ここで使っているテンプレートエンジンはICanHaz.jsです.

要素を作成する場合

次は,既存のの要素ではなく,あるボタンをクリックして 要素を作成するケースを考えてみてください. クリック時に始めてclassがnewされ,それによってDOMに追加されるケースです.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Class HogeView extends Backbone.View
  initialize: ->
    do render
  render:->
    @$el.html(<HTML rendering by template engine>)

Class ButtonView extends Backbone.View
  el: '.add-btn'
  events:
    ‘click': 'append'
  append:->
    v = new HogeView()
    @$el.append(v.el)

<hoge.jade>
a.add-btn add
script#hoge_tmpl(type="text/html")
  .hoge
    p this is hoge 

こんな感じですかね.

ここで問題が. Hogeviewにはelを指定していないので,new時にデフォルトで新しい空divがelになります. そのあと,renderで.htmlするので空divの中にテンプレートの内容が入ったものがelになります.

1
2
3
4
5
<div>
  <div class=“hoge”>
    <p>this is hoge</p>
  </div>
</div>

className/tagNameの導入?

これを回避するには,classNametagNameといったpropertyを使うのが一つの方法です.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class HogeView extends Backbone.View
  className: '.hoge'
  initialize: ->
    do @render
  render:->
    @$el.html ich.hoge_tmpl()

class ButtonView extends Backbone.View
  el: '.add-btn'
  events:
    'click': 'append'
  append:->
    v = new HogeView()
    @$el.append v.el

<hoge.jade>
a.add-btn add
script#hoge_tmpl(type="text/html")
    p this is hoge 

ただし,classNameを用いるためにテンプレートの一番親の要素(.hoge)をHogeViewに持ってきたため見通しが悪い.

setElementを使う

そこでsetElementを使います.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class HogeView extends Backbone.View
  initialize: ->
    do @render
  render:->
    @setElement ich.hoge_tmpl()

class ButtonView extends Backbone.View
  el: '.add-btn'
  events:
    'click': 'append'
  append:->
    v = new HogeView()
    @$el.append v.el

<hoge.jade>
a.add-btn add
script#hoge_tmpl(type="text/html")
  .hoge
    p this is hoge 

とすると空divに挟まずに設定できます.

Modelの更新をトリガーにViewを更新する場合

この方法でも,以下のケースのように一度setElementをしたものを更にsetElementで置き換えるようなコードでは正しく動作しません.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class HogeModel extends Backbone.Model
  defaults:
    index: 0
  initialize: ->
    @listenTo Backbone, 'inc', @inc
  inc: ->
    i = @get 'index'
    @set 'index', i+1
class HogeView extends Backbone.View
  initialize: ->
    @index = 0
    @listenTo @model, 'change', @render
    do @render
  render:->
    @setElement ich.hoge_tmpl
      index: @index++

class ButtonView extends Backbone.View
  el: '.add-btn'
  events:
    'click': 'render'
  initialize: ->
    v = new HogeView
      model: new HogeModel()
    @$el.append v.el
  render:->
    Backbone.trigger 'inc'

<hoge.jade>
script#hoge-tmpl(type=“text/html“)
  .hoge
    p this is hoge

最終系

これを解決するにはこのようにしてあげれば良いです.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class HogeModel extends Backbone.Model
  defaults:
    index: 0
  initialize: ->
    @listenTo Backbone, 'inc', @inc
  inc: ->
    i = @get 'index'
    @set 'index', i+1
class HogeView extends Backbone.View
  initialize: ->
    @index = 0
    @listenTo @model, 'change', @render
    do @render
  render:->
    $oldel = @$el
    $newel = ich.hoge_tmpl
      index: @index++
    @setElement $newel
    $oldel.replaceWith $newel

class ButtonView extends Backbone.View
  el: '.add-btn'
  events:
    'click': 'render'
  initialize: ->
    v = new HogeView
      model: new HogeModel()
    @$el.append v.el
  render:->
    Backbone.trigger 'inc'

<hoge.jade>
script#hoge-tmpl(type=“text/html“)
  .hoge
    p this is hoge

Conclusion

1
2
3
4
5
6
7
8
9
Class HogeView extends Backbone.View
  initialize: ->
    @listenTo @model, ‘change’, render
    do @render
  render:->
    $oldel = @$el
    $newel = $(<render HTML by template engine>)
    @setElement $newel
    $oldel.replaceWith $newel

のようにすることで,テンプレートの構造そのままをViewに適用することが出来て,正しく表示も更新されます.

refs

http://stackoverflow.com/questions/11594961/backbone-not-this-el-wrapping

Githubの二段階認証を設定しよう

Githubがログインアタック受けたみたいですね.Weak passwords brute forced そんなときにハラハラしないために2段階認証(two-factor authentication)を入れましょう.

Githubにログインして右上の工具マーク(Account Setting)から設定画面に行きます. 2013-11-22 10 56 33

Account Settings を選んで,Two-factor authenticationSet up two-factor authenticationを選択します. 2013-11-22 10 56 47

SMSかアプリか選べと言われるのでSet up using appを選びます. 2013-11-22 10 57 09

すると,右側にQRコードが表示された画面になるので,Google Authenticator でQRコードを読み込ませます. すぐに6桁のコードが表示されるので,2. Enter the 6-digit code that the application generatesに入力してEnableします. 2013-11-22 10 57 21

これで2段階認証の設定が完了です!

エジプトからのアクセスでファラオに呪いをかけられる心配しなくてよくなりましたね!

MongoDBのShellを使いやすくする方法

最近DBはMongoDBしか使ってないぽちです,おはようございます!

Node.js + MongoDBの組み合わせで使うことがほとんどなので,スクリプトからはmongooseを使うのですが,ちょっとDBの中身を確認したりするのでmongodbのshellを使うことも多いです.

普段mongooseを使っていると,mongodbのshellが貧弱で困ることがあります.

1
2
> db.users.findOne({_id:"<存在するObjectId>"})
null

みたいな.

正しくはこう書かねばなりません.

1
2
3
4
5
> db.users.findOne({_id:ObjectId("<存在するObjectId>")})
{
  "_id":ObjectId("<存在するObjectId>")
  :
}

めんどくさい!ObjectIdと毎回タイプするのがめんどくさい!

1
> db.users.findOneById("<存在するObjectId>")

と書ければ楽だなーと思って探したら,

1
> DBCollection.prototype.findOneById = function(id){return this.findOne({_id:ObjectId(id)});};

とかやってあげると

1
2
3
4
5
db.users.findOneById("<存在するObjectId>")
{
  "_id":ObjectId("<存在するObjectId>")
  :
}

楽ちんだ!

findOneByIdとか長い!

1
2
3
4
5
6
> DBCollection.prototype.findOneById = DBCollection.prototype.fbi = function(id){return this.findOne({_id:ObjectId(id)});};
db.users.fbi("<存在するObjectId>")
{
  "_id":ObjectId("<存在するObjectId>")
  :
}

楽ちんだ!!!!さすがFBI!!!連邦捜査局!!!

はい,でもこれを毎回打つのが超めんどくさい.

そういう時は,~/.mongorc.jsを配置しましょう.

1
2
3
4
5
6
7
$ echo "DBCollection.prototype.findOneById = DBCollection.prototype.fbi = function(id){return this.findOne({_id:ObjectId(id)});};" > ~/.mongorc.js
$ mongo hoge
> db.users.fbi("<存在するObjectId>")
{
  "_id":ObjectId("<存在するObjectId>")
  :
}

楽ちん!

上ではfindOneByIdだけですが,どんどん追加できるので,決まったaggregateがあるならパラメータを渡す簡単構文でaggregateしたりmongooseでいうpopulate的な動作も出来ると思います.

meshというmongo shellに色々ライブラリを読み込んでくれるのもあるのですが,欲しいのがなかったので自分で作って公開するといいよ!

まとめ

  • ~/.mongorc.jsにmongo shellを拡張するスクリプトを置くと便利!

Backbone.jsのイベントをデバッグ出力するやつをモジュール化した

土日で艦これイベント海域E-4をなんとか突破したぽちです,おはようございます.

今日は島風轟沈の日らしいですね.

ktty1220 さんのBackbone.jsで全イベントをconsoleに出力するデバッグ用スクリプト

が便利だったので使ってたのですが, 毎回backbone.debug.jsをプロジェクトにコピーして回るのが大変だったので bowerでサクッとインストール出来るようにしてみました.

backbone-event-logger

使い方

1
$ bower install backbone-event-logger

でインストールされます.

あとは

1
2
<script src="/bower_components/backbone/backbone-min.js"></script>
<script src="/bower_components/backbone-event-logger/backbone-event-logger.min.js"></script>

みたいに読みこめば,ガンガンログが出ます.

オリジナルとの違い

オリジナルと変更した部分としては

  • Backbone[<ModelとかRouterとか>]::initializeで自動的にログ出力を有効化
  • デフォルトで@constructor.nameをログに出すようにした
  • coffee化
  • 名前を.debugから.event-loggerへ(デバッグっていうよりロガーだなと思ったので)
  • AMD/RequireJSでも読み込めるようにしてみた(けど試してない)

こんな感じで出ます

1
23:04:42 Model:ContentModel  remove [> [ContentModel, Contents, Object]

ContentModel って書いてる部分が@constructor.nameで出してるところ.

イマイチなところ

オリジナルにあったスタイルの変更等はbower installした後に bower_components内を変更しないといけなくて,悩んでいるところ. (自動適用にしたので外部から設定する口を用意してもイマイチかなと)

ところでbower installした時ってbackbone-event-logger.min.jsbackbone-event-logger.js 以外のファイルは配置されるべきなんだろうか?ルールがわからない. (backbone.jsのbowerも配置されてた)

まとめ

iOS7のパララックス効果で左右に動く壁紙を自作する

iPhone 5s/5cを買うのを諦めたぽちです,こんばんは!

そんなわけでとっととiPhone4SにiOS7をいれて使ってます.

今まで使ってた壁紙が使えなくなって悲しみの極みですが, 嘆いていても仕方がないので,新しく作るしかないです.

iOS7からは左右に振ると壁紙が左右にキモい動きをする機能が入ってるっぽいです. 検索したらキモい動きを切る方法ばっかり出てきたので,作り方を書いておくです.

実は超カンタンで,

1
2
3
4
iPad 2 and iPad mini: 1,424 x 1,424
iPad 3 and iPad 4: 2,448 x 2,448
iPhone 4S: 1,360 x 1,040
iPhone 5: 1,536 x 1,040

のサイズで作ればいいだけでした.

この機能の呼び名がパララックス効果壁紙(parallax effect wallpapers)とか ダイナミック壁紙(dynamic wallpapers)とか色々言われてるっぽいですけど,どれが一般的な呼び名なんでしょうね.

hubでpull-requestした後に自動でgithub上のPullRequestをブラウザで開く方法

GitHubを使って開発していると,PullRequestをブラウザでぽちぽちやるのは凄くダルいです.特にGithub Flow だとぽこぽこPullRequest投げまくるので毎回ブラウザ開いてPullRequest投げるのが凄くダルい.

そこでコマンドラインからPullRequestを送れるhub を使います.

hubコマンドは便利なんだけど,hub pull-requestするとデフォルトでPullRequestされたGitHubのPullRequestのURL(https://github.com/pchw/fontawesome/pull/1 みたいなの)をTerminalに出力するだけです.

チームでやってると,PullRequest送ったあとにちょっとコメントしたりするのがあったりして,いちいちTerminalからコピペしてブラウザに貼り付けるのがダルいです.

そこでhubにPullRequest後に勝手にブラウザで今作ったGitHubのPullRequestのURLを開くオプションを追加しようとちょっとPullRequestを送ってみたら

1
open `git pull-request`

ってやればいいじゃんって教えてもらいました.

便利だからみんな使うといいと思うよ.

Sublime Text2で1行が80文字以内かわかりやすくする方法

別に80文字以上でもいいじゃんとか思っていたんですが, GitHubでレビューする時に文字数が多いと横スクロールしにくくてやりにくいので.

Preferences > Settings - User を開いて(またはCmd+,

1
2
3
4
5
"rulers":
  [
    80,
    120
  ],

を追加して保存すれば80行目と120行目に線が表示されるようになります.

OctopressとTravis CIを連携させてBlog生成を自動にする

Octopressってめんどくさいよね

Octopressはmarkdownでブログ書けて,見た目も良い感じで導入した当初はすげーテンションがあがって記事を書きまくってたのですが,rake genereteとかするのが超めんどくさい.さらにちゃんとセットアップしてるマシンでやらないとrubyのバージョン違いでなんかエラー出たりして超めんどくさいので,最近記事かくのがおっくうでした.

そこで,Travis CIとOctopressをforkしてるリポジトリを連携させることでそういう煩わしさを減らしてブログを書けることが判明しました!色々ハマりどころがあって1日ぐらいかかったので解説記事を書くよ!

ちなみに,TravisCIと連携させてgenerateするようにしておけば,Prose.io(github連携のWebエディタ)を使ったりするとOctopressでgenerate出来ない環境とかでもブログが書ける! GitMongo使ったりすれば,iPad/iPhoneからOctopressでブログが書ける!

というわけで手順解説です.

OctopressとTravisを連携させる

今回の構成はこんな感じ octopress-travis

作業は全部↑で言うOctopress(forked)に対して行います.

下に書く.travis.ymlのREPOで指定している所が配置するリポジトリです.(上図のGitHub Pagesと書いてるところ)

まず,Travis CIと連携するための.travis.ymlをプロジェクトのrootに置きます.

.travis.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
---
language: ruby
rvm:
- 1.9.3
before_script:
- git config --global user.name "ユーザ名(via TravisCI)"
- git config --global user.email "メールアドレス"
- git remote set-url origin $REPO.git
- if [ -z "$id_rsa_{1..32}" ]; then echo 'No $id_rsa_{1..32} found !' ; exit 1; fi
- echo -n $id_rsa_{1..32} >> ~/.ssh/travis_rsa_64
- base64 --decode --ignore-garbage ~/.ssh/travis_rsa_64 > ~/.ssh/id_rsa
- chmod 600 ~/.ssh/id_rsa
- echo -e "Host github.com\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config
- rake setup_github_pages[$REPO]
script:
- rake generate
after_script:
- rake deploy
env:
  global:
  - REPO="git@github.com:ユーザ名/GitHubページを配置するリポジトリ.git"

Travis CI用の鍵作成

1
2
3
4
5
6
7
8
9
10
# 鍵生成
# passphraseは空で.
ssh-keygen -t rsa -C "<メールアドレス>" -f ~/.ssh/travis_rsa

# クリップボードに公開鍵をコピー
cat ~/.ssh/travis_rsa.pub | pbcopy


# SSH鍵設定ページを開いてAdd SSH keyする
open  https://github.com/<GitHubアカウント名>/<Octopress用リポジトリ名>/settings/keys

ブラウザが開くので,Add SSH Keyボタンを押して追加します.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 元からOS Xに入っているbase64コマンドだと --wrap=0オプションが使えないのでbase64コマンドをインストール
brew install base64

# 鍵をbase64化
# brewのバージョンなどによってinstallされるPathは異なることがある
/usr/local/Cellar/base64/1.5/bin/base64 --wrap=0 ~/.ssh/travis_rsa > ~/.ssh/travis_rsa_64


# travisコマンドをインストール
brew install travis

# travis encryptが100文字までしか受け付けないので,100文字ごとにtravis encryptに渡す
--addで.travis.ymlに自動追記してくれる
bash <(cat ~/.ssh/travis_rsa_64 | perl -pe 's/(.{100})/$1\n/g' | nl | perl -pe 's/\s*(\d+)\s*(.*)/travis encrypt -r <GitHubアカウント名>\/<Octopress用リポジトリ名> id_rsa_$1="$2" --add env.global/')
 
# id_rsa_Xが何個あるかをカウント
cat ~/.ssh/travis_rsa_64 | perl -pe 's/(.{100})/$1\n/g' | nl | tail

.travis.ymlの書き換え

$id_rsa_{1..32} みたいに書いている場所は↑のid_rsa_Xが何個あるかをカウントの部分でカウントした数にします.

僕の場合は32でした.

リポジトリにpushする

$ git push remote source

Travis CI側の設定

https://travis-ci.org/profile からリポジトリの所をonにする.

https://github.com/ユーザ名/リポジトリ名/settings/hooks#travis_minibucket からtravisのhookが設定されているので,TestHookを押す.

https://travis-ci.org/にアクセスするとなんかビルドが始まって勝手に更新される!

ハマりどころ

Enter passphrase for key ‘/home/travis/.ssh/id_rsa’: とか出やがって止まる

  1. travis encryptに指定するユーザ名,リポジトリ名をミスってる
  2. id_rsa_Xの番号をミスってる(ちゃんとid_rsaXの数分Secureの行数分指定しているか)
  3. 鍵生成でミスってる(passpraseをemptyに)

base64コマンドがbase64: unrecognized option `–wrap=0’とか吐く

brew install base64して入ったbase64で実行する. /usr/bin/base64 を使うと--wrapオプションが無い.

参考資料

[RubyMotion] Motion-fontawesomeっていうgems作りました

1ヶ月ぶりの記事になってしまった!お久しぶりです.

motion-fontawesomeというgemsを作ったので宣伝がてらエントリー書きます!

Retina環境の増加

Retina iPhone/iPadはかなり普及したんじゃないでしょうか? ちょっと何か作るのにTabBarやToolbarのアイコンをいちいち探すのが面倒で,GlyphiconsのFREEを使っていたりしたんですが,非Retinaで荒いし他のアイコンを探そうとすると種類が少なかったり,Retinaじゃなかったりで面倒くさいことこの上ないです. (そもそも無料で探してるのが悪かったりするんですけど)

そこでFontAwesomeというWebfontを使うと便利です.

FontAwesomeとは

元々はTwitter Bootstrapで使えるようにデザインされたアイコンフォント集です.

フォントなので拡大縮小しても荒くならないため,Retinaでも例え超Retina的なのが出てきても対応できますし,色んなサイズが必要になる場面でも力を発揮してくれます.

FontAwesomeをRubyMotionで使おうとすると

しかし,普通に使おうとすると,色々とやることがあって,

  • resourcesにフォントを追加
  • app.fontsに追加
  • 欲しい記号を出すためのUTF8の文字コードを調べないといけない
  • chr(Encoding::UTF_8)とか使って文字コードから文字へ変換してやらないといけない

みたいなことをやらないといけないわけです.

motion-fontawesomeを作りました

そんな面倒なことやってられないので,上記のことを簡単にしてくれるgemsを作りました.

使い方は,

Rakefileでrequire

require 'rubygems'
require 'motion-fontawesome'

使う

label = UILabel.alloc.initWithFrame([[0, 0],[200, 200]])
label.text = FontAwesome.icon(key)
label.font = FontAwesome.fontWithSize(200.0)

アイコンが出る!

simulator

これだけです!上のスクリーンショットを見てもらってもわかると思いますけど,こんなに大きくしても綺麗です.

FontAwesome.icon()に指定する文字列はここから選んで,icon-を取ったものを指定してください.

icon-cameraだったらcameraだけで良いです.)