Dienstag, 12. April 2011

ActiveRecord Migrations in Rails 3.0 Engines

Im aktuellen Rails 3.0 fehlen aus meiner Sicht noch einige recht wichtige Features der Rails-Engines:
  • Datenbank-Migrationen aus Eninges
  • Tasks zum Kopieren oder Verlinken von Assets (/public Verzeichnis in der Engine)
  • Die Möglichkeit eine Engine in den Routen der Applikation zu "mounten" 
All diese Punkte werden zwar mit dem Rails 3.1 Release voraussichtlich behoben sein, aber gerade die ersten beiden Punkte sind auch unter Rails 3.0 relativ einfach selbst zu adressieren. So genügt zum Einbetten und Bereitstellen eigener ActiveRecord-Migrationen ein einfacher rake-task (kopiert aus dem Rails core und leicht modifiziert):
    task :migrate => :environment do
      ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true
      ActiveRecord::Migrator.migrate(MeinEngineNamespace::Engine.find_root_with_flag("db").join('db/migrate'), ENV["VERSION"] ? ENV["VERSION"].to_i : nil)
      Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
    end
Der einzige Unterschied zu dem original-task im Rails core ist das MeinEngineNamespace::Engine.find_root_with_flag("db"). Dort steht im "original" ein einfaches Rails.root.
Ist der obige Task irgendwo in der Engine eingebaut (möglichst auch in einem aussagekräftigen Namespace), so können damit aus einer Applikation alle Migrations-Scripte im db/migration Verzeichnis der Engine "rauf" und auch wieder "runter" migriert werden (analog zum "gewohnten" db:migrate Task).

Donnerstag, 7. April 2011

Benutzerdefinierte HTTP Verben wie 'PURGE' mit Net::HTTP

Betreibt man eine Webanwendung beispielsweise hinter einem vorgelagerten Reverse-Proxy, so kann unter Umständen eine aktive Invalidierung der Cache-Inhalte des Proxys durch die Anwendung selbst erforderlich werden. Das bedeutet, dass die Anwendung aktiv den Reverse-Proxy anweist bestimmte URLs zu "vergessen". Proxys wie beispielsweise Squid bieten die Möglichkeit dies per HTTP mit dem nicht weiter standardisierten Verb "PURGE" zu erledigen. Bei anderen Proxys wie Varnish ist ein PURGE per HTTP zwar nicht vordefiniert aber relativ leicht zu konfigurieren.

Doch wie soll man einen solchen Request aus der Web-Anwendung absetzen? Im Ruby-Umfeld bietet Net::HTTP dafür die Möglichkeit eigene HTTP-Verben zu definieren. Dazu muss man an geeigneter Stelle die folgende Klasse definieren:
require 'net/http'

class Net::HTTP::Purge < Net::HTTPRequest
  METHOD = 'PURGE'
  REQUEST_HAS_BODY = false
  RESPONSE_HAS_BODY = true
end
Dies funktioniert natürlich äquivalent auch mit anderen Verben. Der Request selbst kann dann wie folgt abgesetzt werden:
  http = Net::HTTP.new("mein.proxy.de", "80")
  request = Net::HTTP::Purge.new("/url", "")
  http.request(request)