вторник, октября 03, 2006

Inline layouts

В Rails уже давно есть возможность брать контент экшена не из файла, а передовать его в строке. Таким образом легко сделать простой CMS: берем шаблон из базы и делаем render :inline => . Проблема же построения более менее пригодного CMS заключалась в том, что подобную операцию нельзя было провести с лэйаутами.

Недавно я написал маленький плагин - Nested layouts (http://nested-layouts.rubyforge.org). Я решил, что возможность использования inline лэйаутов будет полезной добавкой к нему.

<% inside_inline_layout @layout_template do %>
content
<% end %>

Так что, кому надо - берите, пользуйте, пишите впечатления.

понедельник, октября 02, 2006

IF/UNLESS блоки в RJS шаблонах

Недавние занятия программированием с Ajax в Rails привели к созданию патча для поддержки создания IF блоков в javascript'е. Основная идея (помимо условий в виде строк) - поддержка элемент-прокси:

page.if page['element_id'].visible do
page['element_id'].hide
end

Проблема в том, что page['element_id'].visible сгенерит код до того, как будет обернуто в IF блок.
Также можно использовать более традиционные методы:

page.unless "$('element_id').visible()" do
page['element_id'].show
end

Патч был отклонен в core, но было предложено сделать плангин для обкатки.

Собственно, вот и он: http://rubyforge.org/projects/js-if-blocks. Инсталировать можно так:

./script/plugin install svn://rubyforge.org/var/svn/js-if-blocks/trunk/js-if-blocks

Тепрь думаю о добавлении возможности повторного использования proxy объектов, например:

element = page['element_id]
page.if element.visible do
element.hide
end

пятница, сентября 15, 2006

RJS tricks

Just have read presentation slides from Euro RailsConf 2006 talk "Sharing RJS: Reuse at the app level" and I think it is VERY helpful.

среда, сентября 13, 2006

Rails javascript generation and conditions

I've recently faced the problem: I need to generate javascript that would insert some HTML block unless it is already there. So, the code should be something like this:

if( !($('block_id')) ) {
// insert block
}

All that should go to RJS template.

First revision:

page << "if( !($('block_id')) ) {"
page.insert_html :before, 'container_id', 'my html'
page << "}"

Next, it would be nice to make use of JavaScriptProxy objects (that are spawn, e.g., by page#[] method). The problem is that HTML is emited when those proxy created or evaluated, thus it's not possible to wrap that javascript code into e.g. "if( ... )"..
Solution, first try:

class << page
def if(expr)
self << "if( #{expr} ) {"
yield if block_given?
self << "}"
end
end

page.if "$('#{element_id}').visible()" do
page[element_id].hide
end

This is better then the first one, but too ugly though. A better solution would include some black magick:

module AcitiveView
module Helpers
class JavaScriptProxy
def respond_to?(name)
return true if name.to_sym == :to_script
super
end

def to_script
@generator.instance_variable_get('@lines').pop.chomp(';')
end
end
end
end

Then:

class << page
def if(expr)
self << "if( #{expr.respond_to?(:to_script) ? expr.to_script : expr} ) {"
yield if block_given?
self << "}"
end
end

page.if(page[element_id].visible) do
...
end

And finally, the more peaceful variant:

module ApplicationHelper
def if_exists(page, element_id)
page << "if( !($('#{element_id}')) ) {"
yield if block_given?
page << "}"
end
end

if_exists page, 'block_id' do
page.insert_html .....
end

воскресенье, сентября 10, 2006

Site map

How do you divide site into areas ? How do you highlight current link ?

Definition

You've got site with several navigation bars, e.g. top menu and sidebar. You want current location highlighted on both top menu and sidebar.

Analysis

Rails has link_to_unless_current method to deal with such things, but it will not be enough: it handles link to only one page, but you need the bunch of pages be linked to. Link to can become selected only on one of the page, but on others it will not be selected (because they have different urls).

Solution

My solution was to develop some site structure markup that could associate each page with some "area" or "section" or kind of..

I wrote site_map plugin that handles that:

SiteMap.draw do |map|
map.area 'member' do
map.section 'profile', :controller => 'member' do
['profile', 'profile_update',
'company_profile', 'company_profile_update'
].each do |action|
map.location :action => action
end
end
end

map.area 'admin' do
map.section 'users', :controller => 'users'
map.section 'products', :controller => 'products'
end
end

In controller and in view there is a current_location method available that returns a location descriptor - object, that has area, section, etc. values populated with names that correspond to current page. You can use it to find out what link should be "selected":

<%= link_to_if current_location.section == 'users',
'User management', :controller => 'users' %>