93
Ruby and Cocoa Facilitate development on OSX

Fun with Ruby and Cocoa

Embed Size (px)

DESCRIPTION

Use RubyCocoa and MacRuby to write scripts that take advantage of the Cocoa frameworks.

Citation preview

Page 1: Fun with Ruby and Cocoa

Ruby and CocoaFacilitate development on OSX

Page 2: Fun with Ruby and Cocoa

About me

Page 3: Fun with Ruby and Cocoa

About me

Page 4: Fun with Ruby and Cocoa

Rapperswil

Page 6: Fun with Ruby and Cocoa

huesler informatik

Page 7: Fun with Ruby and Cocoa

huesler informatik

upstream agile

Page 8: Fun with Ruby and Cocoa

huesler informatik

upstream agile

co-up.de

Page 9: Fun with Ruby and Cocoa

Ruby and Cocoa

Page 10: Fun with Ruby and Cocoa

Ruby

Page 11: Fun with Ruby and Cocoa

passionatehttp://www.flickr.com/photos/gi/378823/sizes/o/

Page 12: Fun with Ruby and Cocoa

Apple OSX

Page 13: Fun with Ruby and Cocoa
Page 14: Fun with Ruby and Cocoa

passionate

http://www.flickr.com/photos/gi/378823/sizes/o/

Page 15: Fun with Ruby and Cocoa

Landscape

http://www.flickr.com/photos/kiumo/4203883504/sizes/o/

Page 16: Fun with Ruby and Cocoa

Cocoa

http://www.flickr.com/photos/luder5/4100921399/sizes/l/

Page 17: Fun with Ruby and Cocoa

Cocoa

Page 18: Fun with Ruby and Cocoa

Cocoa

• Core Foundation

Page 19: Fun with Ruby and Cocoa

Cocoa

• Core Foundation

• Appkit

Page 20: Fun with Ruby and Cocoa

Cocoa

• Core Foundation

• Appkit

• Core * (Audio/Video/Image/Data etc.)

Page 21: Fun with Ruby and Cocoa

Cocoa

• Core Foundation

• Appkit

• Core * (Audio/Video/Image/Data etc.)

• Scripting bridge

Page 22: Fun with Ruby and Cocoa

Cocoa

• Core Foundation

• Appkit

• Core * (Audio/Video/Image/Data etc.)

• Scripting bridge

• others

Page 23: Fun with Ruby and Cocoa

Interface Builder

Page 24: Fun with Ruby and Cocoa

XCode

Page 25: Fun with Ruby and Cocoa

Objective-C

Page 26: Fun with Ruby and Cocoa

NSMutableArray *anArray = [[NSMutableArray alloc] init

];[anArray addObject:@"Element 1"];[anArray addObject:@"Element 2"];[anArray addObject:@"Element 3"];

//Use a for each loop to iterate through the arrayfor (NSString *s in anArray) { NSLog(s);}//Release the array[anArray release]

Page 27: Fun with Ruby and Cocoa
Page 28: Fun with Ruby and Cocoa

Ruby And Cocoa

Page 29: Fun with Ruby and Cocoa
Page 30: Fun with Ruby and Cocoa

Ruby Cocoa

Page 31: Fun with Ruby and Cocoa
Page 32: Fun with Ruby and Cocoa

HotCocoa

picture shamelessly cropped from http://www.slideshare.net/mattetti/macruby-hotcocoa-presentation-by-rich-kilmer

Page 33: Fun with Ruby and Cocoa

Scripting Bridge

http://www.flickr.com/photos/bensonkua/2851908095/sizes/l/

Page 34: Fun with Ruby and Cocoa

Good to know

Page 35: Fun with Ruby and Cocoa

Good to know

• Available commands for each application are in a .sdef file

Page 36: Fun with Ruby and Cocoa

Good to know

• Available commands for each application are in a .sdef file

• Terminal.app/Contents/Resources/Terminal.sdef

Page 37: Fun with Ruby and Cocoa

Good to know

• Available commands for each application are in a .sdef file

