ぽっちぽちにしてやんよ

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

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だけで良いです.)

RubyMotion 1.15のすげえアップデート

RubyMotion 1.15ですごいアップデートが来るよってlrzさんが言ってました.

それが今日来たのですが,スーパークールなニューフィーチャーはTestFrameworkに関するものでした.

今までRubyMotionはbaconを使ったTestFrameworkが使われていて, rake specとするとSimulatorが実行されメソッドを呼んで値を検証してという形でした.

今回のアップデートでは,指でタップしたりDeviceイベント(画面を回転させたり)といったこともテストで出来るようになりました.

ObjC + XcodeだとjsでUIAutomationのテストを行うらしいんですが(やったことない),その機能がRubyMotionのbaconに統合された感じです.

describe "The Timer view controller" do
  tests TimerController

  it "start a timer" do
    tap 'Start'
    controller.timer.isValid.should == true
  end
end

とすれば,StartというLabelのついてるButtonをタップしてcontrollerのtimerを検証してます.

実際にVideoを見てもらうのが早いと思います.Viewがガンガン動いて面白いです.

公式Blogの情報はこちら

ドキュメントはこちら

7/22(日)に行われる第一回RubyMotion勉強会での発表ネタは今回のアップデートのテストに関することにしようと思います. あと5人ぐらい空きがあるようですね.

修正: 第一回RubyMotion勉強会は 7/22(日) でした.

他のアップデートは↓のような感じです.

更新履歴(訳)

  • UIAutomationの機能を使えるTestが書けるようになったし,jsじゃなくてpure Rubyで書ける.
  • spec/helpersの中のファイルがrake specで使われるようになったよ
  • rake specにfileオプションが追加されて,テストする対象が選べるようになったよ.
  • RUBYMOTION_ENVで実行時にtestなのかdevelopmentなのかreleaseなのかを判別出来る様になった.
  • rake staticが追加されてuniversal statc libraryを作れるようになった.
  • weak frameworkのサポート(app.weak_freameworks << ‘Twitter’とかする)
  • symlinkのファイルがresourceディレクトリに入っていた場合にコピーされてたのを修正.

更新履歴(原文)

= RubyMotion 1.15 =

  * Improved the spec framework to leverage UIAutomation's functionality. This
    lets you write functional tests on views and controllers by using the same
    event generators, but in pure Ruby (and not Javascript).
    Check http://www.rubymotion.com/developer-center/articles/testing for more
    information about the new API. Feature contributed by Eloy Duran.
  * Introduced spec helpers. `rake spec' will now honor the files inside the
    `spec/helpers' directory (if it exists) and compile them before the spec
    files.
  * Introduced the `files' option to `rake spec', which can be used to filter
    the spec files that should be run. Filters can be either the basename of
    a spec file or its full path, and are separated by a comma.
    Example: rake spec files=main_spec,kvo_spec,spec/foo_spec.rb 
  * Introduced the RUBYMOTION_ENV constant in the runtime which can have one of
    the following string values: 'test', 'development' and 'release'.
  * Introduced the `rake static' task which creates a universal static library
    containing the project's object files and the RubyMotion runtime, which is
    suitable for inclusion in Objective-C/Xcode projects. 
  * Introduced weak frameworks support. The `app.weak_frameworks' setting can
    be set to an array of framework names, similar to how `app.frameworks'
    works (ex. app.weak_frameworks += ['Twitter']). Patch by Satoshi Ebisawa.
  * Fixed a bug in the build system where  files within symlinks inside of the
    resources directory would not be copied. Patch by Nick Quaranto.

[RubyMotion] Nitronを使ってOutletやActionっぽいことできるよ

前にStoryboardを使ってRubyMotionで開発する方法とか[RubyMotion] InterfaceBuilderと合わせて使って楽をしよういう記事を書いたのですが,IdentifierとかTagを使ったりでちょっと面倒くさかったり,コントロールが増えてくるとTag番号がどのコントロールかわからなくなったりで煩雑でした.

そこで,Nitronというライブラリを使って普通のXcode+ObjCで言うIBOutletとかIBAction的なことを簡単にやりましょうという記事です.

Nitron

公式のgithub内のWikiにチュートリアルがありますが,ModelやNitronのViewControllerとかの説明やCoreData周りなどにも言及していてOutlet的なところだけやりたい人にはちょっと大掛かりです.

そのため,今回は本当にOutletやAction周りだけを使いたい人向けのシンプルコースです.

Nitroのインストール

$ gem install nitron

なにはともあれgemインストール.

プロジェクトの作成

$ motion create simplenitro

app_delegate.rb の削除

Nitronはapp_delegateに書いてあるようなのがライブラリ内部で勝手にやってくれます. app_delegate.rbがあると,そちらが優先されてしまい,何も画面に出なくてハマるのでサクッと削除しておきます.

$ rm app/app_delegate.rb

Rakefile

$ cat Rakefile
# -*- coding: utf-8 -*-
$:.unshift("/Library/RubyMotion/lib")
require 'motion/project'
require 'rubygems'
require 'nitron'

Motion::Project::App.setup do |app|
  # Use `rake config' to see complete project settings.
  app.name = 'simplenitro'
