Cocoa + Ruby + ActiveRecord = CoreDataがわからない人

結局、CoreDataがよく理解できてないわたくし・・・
そうだ!僕にはActiveRecordがあるじゃないか(逃


というわけで、RubyCocoaActiveRecordを使ってみる。
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

続いてRubyCocoaActiveRecordの組み合わせサンプルコード。ほぼ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

するとウィンドウが表示される。

ボタンをクリックすると、

  1. ActiveRecordでDBからレコードを検索・取得し、
  2. コンソールに名前と職業を表示しつつ、
  3. それを音声で読み上げる。

と動作する。実に簡単だ。ちなみに水戸のご老公は「マイトノゴラウカラウ」だそうである。


もっともこれはやっぱり逃げでしかなくて、InterfaceBuilderで画面をくみ上げるようなものはCoreDataを使うほうがずっーーといいのだろうけど。