久しぶりにiOSアプリをビルドしようとしたらエラー

ビルドしようとしたら下記のエラー。

No architectures to compile for (ONLY_ACTIVE_ARCH=YES, active arch=x86_64, VALID_ARCHS=i386).

ググったら、こんなん http://stackoverflow.com/questions/22328882/xcode-5-1-no-architectures-to-compile-for-only-active-arch-yes-active-arch-x 出てきた。

なので、言われたとおり、

  • Xcode上でPodsプロジェクトを右クリック→Delete→Remove Reference
  • Xcodeを終了
  • コマンドラインから pod install
  • .xcworkspace を再度開く
  • Podsプロジェクトを選択し、Build Settings→Build Active Architecture Only をNOに (debugがYESになってた)
  • 同じく、自分のプロジェクトのBuild Active Architecture Only もNOに (debugがYESになってた)

したら、iOSシミュレータで動くようになった。よかった。

久しぶりにpod installしたらエラー

rubyのバージョン変えてたので、まずはgemのインストール

gem install cocoapods
rbenv rehash

で、

pod install

すると、エラーが出てくる。 こんなの

Psych::SyntaxError - (/Users/〜〜〜/.cocoapods/repos/master/CocoaPods-version.yml): mapping values are not allowed in this context at line 3 column 4

ググったら、 https://github.com/CocoaPods/CocoaPods/issues/1853 こんなのが出てきたので、書いてあったコマンドを実行

rm -rf ~/.cocoapods/repos/master
pod setup

で、再度

pod install

すると正常に動いた模様。

Vagrantをprovisionしたときにディレクトリを作る

メモ:

ステージング・本番ではcapistranoでデプロイするけど、 ローカルのVagrantではそんなことしないので、 pidを格納するディレクトリが無くて困った。
(nginxで見に行く先を変えればいいけど、そんな部分のchefをいじるのも面倒。。。)

んで、Vagrantfileのsynced_folderとして、テキトーなディレクトリを指定したけど、 sockファイルを作ろうとして、エラーになった。

http://stackoverflow.com/questions/16388342/unicorn-fails-to-start-on-vagrant-box-due-to-errnoeperm
synced_folderしたディレクトリには作れないっぽい。

どうせなので、provision時のshellを使ってみた。

  $script = <<SCRIPT
mkdir -p /srv/web/shared/pids
chown vagrant:vagrant -R /srv/web/shared
SCRIPT
  config.vm.provision "shell", inline: $script

単純にmkdirすると、root権限で作られたので、chownでログインユーザのものに変更。

無事、sockファイルが作れて、nginx -> unicornでページが表示された。

メモ:rspecで変更が無いことをテストする

自分用メモ

expect{hoge}.not_to change{fuga}

で変更が無いことをテストできる

Rank.get_rank_id では、パラメータによって次のランクが返却されるイメージ User#calc_rank!では、Rank.get_rank_id を呼び出し、その結果をDBに保存するイメージ

require 'spec_helper'

describe User do
  describe '#calc_rank!' do
    let!(:user) { FactoryGirl.create(:user, rank_id: 2) }
    it do
      Rank.stub(:get_rank_id).and_return(2)
      expect{user.calc_rank!}.not_to change{User.find(user.id).rank_id}
    end
  end
end

Alfred workflowで新規ウィンドウを開く

最近、Alfredを使い始めました。

どうせなら、ということでPowerPackも買って、workflowを使えるようにしました。

とか入れてます。

で、それなりに便利に使ってたんですが、FinderとかiTerm2を起動しようとして、別のデスクトップに起動してたのがアクティブになるのが不便。。。

というわけで、下記を追加してみた。

Finderを新規に起動する

  • Alfredの設定画面を開く
  • Workflowsタブを開く
  • 左ペインの右下にある"+"を押す
  • Templates -> Essentials -> Keyword to AppleScript を選択する
  • keywordをダブルクリックする
    • Keywordに"nfinder"を入力
    • No Argumentを選択
    • titleに"new finder"とかを入力
  • Run NSAppleScriptをダブルクリックする
on alfred_script(q)
  tell application "Finder"
    make new Finder window
    set frontmost to true
  end tell
end alfred_script

上記手順により、Alfred呼び出し -> "nfin"とか入力 -> Enter でFinderが新規に起動します。

iTerm2を新規に起動する

Finderの時と手順は同様。
keywordは"niterm"にしました。
AppleScriptは下記の通り。

