nirasan's tech blog

趣味や仕事の覚え書きです。Linux, Perl, PHP, Ruby, Javascript, Android, Cocos2d-x, Unity などに興味があります。

Rails 4.2 で Heroku にデプロイしたメモ

はじめに

  • Rails 4.2 で開発していたアプリを Heroku にデプロイした時にやったことメモ

環境

Heroku にユーザー登録

Heroku アプリの作成

$ cd /path/to/rails/app
$ heroku create

Heroku 用の gem の追加

$ vi Gemfile
-gem 'sqlite3'
+gem 'sqlite3', :group => [:development, :test]
+gem 'pg', :group => [:production] # heroku で DBMS として PostgreSQL を使用するために必要
+gem 'rails_12factor', group: :production # heroku で Rails 4 を動かすために調整してくれるやつ
$ bundle install --without production

Devise の設定

  • Devise を使っている場合
$ vi config/initializers/devise.rb
config.secret_key = '<%= ENV["DEVISE_SECRET_KEY"] %>' if Rails.env.production? 
heroku config:add DEVISE_SECRET_KEY=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

データベースの設定

$ vi config/database.yml
production:
  adapter: postgresql
  encoding: unicode
  pool: 5

シークレットキーの設定

  • シークレットキーを環境変数から読み込むように
$ vi config/secrets.yml
  secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>                                                                                                                                    
  • .gitignore に config/secrets.yml が登録されている場合は削除する

デプロイ

$ git commit -am 'for heroku'
$ git push heroku master
$ heroku run rake db:migrate

Rails 4.2 で destroy のバリデーションをする

任意の条件でバリデーションをする

  • http://qiita.com/tbaba/items/9ea139dc77443e6d7be2 の通り、before_destroy に指定したメソッド内で条件を判定し、destroy 不可であれば return false する
  • 前記記事の通り before_destroy :method_name の記法ではなく、before_destroy do ... end で宣言すると、return false 時に LocalJumpError の例外が発生して意図したように動作しなくてはまった

関連レコードがある場合は destroy できないように

  • バリデーションの条件として、関連するレコードがあったら destroy 出来なくしたいだけの場合は has_meny のオプションだけで対応できる
models
has_many :associate_name, dependent: :restrict_with_error
# has_many のその他のオプション
# http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_many
controllers
if @result = @obj.destroy
  # 成功
else
  # 失敗
end

Unity Ads 導入メモ

やったこと

  • Unity Ads のサイトにアカウント登録
  • Unity Ads でアプリの登録とID取得
  • Asset Store から Unity Ads のインポート
  • 広告の呼び出しスクリプトの作成と実行

スクリプト

AdsManager
using UnityEngine;
using System.Collections;
using UnityEngine.Advertisements;

public class AdsManager : MonoBehaviour {

	void Awake() {
		if (Advertisement.isSupported) {
			Advertisement.allowPrecache = true;
			Advertisement.Initialize (Config.unityAdsId, true);

		} else {
			Debug.Log("Platform not supported");
		}
	}

	public void Show() {
		Advertisement.Show(null, new ShowOptions {
			pause = true,
			resultCallback = result => {
				Debug.Log("広告閲覧終了。広告閲覧報酬を付与するならここで。");
				Debug.Log(result.ToString());
			}
		});
	}
}
Config
using UnityEngine;
using System.Collections;

public class Config {

	#if UNITY_IPHONE
	public static string unityAdsId = "iOSアプリのID";
	#elif UNITY_ANDROID
	public static string unityAdsId = "AndroidアプリのID";
	#endif
}
広告表示
	GameObject.FindObjectOfType<AdsManager>().Show();

MagicalRecord で Date の範囲検索

  • Entity エンティティの creationDate 属性に作成日時が入っているものとして、今日作成されたデータを取得する場合
// 今日の0時0分0秒を取得
NSCalendar* calendar = [NSCalendar currentCalendar];
unsigned int flags = NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay;
NSDateComponents* components = [calendar components:flags fromDate:[NSDate date]];
NSDate* fromDate = [calendar dateFromComponents:components];

// 明日の0時0分0秒
NSDate* toDate = [fromDate dateByAddingTimeInterval:60*60*24];

// 検索条件の作成と実行
NSPredicate* p = [NSPredicate predicateWithFormat:@"(creationDate >= %@) AND (creationDate < %@)", fromDate, toDate];
Entity* entity = [Entity MR_findFirstWithPredicate:p];

MagicalRecord を試したメモ

