Hexaの日記 このページをアンテナに追加 RSSフィード

2009-08-22

『たのしいCocoaプログラミング』のチュートリアルを RubyCocoa で書いてみた

Python Hack-a-thon #1 に参加して Python の勉強以外に,『たのしいCocoaプログラミング』のチュートリアルの「RSS リーダ」を RubyCocoa に書き換える,ということをやってみました.


Interface Builder で設定する箇所については,ほぼ『たのしいCocoaプログラミング[Leopard対応版]』と同じです.

(GC の設定だけは,デフォルトの状態から変更していません)


ソースは下記のようになります.

RubyCocoa のサンプルを見る限りでは,もっと,RubyCocoa らしい書き方があるようです.


require 'osx/cocoa'

class AppController < OSX::NSObject
  ib_outlet :urlTextField
  ib_outlet :titleTextField
  ib_outlet :linkTextField
  ib_outlet :tableView
  ib_action :readURL

  @document = OSX::NSXMLDocument

  def readURL(sender)
    url = OSX::NSURL.URLWithString @urlTextField.stringValue
    @document = OSX::NSXMLDocument.alloc.initWithContentsOfURL_options_error url, 0, nil

    return if @document.nil?

    nodes = @document.nodesForXPath_error "/rss/channel/title", nil
    if nodes.count == 0
      nodes = @document.nodesForXPath_error "/rdf:RDF/channel/title", nil
    end

    if nodes.count == 0
      nodes = @document.nodesForXPath_error "/feed/title", nil
    end

    if nodes.count == 1
      titleNode = nodes.objectAtIndex 0

      title = titleNode.stringValue
      @titleTextField.setStringValue title
    end

    nodes = @document.nodesForXPath_error "/rss/channel/link", nil
    if nodes.count == 0
      nodes = @document.nodesForXPath_error "/rdf:RDF/channel/link", nil
    end

    if nodes.count == 0
      nodes = @document.nodesForXPath_error "/feed/link", nil
    end

    if nodes.count == 1
      linkNode = nodes.objectAtIndex 0

      link = linkNode.stringValue
      @linkTextField.setStringValue link
    end

    @tableView.reloadData
  end

  def numberOfRowsInTableView(sender)
    return if @document.nil?
    
    nodes = @document.nodesForXPath_error "/rss/channel/item", nil

    if nodes.count == 0
      nodes = @document.nodesForXPath_error "/rdf:RDF/item", nil
    end

    if nodes.count == 0
      nodes = @document.nodesForXPath_error "/feed/entry", nil
    end

    nodes.count
  end
  
  def tableView_objectValueForTableColumn_row(tableView, tableColumn, row)
    return nil if @document.nil?

    identifier = tableColumn.identifier

    nodes = @document.nodesForXPath_error "/rss/channel/item", nil
    if nodes.count == 0
      nodes = @document.nodesForXPath_error "/rdf:RDF/item", nil
    end

    if nodes.count == 0
      nodes = @document.nodesForXPath_error "/feed/entry", nil
    end

    node = nodes.objectAtIndex row
    if identifier == "title"
      nodes = node.nodesForXPath_error "title", nil
      if nodes.count == 1
        node = nodes.objectAtIndex 0
        return node.stringValue
      end
    end

    if identifier == "link"
      nodes = node.nodesForXPath_error "link", nil
      if nodes.count == 1
        node = nodes.objectAtIndex 0
        return node.stringValue
      end
    end

  end
end

追記(2009/9/22)

ちょっと修正しました.

require 'osx/cocoa'

class AppController < OSX::NSObject
  ib_outlet :urlTextField
  ib_outlet :titleTextField
  ib_outlet :linkTextField
  ib_outlet :tableView
  ib_outlet :document

  ib_action :readURL |sender|
    url = OSX::NSURL.URLWithString @urlTextField.stringValue
    @document = OSX::NSXMLDocument.alloc.initWithContentsOfURL_options_error url, 0, nil

    return if @document.nil?

    nodes = @document.nodesForXPath_error "/rss/channel/title", nil
    if nodes.count == 0
      nodes = @document.nodesForXPath_error "/rdf:RDF/channel/title", nil
    end

    if nodes.count == 0
      nodes = @document.nodesForXPath_error "/feed/title", nil
    end

    if nodes.count == 1
      titleNode = nodes.objectAtIndex 0

      title = titleNode.stringValue
      @titleTextField.setStringValue title
    end

    nodes = @document.nodesForXPath_error "/rss/channel/link", nil
    if nodes.count == 0
      nodes = @document.nodesForXPath_error "/rdf:RDF/channel/link", nil
    end

    if nodes.count == 0
      nodes = @document.nodesForXPath_error "/feed/link", nil
    end

    if nodes.count == 1
      linkNode = nodes.objectAtIndex 0

      link = linkNode.stringValue
      @linkTextField.setStringValue link
    end

    @tableView.reloadData
  end

  def numberOfRowsInTableView(sender)
    return if @document.nil?
    
    nodes = @document.nodesForXPath_error "/rss/channel/item", nil

    if nodes.count == 0
      nodes = @document.nodesForXPath_error "/rdf:RDF/item", nil
    end

    if nodes.count == 0
      nodes = @document.nodesForXPath_error "/feed/entry", nil
    end

    nodes.count
  end
  
  def tableView_objectValueForTableColumn_row(tableView, tableColumn, row)
    return nil if @document.nil?

    identifier = tableColumn.identifier

    nodes = @document.nodesForXPath_error "/rss/channel/item", nil
    if nodes.count == 0
      nodes = @document.nodesForXPath_error "/rdf:RDF/item", nil
    end

    if nodes.count == 0
      nodes = @document.nodesForXPath_error "/feed/entry", nil
    end

    node = nodes.objectAtIndex row
    if identifier == "title"
      nodes = node.nodesForXPath_error "title", nil
      if nodes.count == 1
        node = nodes.objectAtIndex 0
        return node.stringValue
      end
    end

    if identifier == "link"
      nodes = node.nodesForXPath_error "link", nil
      if nodes.count == 1
        node = nodes.objectAtIndex 0
        return node.stringValue
      end
    end

  end
end