• Terminal.app/Contents/Resources/Terminal.sdef

• gem rb-appscript (native extension so not for MacRuby)

Page 38: Fun with Ruby and Cocoa

1 <command name="do script" code="coredosc" 2 description="Runs a UNIX shell script or command."> 3 <cocoa class="TTDoScriptCommand"/> 4 <direct-parameter type="text" 5 description="The command to execute." optional="yes"/> 6 <parameter name="with command" 7 description="Data to be passed to the Terminal..." 8 code="cmnd" optional="yes" hidden="yes"> 9 <cocoa key="Command" /> 10 <type type="text" /> 11 <type type="any" /> <!-- support null case --> 12 </parameter> 13 <parameter name="in" 14 description="The tab in which to execute the command" 15 code="kfil" optional="yes"> 16 <cocoa key="Target" /> 17 <type type="tab" /> 18 <type type="window" /> 19 <type type="any" /> <!-- support null case --> 20 </parameter> 21 <result type="tab" 22 description="The tab the command was executed in." /> 23 </command> 24

Page 39: Fun with Ruby and Cocoa
Page 40: Fun with Ruby and Cocoa

Control Terminal

Page 41: Fun with Ruby and Cocoa

1 #!/usr/bin/env ruby 2 require 'rubygems' 3 require 'appscript' 4 include Appscript 5 6 terminal = app('Terminal') 7 current_window = terminal.windows.first 8 current_tab = current_window.tabs.last 9 process = app("System Events").application_processes[ 10 "Terminal.app" 11 ] 12 13 process.keystroke('t', :using => :command_down) 14 terminal.do_script('top', :in => current_tab)

Page 42: Fun with Ruby and Cocoa

1 #!/usr/bin/env ruby 2 require 'rubygems' 3 require 'appscript' 4 include Appscript 5 6 terminal = app('Terminal') 7 current_window = terminal.windows.first 8 current_tab = current_window.tabs.last 9 process = app("System Events").application_processes[ 10 "Terminal.app" 11 ] 12 13 process.keystroke('t', :using => :command_down) 14 terminal.do_script('top', :in => current_tab)

Page 43: Fun with Ruby and Cocoa

1 #!/usr/bin/env ruby 2 require 'rubygems' 3 require 'appscript' 4 include Appscript 5 6 terminal = app('Terminal') 7 current_window = terminal.windows.first 8 current_tab = current_window.tabs.last 9 process = app("System Events").application_processes[ 10 "Terminal.app" 11 ] 12 13 process.keystroke('t', :using => :command_down) 14 terminal.do_script('top', :in => current_tab)

Page 44: Fun with Ruby and Cocoa

1 #!/usr/bin/env ruby 2 require 'rubygems' 3 require 'appscript' 4 include Appscript 5 6 terminal = app('Terminal') 7 current_window = terminal.windows.first 8 current_tab = current_window.tabs.last 9 process = app("System Events").application_processes[ 10 "Terminal.app" 11 ] 12 13 process.keystroke('t', :using => :command_down) 14 terminal.do_script('top', :in => current_tab)

Page 45: Fun with Ruby and Cocoa

Problems with MacRuby

Page 46: Fun with Ruby and Cocoa

Problems with MacRuby

Page 47: Fun with Ruby and Cocoa

Problems with MacRuby

• Some methods don’t seem to be available

Page 48: Fun with Ruby and Cocoa

iTunes

Page 49: Fun with Ruby and Cocoa

1 #!/usr/bin/env macruby 2 3 framework 'ScriptingBridge' 4 5 itunes = SBApplication.applicationWithBundleIdentifier( 6 "com.apple.iTunes" 7 ) 8 library = itunes.sources.objectWithName("Library") 9 10 library.userPlaylists.each do |playlist| 11 puts playlist.name 12 end

Page 50: Fun with Ruby and Cocoa