はじめに

  • iOS アプリ開発で ActiveRecord ライクに CoreData を扱うことが出来る MagicalRecord を試したメモ
  • CocoaPod を使うのも初めてなのでそこもちょっと詳しく

動作環境

  • XCode 6.1.1
  • MagicalRecord 2.2
  • mogenerator 1.27

CoreDateの用語

  • 管理オブジェクト(NSManagedObject) : レコード
  • 管理オブジェクトコンテキスト(NSManagedObjectContext) : ORM
  • 管理オブジェクトモデル(NSManagedObjectModel) : スキーマ

プロジェクトの作成

  • "Use Core Data" にチェックを入れて作成

MagicalRecord のインストール

CocoaPod のインストール

  • インストールしていなければ
sudo gem install cocoapods

MagicalRecord のインストール

# cd /path/to/xcode/project
# /usr/bin/pod init
# vi Podfile
target 'ProjectName' do
  pod "MagicalRecord"
  pod "MagicalRecord/Shorthand"
end
# /usr/bin/pod update

MagicalRecord を読み込む

  • pod update によって CocoaPods が有効な状態の XCode プロジェクトファイル(APPNAME.xcworkspace)が生成されているので、これを開く
# open App.xcworkspace
  • CocoaPods のビルドを行う
    • 画面左上の方にあるビルド対象のセレクトボックスをクリックして "New Scheme" を選択
    • "Pods-APPNAME" を選択してビルド対象として作成
    • ビルドデバイスを "iOS Device" に変更し、ビルドを実行

エンティティ(テーブル)の追加

  • APPNAME.xcdatamodeld ファイルを選択
  • "Add Entity" ボタンでエンティティの作成
  • 画面右のインスペクタで Entity の Name と Class を設定 (Class はクラスファイル生成時に必要)
  • エンティティを選択して "Add Attribute" ボタンでアトリビュート(カラム)の作成

リレーションシップについて

  • http://qiita.com/yuiseki/items/33ec35ec99b5304be90a
  • Auther エンティティと Book エンティティが 1:多 の関係になるとする
  • Auther の設定
    • Auther の Relationships で "+" を押下しリレーションシップを作成
      • Relationships を books
      • Destination を Book
      • Inverse はとりあえずそのまま(Book 側の Inverse を設定すれば自動でセットされる)
    • Auther の books を選択した状態のインスペクタで、Relationships の Type を "To Many" に
    • 同じく "Delete Rule" を "Cascade" にすると Auther 削除時に関連する Book も削除されるので任意で設定
  • Book の設定
    • Book の Relationships で "+" を押下しリレーションシップを作成
      • Relationships を auther
      • Destination を Auther
      • Inverse は books
    • Book の auther を選択した状態のインスペクタで、Relationships の Type を "To One" に

カスタム管理オブジェクトクラスの作成

mogenerator のインストール

# brew install mogenerator
# /usr/local/bin/mogenerator -v

ファイルの作成

# /usr/local/bin/mogenerator -m Model.xcdatamodeld/Model.xcdatamodel -O CoreData/ --template-var arc=true

MagicalRecord の初期化

AppDelegate.h

#import <MagicalRecord.h>
#import <MagicalRecord+Setup.h>

AppDelegate.m

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [MagicalRecord setupCoreDataStackWithAutoMigratingSqliteStoreNamed:@"app.db"]; // 追記
    return YES;
}
- (void)applicationWillTerminate:(UIApplication *)application {
    [MagicalRecord cleanUp]; // 追記
}

MagicalRecord を使う

ViewController.h

  • Auther.h, Book.h はカスタム管理オブジェクトクラス
#import <CoreData/CoreData.h>
#import <CoreData+MagicalRecord.h>
#import "Auther.h"
#import "Book.h"

ViewController.m

- (void)viewDidLoad {
    [super viewDidLoad];
    
	// Auther の作成
    Auther* auther = [Auther MR_createEntity];
    auther.name = @"Neal Stephenson";
    
	// Book の作成と Auther との関連付け
    Book* book1 = [Book MR_createEntity];
    book1.title = @"Snow Crash";
    [auther addBooksObject:book1];

    Book* book2 = [Book MR_createEntity];
    book2.title = @"The Diamond Age";
    [auther addBooksObject:book2];

    Book* book3 = [Book MR_createEntity];
    book3.title = @"Cryptonomicon";
    [auther addBooksObject:book3];
	
	// 作成したレコードの登録
    [[NSManagedObjectContext MR_defaultContext] MR_saveToPersistentStoreAndWait];
    
	// Auther の一覧を取得
    NSArray* array = [Auther MR_findAll];
    int i = 0;
    NSLog(@"Count:%lu", (unsigned long)[array count]);
    for (Auther* a in array) {
        NSLog(@"#####%d name=%@", i, a.name);
		// Auther に関連付いた Book の取得
        for (Book* b in a.books) {
            NSLog(@"----- title=%@", b.title);
        }
        i++;
    }
	
	// 削除
	[auther MR_deleteEntity];
    [[NSManagedObjectContext MR_defaultContext] MR_saveToPersistentStoreAndWait];
}

