How to install ruby-debug on Windows and Ruby 1.9

Heute ein Artikel in Englisch, damit auch die internationale Gemeinde etwas davon hat ;-)

The Rails Debugging Guide told me that I have to install ruby-debug19 instead of ruby-debug when I want to debug on the Ruby 1.9 platform. So I tried to install it on a system which has Ruby 1.9.2 and Windows 7 x64 installed and I ran into trouble:

c:\Railsapps\tuneo>gem install ruby-debug19 --platform=mswin32
Temporarily enhancing PATH to include DevKit...
Building native extensions. This could take a while...
ERROR: Error installing ruby-debug19:
ERROR: Failed to build gem native extension.

C:/Ruby/bin/ruby.exe extconf.rb
checking for vm_core.h... *** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of
necessary libraries and/or headers. Check the mkmf.log file for more
details. You may need configuration options.

Provided configuration options:
--with-opt-dir
--without-opt-dir
--with-opt-include
--without-opt-include=${opt-dir}/include
--with-opt-lib
--without-opt-lib=${opt-dir}/lib
--with-make-prog
--without-make-prog
--srcdir=.
--curdir
--ruby=C:/Ruby/bin/ruby
--with-ruby-dir
--without-ruby-dir
--with-ruby-include
--without-ruby-include=${ruby-dir}/include
--with-ruby-lib
--without-ruby-lib=${ruby-dir}/lib
C:/Ruby/lib/ruby/1.9.1/mkmf.rb:368:in `try_do': The complier failed to generate
an executable file. (RuntimeError)
You have to install development tools first.
from C:/Ruby/lib/ruby/1.9.1/mkmf.rb:452:in `try_cpp'
from C:/Ruby/lib/ruby/1.9.1/mkmf.rb:834:in `block in have_header'
from C:/Ruby/lib/ruby/1.9.1/mkmf.rb:693:in `block in checking_for'
from C:/Ruby/lib/ruby/1.9.1/mkmf.rb:280:in `block (2 levels) in postpone
'
from C:/Ruby/lib/ruby/1.9.1/mkmf.rb:254:in `open'
from C:/Ruby/lib/ruby/1.9.1/mkmf.rb:280:in `block in postpone'
from C:/Ruby/lib/ruby/1.9.1/mkmf.rb:254:in `open'
from C:/Ruby/lib/ruby/1.9.1/mkmf.rb:276:in `postpone'
from C:/Ruby/lib/ruby/1.9.1/mkmf.rb:692:in `checking_for'
from C:/Ruby/lib/ruby/1.9.1/mkmf.rb:833:in `have_header'
from extconf.rb:15:in `block in

