Neues in ActiveSupport 1.3.0

Object – Erweiterungen für alle Ruby-Objekte

with_options

Die neue Methode Object#with_options ermöglicht es, mehrere Methodenaufrufe eines Objektes mit denselben Optionen zu versehen.

Beispiel:
ActionController::Routing::Routes.draw do |map|
  # Routes für Account
  map.with_options(:controller => 'account') do |account|
    account.home   '',       :action => 'dashboard'
    account.signup 'signup', :action => 'new'
    account.logout 'logout', :action => 'logout'
  end

  # erzeugt die Aufrufe
  map.home   '',       :action => 'dashboard', :controller => 'account'
  map.signup 'signup', :action => 'new',       :controller => 'account'
  map.logout 'logout', :action => 'logout',    :controller => 'account'
end

Vorsicht: In der aktuellen Implementation überschreiben die allgemeinen Optionen die speziellen, also:

$stdout.with_options(:test => 'allgemein') do |foo|
  foo.p(:test => 'speziell')  #-> {:test=>"allgemein"}
end

Es gibt dafür einen Patch.

to_json

Jedes Objekt kann mit Object#to_json in einen JSON-String umgewandelt werden, Beispiele:

[true, false, nil].to_json --> [true, false, null]
'Hallo'.to_json --> "Hallo"
Person.find(:first).to_json --> {"attributes": {"id": "1", "name": "Scott Raymond"}}

extended_by

Object#extended_by liefert die Module, die in dieses Objekt eingemixt wurden, abzüglich Kernel.

class Foo
  include Enumerable
end
foo = Foo.new
foo.extend Comparable
foo.extended_by  #-> [Comparable, Enumerable]

Object#extend_with_included_modules_from (was für ein Name) sucht sich alle Mixins des Parameters und mixt sie in sich selbst ein:

x = 'dings'
x.extend_with_included_modules_from foo
x.is_a?(Comparable) && x.is_a?(Enumerable)  #-> true

copy_instance_variables_from

Object#copy_instance_variables_from kopiert die Instanzvariablen von einem Objekt in ein anderes.

Module und Klassen

Delegation (= Weiterleiten von Botschaften an andere Objekte) funktioniert jetzt nicht nur für einfache Objekte, sondern für beliebige Ausdrücke.

Simples Beispiel:

class Schaf
  def test
    'Määäh!'
  end
end

class Wolf
  delegate :test, :to => 'Schaf.new'
end

Wolf.new.test  #-> "Määäh!"

Beispiel für ein Model:

class Account < ActiveRecord::Base
  has_one :subscription
  delegate :free?, :paying?, :to => :subscription
  delegate :overdue?, :to => "subscription.last_payment"
end test
account.free?    # => account.subscription.free?
account.overdue? # => account.subscription.last_payment.overdue?

Symbol#to_proc

Das ist grandios: Einfache Blöcke mit &: erstellen; statt { |x| x.meth } schreibt man (&:meth).

# map:
'meine gitarre ist kaputt'.split.map(&:capitalize).join(' ')
#-> "Meine Gitarre Ist Kaputt"

# Sortieren nach Größe:
puts %w'Yodas Grammatik ist ziemlich schlecht'.
  sort_by(&:size).reverse.join(' ')
#-> Grammatik ziemlich schlecht Yodas ist

# Alle Administratoren finden:
@admins = User.find(:all).select(&:admin?)
@admins.each(&:save)

Zum Thema gibt es einen Artikel in der Ruby-Mine.

Enumerable

group_by

Die Methode Enumerable#group_by gruppiert eine Liste nach dem Ergebnis eines Blocks. Das Ergebnis ist ein Hash:

Pupil.find(:all).group_by(&:age)
#-> {10 => ['Klaus', 'Nadin'], 11 => ['Julian', ...], ...}

in_groups_of

Arrays haben jetzt eine Methode in_groups_of, die jeweils mehrere Elemente auf einmal an den Block liefert. In der Facet-Bibliothek heißt diese Methode each_slice.

Array(1..9).in_groups_of(4) {|g| p g}
#-> [1, 2, 3, 4]
#-> [5, 6, 7, 8]
#-> [9, nil, nil, nil]

to_xml

Hash und Array (und ActiveRecord::Base) besitzen nun die Methode to_xml:

Post.find(:all).to_xml(:include => :tags)

ergibt

<?xml version="1.0" encoding="UTF-8"?>
<entries>
  <entry>
    <tags>
      <tag>
        <entry-id>1</entry-id>
        <title>Ruby</title>
        <tag-id>1</tag-id>
        <id type="integer">1</id>
      </tag>
    </tags>
    <title>Ruby ist toll!</title>
    <id type="integer">1</id>
    <content>Ruby ist eine dynamische Scriptsprache aus Japan.</content>
    <is-public type="boolean">true</is-public>
    <created-at type="datetime">2006-03-30T22:18:13+02:00</created-at>
  </entry>
  <entry>
    <title>Hunger</title>
    <id type="integer">2</id>
    <content>Ich brauche was zu Essen</content>
    <is-public type="boolean">false</is-public>
    <created-at type="datetime">2006-03-30T22:18:13+02:00</created-at>
  </entry>
</entries>

Weiteres

Logger

Mit Logger#around_* kann man jetzt einfacher Meldungen mit Anfangs- und Endbotschaften umgeben:

logger.around_info "Starte Tierversuch...", "Tierversuch beendet." do
  for tier in tiere
    tier.test
  end
end

Kleinere Änderungen

  • Fixnum#seconds gibt einfach den Zahlenwert zurück, aber man kann jetzt schreiben:
Ei::KOCHZEIT = 3.minutes + 30.seconds
  • Time#beginning_of_quarter liefert den Anfang des Quartals, in dem der Zeitpunkt liegt.
  • Proc#bind und Object#instance_exec ermöglichen InlineRJS und andere nette Konstrukte. instance_exec ist eigentlich eine Methode aus Ruby 1.9, aber die Rails-Hacker haben einen (ziemlich unsauberen) Hack gefunden, der sie in Ruby 1.8 ersetzt. Das Ganze basiert laut Scott Raymond auf whys cloaker-Methode