1 #!/usr/bin/env macruby 2 3 framework 'ScriptingBridge' 4 5 itunes = SBApplication.applicationWithBundleIdentifier( 6 "com.apple.iTunes" 7 ) 8 library = itunes.sources.objectWithName("Library") 9 10 library.userPlaylists.each do |playlist| 11 puts playlist.name 12 end

Page 51: Fun with Ruby and Cocoa

1 #!/usr/bin/env macruby 2 3 framework 'ScriptingBridge' 4 5 itunes = SBApplication.applicationWithBundleIdentifier( 6 "com.apple.iTunes" 7 ) 8 library = itunes.sources.objectWithName("Library") 9 10 library.userPlaylists.each do |playlist| 11 puts playlist.name 12 end

Page 52: Fun with Ruby and Cocoa

Core Location

Page 53: Fun with Ruby and Cocoa

1 #!/usr/bin/env macruby 2 framework 'CoreLocation' 3 4 loc = CLLocationManager.alloc.init 5 loc.delegate = self 6 loc.startUpdatingLocation 7 8 # keep the script running 9 NSRunLoop.currentRunLoop.runUntilDate( 10 NSDate.distantFuture 11 )

Page 54: Fun with Ruby and Cocoa

1 #!/usr/bin/env macruby 2 framework 'CoreLocation' 3 4 loc = CLLocationManager.alloc.init 5 loc.delegate = self 6 loc.startUpdatingLocation 7 8 # keep the script running 9 NSRunLoop.currentRunLoop.runUntilDate( 10 NSDate.distantFuture 11 )

Page 55: Fun with Ruby and Cocoa

1 def locationManager( 2 manager, 3 didUpdateToLocation: new_location, 4 fromLocation: old_location 5 ) 6 7 puts "loc: #{new_location.description}" 8 end

Page 56: Fun with Ruby and Cocoa

1 #!/usr/bin/env macruby 2 framework 'CoreLocation' 3 4 loc = CLLocationManager.alloc.init 5 loc.delegate = self 6 loc.startUpdatingLocation 7 8 # keep the script running 9 NSRunLoop.currentRunLoop.runUntilDate( 10 NSDate.distantFuture 11 )

Page 57: Fun with Ruby and Cocoa

1 #!/usr/bin/env macruby 2 framework 'CoreLocation' 3 4 def locationManager(manager, 5 didUpdateToLocation: new_location, 6 fromLocation: old_location 7 ) 8 puts "location: #{new_location.description}" 9 end 10 11 loc = CLLocationManager.alloc.init 12 loc.delegate = self 13 loc.startUpdatingLocation 14 15 # keep the script running 16 NSRunLoop.currentRunLoop.runUntilDate( 17 NSDate.distantFuture 18 )

Page 58: Fun with Ruby and Cocoa

Grand Central Dispatch

Page 59: Fun with Ruby and Cocoa

Grand Central Dispatch

Page 60: Fun with Ruby and Cocoa

Grand Central Dispatch

• MacRuby only

Page 61: Fun with Ruby and Cocoa

Grand Central Dispatch

• MacRuby only

• Synchronous

Page 62: Fun with Ruby and Cocoa

Grand Central Dispatch

• MacRuby only

• Synchronous

• Asynchronous

Page 63: Fun with Ruby and Cocoa

Grand Central Dispatch

• MacRuby only

• Synchronous

• Asynchronous

• Parallel

Page 64: Fun with Ruby and Cocoa

Grand Central Dispatch

• MacRuby only

• Synchronous

• Asynchronous

• Parallel

• Synchronization

Page 65: Fun with Ruby and Cocoa

Asynchronous

Page 66: Fun with Ruby and Cocoa

1 #!/usr/bin/env macruby 2 3 queue = Dispatch::Queue.new( 4 'ch.huesler-informatik.scotrubyconf.gcd' 5 ) 6 7 queue.async do 8 puts 'Starting asyn. NONE BLOCKING!' 9 sleep 2.00 10 puts "Finished asyn" 11 end 12 puts "code not being blocked" 13 14 NSRunLoop.currentRunLoop.runUntilDate( 15 NSDate.distantFuture 16 ) 17