'
from C:/Ruby/lib/ruby/gems/1.9.1/gems/ruby_core_source-0.1.4/lib/ruby_co
re_source.rb:18:in `call'
from C:/Ruby/lib/ruby/gems/1.9.1/gems/ruby_core_source-0.1.4/lib/ruby_co
re_source.rb:18:in `create_makefile_with_core'
from extconf.rb:20:in `
'

Gem files will remain installed in C:/Ruby/lib/ruby/gems/1.9.1/gems/linecache19-
0.5.11 for inspection.
Results logged to C:/Ruby/lib/ruby/gems/1.9.1/gems/linecache19-0.5.11/ext/trace_
nums/gem_make.out

This is the way I solved it:
1. Start cmd, change to your devkit folder and execute devkitvars.bat
If you have not yet installed the devkit on your system check this Devkit on github.com

2. Execute the gem install command:
gem install ruby-debug19 --platform=mswin32Note: the platform depends on your ruby installtion. To find out which platform you are using, check your ruby version:
c:\Railsapps>ruby -v
ruby 1.9.2p0 (2010-08-18) [i386-mingw32]

After that the ruby debugger is running on the system. Don’t wonder if the execution of the install command takes a while.

Share

Named Routes in Models und anderen Klassen verwenden

Named Routes in Ruby on Rails sind eine feine Sache. Standardmäßig können sie im Controller und in Views verwendet werden, hier ist ihr Einsatzzweck vorgesehen. In seltenen Fällen kann es jedoch dazu kommen, dass man sie auch in einem Model oder einer anderen Klasse verwenden möchte. Das Erste was man feststellt ist, dass das nicht einfach geht.

Um die Named Routes in der Klasse verfügbar zu machen, muss das entsprechende Modul inkludiert werden:

  1. class App
  2.  
  3.   include ActionController::UrlWriter
  4.  
  5.   def initialize()
  6.     #…
  7.   end
  8. end

Wer lediglich relative Pfade ausgeben möchte, dem wird das genügen. Wer jedoch absolute Pfade mittels *_url Methoden nutzen möchte, der wird auf diese Fehlermeldung stoßen:

Missing host to link to! Please provide :host parameter or set default_url_options[:host]

Das Problem ist, dass der Host, für den die absoluten Pfade erstellt werden sollen, nicht bekannt ist. Dem kann abgeholfen werden, in dem man nach der include Anweisung die entsprechende Variable setzt:

  1. default_url_options[:host] = SITE_HOST

Wie man sieht, verwende ich die Konstante SITE_HOST, die ich in der config-Datei der entsprechenden Environment gepflegt ist. Der Einfachheit wegen, würde ich das auch so weiter empfehlen ;)

Share

Rails Console mit bestimmter Environment laden

Zum Debugging habe ich gerade nach der Möglichkeit gesucht, die Ruby on Rails Console, die standardmäßig in der Development Environment startet, innerhalb der Production Umgebung zu starten.

Die Lösung ist einfach – und war mir trotzdem nicht bekannt ;)

ruby script/console production

Es muss lediglich der Name der Umgebung ohne weitere Parameter angegeben werden.

Share

ActionMailer E-Mail Versand testen

Ich habe soeben nach einer Möglichkeit gesucht, um den Versand einer E-Mail mittels ActionMailer in meinen Functional-Tests zu testen.
Beim Rails-Testing werden die E-Mails nicht real versandt, sondern nur in einer Queue gespeichert. Das ermöglicht es einfach den Inhalt der Queue abzufragen und somit den Versand der E-Mail sicher zu stellen.
Allerdings sollte auch bedacht werden, dass dadurch nur das reine Erstellen der E-Mail geprüft wird. Mögliche SMTP-Fehler beim Versand etc. bleiben dadurch unentdeckt!

  1. test "should send lost password mail" do
  2.  ActionMailer::Base.deliveries.clear
  3.  post(:lost_pwd, { :username => 'calvin', :email => 'calvin@krani.net' })
  4.  assert !ActionMailer::Base.deliveries.empty?    
  5. end

In diesem Beispiel soll geprüft werden, ob nach dem Aufruf der „Lost Password“ Action auch wirklich die E-Mail mit dem neuen Passwort generiert wird.
Hinweis: Es empfiehlt sich genauso wie in dem Beispiel, in jedem Test zunächst die Queue zu leeren, um sicher zu stellen, dass nicht schon E-Mails aus vorhergehenden Tests in der Queue stehen.

Share

ActiveRecord: Vorsicht bei der Massenänderung von Attributen

Rails bietet einfache Möglichkeiten um einem Objekt alle benötigten Attribute zuzuweisen. Einem ActiveRecord Model können z.B. schon bei der Instanziierung alle gewünschten Attribute aus den Request-Parametern (z.B. aus einem Formular) übergeben werden. Dieses Vorgehen nennt sich im Rails-Jargon „Mass-assignment“.

Nehmen wir an wir haben ein Model „Comment“, dass die Kommentare von Benutzern einer Webseite speichert. Um Spam vorzubeugen, sollen alle Kommentare erst nach redaktioneller Überprüfung angezeigt werden, weshalb das Model ein Attribut „reviewed“ besitzt. Nur wenn dieses den Wert „1“ hat, wird der Kommentar öffentlich angezeigt.
In der Methode für die Speicherung eines neuen Kommentars steht nun folgender Code:

  1. @comment = Comment.new(params[:comment])

Würde der Anwender den Request nun so manipulieren (z.B. durch das Einfügen eines weiteren Feldes im HTML-Formular), dass dem Host der Parameter params[:comment][:reviewed] mit dem Wert „1“ übergeben wird, wäre die redaktionelle Prüfung umgangen.

Um das zu verhindern, gibt es in ActiveRecord die Methode attr_accessible. Mit dieser kann eine Whitelist der Attribute erstellt werden, die durch Mass-assignment verändert werden dürfen. Die Methode attr_protected bietet die selbe Funktionalität, jedoch mit einem Blacklist-Verfahren, d.h. es werden Attribute definiert, die nicht verändert werden dürfen.

Im Beispiel der Kommentare, müsste man also im Model „Comment“ diesen Code hinterlegen:

  1. attr_protected(:reviewed)

Dann kann das Attribut nur noch mit dem direkten Zugriff manipuliert werden z.B:

  1. comment.reviewed = true

Ich persönlich bevorzuge übrigens das Whitelist-Verfahren, da somit Flüchtigkeitsfehler bei der Entwicklung vermieden werden können ;-)

Share

Praktische Einzeiler In Ruby

Hier ein kurzer Tipp um den Quellcode in Rails bzw. Ruby etwas übersichtlicher zu gestalten.

In dem man in Ruby einer Anweisung eine Bedingung mittels if oder unless nachstellt, kann bestimmt werden, ob die Anweisung ausgeführt wird oder nicht.

Das heißt derartiger Code

  1. if @category_id == '1'
  2.    @title = 'Home'
  3. end

wird ersetzt durch:

  1. @title = 'Home' if @category_id == '1'

Bei Verwendung von unless statt if wird die Negativität der Bedingung geprüft.

Weiterhin können mit dem Schlüsselwort and zwei Anweisungen miteinander verknüpft werden.  Ich nutze das besonders oft um die Weiterleitung und die Übergabe einer Flash-Nachricht miteinander zu verbinden:

  1. flash[:error] = 'Objekt nicht gefunden' and redirect_to(index_path)
Share