on alfred_script(q)
    if application "iTerm2" is running or application "iTerm" is running then
        run script "
          on run {q}
              tell application \":Applications:iTerm.app\"
                  create window with default profile
                  select first window
                  tell the first window
                      if onlywindow is false then
                          create tab with default profile
                      end if
                      tell current session to write text q
                  end tell
              end tell
          end run
      " with parameters {q}
    else
        run script "
          on run {q}
              tell application \":Applications:iTerm.app\"
                  activate
                  try
                      select first window
                  on error
                      create window with default profile
                      select first window
                  end try
                  tell the first window
                      tell current session to write text q
                  end tell
              end tell
          end run
      " with parameters {q}
    end if
end alfred_script

これも、Alfred呼び出し -> "nit"とか入力 -> Enter でiTerm2が新規に起動します。
https://gist.github.com/reyjrar/1769355 を参考にしたのですが、zshを利用していたので書き換えました。

アイコンとかサクッと作れれば、フツーに公開するんだけどな。。。

KFCocoaPodsPlugin をインストールしてみる

前に iOSアプリ開発が捗るXcodeプラグイン - Qiita [キータ]ricobeck/KFCocoaPodsPlugin · GitHub というプラグインが紹介されていました。

便利そうだなーと思いつつ、放置していたのですが、年末年始で少し時間が出来たので、導入してみました。

ただし、githubの説明が

Clone this repo or download the zip, run 'pod install' in the terminal, open the workspace file in Xcode and compile. After a restart of Xcode you have a 'CocoaPods' Menu Item in the Products menu.

しか無かったので、念のためメモ。
※cocoapodsはインストール済みの前提

手順

今回、 ~/bin に入れておくとします。

cd ~/bin
git clone git@github.com:ricobeck/KFCocoaPodsPlugin.git
cd KFCocoaPodsPlugin
pod install
open KFCocoaPodsPlugin.xcworkspace

Xcodeが開くので、Product -> Buildとし、Xcodeを一旦完全に終了させ、再度起動。

すると、公式のgithubにあるように、Productメニュー無いにCocoaPodsが表示されます。
これで入力補完も効くし、黒い画面が無くてもpod install が出来ます。

SeleniumをRubyから呼んでみる

Gemfileを作って編集する。

bundle init
vim Gemfile

入力内容は下記の通り。

source "https://rubygems.org"
gem "selenium-webdriver", "‾> 2.38.0” 

bundle installを実行する。

bundle install --path vendor/bundle 

rubyスクリプトファイルを作成する。

vim drive.rb

内容は下記の通り。

require 'rubygems'
require 'selenium-webdriver'

driver = Selenium::WebDriver.for :firefox
driver.get "http://google.com"

element = driver.find_element :name => "q"
element.send_keys "Cheese!"
element.submit

puts "Page title is #{driver.title}"

wait = Selenium::WebDriver::Wait.new(:timeout => 10)
wait.until { driver.title.downcase.start_with? "cheese!" }

puts "Page title is #{driver.title}"
driver.quit 

実行してみる。

ruby drive.rb

怒られる。selenium-webdriverが読み込めない模様。

