środa, 21 grudnia 2011

#next na każdym znaku Stringa

Jakiś czas temu deliberowaliśmy ( :] ) na forum jak najszybciej i najbardziej elegancko rozwiązać wywołanie #next na każdym znaku Stringa. Jeżeli ktoś nie wie jak działa String#next:

"a".next #=> "b"
"b".next #=> "c"
view raw next.rb hosted with ❤ by GitHub

Niestety jeżeli wywołamy #next na ciągu znaków nie zadziała tak jakbyśmy chcieli:

"abc".next #=> "abd"
"xyz".next #=> "xza"
view raw next.rb hosted with ❤ by GitHub

środa, 14 grudnia 2011

Mongoid: polimorficzne embeds_one

Nie da się


Załóżmy, że nasza aplikacja ma model MediaObject, który posiada tytuł, głosy itd. ale specyficzne informacje n/t obiektu (treść/link etc.) są w innym modelu (Media::Text, Media::Image etc.). Relacja oczywiście jeden-do-jeden. Jeżeli zrobimy to na zasadzie referenced assosiation, czyli relacje rodem z baz SQL, będziemy mieli zawsze n+1 zapytań. Mongoid co prawda obsługuje eager loading, ale nie dla relacji polimorficznych. Co więc możemy zrobić?

Tworzymy model MediaObject:
# /app/models/media_object.rb
class MediaObject
include Mongoid::Document
embeds_one :resource, class_name: 'Media::Resource'
field :title, type: String
end
view raw media_object.rb hosted with ❤ by GitHub

Oraz model Media::Resource - po nim będą dziedziczyć wszystkie modele z modułu Media
# /app/models/media/resource.rb
class Media::Resource
include Mongoid::Document
embedded_in :media_object
end
view raw resource.rb hosted with ❤ by GitHub

Teraz możemy dodać np. model Media::Text
# /app/models/media/text.rb
class Media::Text < Media::Resource
field :content, type: String
end
view raw text.rb hosted with ❤ by GitHub

Użycie:
media_object = MediaObject.new(title: "This is title")
media_object.resource = Media::Text.new(content: 'This is content')
media_object.save
view raw usage.rb hosted with ❤ by GitHub


Żeby ułatwić sobie życie, możemy wydelegować #content do MediaObject
# /app/models/media_object.rb
class MediaObject
include Mongoid::Document
embeds_one :resource, class_name: 'Media::Resource'
field :title, type: String
delegate :content, to: :resource, allow_nil: true
end
view raw media_object.rb hosted with ❤ by GitHub

Opcja allow_nil pozwala trzymać w #content pustą wartość. W tym przypadku to bardzo ważne, bo nie wiemy czy napewno wszystkie modele Media będą miały taką metodę. Gdyby nie miały, bez tej opcji model zawrze rzucał by wyjątek. Teraz użycie wygląda tak:
media_object = MediaObject.first
puts media_object.content
# zamiast:
media_object = MediaObject.first
puts media_object.resource.content
view raw media.rb hosted with ❤ by GitHub

wtorek, 13 grudnia 2011

Bump to 1.9.3-p0

Dzisiaj (dopiero) zrobiłem update Ruby do wersji 1.9.3-p0. Dużo naczytałem się o poprawie startu appki, wykonywaniu testów itd.

Sprawdźmy jak to wygląda w praktyce :)

rvm get head
rvm reload
rvm install 1.9.3-p0
rvm use 1.9.3-p0 --default
view raw install-193.sh hosted with ❤ by GitHub

Po zainstalowaniu odpaliłem testy powstającej (a więc narazie niedużej) aplikacji Railsowej (Rails 3.1.3)

Ruby 1.9.2

Ruby 1.9.3

Średnie czasy:
Ruby 1.9.2 =~ 16.69
Ruby 1.9.3 =~ 11.12(!)

A więc różnica ponad 5.5s. Całkiem sporo jak na 140 testów.

Update'ować? Opinie są różne, ale wydaje mi się, że jeżeli ma się dobrze otestowaną aplikację można spróbować. Jeśli testy przejdą to po co tracić codziennie te kilka minut (5s * odpalanie_testów_miliard_razy)?