end

require 'rubygems'require 'nitron'を追加するだけですね. 公式だと,Bundlerを使う方法が紹介されてます.

storyboardの追加

公式では,普通にXcodeのプロジェクトを作ってますが,要らないファイルが死ぬほど付いてくるので,単にXcodeを立ちあげてstoryboardファイルだけ追加しましょう.

File>New>FileiOS>User Interface>Storyboardを選べばよいです.

new Storyboard

名前はMainStoryboard.storyboardにして,./resourcesに作成します.

UINavigationControllerとUIViewControllerの追加

こうやって作ったStoryboardには何も置かれていないので,UINavigationControllerを置きます.

navigation

不要なUITableViewControllerがくっついて来るのでdeleteボタンで消します.

UIViewControllerをD&Dして置きます.

UINavigationControllerを選択してUtility View(右側のペイン)の一番右のタブを選んでRelationShipの所から引っ張って,追加したUIViewControllerに繋ぎます.

root view controller

UIViewControllerを選択して,Utility Viewの左から3番目のタブを選んで,ClassをRootViewControllerとかにしておきます.

UITextFieldとUIButtonの追加

そのままでは何もないので,入力欄とボタンを追加します.

control

UITextFieldを選択して,Utility Viewの左から3番目のタブを選んで, User Defined Runtime Attributesのところの + を選択して

  • KeyPath: outlet
  • Type: String
  • Value: randomText

とします.

outlet

同様にUIButtonの方も

  • KeyPath: outlet
  • Type: String
  • Value: random

としておきます.

この時点でstoryboardの操作は終わりなので保存して閉じます.

RootViewControllerの追加

root_view_controller.rbを追加して,RootViewControllerクラスを作成します.

class RootViewController < Nitron::ViewController
    on :random do
        randomText.text = @seed.sample
    end
end

class RootViewController
    def viewDidLoad
        super
        @seed = ["hoge", "fuga", "moge", "foo", "bar"]
    end 
end

先ほどoutletで指定したrandomがon :random ブロックという形でイベントを受けれます.

また,先ほどoutletでしていたrandomTextという名前でUITextFieldへのアクセスが可能になりますので,textにランダムで選んだ文字列を入れてみます.

実行

$ rake

これで,シミュレータが立ち上がり,Buttonを押した時にランダムで文字が変わります.

まとめ

Nitronを使うことで,storyboardとRubyMotionとの連携が更に高まり開発効率が上がります. 特に軽いプロトタイプやモックなどはStoryboardでサッと作れるので,それに簡単にRubyMotionで動作を付けれれば素早い開発が可能なのではないでしょうか!

RubyMotionでUIViewを継承したViewを作る!

アプリを作っていると独自UIViewを作りたいことが多々あります.