`require': cannot load such file -- selenium-webdriver (LoadError)

bundle exec で実行してみる。

bundle exec ruby drive.rb

怒られる。firefoxがインストールされてないから?

Could not find Firefox binary (os=macosx). Make sure Firefox is installed or set the path manually with Selenium::WebDriver::Firefox::Binary.path= (Selenium::WebDriver::Error::WebDriverError)

chromeに変えてみて、再実行。やっぱり怒られる。chromedriverが無いとダメらしい。

Unable to find the chromedriver executable. Please download the server from http://chromedriver.storage.googleapis.com/index.html and place it somewhere on your PATH. More info at http://code.google.com/p/selenium/wiki/ChromeDriver. (Selenium::WebDriver::Error::WebDriverError)

言われたとおり、http://chromedriver.storage.googleapis.com/index.html をブラウザで開いて、最新版(2.7)のmac用をダウンロード。 解凍すると、 chromedriver というファイルが出てくるので、PATHが通っているディレクトリに移動。

で、再実行。

bundle exec ruby drive.rb

無事ブラウザが開き、googleの検索結果が表示され、消えた。 コンソールには

Page title is Google
Page title is Cheese! - Google 検索

が表示されてた。

・公式のサンプルコードなど
http://docs.seleniumhq.org/docs/03_webdriver.jsp
・参考になりそうなところ
http://qiita.com/tomerun/items/9cb81d7a98150ff22f53
http://morizyun.github.io/blog/selenium-scraping-webdriver-ruby/

doubleのまま計算を続けると、計算誤差が増えるって話

doubleでの計算は、計算誤差が生まれるよ、っていう話。

a の値が正確に"0.9"ではないため、どちらにせよ誤差が生まれるけどね。

コードレビューしてて、doubleだけで計算してるのがあった。。。
小数点を含む計算って、BigDecimalを使うのが当たり前じゃないのか・・・?

iOS用ウェブアプリで出来ないこと

iOS上で動作するサービスを作成しようと思った時、下記のような選択肢があるかと思います。

  • ネイティブアプリ(Objective-C で作るようなもの)
  • ブラウザアプリ(HTML / JavaScript で作るようなもの)

それ以外にも、それらを組み合わせた

  • ハイブリッドアプリ(Objective-C で作った中に、HTML / JavaScriptで作った画面を入れるようなもの)

もありますね。

今回、ブラウザアプリ(Webアプリ?)を作った際に、ネイティブだったら出来るのに、と思ったことを中心にまとめてみます。 (Androidでも、大体同じことが言えるかもしれません)

PUSH通知

ユーザの呼び戻しには、PUSH通知が有効化と思いますが、ブラウザアプリでは実現できない。

  • メールアドレスを取得し、それを利用する
  • SNSを活用する

あたりで、うまくユーザを呼び戻す必要がある。

クリップボードの利用

PCブラウザでは、「クリップボードにコピーする」ボタンを見たことがあるかと。
これは Flash を利用しており、iOSでは動作しない。
input タグを用意しておき、そこをタップした時に、全選択→iOS標準の「コピー」が表示されるという風にする、というのが妥協案かと。

消えない(消せない)情報を保存する

ネイティブアプリなら、KeyChain が使える。これに入れておけば、アンインストールしても消えない。(はず
ブラウザアプリの場合、データの保存場所としては、Cookie, localstorage などがあるが、 「Cookieとデータを消去」で簡単に消せる。
消されて困る情報は、サーバに保存することになるが、その情報と付き合わせるために、ユーザ認証が必要になる。

特定の環境下で動作させる(Safariだけで動作させる)

ブラウザアプリの場合、Safari 以外で動作する可能性がある。
例えば、Chrome, Operaといったブラウザアプリ、Facebook, TwitterといったSNSアプリなど。
そのへんを、User Agent である程度判定できるが、その後の操作はユーザ任せになる。
そもそも、アプリ側で提供されていないと、Safariで表示させることは不可能になる。

受託の場合は、どこまで対応するか・どのように対応するかなどを、最初に決めておかないともめるかも。

フルスクリーン表示

可視領域を広げる、没入感を与えるといった効果がある、フルスクリーン表示ですが、ユーザに強制することは出来ない。
meta タグなどを利用し、「ホーム画面に追加」を行ってもらい、そこから起動してもらえば、可能。
http://mawatari.jp/archives/web-apps-like-native-apps

アプリがインストールされているかを確認(2013/12/3 追加

アプリがインストールされていたら、アプリ起動ボタンを表示し、
アプリがインストールされていなかったら、アプリダウンロードボタンを表示する。
みたいなことは出来ない。

http://havelog.ayumusato.com/develop/javascript/e564-url-scheme-fallback.html こちらにあるように、URLスキームで起動してみて、画面が切り替わって無ければ、ストアへ飛ばす。という動きなら出来る模様。
ただし、見た目はあんまり綺麗には出来ないみたい。(標準のアラートが出てしまったり

Webとネイティブを提供していて、ネイティブも紹介したいだけなら、App Bannerを使えばいいと思います。 https://developer.apple.com/library/ios/documentation/AppleApplications/Reference/SafariWebContent/PromotingAppswithAppBanners/PromotingAppswithAppBanners.html

まとめ

いろいろ制約はありますが、Objective-Cエンジニアを必要としないのは、人的リソースの確保はしやすいかも。
ただし、ちゃんとした HTML / JavaScript を書けるエンジニアを探すのも難しいかも。(有象無象が多いイメージ

appleへの申請を必要とせず、好きなタイミングで、全てのユーザに同じ動作環境を提供出来るのは、魅力的だと思います。

他にも、思い出したら・気がついたら追加しようと思います。

第四回 #渋谷java に参加しました

参加してきました。(スポット参戦中のプロジェクトから抜けて)

まとめ

  • Grails 使ってみるのはありかも
  • groovy 触ってみようかな(とりあえずテストからかな?)
    • そのときはSpock を使ってみよう
  • Gradle もそろそろ使いたいなー
  • JUnitにおけるオブジェクトの等価比較って、他の人も悩んでたんだなー
    • コード補完で頑張る以外にも、がんばれば方法はあるんだなー(JSONPath で除外はありかも)
  • Servlet のURLって、きれーに出来ないのかなーって思ってたけど、UrlRewriteFilter を使えばよかったのか
  • Reactive Extensions って、うまく使えれば便利そうだなー
    • ただし、Java8のラムダを使わないと記述量が増えそう
  • 並行処理とか、真面目に学ばないとなー
  • テンプレートエンジンって、色んなのがあるんだなー
    • Mayaa につづき、Thymeleaf ってのもあるんやなー
      • 個人的には、Java, JS 両方で出来るらしい、Mustache(Handlebars?) を、まずは使ってみたいけど
  • Juzu Web Framework は、、、まぁ、いいか。

感想

  • こういう、色んな技術を知れる場はいいですね〜
  • 仕事が無ければ、もうちょっと他の参加者と話してみたかったけど、残念

雑多なメモ


http://www.slideshare.net/kazuhiroserizawa988/shibuya-java4op

・_y_u_さん Grailsのちょっとイイ話
http://www.slideshare.net/sudoyu/20131116-shibuyajava-grailsgoodness


 NTTソフトウェアの Grails推進室の方

JGGUGの方

Grails
・WebアプリケーションフレームワークRuby on RailsのJavaVMで動く奴てきな
・XML設定不要
 Groovyのコードで書ける

イイところ

Javaの世界と地続き

JVM環境から逃げたいって人いないですよねー。

中身(一部)
Spring Framework
Hibernate
Apache Tomcat Embed 7
Apache Ivy
H2 Database

ほとんどのServletコンテナで動く

性能
http://www.techempower.com/benchmarks/
Scala/Lift, Play、Ruby/Rails より速い!


Javaの資産の活用

・Grailsアプリはただのwar
 いままでの運用ノウハウが活かせる
・Spring / Hibernate の経験が活かせる
 学習コストが抑えられる

Groovyで書ける

クロージャ
メタプログラミング / MOP
AST変換
 DSLを簡単に作れる
Groovy-JDK
 演算子オーバーロード
チェック例外のチェック省略

本:プログラミングGROOVY


Spockでテスト

JGGUGのワークショップ資料をご覧ください

GVM
 rbenv
 Goとかぶってる・・・
Grails wrapper
 Gradle wrapper チェックアウト→コマンドでインストールが不要
Grails console
dbconsole
 DBの中身をWebから見れる
プラグイン機構
 Security coreとか(View, Controller, Serviceとかがはいる)
Serviceとトランザクション
 勝手にトランザクション


デメリットいところ

Checkstyle / Findbugsが使えない
 CodeNarcを使うことになる
パートナー探し
 Spring / Hibernateの経験があれば!
メモリフットプリントが大きい
 PaaS利用では大きなデメリット

ログ出力
 log4j内蔵
スタックトレース
 development モードで画面に出てくる
マルチデータソース
 デフォルトで対応
NoSQL
 プラグインで対応
Hibernateを使いたくない / SQLを生で書きたくない
 HibernateのネイティブSQL API
 Groovy SQLを使う(キャッシュ管理?が必要)
 MyBatis
Grailsプラグインの一覧
 list-pluginsコマンド
Grailsに対応しているIDE
 IntelliJ IDEA が一番いい
Struts1からの移行
 変態プラグインがある
Grailsの書籍
 2.x系に対応した日本語の書籍は無い
 本家のドキュメント


実際の案件で20件ぐらいやってる
ものすごい大規模システムで使ったよ、というのはまだない




jfluteさん MySQLJDBCドライバに振り回されるDBFlute

フリープログラマーさん
現場での教育や、フレームワークの拡張とかやってます

中がGroovyで出せる
0x1f004


MySQLさん
なぜあなたは、バッチ更新しないの?
for文で回っとる
別マシンへの通信コストとかあるから、ちゃんと考えようねって教えてたのに。。。
まじでー

MySQLさん
なぜあなたは、 カーソルフェッチしないの?
数十万件ぐらいだったら、メモリ上に全部乗っかる
本番運用に入って、大量の会員を抱えたあとに発生する(結合テストとかではみつからない)
fetchSize に Integer.MIN_VALUE を設定すると、1件ずつの取得になる
 MIN_VALUEって、、、
い、Integer.MIN_VALUE技!?
closeしないから、新しいSQLを発行出来ない。。。

sql_calc_found_rows
limitが無かった時の件数が取得できる
→MySQLちょっとやるじゃn



grimroseさん Gradleについて
https://github.com/grimrose/shibuya-java-04

Gradleって何?

現在 version 1.8
世界で一番イケてるビルドツール

本:WEB+DB PRESS val.76
 英語ならオライリーにある
Gradle 日本語ドキュメント
 実験的なところは追いついてないところもある


AntからGradle

build.xmlのインポート
17章に書いてある

project: "Ant + Ivy”


MavenからGradle

project: “maven”
ひな形プロジェクト 1.9からコマンドが変わってる
 pom.xmlから生成可能
maven plugin→使えません

コスト と メリット
mavenのレールから外れてる
pom.xmlがツライ
pluginをそんなに使っていない
使いたいpluginがみつからない
→上記に当てはまるひとは gradleへ

“mvn …”.execute()


プロジェクトに柔軟性を
・gradle wrapper
・custom task or plugin
・task test
・groovy





komiya_atsushiさん Junitのオブジェクト透過比較を怠けたい
https://github.com/komiya-atsushi/shibuya-java4

代々木の緑色の会社(渋谷じゃない)

#TokyoWebmining 事務局


「あるメソッドから返却されるオブジェクトの内容が想定しているとおりか?」をテスト

Dto
  List
    BookDto

アプローチ
・assertThat() をひたすら並べる
 →つらい
・equals() を実装する
 →普通にやるのはつらい、Commonsつかう?
・専用のMatcherを実装する
 →テストコード量が増える

汎用的な透過比較 Matcher を実装してみた
komiya-atsushi/shibuya-java4

IsEquivalentTo の概要&機能
・データ構造を再帰的にたどる
・equalsをオーバライド
・Json-path的な記法で
・比較的親切(?)なメッセージ


他の人の意見としては、
・コード補完でがんばる
・toStringでわりきっちゃう



kazurofさん UrlRewriteFilterに手を入れてみた
http://sssslide.com/www.slideshare.net/kazurof/urlrewritefilterissue120

外向きには綺麗なURLを書き換えるライブラリ

外部:/data/123/…
↓
内部:/data/get.do?id=123…

3系→4系
URL変換動作の${…}の
関数の連続はNG
ネストはOK

木構造で解釈
Interpreterパターン

実際に実装してみることが大事ですね



ngsw_taroさん RxJava使ってみた

夢と魔法の待ち時間
https://play.google.com/store/apps/details?id=com.taroid.parkwaittimegetter

自称Kotlinエバンジェリスト

Reactive Extensions のJava実装

LINQのデータソースをイベントと非同期処理に拡張したもの

・学習コスト若干高め
・ 書きやすい、読みやすい
・SAM無名クラス(Java8 早く、、、)



azusaさん 真にスレッドセーフなHashMapとは
http://www.slideshare.net/setoazusa/hash-map-28306457

そのクラスが使われるコンテキストに強く依存する

本:Java並行処理プログラミング

タイトルは釣り

ConcurrentHashMap使えばいいんじゃね?
新規なら、それ一択

無条件には置き換えられない
HashMap extends AbstractMap
ConcurrentHashMap extends AbstractMap

HashMap の値にnullは入る
ConcurrentHashMap の値に nullはNullPointerException

synchronizedMap を仕方ないので使う 
ループするときは呼び出し元で同期しないといけない

シリアライズは同期できない

ひっかかったケース
HttpSessionにHashMapを格納

スレッドごとにオブジェクトを分割すればいい


まとめ
コレクションAPIには地雷があります

スケールアウトの容易さという点では、railsが羨ましい

Java並行処理プログラミング を読みましょう 



eiryuさん Thymeleafのすすめ

TwFavViewの作者

Javaのテンプレートエンジン
たいむりーふ
最新は2.1系

ブラウザで見てもJSPのように崩れない

spring統合用のモジュールがある

属性にth:をつけるだけ
 data-xxxみたいなのはth:attr
マニュアルは短いから、全部読めばいい
サンプル:stfm

th:text
th:each
th:remove=“all-but-first"

JavaScript inlining
 URLとか、言語対応とか

JSP書いてた人なら問題なく書けるはず
 スクリプトレットもりもり使ってなければ

ブラウザで確認できることはかなり便利

デザイナさんとルール
XHTMLで記述する
JavaScriptで扱うためのclass名は、”js-“で始める
極力デザイナー作成部分は触らない。逆もまた然り。
 追加するイメージ

デメリット
th:ifの制御
ヘッダ、フッタなど、include はコピペしないといけない
Spring以外では、そんなに、、、




seri_k 謎のWAF Juzu FrameWorkを試してみたリベンジ

 あんまり使われてない?
play 1.x系に近い

ViewとController機能のみ薄いフレームワーク
template は groovy ベース
Annotationで機能や設定を指定

mavenでベースをサクッと作れる

Annotation Processing を利用する
factory pathにjuzu-core.jar 

更新を検知して、ファイルが消えちゃう。。。?
 cleanすると、再生成