Ruby on Railsの開発環境構築
Using Vagrant for Rails Development - GoRails のを参考に実施
VagrantとVirtualBoxをインストール
Downloads – Oracle VM VirtualBox
$ vagrant plugin install vagrant-vbguest $ vagrant plugin install vagrant-librarian-chef
$ cd MY_RAILS_PROJECT # Change this to your Rails project directory $ vagrant init $ touch Cheffile
site "http://community.opscode.com/api/v1" cookbook 'apt' cookbook 'build-essential' cookbook 'mysql' cookbook 'ruby_build' cookbook 'nodejs', git: 'https://github.com/mdxp/nodejs-cookbook' cookbook 'rbenv', git: 'https://github.com/fnichol/chef-rbenv' cookbook 'vim'
# -*- mode: ruby -*- # vi: set ft=ruby : VAGRANTFILE_API_VERSION = "2" Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| # Use Ubuntu 14.04 Trusty Tahr 64-bit as our operating system config.vm.box = "ubuntu/trusty64" # Configurate the virtual machine to use 2GB of RAM config.vm.provider :virtualbox do |vb| vb.customize ["modifyvm", :id, "--memory", "2048"] end # Forward the Rails server default port to the host config.vm.network :forwarded_port, guest: 3000, host: 3000 # Use Chef Solo to provision our virtual machine config.vm.provision :chef_solo do |chef| chef.cookbooks_path = ["cookbooks", "site-cookbooks"] chef.add_recipe "apt" chef.add_recipe "nodejs" chef.add_recipe "ruby_build" chef.add_recipe "rbenv::user" chef.add_recipe "rbenv::vagrant" chef.add_recipe "vim" chef.add_recipe "mysql::server" chef.add_recipe "mysql::client" # Install Ruby 2.1.2 and Bundler # Set an empty root password for MySQL to make things simple chef.json = { rbenv: { user_installs: [{ user: 'vagrant', rubies: ["2.1.2"], global: "2.1.2", gems: { "2.1.2" => [ { name: "bundler" }, { name: "rails", version: "4.1.6" } ] } }] }, mysql: { server_root_password: '' } } end end
以下エラー対応
==> default: could not find recipe server for cookbook mysql ==> default: ==> default: [2015-04-04T10:18:45+00:00] ERROR: Running exception handlers ==> default: [2015-04-04T10:18:45+00:00] ERROR: Exception handlers complete ==> default: [2015-04-04T10:18:45+00:00] FATAL: Stacktrace dumped to /var/chef/cache/chef-stacktrace.out ==> default: [2015-04-04T10:18:45+00:00] ERROR: could not find recipe server for cookbook mysql ==> default: [2015-04-04T10:18:46+00:00] FATAL: Chef::Exceptions::ChildConvergeError: Chef run process exited unsuccessfully (exit code 1) Chef never successfully completed! Any errors should be visible in the output above. Please fix your recipes so that they properly complete.
Cheffileを以下のように修正
cookbook 'mysql','5.6'
$ vagrant provision
==> default: [2015-04-04T10:48:42+00:00] INFO: Running queued delayed notifications before re-raising exception ==> default: [2015-04-04T10:48:42+00:00] ERROR: Running exception handlers ==> default: [2015-04-04T10:48:42+00:00] ERROR: Exception handlers complete ==> default: [2015-04-04T10:48:42+00:00] FATAL: Stacktrace dumped to /var/chef/cache/chef-stacktrace.out ==> default: [2015-04-04T10:48:42+00:00] ERROR: rbenv_gem[2.1.2::bundler (vagrant)] (vagrant) (rbenv::user line 63) had an error: NoMethodError: undefined method `rbenv_rehash' for #<Chef::Provider::Package::RbenvRubygems:0x00000003e77ec8> ==> default: [2015-04-04T10:48:43+00:00] FATAL: Chef::Exceptions::ChildConvergeError: Chef run process exited unsuccessfully (exit code 1) Chef never successfully completed! Any errors should be visible in the output above. Please fix your recipes so that they properly complete.
cookbooks/rbenv/libraries/chef_provider_package_rbenvrubygems.rbを
def rehash rbenv_rehash new_resource do root_path rbenv_root user rbenv_user if rbenv_user action :nothing end.run_action(:run) end
以下に編集
def rehash e = ::Chef::Resource::RbenvRehash.new(new_resource, @run_context) e.root_path rbenv_root e.user rbenv_user if rbenv_user e.action :nothing e.run_action(:run) end
$ vagrant provision
以上でできたわけですが、chef-rbenvのリポジトリは一年以上放置されていてるようだったので、forkして修正を取り込みました
cocos2d-js 3.xでJS_CallFunctionでcrash
cocos2d-jsを2系から3系に変更した際にjavascriptのコールバックを呼ぶとexec bad accessでcrashするようになりました。
解決方法はこちらで JS_CallFunctionName crashed on iAP finished callback - Cocos2d-x Forum
JSB_AUTOCOMPARTMENT_WITH_GLOBAL_OBJCET
をJS_CallFunctionする前に呼べばよいです。
all asynchronous function call have this problem
と書いてあったのでC++から非同期でjavascriptをたたく場合にはJSB_AUTOCOMPARTMENT_WITH_GLOBAL_OBJECTをつかう必要がありそう
Objective-Cでnil結合演算子的なことをするには
Swiftだとnil結合演算子として"??"が用意されてますが、 Objective-C(というかC)でも似たようなことができます。
ここ(Conditionals - Using the GNU Compiler Collection (GCC))に書いてあるように3項演算子の真ん中を省略すると、条件式が非0の場合に条件式の値になるので
x ? x : y
と
x ?: y
は同じになります。(厳密にはxの評価される回数が異なりますが)
なのでObjective-C的にnilだったらデフォルト値をつっこむみたいな処理を書くと
NSString* outputString = (inputString != nil) ? inputString : @"default";
を
NSString* outputString = inputString ?: @"default";
このように短く書くことができます
CIのサービスいろいろ
build.gradleからAndroidのバージョンベタ書きを避ける
Androidでgradleを使うと、デフォルトではこんな感じにバージョン情報がベタ書きで生成されます。
apply plugin: 'android' android { compileSdkVersion 17 buildToolsVersion "21.1.2" defaultConfig { minSdkVersion 8 targetSdkVersion 17 } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' } }
build.gradle一つですむプロジェクトであれば、これでもよいかもですが、マルチプロジェクトにしている場合だと変更する場合にたくさん書き換えることとなりミスの元となります。
そこで以下のようにgradle.propertiesに切り分けておきます。
gradle.properties
ANDROID_BUILD_MIN_SDK_VERSION=9 ANDROID_BUILD_TARGET_SDK_VERSION=21 ANDROID_BUILD_TOOLS_VERSION=21.1.2 ANDROID_BUILD_SDK_VERSION=21
build.gradle
android { compileSdkVersion Integer.parseInt(project.ANDROID_BUILD_SDK_VERSION) buildToolsVersion project.ANDROID_BUILD_TOOLS_VERSION defaultConfig { minSdkVersion Integer.parseInt(project.ANDROID_BUILD_MIN_SDK_VERSION) targetSdkVersion Integer.parseInt(project.ANDROID_BUILD_TARGET_SDK_VERSION) }
参考
https://github.com/facebook/facebook-android-sdk/blob/master/gradle.properties
https://github.com/facebook/facebook-android-sdk/blob/master/facebook/build.gradle
AndroidStudioでプロジェクトを開こうとするとCause: org/codehaus/groovy/runtime/typehandling/ShortTypeHandling
AndroidStudioでbuild.gradleを指定してプロジェクトを開こうとすると
Cause: org/codehaus/groovy/runtime/typehandling/ShortTypeHandling
というダイアログが表示されてプロジェクトが開かない・・・ という状態になりました。
gradle-wrapper.propertiesのgradleのバージョンが古い(1.x.xとか)とこうなるようで
gradle/wrapper/gradle-wrapper.propertiesを以下のように2.2.1にすると解決
distributionUrl=http\://services.gradle.org/distributions/gradle-2.2.1-all.zip
Android NDKでerror: 'to_string' is not a member of 'std'
androidでC++のstd::to_stringを使うと以下のようなビルドエラーがでました。(環境:Android NDK r10d)
error: 'to_string' is not a member of 'std'
もちろんC++11の設定自体はできていて、他のstd::functionなどは問題なく使えます。
LOCAL_CFLAGS := -std=c++11
NDKのlibstdc++ではまだto_stringをサポートしてない?ぽいので自前で用意するしかなさそうな
template <typename T> std::string to_string(T value) { std::ostringstream os ; os << value ; return os.str() ; }
APP_STL を gnustl_staticではなく、stlport_staticにするとto_string使うことができましたが、C++ exception supportがない模様
The Developer's Guide | Android Developers
参考
Swiftで引数リスト(CVaListPointer)の渡し方
SwiftからObjective-Cのメソッドを呼ぶ際に、引数が引数リスト(va_list)の場合、Swiftでは型が以下のようなCVaListPointerになります。
class func raise(_ name: String, format format: String, arguments argList: CVaListPointer)
結論からいきますと、getVaListという関数を使って、getVaListの引数に引数リストとして渡したいArrayを突っ込めばよいです。
NSException.raise("hoge exception", format:"exception raised %d", arguments:getVaList([1]))
以下脱線
CVaListPointerの定義を調べてみるとこのようになるほどわからん感じの構造体です。
/// The corresponding Swift type to `va_list` in imported C APIs. struct CVaListPointer { init(_fromUnsafeMutablePointer from: UnsafeMutablePointer<Void>) }
CVaListPointerを生成するためのメソッドが用意されていてこれを使う。
/// Returns a `CVaListPointer` built from `args` that's backed by /// autoreleased storage. /// /// .. Warning:: This function is best avoided in favor of /// `withVaList`, but occasionally (i.e. in a `class` initializer) you /// may find that the language rules don't allow you to use /// `withVaList` as intended. func getVaList(args: [CVarArgType]) -> CVaListPointer
CVarArgTypeはprotocol
protocol CVarArgType { /// Transform `self` into a series of machine words that can be /// appropriately interpreted by C varargs func encode() -> [Word] }
ということからgetVaListの引数には、CVarArgTypeプロトコルを実装しているクラスのArrayということになります。
Intなどの型はCVarArgTypeをextensionで実装してるので使えます。
extension Int : CVarArgType { /// Transform `self` into a series of machine words that can be /// appropriately interpreted by C varargs func encode() -> [Word] }
調べた感じだとStringは一見CVarArgTypeを実装していないように見えるんですが、ArrayにString入れても使えるのはなんでかな・・
xcodebuildでdevice向けのビルドするときのdestinationの指定方法
以前xcodebuildのパラメータの指定方法についてこちらに書きました。
Travis CIでObjective-C/Swiftのテストを実行する - Pragmatic ball boy
テストするときはシミュレータを指定するのでこれで問題ないのですが、 実機向けのstaticライブラリを用意したりするときには、destinationの指定方法が以下のように変わります。
- -destination(必須)
ここで問題なのが、nameとidのところなんですが、ビルドマシンに実機を常に指しておけば指定できますが、たいていはそういう使い方しないと思います。
XcodeからビルドするときはiOS Deviceを指定すれば実機なくてもビルドだけはできるのでそれと同じことはxcodebuildでもできるはず。
xcodebuildのドキュメントを見るとgeneric/をつければ良いと書いてあります。
To build against a platform generically instead of a specific device, the destination specifier may be prefixed with the optional string "generic/", indicating that the platform should be targeted generically.
よって、以下のようにgeneric/platform=iOSと指定することでiOS Deviceを指定するのと同じようにビルドすることができます。
$ xcodebuild -scheme MyScheme -destination 'generic/platform=iOS' build
ちなみに実機とsimulator両方で使えるfat binaryを作りたい場合は、destinationは複数指定できるので以下のように、2つdestinationを使います。
$ xcodebuild -scheme MyScheme -destination 'generic/platform=iOS' -destination 'platform=iOS Simulator,name=iPhone 6,0S=8.0' build
fat binaryに含まれているアーキテクチャを確認するには以下のコマンドを使えばよいです。
$ xcrun lipo -info *.a
FragmentでUnfortunately, XXX has stoppedでクラッシュする
Android StudioのNew->Fragmentで追加したFragmentを使って表示しようとすると
"Unfortunately, XXX has stopped"
というポップアップが出てクラッシュするという問題が発生。 Logにも何も出ていない・・・
どこでクラッシュしてるのかさっぱりわからなかったので、仕方なく追加したFragmentにブレークポイントをはりまくって確認したところ、onAttachを最後にお亡くなりになっていた。
@Override public void onAttach(Activity activity) { super.onAttach(activity); try { mListener = (OnFragmentInteractionListener) activity; } catch (ClassCastException e) { throw new ClassCastException(activity.toString() + " must implement OnFragmentInteractionListener"); } }
Fragmentを呼ぶActivity側をOnFragmentInteractionListener interfaceに対応させないとダメのようだ。
よって以下のようにActivityにimplements XXXFragment.OnFragmentInteractionListener を追加し、onFragmentInteractionをオーバーライドすればOK。
public class XXXActivity extends ActionBarActivity implements XXXFragment.OnFragmentInteractionListener { /// 省略 @Override public void onFragmentInteraction(Uri uri) { }
よく見たら作成されたFragmentに"Activities that contain this fragment must implement the XXXFragment.OnFragmentInteractionListener" て書いてありましたね・・、よく読めってことか
/** * A simple {@link Fragment} subclass. * Activities that contain this fragment must implement the * {@link XXXFragment.OnFragmentInteractionListener} interface * to handle interaction events. * Use the {@link XXXFragment#newInstance} factory method to * create an instance of this fragment. */ public class XXXFragment extends Fragment {
追記:
Fragmentを追加する際に、optionで"Include interface callbacks?"にデフォルトでチェックが入っているため、今回のようになったようでした。
iOS保守するときに年に一回くらい使うwikipediaのリンク
32bit/64bit混成バイナリをiOS5.1.1端末にインストールできない
CocoaTouch 64ビット移行ガイド より、iOS5.1.1以降で32bit/64bitの混成バイナリが利用可能と書いてあります。
しかし、ipaファイルを作ってiOS5.1.1にインストールしようとすると失敗してしまいます。
AppleのDeveloper forumに情報があったのすが、 https://devforums.apple.com/message/1071370#1071370
iOS5.1.1では64bitの入った混成バイナリのインストールが不可能で、古いデバイスに対応するためにどうやらApple側でAppStoreにサブミットされたバイナリをiOS5.1.1用に64bitバイナリを含めないように加工してるのでは、、みたいな憶測が書かれていました。
実際AppStoreからダウンロードしたらインストールできたみたいなことは書いてあったので混成バイナリであってもiOS5.1.1で動作はするっぽいですが、事前に確認できないのはちょっと気持ち悪いですね。。
なので、どうしてもiOS5.1.1で動作確認したい場合は、iOS5.1.1の評価用にだけ32bitだけでビルドするように変更しなければならないということになります。
Genymotionのインストール
手元にAndroidデバイスがないけどちょっとエミュレーターで動かしたい場合に、通常のエミュレーターだと遅すぎて使い物にならないのでGenymotionを利用しています。
インストールした時の手順のメモです。
Genymotionのダウンロードとインストール
ここからダウンロード(要Login)
ダウンロードしたdmgファイルを開き、GenymotionとGenymotion ShellをApplicationフォルダにドラックアンドドロップ
VertualBoxのダウンロードとインストール
ここからダウンロード
Downloads – Oracle VM VirtualBox
ダウンロードしたdmgファイルを開き、VirtualBox.pkgをダブルクリックするとインストーラーが立ち上がるのでポチポチ押していけばインストールが完了します。
Genymotionの起動
ApplicationフォルダのGenymotionを開きます。
"You do not have any virtual device yet. Do you want to add a new one?" とダイアログが出るのでYesにします。
virtual deviceが何もない状態なので、下のピンクの枠をのSign inクリックしてログインします。
ログインすると下のようにvirtual deviceが取得され利用できるようになります。
あとは使いたいdeviceを選んで、nextを押していくとOK
そうするとvirtual deviceが作成されます
上のスタートボタンを押すと起動します
一瞬で起動するのでデフォルトのエミュレータとの差は雲泥の差ですね
参考
ユーザーガイド Genymotion
SwiftでUnitTestするときに"Use of undeclared type"になる場合の対処法
テストを実行するとこういうエラーがでることがあると思います。
Use of undeclared type 'Todo'
does not have a member named 'todo'
import XCTest import TodoApp class TodoTests: XCTestCase { var todo: Todo! override func setUp() { super.setUp() todo = Todo() }
たまに解決方法として全部publicにするとかいうのを見かけるのですが、それは間違いです。もしそうだとするとpublicなクラスしかテストできないってことになるので普通に考えたら明らかにおかしいですよね。。
ここから解決方法です。
方法その1
Swift1.x系での対処方法です。Swift2.x系でも使えます。
問題は、Test Targetの設定にあります。 ProjectのUnitTest用のTargetを選択し、Build Phasesのタブを選択します。
エラーがでる場合は、Compile Sourcesにテスト対象のクラスが追加されていないと思われます。
そこでこのようにテスト対象のクラスを追加してやればエラーは消えるはずです。
クラスをプロジェクトに追加される際に、どのTargetに対して追加するか聞かれるダイアログがでると思いますが、その際にテストTargetも対象にいれるのを忘れないように気をつけるとこういったことは防げます。
方法その2
Swift2.xから使える方法です。
importの前に@testableをつければよいです。
import XCTest @testable import TodoApp
RealmなどTestTargetでテスト対象のクラス一緒にコンパイルできない場合はこの方法でやるしかないです。