「xcode-install」を使用してXcodeもRubyで管理する
「xcode-install」とは
少し前までGitHubのRubyカテゴリのトレンドにランクインしていて、
気になってはいたのですが導入してみたらすごく簡単にXcodeを管理することが出来ました。
今まで、Xcodeを複数バージョン管理しようと思うと、
ブラウザを立ち上げ、
開発者ポータルへログインし、
Xcodeを選び、
最新のXcodeが大きく表示され、
それは要らない!となり、
「View All」をクリック、やっと辿り着いた....
マウスいらーん!!!!! (ノ ゚Д゚)ノ ==== ┻━━┻
「xcode-install」を使用すると、
rbenvのように、バージョンの異なったXcodeを管理することができます。
つまり、コマンドベースで管理できるようになるのです。
導入
Gemなのでいつも通りです。
$ gem install xcode-install --no-ri --no-rdoc Fetching: multipart-post-2.0.0.gem (100%) Successfully installed multipart-post-2.0.0 Fetching: faraday-0.9.2.gem (100%) Successfully installed faraday-0.9.2 Fetching: faraday_middleware-0.10.0.gem (100%) Successfully installed faraday_middleware-0.10.0 Fetching: plist-3.1.0.gem (100%) Successfully installed plist-3.1.0 Fetching: multi_xml-0.5.5.gem (100%) Successfully installed multi_xml-0.5.5 Fetching: security-0.1.3.gem (100%) Successfully installed security-0.1.3 Fetching: colored-1.2.gem (100%) Successfully installed colored-1.2 Fetching: highline-1.7.8.gem (100%) Successfully installed highline-1.7.8 Fetching: credentials_manager-0.9.0.gem (100%) Successfully installed credentials_manager-0.9.0 Fetching: spaceship-0.3.4.gem (100%) Successfully installed spaceship-0.3.4 Fetching: claide-0.9.1.gem (100%) Successfully installed claide-0.9.1 Fetching: xcode-install-0.9.6.gem (100%) /Users/ryota/.rbenv/rbenv.d/exec/gem-rehash/rubygems_plugin.rb:6: warning: Insecure world writable dir /usr in PATH, mode 040777 Successfully installed xcode-install-0.9.6 12 gems installed
次に、ログイン情報を環境変数にexportします。
ここで値を設定しないと何も動作しません。
ファイルに記述した方は再読込も忘れずに。。
export XCODE_INSTALL_USER=YOUR_USER_VALUE export XCODE_INSTALL_PASSWORD=PASSWORD_VALUE
以上で導入完了です。rbenvを使用した方は必要時応じて、rehashをしておきましょう。
下記のようにヘルプが表示されたら大丈夫です。
$ xcode-select help Usage: $ xcode-install COMMAND Xcode installation manager. Commands: + cleanup Cleanup cached downloads. + install Install a specific version of Xcode. + installed List installed Xcodes. + list List Xcodes available for download. + select Select installed Xcode via `xcode-select`. + selected Show version number of currently selected Xcode. + uninstall Uninstall a specific version of Xcode. + update Update cached list of available Xcodes. Options: --version Show the version of the tool --verbose Show more debugging information --no-ansi Show output without ANSI codes --help Show help banner of specified command
Xcode 6.3.2(Objective-C) + Quick + Nimble + KIF がうまく動かなかったハナシ
いつもはSpectaでテストをすることが多いのですが、
ミーハーな私は、巷でじわじわきているQuickを導入してみたが、うまくいかなかったので記します。
環境はこちら
- Xcode 6.3.2 (訳あってObjective-C)
- KIF (3.3.0):
- KIF/Core (= 3.3.0)
- KIF/Core (3.3.0)
- Nimble (0.4.2)
- Quick (0.3.1)
QuickとNimbleを用いてモデルのユニットテストを記述していた際にはうまく動作していたのですが、
いざUIテストの記述をしていた際におかしな動きになりました
なにが起こったか?
「KIFを使用してテストを行った際にきちんとエラーがキャッチされず、テストが失敗したことにならない」
なにが原因か?
そもそもKIFはテストのフレームワークに依存せずしない作りになっており、エラーの発生はフレームワーク側へ移譲する実装をしています。
If you want to use KIF with a test runner that does not subclass XCTestCase, your runner class just needs to implement the KIFTestActorDelegate protocol which contains two required methods.
(void)failWithException:(NSException *)exception stopTest:(BOOL)stop;
(void)failWithExceptions:(NSArray *)exceptions stopTest:(BOOL)stop;In the first case, the test runner should log the exception and halt the test execution if stop is YES. In the second, the runner should log all the exceptions and halt the test execution if stop is YES. The exceptions take advantage of KIF's extensions to NSException that include the lineNumber and filename in the exception's userData to record the error's origin.
https://github.com/kif-framework/KIF#use-with-other-testing-frameworks
つまり、Quick側の以下のメソッドの中身が怪しい
(void)failWithException:(NSException *)exception stopTest:(BOOL)stop; (void)failWithExceptions:(NSArray *)exceptions stopTest:(BOOL)stop;
さっそくQuickのソースコードを読んでみる。
/** This method is used to record failures, whether they represent example expectations that were not met, or exceptions raised during test setup and teardown. By default, the failure will be reported as an XCTest failure, and the example will be highlighted in Xcode. */ - (void)recordFailureWithDescription:(NSString *)description inFile:(NSString *)filePath atLine:(NSUInteger)lineNumber expected:(BOOL)expected { if (self.example.isSharedExample) { filePath = self.example.callsite.file; lineNumber = self.example.callsite.line; } [super recordFailureWithDescription:description inFile:filePath atLine:lineNumber expected:expected]; }
テストを失敗させ、XCode上のファイルにハイライトをつける処理を実装している。
一見間違ってなさそうに見えるので、かなり混乱しましたが、
「recordFailureWithDescription:inFile:atLine:expected:」を呼び出すのはselfに対してではなく、現在実行されているテストのXCTestCaseのインスタンスというのが正解です。
使用しているv0.3.1より上のバージョンにコミットが積まれていました。
ただしこの修正が含まれているバージョンを使用するには、Xcode 7 / Swift 2.0 である必要があります。
つまり詰んだ...orz
解決法
カテゴリで拡張するしかない?
そもそも自分の使い方が悪いんじゃないか、導入の仕方がまずい?など色々悩みましたが、
Spectaでやっていた際には起きなかった事象なので無理やりカテゴリで解決しました。。
#import <Quick/QuickSpec.h> #import <Quick/Quick-Swift.h> #import <Quick/QuickConfiguration.h> #import <Quick/NSString+QCKSelectorName.h> #import <objc/runtime.h> @interface QuickSpec (WithObjC) @property (nonatomic, strong) XCTestRun *testRun; @property (nonatomic, strong) Example *example; @end @implementation QuickSpec (WithObjC) static QuickSpec *currentSpec = nil; @dynamic testRun, example; - (void)setTestRun:(XCTestRun *)testRun { objc_setAssociatedObject(self, _cmd, testRun, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (XCTestRun *)testRun { return objc_getAssociatedObject(self, @selector(setTestRun:)); } - (void)performTest:(XCTestRun *)run { self.testRun = run; [super performTest:run]; } + (SEL)addInstanceMethodForExample:(Example *)example { IMP implementation = imp_implementationWithBlock(^(QuickSpec *self){ currentSpec = self; [example run]; }); const char *types = [[NSString stringWithFormat:@"%s%s%s", @encode(id), @encode(id), @encode(SEL)] UTF8String]; SEL selector = NSSelectorFromString(example.name.qck_selectorName); class_addMethod(self, selector, implementation, types); return selector; } - (void)recordFailureWithDescription:(NSString *)description inFile:(NSString *)filePath atLine:(NSUInteger)lineNumber expected:(BOOL)expected { if (self.example.isSharedExample) { filePath = self.example.callsite.file; lineNumber = self.example.callsite.line; } [currentSpec.testRun recordFailureWithDescription:description inFile:filePath atLine:lineNumber expected:expected]; } - (void)failWithException:(NSException *)exception stopTest:(BOOL)stop { self.continueAfterFailure = !stop; NSException *copyException = [exception copy]; [self recordFailureWithDescription:copyException.description inFile:copyException.userInfo[@"SenTestFilenameKey"] atLine:[copyException.userInfo[@"SenTestLineNumberKey"] integerValue] expected:YES]; } - (void)failWithExceptions:(NSArray *)exceptions stopTest:(BOOL)stop { NSException *lastException = exceptions.lastObject; for (NSException *exception in exceptions) { [self failWithException:exception stopTest:(exception == lastException ? stop : NO)]; } } @end
多分(間違いなく)もっといい方法がある気がしますが、
発生していたエラーは解決し、KIFでテストが失敗した際にもうまく動作するようになりました。
しばらくこれで様子をみます。
最適な解決法があればツッコミをお願いします。
iOSアプリ【PictMemo】リリースしました。(してましたw)
明けましておめでとうございます。
20歳になりました、友村です。
題名の通り、iOSアプリをリリースしてました。
英語と、日本語でリリースしてます。
なぜ'してました'かというと、リリースしたのは少し前で、19歳のうちに何かを残したかったからです。
ただ、修正したい箇所があったので、修正して、今「AppStore」で出ているのはバージョン1.1.0です。
アプリの名前は【PictMemo】です。
PictMemoではないです。
【PictMemo】です。
俗に言う"隅付き括弧"というやつが付いてます!←重要!!
何をするアプリか、画像にメモを残すアプリです。
◯メモとは?
単純にキーボードで入力して残すメモと、声を録音して残すメモと、おおまかに分けて2つあります。
あと、上級者向けですが、画像というのは見えないデータ(メタデータ)というものを持ってます。
それは、位置情報だったり、カメラの機種だったり、フラッシュの情報だったり、..etc
実は画像一枚に色んな情報が隠されています。
PictMemoでは、画像の見えないデータにもメモを埋め込むことが出来ます。(PictMemo内で撮影した画像のみ)
また、画像のメタデータを分かりやすく表示してくれます(撮影地等のすべてのメタデータ)。
個人的には、この機能が結構お気に入りです。
他にも機能はありますが、紹介はこれくらいにするので、iOSユーザの方は是非使用してみてください。
現在、バージョン2.0.0を申請中で、更に使いやすくなってます。
【PictMemo】のテーマカラーは、「ミッドナイト・ブルー」という色で、流行りのフラットUIを作成するために推奨されている色の一部です。
下記のURLのサイトを参考にしました。
http://flatuicolors.com/
【PictMemo】公式サイトも作成しました。
Ruby製のソフトウェアであるnanocで、さくっと作成しました。
このアプリ開発で得たノウハウも後日まとめて紹介できたらいいなと思います。
以下、「AppStore」でも見れますが、アプリの紹介文です。
【PictMemo】 - 写真にメモを残して整理 -
【PictMemo】は"写真管理を楽にしてメモを残す"アプリです
■PictMemoはあなたがお持ちになっている写真に簡単にメモを残す事ができます
iPhoneに搭載されている写真アプリのように簡単に写真を選択して、メモを残す事ができます。
これはとても簡単で、後で一覧表示することが出来ます。■手書きメモだけでなく、ボイスメモを残せる
PictMemoでは手入力のメモだけでなく、ボイスメモを残す事ができます。
こちらもとても簡単でかつ、手入力を行う画面と同じ画面で行うことが出来ます。
ボイス録音ボタンを押せば、あなたは端末に向かって声を発するだけです。■お気に入り写真メモ機能
PictMemoで作成した画像メモに、お気に入りを登録することが出来ます。
登録はとても簡単で、一度登録してしまえば、後はお気に入り一覧表示機能から簡単に閲覧可能です。■見やすいメモ一覧画面
PictMemoで登録した画像メモは一覧表示で見る事ができます。
とてもシンプルで分かりやすく、ボイスメモを残した場合には音声の再生を行うことも出来ます。■充実した画像メモ共有機能
PictMemoで登録した画像メモは共有することが出来ます。
例えばメールであれば、メモを本文にして、画像を添付したメールを自動的に作成します。
後は、あなたが宛先を選んで、「送信」するだけです。
他にも、Twitter, Facebook, AirDrop等があります。もちろん共有した後にも画像メモは残るので、後でPictMemo内で見る事ができます。
■他のアプリで撮影した写真へのメモの書き込みもできる
PictMemoは、iPhoneのカメラロールを使用しますので、iPhoneに保存してある画像であればどの画像にもメモを残すことが出来ます。
■写真の撮影地・住所の確認が出来る
PictMemoは、画像に付属しているメタデータ(見えないデータ)を解析して、
撮影地の地図表示、住所表示などを行い表示してくれます。
その他、シャッタースピード、フラッシュ等の詳しい情報も閲覧可能です。■PictMemo内で写真を撮影することが出来る
もちろんPictMemo内で写真を撮影することも出来ます。
その場合はとてもスムーズに、メモ入力まで行うことが出来ます。
最初からメモを残す目的での撮影の場合には、PictMemoからの撮影を推奨します。
その場合にも、画像はカメラロールに保存するので、他の写真管理アプリかも参照することが出来ます。■写真にメモデータを埋め込むことが出来る
PictMemoでは、画像に埋め込まれているメタデータにメモを埋め込むことが出来ます。
◯上級者向け,PictMemo内で写真を撮影した場合のみ
◯本アプリケーションは、カメラロールに依存しているため、写真データを保存していません。標準の写真アプリで写真を削除すると、本アプリケーションでも閲覧することが出来なくなるので、ご注意ください。
ActiveRecordを継承しないモデルオブジェクトで検索機能の実装
こんにちは。今回はRailsについてです。
ActiveRecordを継承しないモデルオブジェクト
Railsを開発するうえで必須になってくるModelオブジェクト。
今回はActiveRecord::Baseを継承せずに、モデルと同じような動きをするRubyオブジェクトを作成して、検索機能を実装します。
作成したRubyオブジェクトにモデルと同じような動きを持たせるメリットとして、
- バリデーション機能が使える
- モデルオブジェクトとして振る舞うので、form_forの引数に渡せる
- バリデーション時のエラーメッセージを簡単に他言語対応できる
上記の機能を持たせることによって、大きくその恩恵をうけることが出来るのが、フォームなどを使用する、検索機能などです。
今回は、productsリソースに基づいて、検索機能を実装します。
productsテーブルのカラムは1つで、"name"属性、タイプは"string"です。
Objective-Cのカテゴリによる既存クラスの拡張とRubyのオープンクラスによる既存クラスの拡張
こんにちは。今回は題名通り、Objective-Cのカテゴリによる既存クラスの拡張とRubyのオープンクラスによる既存クラスの拡張のやり方を紹介します。
今回はObjective-CのNSStringクラスへ@"sample string"を返す、sampleというクラスメソッドと、
RubyのStringクラスへ'sample string'を返す、sampleというクラスメソッドを作成します。
まずObjective-cによる既存クラスの拡張です。
Objective-Cによる既存クラスの拡張
Objective-Cによる既存クラスの拡張を行うにはカテゴリという機能を使用します。
カテゴリとは、クラスの持つメソッドをカテゴリごとに分類するメソッドです。
この機能を使用することによって、メソッドが沢山ある大きなクラスを分かりやすく分類することが出来ます。
カテゴリの名前は自由につけることが出来ますが、用途によって分かりやすい名前がいいと思います。
今回は「Sample」というカテゴリ名にします。
まずiPhoneアプリの新しいプロジェクトを作成します。
Xcodeを起動して、「Create a new Xcode project」をクリックして作成します。
テンプレートは「empty project」でプロジェクト名は「SampleCategory」にします。
次に、フォルダツリーの上で、右クリックして、「New File」-> 「Objective-C class」を選択します。
その次に下記の画面で、classに「Sample」、subclass ofに「NSString」を選択します。
後で修正するので、実際のところなんでもいいです。
ファイルを作成したら 、
「Sample.h」を「NSString+Sample.h」に、
「Sample.m」を「NSString+Sample.m」にリネームします。
ファイルの名前はなんでもいいのですが慣習的に、「拡張するクラス名+カテゴリ名」とするのが一般的のようです。
この2つのファイルにカテゴリ拡張のコードを記述します。
まず、NSString+Sample.hを下記のようにします。
・NSString+Sample.h
#import <Foundation/Foundation.h> @interface NSString (Sample) //拡張するクラス名(カテゴリ名) +(NSString*) sample; //作成するクラスメソッドの宣言 @end
ヘッダファイルの実装はこれで終わりです。
次に、NSString+Sample.mを下記のように実装します。
・NSString+Sample.m
#import "NSString+Sample.h" @implementation NSString (Sample) +(NSString*)sample{ return @"sample string"; } @end
これで実装は終了です。
最後に結果を確認するにAppDelegate.mにメソッドの返り値をアラート表示するプログラムをつけたします。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; // Override point for customization after application launch. self.window.backgroundColor = [UIColor whiteColor]; [self.window makeKeyAndVisible]; // ここからアラート表示プログラムの追加 UIAlertView *alert =[[UIAlertView alloc] initWithTitle: @"タイトル" message: [NSString sample] // 拡張したメソッドを実行 delegate:nil cancelButtonTitle:nil otherButtonTitles:@"OK", nil ]; [alert show]; //ここまで return YES; }
まぁ、結果が確認できればOKなので、NSLogでもいいんですけどね。
実行結果が下記のようになります。
結果が確認出来ました。
注意点とては、クラスのインスタンス変数は追加できません。
以上が、Objective-Cのカテゴリによる既存クラスの拡張です。
非常に強力で、魅力的な機能です。
Rubyのオープンクラスによる既存クラスの拡張
Rubyのオープンクラスによる既存クラスの拡張は、Objective-Cのカテゴリによる既存クラスの拡張に比べ、
非常に簡単に実装できます。(個人的に)
まずstring.rbというファイルを作成し、下記の実装をします。
今回は、メソッドの定義と同じファイルに結果を出力するコードを書きます。
class String def self.sample 'sample string' end end puts String.sample
実装はこれで終わります。
これで、既存のStringというクラスにsampleというクラスメソッドが追加できました。
最後に実行して、
$ ruby string.rb
#=> sample string
という結果になればOKです。
最後に
以上がObjective-CとRubyによる、既存クラス拡張例です。
非常に強力ですが、使い所や、間違った使い方をしてしまうと、解決しにくいバグを作ってしまう可能性があるので注意して下さい。
とはいえ、開発をしていればこういったことをしたいと思う時が必ずくると思うので、
頭の隅にでも記憶しておいてもらえれば幸いです。
今回の作成したサンプルのソースです。GitHubで確認できます。
- Objective-Cのカテゴリによる既存クラスの拡張
https://github.com/tomomura/objective-c-sample-category
- Rubyのオープンクラスによる既存クラスの拡張