Page 67: Fun with Ruby and Cocoa

1 #!/usr/bin/env macruby 2 3 queue = Dispatch::Queue.new( 4 'ch.huesler-informatik.scotrubyconf.gcd' 5 ) 6 7 queue.async do 8 puts 'Starting asyn. NONE BLOCKING!' 9 sleep 2.00 10 puts "Finished asyn" 11 end 12 puts "code not being blocked" 13 14 NSRunLoop.currentRunLoop.runUntilDate( 15 NSDate.distantFuture 16 ) 17

Page 68: Fun with Ruby and Cocoa

1 #!/usr/bin/env macruby 2 3 queue = Dispatch::Queue.new( 4 'ch.huesler-informatik.scotrubyconf.gcd' 5 ) 6 7 queue.async do 8 puts 'Starting asyn. NONE BLOCKING!' 9 sleep 2.00 10 puts "Finished asyn" 11 end 12 puts "code not being blocked" 13 14 NSRunLoop.currentRunLoop.runUntilDate( 15 NSDate.distantFuture 16 ) 17

Page 69: Fun with Ruby and Cocoa

Synchronous

Page 70: Fun with Ruby and Cocoa

1 #!/usr/bin/env macruby 2 3 queue = Dispatch::Queue.new( 4 'ch.huesler-informatik.scotrubyconf.gcd' 5 ) 6 7 queue.sync do 8 puts 'Starting sync. BLOCKING!' 9 sleep 3.0 10 puts 'Finished sync' 11 end 12 puts "code being blocked" 13 14 NSRunLoop.currentRunLoop.runUntilDate( 15 NSDate.distantFuture 16 ) 17

Page 71: Fun with Ruby and Cocoa

Synchronized

Page 72: Fun with Ruby and Cocoa

1 #!/usr/bin/env macruby 2 3 worker_queue = Dispatch::Queue.new( 4 'ch.huesler-informatik.scotrubyconf.gcd' 5 ) 6 group = Dispatch::Group.new 7 8 0.upto(10) do |i| 9 puts "Dispatch #{i} to GCD" 10 worker_queue.async(group) do 11 puts "working on #{i}" 12 end 13 end 14 puts "waiting for gcd" 15 group.wait 16 puts "done" 17

Page 73: Fun with Ruby and Cocoa

1 #!/usr/bin/env macruby 2 3 worker_queue = Dispatch::Queue.new( 4 'ch.huesler-informatik.scotrubyconf.gcd' 5 ) 6 group = Dispatch::Group.new 7 8 0.upto(10) do |i| 9 puts "Dispatch #{i} to GCD" 10 worker_queue.async(group) do 11 puts "working on #{i}" 12 end 13 end 14 puts "waiting for gcd" 15 group.wait 16 puts "done" 17

Page 74: Fun with Ruby and Cocoa

1 #!/usr/bin/env macruby 2 3 worker_queue = Dispatch::Queue.new( 4 'ch.huesler-informatik.scotrubyconf.gcd' 5 ) 6 group = Dispatch::Group.new 7 8 0.upto(10) do |i| 9 puts "Dispatch #{i} to GCD" 10 worker_queue.async(group) do 11 puts "working on #{i}" 12 end 13 end 14 puts "waiting for gcd" 15 group.wait 16 puts "done" 17

Page 75: Fun with Ruby and Cocoa

Concurrent

Page 76: Fun with Ruby and Cocoa

1 #!/usr/bin/env macruby 2 3 group = Dispatch::Group.new 4 result = [] 5 1.upto(10).each do |i| 6 Dispatch::Queue.concurrent.async(group) do 7 sleep 2 8 result << i 9 end 10 end 11 group.wait 12 puts result.inspect

Page 77: Fun with Ruby and Cocoa

.plist files

Page 78: Fun with Ruby and Cocoa

