CoreDataQuery という CoreData ラッパー gem が公開された
開発元は RubyMotionQuery と同じ InfiniteRed。 RubyMotion ではラッパー gem じゃなくて、MagicalRecord や NLCoreData のような Objective-C のライブラリを使えばいいやって思いはじめていたとき、この gem を観測。 ざっと目を通したところ興味を引かれる gem だったので触ってみた。
CoreDataQuery の特徴
MagicalRecord や NLCoreData、MotionData や MotionDataWrapper と比べて、 CoreDataQuery が優れている点は次の2つ。
Ruby でスキーマを定義できる
MagicalRecord や NLCoreData、あと MotionDataWrapper を使う場合、 Xcode Data Modeler でスキーマ定義していた。
でもせっかく Ruby で iOS アプリを開発しているんだから、 ActiveRecord のマイグレーションファイルみたいに Ruby でスキーマを定義したい。 CoreDataQuery はそんな望みを叶えてくれる。
Ruby で記述したスキーマから、CoreDataQuery が提供するタスクを使って xcdatamodeld ファイルを生成する。 実際にこの機能を提供しているのは CoreDataQuery ではなくて ruby-xcdm っていう gem だけど。
Named Scope をサポートしている
ActiveRecord と同じように
class Author < CDQManagedOjbect scope :a_authors, where(:name).begins_with('A') scope :prolific, where(:publish_count).gt(99) end
という風に Named Scope を定義できる。 MotionDataWrapper は実装を試みているけどまだ完了していない(2014/01/15 時点)。
CoreDataQuery では where や sort_by が使えるけど、ActiveRecord とは使い方が少々異なっていた。
実際に CoreDataQuery を使ってみる
サンプルプロジェクトを作成
motion create CDQSample
でサンプルプロジェクトを作成。
Gemfile に
gem 'cdq'
を追加して
bundle install
を実行すればインストール完了。
あとは
bundle exec cdq init
を実行するとプロジェクトに対して下記の処理を行われる。
- schemas ディレクトリが作成される
- schemas/0001_initial.rb という最初のスキーマの雛形が作成される
- build:simulator タスクが schema:build に依存する記述が Rakefile に追加される
スキーマの作成
cdq init で作成されたファイルにスキーマを定義する。
schema "0001 initial" do entity "Entry" do string :body datetime :created_at datetime :updated_at end end
エンティティの属性には次の型が使える。
- integer16
- integer32
- integer64
- decimal
- double
- float
- string
- boolean
- datetime
- binary
- transformable
スキーマを定義したら
bundle exec rake schema:build
を実行すれば xcdatamodeld ファイルが生成される。 ただ build:simulator タスクを実行すると schema:build も実行されるから、 このタスクをわざわざ実行しなくても大丈夫。
モデルの作成
bundle exec cdq create model entry
を実行すると
- app/models/entry.rb
- spec/models/entry.rb
が生成される。モデルのソースコードがこちら。
class Entry < CDQManagedObject end
CDQManagedObject は NSManagedObject を継承して機能拡張したクラス。 実際には間に CoreDataQueryManagedObjectBase っていうクラスを挟んでいるけど。 CDQManagedObject には create や destroy なんかが定義されている。
CoreDataQuery を使ったサンプル
CoreDataQuery を使って Create・Read・Delete を行うサンプルは次の通り。 先ほど作成したスキーマとモデルをサンプルで使用している。
# coding: utf-8 class AppDelegate include CDQ def application(application, didFinishLaunchingWithOptions:launchOptions) # CoreDataQuery のセットアップ cdq.setup @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds) @window.rootViewController = MainViewController.controllerWithNavigation @window.makeKeyAndVisible true end end class MainViewController < UITableViewController def self.controllerWithNavigation UINavigationController.alloc.initWithRootViewController(self.new) end def viewDidLoad super addButton = UIBarButtonItem.alloc.initWithBarButtonSystemItem( UIBarButtonSystemItemAdd, target: self, action: "createEntry" ) self.navigationItem.title = "CDQSample" self.navigationItem.leftBarButtonItem = self.editButtonItem self.navigationItem.rightBarButtonItem = addButton end def entries @entries ||= begin Entry.where(:body). contains("Te"). sort_by(:created_at, :descending). array end end def createEntry Entry.create( body: "Test", created_at: NSDate.date, updated_at: NSDate.date ) cdq.save reloadData end def reloadData @entries = nil self.tableView.reloadData end def tableView(tableView, numberOfRowsInSection:section) self.entries.size end def tableView(tableView, cellForRowAtIndexPath:indexPath) cell_id = "main-view-cell" entry = self.entries[indexPath.row] cell = tableView.dequeueReusableCellWithIdentifier(cell_id) || UITableViewCell.alloc.initWithStyle(UITableViewCellStyleDefault, reuseIdentifier:cell_id) cell.textLabel.text = entry.body cell end def tableView(tableView, commitEditingStyle:editingStyle, forRowAtIndexPath:indexPath) case editingStyle when UITableViewCellEditingStyleDelete entry = self.entries[indexPath.row] entry.destroy cdq.save reloadData end end end
結局 CoreDataQuery ってどうなの?
自分が知る CoreData のラッパー gem の中では、今のところ最も完成度が高い。 ラッパー gem の中では CoreDataQuery 一択かも。
MagicalRecord や NLCoreData といった Objective-C ライブラリと比べてどちらが良いかは、 現段階ではなんとも言えない。 スキーマのマイグレーションをまだ試していないし、 スレッド毎に NSManagedObjectContext を管理するのも試していない。
Ruby の DSL でスキーマを定義できたり、Named Scope が使えるのは個人的にかなりツボ。 今開発しているアプリに導入してみようと思う。