Ruby and Cocoa
 Facilitate development on OSX
About me
About me
Rapperswil
http://www.flickr.com/photos/turtlemom_nancy/4026208166/sizes/l/
huesler informatik
huesler informatik

upstream agile
huesler informatik

upstream agile

co-up.de
Ruby and Cocoa
Ruby
passionate
  http://www.flickr.com/photos/gi/378823/sizes/o/
Apple OSX
passionate
  http://www.flickr.com/photos/gi/378823/sizes/o/
Landscape
 http://www.flickr.com/photos/kiumo/4203883504/sizes/o/
Cocoa
http://www.flickr.com/photos/luder5/4100921399/sizes/l/
Cocoa
Cocoa
•   Core Foundation
Cocoa
•   Core Foundation
•   Appkit
Cocoa
•   Core Foundation
•   Appkit
•   Core * (Audio/Video/Image/Data
    etc.)
Cocoa
•   Core Foundation
•   Appkit
•   Core * (Audio/Video/Image/Data
    etc.)
•   Scripting bridge
Cocoa
•   Core Foundation
•   Appkit
•   Core * (Audio/Video/Image/Data
    etc.)
•   Scripting bridge
•   others
Interface Builder
XCode
Objective-C
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 array
for (NSString *s in anArray) {
     NSLog(s);
}
//Release the array
[anArray release]
Ruby And Cocoa
Ruby Cocoa
HotCocoa
picture shamelessly cropped from http://www.slideshare.net/mattetti/macruby-hotcocoa-presentation-by-rich-kilmer
Scripting Bridge
    http://www.flickr.com/photos/bensonkua/2851908095/sizes/l/
Good to know
Good to know
•   Available commands for each
    application are in a .sdef file
Good to know
•   Available commands for each
    application are in a .sdef file
•   Terminal.app/Contents/Resources/
    Terminal.sdef
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)
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
Control Terminal
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)
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)
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)
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)
Problems with MacRuby
Problems with MacRuby
Problems with MacRuby

•   Some methods don’t seem to be
    available
iTunes
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
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
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
Core Location
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   )
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   )
1 def locationManager(
2         manager,
3         didUpdateToLocation: new_location,
4         fromLocation: old_location
5   )
6
7   puts "loc: #{new_location.description}"
8 end
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   )
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   )
Grand Central
  Dispatch
Grand Central Dispatch
Grand Central Dispatch
•   MacRuby only
Grand Central Dispatch
•   MacRuby only
•   Synchronous
Grand Central Dispatch
•   MacRuby only
•   Synchronous
•   Asynchronous
Grand Central Dispatch
•   MacRuby only
•   Synchronous
•   Asynchronous
•   Parallel
Grand Central Dispatch
•   MacRuby only
•   Synchronous
•   Asynchronous
•   Parallel
•   Synchronization
Asynchronous
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
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
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
Synchronous
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
Synchronized
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
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
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
Concurrent
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
.plist files
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   )
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   )
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   )
Keychain
 Access
Details
Details
•   MacRuby has issues with void
    pointer (patch pending)
Details
•   MacRuby has issues with void
    pointer (patch pending)
•   Use objective c wrapper instead
    (dynlib or bundle)
Details
•   MacRuby has issues with void
    pointer (patch pending)
•   Use objective c wrapper instead
    (dynlib or bundle)
•   Generate metadata to make it
    work
Details
•   MacRuby has issues with void
    pointer (patch pending)
•   Use objective c wrapper instead
    (dynlib or bundle)
•   Generate metadata to make it
    work
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
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   )
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   )
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 )
1 # Query   the keychain
2 status,   *password = SecKeychainFindGenericPassword(
3           nil,
4           service.length,
5           service,
6           account.length,
7           account
8       )
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}"
That’s all!
Questions?

Fun with Ruby and Cocoa

  • 1.
    Ruby and Cocoa Facilitate development on OSX
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
    Cocoa • Core Foundation
  • 19.
    Cocoa • Core Foundation • Appkit
  • 20.
    Cocoa • Core Foundation • Appkit • Core * (Audio/Video/Image/Data etc.)
  • 21.
    Cocoa • Core Foundation • Appkit • Core * (Audio/Video/Image/Data etc.) • Scripting bridge
  • 22.
    Cocoa • Core Foundation • Appkit • Core * (Audio/Video/Image/Data etc.) • Scripting bridge • others
  • 23.
  • 24.
  • 25.
  • 26.
    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 array for (NSString *s in anArray) { NSLog(s); } //Release the array [anArray release]
  • 28.
  • 30.
  • 32.
    HotCocoa picture shamelessly croppedfrom http://www.slideshare.net/mattetti/macruby-hotcocoa-presentation-by-rich-kilmer
  • 33.
    Scripting Bridge http://www.flickr.com/photos/bensonkua/2851908095/sizes/l/
  • 34.
  • 35.
    Good to know • Available commands for each application are in a .sdef file
  • 36.
    Good to know • Available commands for each application are in a .sdef file • Terminal.app/Contents/Resources/ Terminal.sdef
  • 37.
    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)
  • 38.
    1 <command name="doscript" 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
  • 40.
  • 41.
    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)
  • 42.
    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)
  • 43.
    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)
  • 44.
    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)
  • 45.
  • 46.
  • 47.
    Problems with MacRuby • Some methods don’t seem to be available
  • 48.
  • 49.
    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
  • 50.
    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
  • 51.
    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
  • 52.
  • 53.
    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 )
  • 54.
    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 )
  • 55.
    1 def locationManager( 2 manager, 3 didUpdateToLocation: new_location, 4 fromLocation: old_location 5 ) 6 7 puts "loc: #{new_location.description}" 8 end
  • 56.
    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 )
  • 57.
    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 )
  • 58.
  • 59.
  • 60.
  • 61.
    Grand Central Dispatch • MacRuby only • Synchronous
  • 62.
    Grand Central Dispatch • MacRuby only • Synchronous • Asynchronous
  • 63.
    Grand Central Dispatch • MacRuby only • Synchronous • Asynchronous • Parallel
  • 64.
    Grand Central Dispatch • MacRuby only • Synchronous • Asynchronous • Parallel • Synchronization
  • 65.
  • 66.
    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
  • 67.
    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
  • 68.
    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
  • 69.
  • 70.
    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
  • 71.
  • 72.
    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
  • 73.
    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
  • 74.
    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
  • 75.
  • 76.
    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
  • 77.
  • 78.
    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 )
  • 79.
    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 )
  • 80.
    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 )
  • 81.
  • 82.
  • 83.
    Details • MacRuby has issues with void pointer (patch pending)
  • 84.
    Details • MacRuby has issues with void pointer (patch pending) • Use objective c wrapper instead (dynlib or bundle)
  • 85.
    Details • MacRuby has issues with void pointer (patch pending) • Use objective c wrapper instead (dynlib or bundle) • Generate metadata to make it work
  • 86.
    Details • MacRuby has issues with void pointer (patch pending) • Use objective c wrapper instead (dynlib or bundle) • Generate metadata to make it work
  • 87.
    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
  • 88.
    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 )
  • 89.
    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 )
  • 90.
    1 # Addpassword 2 SecKeychainAddGenericPassword( 3 nil, 4 service.length, 5 service, 6 account.length, 7 account, 8 original_password.length, 9 original_password, 10 nil 11 )
  • 91.
    1 # Query the keychain 2 status, *password = SecKeychainFindGenericPassword( 3 nil, 4 service.length, 5 service, 6 account.length, 7 account 8 )
  • 92.
    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}"
  • 93.