1 #!/usr/bin/env ruby 2 require "osx/cocoa" 3 include OSX 4 5 file_name = 'Info.plist' 6 plist = NSDictionary.dictionaryWithContentsOfFile( 7 file_name 8 ) 9 10 plist['CFBundleVersion'] = '2.0.1' 11 12 plist.writeToFile_atomically( 13 file_name, 14 true 15 )

Page 79: Fun with Ruby and Cocoa

1 #!/usr/bin/env ruby 2 require "osx/cocoa" 3 include OSX 4 5 file_name = 'Info.plist' 6 plist = NSDictionary.dictionaryWithContentsOfFile( 7 file_name 8 ) 9 10 plist['CFBundleVersion'] = '2.0.1' 11 12 plist.writeToFile_atomically( 13 file_name, 14 true 15 )

Page 80: Fun with Ruby and Cocoa

1 #!/usr/bin/env ruby 2 require "osx/cocoa" 3 include OSX 4 5 file_name = 'Info.plist' 6 plist = NSDictionary.dictionaryWithContentsOfFile( 7 file_name 8 ) 9 10 plist['CFBundleVersion'] = '2.0.1' 11 12 plist.writeToFile_atomically( 13 file_name, 14 true 15 )

Page 81: Fun with Ruby and Cocoa

Keychain Access

Page 82: Fun with Ruby and Cocoa

Details

Page 83: Fun with Ruby and Cocoa

Details

• MacRuby has issues with void pointer (patch pending)

Page 84: Fun with Ruby and Cocoa

Details

• MacRuby has issues with void pointer (patch pending)

• Use objective c wrapper instead (dynlib or bundle)

Page 85: Fun with Ruby and Cocoa

Details

• MacRuby has issues with void pointer (patch pending)

• Use objective c wrapper instead (dynlib or bundle)

• Generate metadata to make it work

Page 86: Fun with Ruby and Cocoa

Details

• MacRuby has issues with void pointer (patch pending)

• Use objective c wrapper instead (dynlib or bundle)

• Generate metadata to make it work

Page 87: Fun with Ruby and Cocoa

1 export FILE_PATH = ~/Library/BridgeSupport 2 export FRAMEWORK_PATH = ~/Library/BridgeSupport/Security.bridgesupport 3 mkdir $FILE_PATH 4 gen_bridge_metadata -f Security -o $FRAMEWORK_PATH

Page 88: Fun with Ruby and Cocoa

1 require 'osx/cocoa' 2 include OSX 3 require_framework 'Security' 4 5 # Set up some relevant variables 6 7 service = "ch.huesler-informatik.scotrubyconf.keychain" 8 account = "Highlander" 9 original_password = "Rrrueby" 10 11 # Add password 12 SecKeychainAddGenericPassword( 13 nil, 14 service.length, 15 service, 16 account.length, 17 account, 18 original_password.length, 19 original_password, 20 nil 21 )

Page 89: Fun with Ruby and Cocoa

1 require 'osx/cocoa' 2 include OSX 3 require_framework 'Security' 4 5 # Set up some relevant variables 6 7 service = "ch.huesler-informatik.scotrubyconf.keychain" 8 account = "Highlander" 9 original_password = "Rrrueby" 10 11 # Add password 12 SecKeychainAddGenericPassword( 13 nil, 14 service.length, 15 service, 16 account.length, 17 account, 18 original_password.length, 19 original_password, 20 nil 21 )

Page 90: Fun with Ruby and Cocoa

1 # Add password 2 SecKeychainAddGenericPassword( 3 nil, 4 service.length, 5 service, 6 account.length, 7 account, 8 original_password.length, 9 original_password, 10 nil 11 )

Page 91: Fun with Ruby and Cocoa

1 # Query the keychain 2 status, *password = SecKeychainFindGenericPassword( 3 nil, 4 service.length, 5 service, 6 account.length, 7 account 8 )

Page 92: Fun with Ruby and Cocoa

1 # Password-related data. Shifting pointers 2 length = password.shift 3 data = password.shift 4 plain_password = data.bytestr(length) 5 6 puts "Password: #{plain_password}"

Page 93: Fun with Ruby and Cocoa

That’s all!Questions?