Cocoa + Ruby + ActiveRecord = CoreDataがわからない人
結局、CoreDataがよく理解できてないわたくし・・・
そうだ!僕にはActiveRecordがあるじゃないか(逃
というわけで、RubyCocoaでActiveRecordを使ってみる。
Railsが動く環境(DBはSQLite3を想定)は構築済みのこと。
まずはRubyCocoaを導入する。
$ sudo port install rb-cocoa
続いて、DB作成用コードを書く。
mito_setup.rb
require 'rubygems' require_gem 'activerecord' ActiveRecord::Base.establish_connection( :adapter => 'sqlite3', :dbfile => 'mito.db' ) ActiveRecord::Schema.define(:version => 1) do create_table "users", :force => true do |t| t.column "id", :integer, :default => 0, :null => false t.column "name", :string, :default => "", :null => false t.column "occupation", :string, :default => "", :null => false end end class User < ActiveRecord::Base end User.transaction { User.destroy_all User.create(:name => "Mito-no-goroukou", :occupation => "sub-shougun") User.create(:name => "Suke-san", :occupation => "otomo-A") User.create(:name => "Kaku-san", :occupation => "otomo-B") User.create(:name => "ukkari-hachibee", :occupation => "dango-eater") User.create(:name => "yashichi", :occupation => "kazaguruma-mania") User.create(:name => "o-gin", :occupation => "shinobi-girl??") User.create(:name => "tobizaru", :occupation => "shinobi-fighter") }
このmito_setup.rbを実行する。
$ ruby mito_setup.db
するとmito.dbが出来上がる。中身を確認。
$ sqlite3 mito.db SQLite version 3.1.3 Enter ".help" for instructions sqlite> select * from users; 1|Mito-no-goroukou|sub-shougun 2|Suke-san|otomo-A 3|Kaku-san|otomo-B 4|ukkari-hachibee|dango-eater 5|yashichi|kazaguruma-mania 6|o-gin|shinobi-girl?? 7|tobizaru|shinobi-fighter sqlite> .q
続いてRubyCocoaとActiveRecordの組み合わせサンプルコード。ほぼRubyCocoaのHelloWorld.rbのまま。
mito.rb
require 'rubygems' require_gem 'activerecord' require 'osx/cocoa' include OSX ActiveRecord::Base.establish_connection( :adapter => 'sqlite3', :dbfile => 'mito.db' ) class User < ActiveRecord::Base end class AppDelegate < NSObject def applicationDidFinishLaunching(aNotification) puts "Start!" end def sayAllMember(sender) speakUsers User.find(:all, :order => :id ) end def sayOtomo(sender) speakUsers User.find(:all, :order => :id, :conditions => [ "occupation like ?", "%otomo%"] ) end def sayShinobi(sender) speakUsers User.find(:all, :order => :id, :conditions => [ "occupation like ?", "%shinobi%"] ) end def speak(str) script = NSAppleScript.alloc.initWithSource('say "' + str + '"') errinfo = OCObject.new script.executeAndReturnError(errinfo) end def speakUsers(users) users.each {|user| puts "Name: #{user.name}, Occupation: #{user.occupation}" speak user.name sleep 0.2 speak user.occupation sleep 0.5 } end end if $0 == __FILE__ then $stderr.print "just wait..." ; $stderr.flush app = NSApplication.sharedApplication() app.setDelegate(AppDelegate.alloc.init) frame = [200.0, 300.0, 410.0, 100.0] win = NSWindow.alloc.initWithContentRect_styleMask_backing_defer(frame, 15, 2, 0) win.setTitle '水戸黄門' win.setLevel(3) # floating window allmember = NSButton.alloc.initWithFrame [10.0, 10.0, 110.0, 80.0] win.contentView.addSubview(allmember) allmember.setBezelStyle(4) allmember.setTitle('水戸老公の一行') allmember.setTarget( app.delegate ) allmember.setAction( "sayAllMember:" ) otomo = NSButton.alloc.initWithFrame [130.0, 10.0, 80.0, 80.0] win.contentView.addSubview(otomo) otomo.setBezelStyle(4) otomo.setTitle('お供') otomo.setTarget( app.delegate ) otomo.setAction( "sayOtomo:" ) shinobi = NSButton.alloc.initWithFrame [220.0, 10.0, 80.0, 80.0] win.contentView.addSubview(shinobi) shinobi.setBezelStyle(4) shinobi.setTitle('忍') shinobi.setTarget( app.delegate ) shinobi.setAction( "sayShinobi:" ) bye = NSButton.alloc.initWithFrame [320.0, 10.0, 80.0, 80.0] win.contentView.addSubview(bye) bye.setBezelStyle( 4 ) bye.setTarget(app) bye.setAction('stop:') bye.setEnabled( true ) bye.setTitle( 'Goodbye!' ) adios = NSSound.alloc.initWithContentsOfFile_byReference( '/System/Library/Sounds/Basso.aiff', true ) bye.setSound( adios ) win.display() win.orderFrontRegardless() ## but this one does app.run() end
mito.rbを実行する。
$ ruby mito.rb
- ActiveRecordでDBからレコードを検索・取得し、
- コンソールに名前と職業を表示しつつ、
- それを音声で読み上げる。
と動作する。実に簡単だ。ちなみに水戸のご老公は「マイトノゴラウカラウ」だそうである。
もっともこれはやっぱり逃げでしかなくて、InterfaceBuilderで画面をくみ上げるようなものはCoreDataを使うほうがずっーーといいのだろうけど。