ObjCなら

self = [super initWithFrame:frame];
if (self) {
    // 初期化処理
}
return self;

という感じのをinitWithFrameに書いて//初期化処理の部分にその独自クラスで持ってるものを初期化したりします.

RubyMotionに簡単に翻訳すると,

class AwesomeView < UIView
    def initWithFrame(frame)
        self = super.initWithFrame(frame)
        if self
            # 初期化処理
        end

        self
    end
end

と書きたいですが,これはダメダメでエラーになります.(selfを書き換えれないとか,initWithFrameがループして落ちるとか,,,)

RubyMotionでは

class AwesomeView < UIView
    def initWithFrame(frame)
        if super(frame)
            # 初期化処理
        end

        self
    end
end

とするのが正しそうです. (おいおいそれイケてねーよっていう指摘があったら是非教えてください!!!)

あとは,AwesomeなViewを実装してくだけです.

[RubyMotion] NanoStoreを使ってデータを格納しよう!

今日はRubyMotionでちょっとしたデータの保存がしたくなったときに使えるNanoStoreのWrapperを紹介します.

NanoStoreInMotion

NanoStoreというのは,sqliteを保存先としてスキーマレスなKeyValueStoreを実現するものらしいです.NanoStoreInMotionはそれをRubyMotionから使えるようにするWrapperです.

インストール

まずcocoapodsを入れます

$ gem install motion-cocoapods
$ pod setup

NanoStoreInMotionを入れます.

$ gem isntall nano-store

プロジェクトを作って,Rakefileを編集します.

$ motion create nanostoretest
$ cat Rakefile
# -*- coding: utf-8 -*-
$:.unshift("/Library/RubyMotion/lib")
require 'motion/project'
require 'rubygems'
require 'motion-cocoapods'
require 'nano-store'

Motion::Project::App.setup do |app|
  # Use `rake config' to see complete project settings.
  app.name = 'nanotest'
  app.pods do
    dependency 'NanoStore', '~> 2.1.4'
  end
end

使ってみる

前処理

まず,NanoStoreの保存先を決めてインスタンス化します.

NanoStore.shared_store = NanoStore.store(:file, App.documents_path + '/store.db')

App.documents_pathというのは,BubbleWrapの便利Methodで, /Users/xxxxxx/Library/Application Support/iPhone Simulator/5.1/Applications/EDC08B50-2004-42D9-8B67-2E290EF55CFF/Documentsみたいなアプリ毎の保存場所を返してくれます. 中身の実装的にはNSSearchPathForDirectoriesInDomainsでNSDocumentDirectoryを返してるだけです.

NanoStore.shared_store = NanoStore.store(:memory)

とすることで,ファイルに保存するのではなく,オンメモリでDbを使うこともできます.

Model定義

次に,保存するModelを定義します.

Modelを定義するにはNanoStore::Modelを継承したクラスを定義すれば良いです.

class Label < NanoStore::Model
  attribute :text
  attribute :x
  attribute :y
  attribute :height
  attribute :width
end

保存するフィールドはattribute <シンボル名>で定義できます. 上記の例では,text, x, y, height, width というフィールド(というかKey)が定義されます.

attribute :keyは自動的に付与されるので,:keyは書いてはいけません.(書いてもエラーにはならないが,実際に使うときに予期せぬ動作になります.なりました.)

NanoStore はNSDataも入れれるようですが,インデックスが効かないようです.

データ投入

先ほど定義したModelクラスのnewメソッドを呼んでsaveすれば良いです.

label = Label.new(
    :text=>"test",
    :x => 100,
    :y => 200,
    :height => 50,
    :width => 250)
label.save

この時点で保存されて,検索などが出来るようになります.

検索

検索も先ほど定義したModelクラスを通じて行います.

# もうひとつLabelを追加しておく
hogeLabel = Label.new(
    :text=>"test1",
    :x => 50,
    :y => 400,
    :height => 100,
    :width => 50)
