# @title RubyCocoa Programming

RubyCocoa Programming
=====================

## irb - Interactive Ruby

You may want to use `irb` to try the script snippets in this document.
`irb` is a command that lets you use a Ruby interpreter interactively from
the command line. You can start an interactive session with RubyCocoa using the
following command:

    % irb -r osx/cocoa

(NOTE) In Mac OS X 10.9,
use /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/irb.

## Load libraries

    require 'osx/cocoa'      # classes defined in Foundation and AppKit.

or

    require 'osx/foundation' # classes defined in Foundation
    require 'osx/appkit'     # classes defined in AppKit

## A first example with sensory appeal

Let's start with a simple example that will delight your senses -- this script
will play a sound. Try this with [irb]:

    include OSX
    files = `ls /System/Library/Sounds/*.aiff`.split
    NSSound.alloc.initWithContentsOfFile_byReference (files[0], true).play
    NSSound.alloc.initWithContentsOfFile_byReference (files[1], true).play
    NSSound.alloc.initWithContentsOfFile_byReference (files[2], true).play


## Cocoa classes are Ruby classes

In the following code examples, the comments starting with `# =>` show the
string Ruby will output when it executes that line of code.

    p OSX::NSObject # => OSX::NSObject
    nsstr = OSX::NSObject.description
    p nsstr         # => #<OSX::OCObject:0x5194e8 class='NSCFString' id=A97910>
    nsobj = OSX::NSObject.alloc.init
    p nsobj         # => #<OSX::NSObject:0x51f5b4 class='NSObject' id=976D90>

In RubyCocoa, a Cocoa class is defined as a Ruby class under the
`OSX` module. A Cocoa class is a Ruby class and behaves as a Cocoa object.


## Creating a Cocoa object

The normal Cocoa methods are used for creation of Cocoa objects.

    obj = OSX::NSObject.alloc.init
    str = OSX::NSString.stringWithString "hello"
    str = OSX::NSString.alloc.initWithString "world"

Inside RubyCocoa, the created Cocoa object is wrapped in the object of a class
called `OSX::ObjcID`. Usually, you don't need to be conscious of the
existence of an `OSX::ObjcID` class.


## Ownership and memory management

The instance of `OSX::ObjcID` is the real owner of the Cocoa object which
`self` has wrapped. Ownership is automatically lost when the instance of
`OSX::ObjcID` is cleaned by Ruby's garbage collection. Therefore, it is not
necessary to worry about memory management issues such as ownership in
RubyCocoa.

    str = OSX::NSString.stringWithString "hello"
    str = OSX::NSString.alloc.initWithString "world"

Although in Objective-C the two lines above differ as to whether ownership is
generated or delegated, in RubyCocoa there is no need to be conscious of
ownership; the difference between the two techniques shown above
is not such an important issue. In principle, it is not necessary to call
methods such as `release`, `autorelease`, and `retain`, and you do
not need to create `NSAutoreleasePool`s.

- Use Cocoa methods to create Cocoa objects.
- Don't worry about ownership and memory management.


## Return value of methods

    nstr = OSX::NSString.description
    p nstr      # => #<OCObject:0x7233e class='NSCFString' id=687610>
    p nstr.to_s # => "NSString"

    nstr = OSX::NSString.stringWithString "Hello World !"
    p nstr      # => #<OCObject:0x71970 class='NSCFString' id=688E90>
    p nstr.to_s # => "Hello World !"

    nstr = OSX::NSString.stringWithString(`pwd`.chop)
    nary = nstr.pathComponents
    p nary      # => #<OCObject:0x6bb2e class='NSCFArray' id=3C0150>

    ary = nary.to_a
    p ary       # => [#<OCObject:0x6a9b8 class='NSCFString' id=3C2B50>,...]

    ary.map! {|i| i.to_s }
    p ary       # => ["/", "Users", "hisa", "src", "ruby", "osxobjc"]

In RubyCocoa, methods that return Objective-C objects such as `NSString`
and `NSArray` return Cocoa objects, as you might have guessed from these
examples.  The return value is not automatically converted to the corresponding
Ruby class (`String`, for example). For `NSString` and `NSArray`,
`to_s` and `to_a` are defined and can be used.


## Representing Objective-C message selectors

    # play system sounds (2)
    sndfiles.each do |path|
      snd = OSX::NSSound.alloc.initWithContentsOfFile(path, :byReference, true)
      snd.play
      sleep 0.25 while snd.isPlaying?
    end

This is another version of "playing system sounds". This shows the other way
Objective-C message selectors can be represented in the Ruby world.

In Objective-C:

    [obj hogeAt: a0 withParamA: a1 withParamB: a2]

RubyCocoa provides several ways to specify message selectors. The simplest way
is to substitute "`:`" with "`_`".

    obj.hogeAt_withParamA_withParamB_ (a0, a1, a2)