iOSアプリ開発チュートリアルメモ

ナビゲーションコントローラの追加

  • タイトル表示や戻るなどが出来るようになる
  • 追加したいビューコントローラを選択した状態で、メニューから Editor > Embed In > Navigation Controller

カスタムコントローラの追加

  • File > New > File
  • iOS > Source > Cocoa Touch Class
  • 任意の名前、任意の親クラスの選択
    • 素のコントローラなら UIViewController、テーブルなら UITableViewController
  • Storyboard でひもづけたいビューコントローラを選択、アイデンティティインスペクタで Class オプションの変更

ボタン押下で遷移

  • Storyboard でビューコントローラにボタンの追加
  • ボタンを右クリック Touch Up Inside からドラッグして遷移先のビューコントローラでドロップ

戻る遷移とコールバックの登録

  • 戻り先のビューコントローラで -(IBAction)SomeName:(UIStoryboardSegue*)segue; なメソッドを宣言すると戻り時のコールバックメソッドになる
  • Storyboard 戻り元のビューコントローラの Exit を選択、コネクションインスペクタで先ほどのコールバックメソッドから戻るボタンへドラッグしてひもづける

ビューの要素をコントローラにひもづける

  • 右上の Show the Assistant Editor (蝶ネクタイのアイコン) をクリックして Storyboard とコントローラのソースを表示
  • Storyboard で要素を右クリック、Referencing Outlet をコントローラの @interface セクションにひもづける

プロパティを宣言する

  • public なら .h の @interface で
  • private なら .m の @interface で
  • 記法は @property (オプション) 型 プロパティ名;
  • オプション
    • readonly : 読み込み専用
  • プロパティの初期値は nil

メソッドの宣言

遷移前実行メソッド

- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if (sender != self.doneButton) return;
    if (self.textField.text.length > 0) {
        self.toDoItem = [[XYZToDoItem alloc] init];
        self.toDoItem.itemName = self.textField.text;
        self.toDoItem.completed = NO;
    }
}

テーブル関連

セクション数を返すメソッド

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
#warning Potentially incomplete method implementation.
    // Return the number of sections.
    return 0;
}

アイテム数を返すメソッド

  • 例は配列の要素数を返す
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    // Return the number of rows in the section.
    return [self.toDoItems count];
}

ポジション毎にアイテムをセットするメソッド

  • 例は配列から対応する要素を取得してテキストにセットしている
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"ListPrototypeCell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
    XYZToDoItem *toDoItem = [self.toDoItems objectAtIndex:indexPath.row];
    cell.textLabel.text = toDoItem.itemName;
    return cell;
}

ドキュメントを見る

  • Help > "Documentation and API Reference"

ActiveAndroid Tips

はじめに

Date型を扱う

Date now = new Date();
new Select()
	.from(SomeModel.class)
	.where("SomeDate = ?", now.getTime())
	.execute();

実行したSQL文のデバッグ表示

com.activeandroid.util.Log.setEnabled(true);

リレーションシップ

"1:多"な親子関係の定義

  • Category が親で Item が子
@Table(name = "Categories")
public class Category extends Model {
    @Column(name = "Name")
    public String name;

    public List<Item> items() {
        return getMany(Item.class, "Category");
    }
}

@Table(name = "Items")
public class Item extends Model {

    @Column(name = "Name")
    public String name;

    @Column(name = "Category")
    public Category category;
}

外部キー制約で親の削除時に子も削除する

  • デフォルトでは子のいる親を削除すると例外が発生する
  • Column の定義時に onDelete を適宜指定すると、子も同時に削除できる
@Table(name = "Items")
public class Item extends Model {

    @Column(name = "Name")
    public String name;
	
    // 変更箇所
    @Column(name = "Category", onDelete = Column.ForeignKeyAction.CASCADE)
    public Category category;
}

レコードの存在確認

    public void testExistsWhereClause() {
        cleanTable();
        populateTable();

        From from = new Select()
                .from(MockModel.class)
                .where("intField = ?", 1);

        final List<MockModel> list = from.execute();
        final boolean exists = from.exists();

        assertTrue(exists);
        assertTrue(list.size() > 0);
    }