AngularJSのプロジェクトを共有する
前回の記事で作成したAngularJSのプロジェクトの共有方法について。
デフォルトで.gitignoreがついてくるので、そのまま適当なgitサーバーに追加。 そのプロジェクトをくろーんしてきた人のやることについて。
npm install ... ... bower install ... ... grunt ... ...
これで必要な物件は揃うので、grunt serverで起動。楽ですね。
ちなみに
npmはpackage.json、bowerはbower.jsonを利用します。
また、Gruntfile.jsには、起動時に実行するスクリプトや、監視対象のファイルのパス(ファイルに変更があると登録されているイベントが発火、JSHintを実行する等)が書かれてます。
AngularJSのプロジェクトを作成する
チュートリアルを終え、さてToDoアプリでも作りますかと思ったが、プロジェクトどうやって作るんだろうと思ったので、調べた結果をメモしておきます。
Nodeの環境
そもそものNodeの環境も、nodebrewってのがあるので入れました。
入れ方は下記を参照。
AngularJS generator
yeoman/generator-angular · GitHub
こいつを使います。読み方はヨーマンですかね。
YeomanでAngularJSのテンプレートを作成する
yo angular [app-name]
を実行すると、SassとTwitterBootstrapを使うかどうか、あとは4つばかしのモジュールを入れるかどうかを聞かれるので適当に。
他にもrakeコマンドっぽく、コントローラーの作成等も出来るみたいなので、それはおいおい。
Rubyでインスタンスに対して固有のメソッドを定義する
テスト時に、特定のメソッドが実行されているかどうかを確認したい時に、こういう方法を使ったのでメモ。
要件
private な send_mail メソッドが、実行されていることをRSpecで確認したい。
ベースとなるクラス
class Some def execute # 状況に応じてsend_mailを実行したり、しなかったりする end private def send_mail # メールが送信される的な end end
RSpec内の記述
it 'send_mailメソッドが実行されること' do some = Some.new class << some # someの特異クラス内 attr_accessor :is_executed def send_mail @is_executed = true end end some.execute expect(some.is_executed).to be_true end
局所的にやるんだったら上記の記述でおkですが、複数同じことやるんだったらこんな感じにもできます。
module SendMailChecker attr_accessor :is_executed def send_mail @is_executed = true end end it 'send_mailが実行されること' do some = Some.new some.extend(SendMailChecker) some.execute expect(some.is_executed).to be_true end
オブジェクト.extendしてあげると、オブジェクトの特異クラスにモジュールを取り込むことになるので、先ほどの書き方と一緒になるというわけです。
Rubyで簡易ストラテジー
いい感じに慣れてきたので、Rubyに関するメモを残していこうと思います。
簡易ストラテジーの実装
異なるkeyを渡した場合に、一部のメソッドの動作を変更したい場合を想定。
class SearchClass module IdSearchRequest def self.request(value) # keyがIDの場合のリクエストを返却 end end module NameSearchRequest def self.request(value) # keyがNAMEの場合のリクエストを返却 end end MODULE_BY_KEY = { id: IdSearchRequest, name: NameSearchRequest } def search(key, value) request = MODULE_BY_KEY[key.to_sym]::request(value) # リクエストオブジェクトを使って検索を実行 end end
JavaとかだとEnumを使って実装するんでしょうが、Rubyで実装する場合はこんな感じですかね。
ちなみに、モジュールにインスタンス変数を持たせたい場合は、モジュールを取ってきてclass.eval等でincludeする方法も結構使ってます。
CoreDataを使ってみる
最近はRailsばかりいじってて、久しぶりにXCode開いたら色々忘れていたので、まずはCoreDataについてメモしていきます。
参考にしたのは下記の本です
- 作者: 木下誠
- 出版社/メーカー: ビー・エヌ・エヌ新社
- 発売日: 2011/02/09
- メディア: 単行本
- 購入: 13人 クリック: 364回
- この商品を含むブログ (18件) を見る
今日のゴール
CoreDataを扱うクラスを作成してみる。
1.DataModelを作成する
newfile→Core Data→Data Modelから作成 シンプルにリレーションはなし。
この状態でnewfile→NSManagedObjectSubClassを選択。すると設定したEntityと等価のクラスが作成される。
#import <Foundation/Foundation.h> #import <CoreData/CoreData.h> @interface GuidePageEntity : NSManagedObject @property (nonatomic, retain) NSString * identifyer; @property (nonatomic, retain) NSString * imageUrl; @property (nonatomic, retain) NSNumber * isLastPage; @property (nonatomic, retain) NSNumber * pageNumber; @end
2.CoreDataを扱うクラスを作成する
登場人物の紹介
ここで一旦CoreDataを使う場合に出てくるオブジェクト達を紹介します。
ManagedObjectModel
生成法の一例
// Returns the managed object model for the application. // If the model doesn't already exist, it is created from the application's model. - (NSManagedObjectModel *)managedObjectModel { if (_managedObjectModel != nil) { return _managedObjectModel; } NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"GuidePageModel" withExtension:@"momd"]; _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL]; return _managedObjectModel; }
newfile→Core Data→Data Modelで作成した名前を指定して作成しています。なので、あくまでデータモデルを定義しているだけ(という風に理解している・・・)
Persistent Store Cordinator
// Returns the persistent store coordinator for the application. // If the coordinator doesn't already exist, it is created and the application's store added to it. - (NSPersistentStoreCoordinator *)persistentStoreCoordinator { if (_persistentStoreCoordinator != nil) { return _persistentStoreCoordinator; } NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"GuidePageModel.sqlite"]; NSError *error = nil; _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]]; if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) { NSLog(@"Unresolved error %@, %@", error, [error userInfo]); abort(); } return _persistentStoreCoordinator; }
データ永続化先を指定、データモデルを指定、で生成される。これら両者のブリッジを行っている・・・
Managed Object Context
_managedObjectContext = [[NSManagedObjectContext alloc] init]; [_managedObjectContext setPersistentStoreCoordinator:coordinator]; return _managedObjectContext
基本的にRead/Writeな操作はこの人が提供するので、自分が管理するモデルとストア先を指定してあげる。
よって、これらの初期化諸々を行い、外部に必要なAPIを公開するようなクラスを作成すればいいと思われる。
AmazonS3とiOS間でファイルをやりとりする(その2)
前回 - AmazonS3とiOS間でファイルをやりとりする(その1) - assaulter's diary
の続きになります。僕はこういう感じで実装しました。
ダウンロード
仕様 : ユーザーにはDLの結果のみを見せる(今回はStringを返す)。データはよしなに扱う。
Clientを作成
メンバで保持。初期化時に鍵が必要。また、リージョンを設定しておく(やらない場合はデフォルトが入ったはず)。
- (id)init { if (self = [super init]) { _client = [[AmazonS3Client alloc] initWithAccessKey:ACCESS_KEY_ID withSecretKey:SECRET_KEY]; _client.endpoint = [AmazonEndpoints s3Endpoint:AP_NORTHEAST_1]; } return self; }
ダウンロードapi
/// 公開する関数 - (void)downloadDataWithCompletion:(void(^)(NSString* message))resultBlock { [self getDataWithCompletion:^(S3GetObjectResponse *response) { /// response.bodyに格納されているので、よしなにやる if (response.bodyを使ってよしなにやった結果) { resultBlock(@"DL成功"); } else { resultBlock(NSLocalizedString(@"保存失敗"); } } errorBlock:^(NSError *error) { resultBlock(error.userInfo[@"NSLocalizedDescription"]); }]; } /// ヘルパ関数 - (void)getDataWithCompletion:(void(^)(S3GetObjectResponse* response))responseBlock errorBlock:(void(^)(NSError* error))errorBlock { dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(queue, ^{ S3GetObjectRequest* getRequest = [[S3GetObjectRequest alloc] initWithKey:@"オブジェクトの名前" withBucket:@"バケット名"]; getRequest.contentType = CONTENT_TYPE; S3GetObjectResponse* getResponse = [self.client getObject:getRequest]; /// ここからmain threadで処理される(非同期) dispatch_async(dispatch_get_main_queue(), ^{ if (getResponse.error) { errorBlock(getResponse.error); } else { responseBlock(getResponse); } }); }); }
通信してレスポンスを取ってくるところの非同期処理はGCDを使いました。
参考 - 8.2 Grand Central Dispatch · mixi-inc/iOSTraining Wiki · GitHub
ネストはちょい深くなるけど、mainスレッドに非同期で返す部分をわざわざperformSelectorOnMainThreadでやると関数書かないといけないので、block返すだけとかそういう場合はこっちの方がスッキリするかな。
アップロード
あんま変わりませんね。clientは既に用意されている前提で、ユーザーには結果しか返しません。
/// 公開する関数 - (void)uploadDataWithCompletion:(void(^)(NSString* message))resultBlock { [self putDataWithResponseBlock:^(S3PutObjectResponse *response) { resultBlock(@"uploadに成功した"); } errorBlock:^(NSError *error) { resultBlock(error.userInfo[@"NSLocalizedDescription"]); }]; } /// ヘルパー - (void)putDataWithResponseBlock:(void(^)(S3PutObjectResponse* response))responseBlock errorBlock:(void(^)(NSError* error))errorBlock { dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(queue, ^{ S3PutObjectRequest* putRequest = [[S3PutObjectRequest alloc] initWithKey:@"オブジェクト名" inBucket:@"バケット名"]; putRequest.contentType = CONTENT_TYPE; putRequest.data = 適当なメソッドでデータを取ってくる; S3PutObjectResponse* putResponse = [self.client putObject:putRequest]; dispatch_async(dispatch_get_main_queue(), ^{ // ここからはmainThreadで実行される if (putResponse.error) { errorBlock(putResponse.error); } else { responseBlock(putResponse); } }); }); }