But because this looks awkward, you can omit the last underscore.

    obj.hogeAt_withParamA_withParamB (a0, a1, a2)

When the method name is very long, the relationship between the message
selector keyword and each argument is unclear. In order to improve this:

    obj.hogeAt (a0, :withParamA, a1, :withParamB, a2)


For Cocoa methods that return the `BOOL` type (predicate method), use
the method name suffix "?" to return a Ruby boolean. If this suffix is omitted,
the method will return the value `0` (NO) or `1` (YES). These values
behave as `true` in the Ruby world, so you will get unexpected results.

    nary = OSX::NSMutableArray.alloc.init
    p nary.containsObject("hoge")   # => 0
    p nary.containsObject?("hoge")  # => false
    nary.addObject("hoge")
    p nary.containsObject("hoge")   # => 1
    p nary.containsObject?("hoge")  # => true


## Convert Ruby object method arguments when possible

It seems to be usual containsObject of the top and, in case of method
to catch Objective-C object as a value of argument, tries conversion
even if it just hands Ruby object so long as it is possible.


## Handling method name conflicts

    klass = OSX::NSObject.class
    p klass     # => OSX::OCObject
    klass = OSX::NSObject.oc_class
    p klass     # => #<OCObject:0x82f22 class='NSObject' id=80819B0C>

When the same method name exists in Ruby and Objective-C, like in the case of
`Object#class`, prefix the method name with "`oc_`".

## Inheriting from Cocoa

So far, we've discussed existing Cocoa classes and their
instances. From this point, we'll discuss the definition and
instantiation of derived class of Cocoa, which is also needed when
writing RubyCocoa applications. Since the implementation of derived
class mechanism for RubyCocoa is a little tricky, there are some
restrictions and peculiarities.


## Defining a Cocoa-inherited class

The class of the Cocoa objects set up in the GUI definition file (nib file)
created by Interface Builder is defined as an inherited class (after 0.2.0).
For example, the Controller of the MVC model as described in many Cocoa
tutorials is defined in Ruby like this:

    class AppController < OSX::NSObject
    
      ib_outlets :messageField
    
      def btnClicked(sender)
        @messageField.setStringValue "Merry Xmas !"
      end
    
    end

The inherited class definition of Cocoa in RubyCocoa is similarly
described to be the inherited class definition by the usual Ruby in
this way.


## Defining Interface Builder outlets

The outlet set as the class in the nib file is written to be:

    ns_outlets :rateField, :dollerField

in the definition of an inherited class. In fact, `ns_outlets` is the same
as `Module#attr_writer`. Therefore, a definition can alternatively be given
this way:

    def rateField= (new_val)
      @rateField = new_val
    end

`ns_outlets` also has an alias called `ib_outlets`.


## Overriding a method

When overriding a method defined by the parent class, it is necessary to
declare the override using `ns_overrides` (alias `ib_overrides`).

    class MyCustomView < OSX::NSView
    
      ns_overrides :drawRect_, 'mouseUp:'
    
      def drawRect(frame)
      end
    
      ...
    end

In the argument of `ns_overrides` what expressed the message selector of
Objective-C as the string or the symbol is given. However, the
notation for omitting ":" and "\_" of the end explained previously
cannot be used. It is necessary to describe correctly according to the
number of arguments.

To invoke the superclass method in an overriding method, prefix the method name
with "`super\_`".

    class MyCustomView < OSX::NSView
    
      ns_overrides :drawRect_
    
      def drawRect (frame)
        p frame
        super_drawRect(frame)   # invoke the implementation of NSView#drawRect
      end
    
    end


## Instantiating a Cocoa-inherited class

When an instance of a Cocoa-inherited class needs to be created in a Ruby
script, it writes like:

    AppController.alloc.init  # use this

like the case of the existing Cocoa class. The usual Ruby idiom:

    AppController.new    # don't use this

cannot be used (it raises an exception). Although there are various situations
in this, since it becomes long, a detailed explanation is omitted here.

These restrictions have deep relation in instance generation being
performed in the turn:

- alloc (in Objective-C world)
- in alloc, create a Ruby object (initialize method is called here)


## Where should initialization code be written?

In Ruby an initialization procedure is written in the
"`initialize`" method generally. But you should be careful in
doing so.  When the "initialize" method is invoked, a Cocoa object in
the Objective-C space is just only given memory. And it is not
initialized yet. Therefore, in the "initialize" method, you must not
invoke a method implemented in Objective-C space. You should use only
a method by Ruby at the point.

If the object needs to be loaded from a nib file, initializing by the
"awakeFromNib" method is safest. Doesn't it seem that it is also
necessary to actually define the inherited class of Cocoa in most of
these cases?

In other cases, initialization is done in the style of Cocoa's `init`.
It is probably a good idea to write to a method with a prefix.

Please do not forget to return `self` from initialization methods.
