понедельник, 29 сентября 2008 г.

to_url_params

Занимаясь дальнейшим расширением стандартных классов:
require 'cgi'

class Hash
def to_url_params
self.map{|key,value|
CGI.escape(key.to_s) + '=' + CGI.escape(value.to_s)
}.join('&')
end
end

(На строки разбил, чтобы сюда влезло, так красивее в одну строку.)
{'a'=>5, 'url'=>'http://ya.ru'}.to_url_params
# "a=5&url=http%3A%2F%2Fya.ru"

Для конкурса на самую нечитаемую программу могу еще чуть короче (этот код я могу только написать, но не прочитать):
require 'cgi'

class Hash
def to_url_params
self.to_a.map{|a| a.map
{|x| CGI.escape(x.to_s)}.join('=')}.join('&')
end
end

пятница, 12 сентября 2008 г.

Вот пара красивостей. Буду рад, если кто-то из гуру увидит это и скажет, как это пишется красивее.

class Array
def max_field_value(field)
self.max{|a,b| a.send(field) <=> b.send(field)}.send(field)
end

def min_field_value(field)
self.min{|a,b| a.send(field) <=> b.send(field)}.send(field)
end
end

class Numeric
def bound(min, max)
[[self, min].max, max].min
end
end

вторник, 9 сентября 2008 г.

DRY

Пока это еще не работающий сайт, а развлечение, я всячески стараюсь следовать рекомендациям как писать красиво. DRY там и все дела. И заметил, что повторяюсь в коде вьюх:

<%= image_tag(get_photo_url(photo.file_path), {:alt => photo.title, :width =>photo.width, :height => photo.height} %>

Это просто выводит тег img для фотки. Так как фотку надо выводить в разных местах - аналогичный код повторяется. Тут photo это моделька фотки, а get_photo_url - метод хелпера.

Есть вариант сделать каждую вставку фотки в качестве render :partial => 'photo', :object => photo и вписать код выше в соответствующий partial (это я только что придумал. Про накладные расходы не знаю, но как-то слишком сложно. Не будем так делать).

Сначала я решил дать модели метод get_image_tag чтобы писать просто <%= photo.get_image_tag %>. Это потребовало подмешать в модели хелперы

include ActionView::Helpers::AssetTagHelper #предоставляющий image_tag
include ActionView::Helpers::TagHelper # нужный предыдущему
include ApplicationHelper # а тут мой get_photo_url

Ерунда какая-то. сомневаюсь, что модель должна заниматься такими вещами. Хотя код сокращается в итоге до <%= photo.get_image_tag %>

Правильная идея - добавить такие вещи как генераторы тегов в хелпер, как в фреймворке.
Тогда в модели нет мусора, а запись немногим сложнее: <%= get_image_tag photo %>.

Вот. А еще красивее чуть переписать настоящий image_tag и писать вообще <%= image_tag photo %>. Чего и вам желаю:)

воскресенье, 7 сентября 2008 г.

Загадка: что делает следущий фрагмент кода?

Это часть метода StrangerController#from_upload, и она работает:)

# upload['file'].original_filename - имя загруженного файла.
# temp_file - куда мы его временно записали
# File#extname - получает из пути файла его расширение

extension = File.extname(upload['file'].original_filename).sub(/\./, '')
read_method = "read_from_#{extension}".to_sym

track.respond_to?(read_method) && track.send(read_method, temp_file)

Грабли

Пока пишу натыкался на следующие грабли.
Вообще процесс отладки пока довольно непонятно как организовать - очевидно, ляпы в модели надо юнит-тестами долбить, а в контроллере?

- несмотря на десять предупреждений в agive web development with rails я умудрился вставить метод в контроллер после private. Типа

class MyController < ApplicationController
@photos = Photo.all
end

private
def some_inner_magick
#...
end

def new
@photo = Photo.new
end
end

Естественно, метод new при такм раскладе не будет доступен для веб-запроса типа localhost/my/new, но прямо этого не скажет:)
Вывод: когда собираешься сделать private-метод, писать всегда после него public. Убрать никогда не поздно:
private
def some_inner_magick
#...
end
public

def new
@photo = Photo.new
end
Вторые грабли - руби настолько динамический язык, что может переопределять методы хоть каждую строчку. И слова не скажет, пока ты будешь биться, правя первый из них, который "не работает":

def added_track
@track = Track.from_upload(params[:upload])

if @track
@track.save!
else
flash[:notice] = "Track could not be read!"
redirect_to :action => 'add_track'
end
end

# some stuff...

def added_track
# old stub
end

Еще одна "проблема" была в том, что ActiveRecord хоть и умен, но связь belongs_to все же надо указать и в миграции (например, t.references :track). Впрочем, тут сообщение об ошибке довольно информативно - видно, что в связанном запросе не хватает поля track_id. Долго искал, нормальное ли это поведение - указывать руками связь. Ленивый стал, всё хочу чтобы мне script/generate само.