hogeLabel.save

allLabels = label.all
# => [#<Label:0x8e622e0>, #<Label:0x8e62720>]

条件を指定して検索は

Label.find(:text=>"test")
# => [#<Label:0x8e622e0>]

とすればできるので,あとは.eachとかユニークなKeyで検索するなら.first とかすれば目当てのものが手に入ります.

Label.find(:x => { NSFGreaterThan => 10 }).each do |lbl|
    p lbl.text
end
# => "test1"
# => "test"

アップデート

更新処理は,上記の検索で得られたもののプロパティを編集して再度saveを呼べば良いです.

update = Label.find(:text=>"test").first
update.text = "test2"
update.save

Label.all.each do |lbl|
    p lbl.text
end
# => "test2"
# => "test1"

削除

削除処理は,同じく上記の検索で得られたもののdeleteを呼べば,削除できます.

del = Label.find(:text=>"test1").first
del.delete

Label.all.each do |lbl|
    p lbl.text
end
# => "test2"

全部削除したい場合は,

Label.delete

とすれば,全部消えます.

Label.delete(:text=>"test1")

のように条件をつければ,条件にヒットしたものを一気に削除できます.

保存されるデータ

さて,実際に保存されているデータはどうなっているのでしょうか?

NanoStore.store時に:fileを指定した場合は指定したPathにsqliteのデータベースファイルが生成されています. その中をsqlite3コマンドかなんかで見ていけば保存されている形を見ることができます.

$ sqlite3 store.db
sqlite> .tables
NSFKeys    NSFValues

どうやらNSFKeysNSFValuesというテーブルが存在するようです.

NSFKeys

$sqlite> select * from NSFkeys;
ROWID|NSFKey|NSFPlist|NSFCalendarDate|NSFObjectClass
  • ROWIDはautonumber
  • NSFKeyはオブジェクトごとに異なるID
  • NSFPlistはオブジェクトのattributeと値をplist形式で表したもの
  • NSFCalendarDateは更新日かな?
  • NSFObjectClass はNanoStore::Modelを継承したクラス名

NSFValues

sqlite > select * from NSFValues;
ROWID|NSFKey|NSFAttribute|NSFValue|NSFDatatype
  • ROWIDはautonumber
  • NSFKeyはNSFKeysテーブルのNSFKey(外部キー)
  • NSAttributeはattributeで定義したシンボル名
  • NSFValueは実際に入っている値
  • NSFDatatypeは型.TEXTとかREALとか

NSFValuesの方にModelクラスの違い関係なくattribute名と値が入っています. クラス名とかの情報はNSFKeysの方に入っていますね.

まとめ

NanoStoreInMotionはデータを保存,検索,削除などが簡単なステップで出来て,ちょっとしたデータの取り扱いに役立つと思います. :memoryを使うことで,Web APIからデータをごっそり取ってきてさっとフィルタリングするような用途にも簡単に使えそうです.

RubyMotion 1.13が早くもリリース

早くもRubyMotion 1.13が出てます.バグ修正が主です.

更新内容

= RubyMotion 1.13 =

  * Fixed a regression in `rake simulator' introduced by the last update.
    Also, the environment variable used to set the SDK target is renamed to
    `target' (ex. rake target=4.3).
  * Fixed a bug in the build system where certain .rb files would be rebuilt
    every time `rake build' was executed, because the object directory was not
    touched.
  * Fixed a bug in the build system where the RUBYOPT variable would not be
    cleared when calling the gen_bridge_metadata tool, which would cause some
    issues later on. Patch by Satoshi Ebisawa.

更新内容(訳)

  • 前回のアップデートで書いてたrake simulatorのエンバグ修正.rake target=4.3みたいに指定するようになった.
  • オブジェクトディレクトリが更新されなかったからrake buildが実行された時に毎回.rbファイルをリビルドするようにビルドシステムを修正
  • RUBYOPT変数がgen_bridge_metadataを呼ぶときにクリアされない問題を修正.