четверг, 8 октября 2009 г.

Неправильная работа в JQuery функции $.browser.version для IE выше 6 версии. Incorrect work JQuery function $.browser.version for IE 6 version above

Совершенно неожиданно для себя, я открыл серьезный глюк в моем любимом Jquery. Если бы мне кто-то сказал, что функция определения версии броузера $.browser.version работает неправильно, я бы спорил на что угодно, что он неправ. Но....

Как известно, все познается на практике. Я совершенно случайно сделал такой финт:

alert('Browser version: '+ $.browser.version);

Каково же было мое удивление, когда я увидел на экране моего броузера IE8 попап: «Browser version: 6.0»!

Я немного погуглял и выяснилось – да, действительно, вражеская бажина... Для всех IE > 6 выводится, что текущая версия експлорера - 6.

Посмотрев множество решений проблемы, перечитав кучу статей, я собрал воедино все лучшее и переопределил для JQuery метод $.browser.version. Чтобы и у вас все работало правильно, необходимо переопределить в $(document).ready обработку таким образом:

(function($) {
  var userAgent = navigator.userAgent.toLowerCase();
  var ie_version = -1;
  if (navigator.appName == 'Microsoft Internet Explorer') {
   var re = new RegExp("msie ([0-9]{1,}[\.0-9]{0,})");
   if (re.exec(userAgent) != null)
   ie_version = parseFloat(RegExp.$1);
  }
  $.browser = {
    version: (ie_version>-1)?ie_version:(userAgent.match( /.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [0,'0'])[1],
    safari: /webkit/.test( userAgent ),
    opera: /opera/.test( userAgent ),
    msie: /msie/.test( userAgent ) && !/opera/.test( userAgent ),
    mozilla: /mozilla/.test( userAgent ) && !/(compatible|webkit)/.test( userAgent )
  };
})(jQuery);

Коментарии в коде думаю не понадобятся, тут все предельно ясно: для ИЕ надо использовать отдельное регулярное выражение. Так как дефолтное работает неправильно.

суббота, 3 октября 2009 г.

JQuery siblings VS find

Простенький пример:

$("div.test").find("li")  и $("div.test").siblings("li")

выполнят одну и ту же функцию - выберут из дива с классом test все элементы li, но по скорости siblings будет значительно выигрывать, так как ищет сестринские элементы в наборе, то-есть только элементы li, а find пропускает через себя все, оставляя только нужные.

Если у вас имеются большие куски ХТМЛ для парсинга, то вам следует использовать sibling если выбираются однотипные элементы. Это является неплохой оптимизацией работы поиска, особенно при больших объемах данных.

пятница, 2 октября 2009 г.

Универсальный механизм отправки AJAX запросов

Я хочу поделится своим опытом работы с JQuery для генерации AJAX запросов. Думаю что интересно будет не только новичкам.

Допустим, у нас есть какой-то проект, который находится на такой стадии разработки, что основной серверный функционал отлажен, дизайн существует в виде каркасов страниц. Нам ставят задачу - сделать обработку всех форм (причем для разных контроллеров) с помощью Аякса. Вполне понятно, что хотелось бы реализовать простой, удобный, а самое главное - универсальный механизм обработки AJAX запросов.

Для начала, нам понадобится подключить собственно сам JQuery, а также плагин JQuery Form. Советую создать JScript файл в котором мы и будем хранить наши универсальные методы обработки (удобно назвать например general.js).

В нем создаем такую функцию:

function FormSubmit(id,url,func){
   var params = $("#"+id).formSerialize();
   $.post(url,params, function(response){
    if(StatusOk(response)){
      call_func(func,response);
    }else{
      ShowValidation(response);
    }
   });
}

Функция принимает 3 параметра:
1 - ID формы со страницы, для того чтобы можно было сериализировать данные формы для отправки
2 - URL скрипта который будет обрабатывать запрос
3 - имя JScript функции-обработчика ответа

Добавим соглашение, что результатом обработки запроса, сервер вернет или текст валидационной ошибки, или: $id."Ok!"

Где id - номер обработанной записи в БД, или номер созданной записи и т.п.
StatusOk(response) - проверяет ответ сервера и возвращает TRUE если он успешный.
Код функции ниже:

function StatusOk(str){
   var re = /Ok!/;
   var result = re.test(str) ? true : false;
   return result;
}

Метод call_func(func,response) вызывает функцию по имени func и передает ей ответ сервера как параметр.

Реализацию привожу ниже:

function call_func(func, param){
  if(func!='') window[func](param);
}

Эта функция позволяет динамически вызывать функции по имени с передачей ей параметров.

ShowValidation(response) - стандартный метод в котором вы будете обрабатывать валидационные сообщения, например выводить их в какой-то общий див со специальным стилем.

Такой подход позволяет очень гибко обрабатывать формы. Например, мы в форме на кнопочку вешаем обработчик onclick='FormSubmit("form_id","/users/update", "userCreate")'

Наша функция соберет данные с формы заданной через id, передаст их на url скрипта обработки, после чего в случае успешного ответа - вызовет ф-ю userCreate (ее надо не забыть создать) и передаст туда ответ сервера для дальнейшей обработки. Как правило, эта функция должна обновлять набор данных на странице после их изменения (создание, редактирование). Если в ответе сервера не присутствует признак успешной операции, то значит ответом является валидационное сообщение которое надо отобразить.

Таким образом, все AJAX запросы выполняются в одном месте. Добавлением третьего параметра - вызов call-back функции, мы добавляем невероятную гибкость нашему методу. Он позволит вынести индивидуальную обработку ответа в нужное место в каждом конкретном случае. При этом, в call-back функции нам не надо заботится о Аяксе и о том как туда попали параметры.

Когда все запросы обрабатываются в одном месте, то вероятность возникновения ошибок, стремится к нулю. Необходимо только придерживаться соглашений для ответа сервера.

Вы можете всячески усовершенствовать данный механизм, который здесь приводится в облегченном варианте. Надеюсь что данный подход будет вам интересен и использован в своих приложениях.

Создаем JQuery фильтр для полей для предотвращения XSS атак. Create JQuery filter for XSS attacks avert

Столкнулся с задачей, когда необходимо фильтровать значение полей ввода, на предмет содержания в них HTML тегов, а также вызовов JScript функций.

В моем конкретном случае, дыра образовалась в инпуте, значение которого обрабатывалось JQuery методом.

Проблему решает написание JQuery фунции, которая может применятся к любому тегу который имеет значение val(). Я вырезаю из него все теги, а также содержимое всех скобок, вместе со скобками.

Реализацию функции привожу ниже:


(function($) {
  $.fn.stripTagsFromVal = function() {
    var regexp = /([<|(]("[^"]*"|'[^']*'|[^'">])*[>|)])/gi;
    this.each(function() {
        $(this).val(
          $(this).val().replace(regexp,"")
        );
      });
    return $(this);
  }
})(jQuery);


Приведу пример использования:
Пусть у нас есть input#test, зададим ему значение:


$("#test").val('< script>alert('XSS attack!')< / script>');


Применим нашу функцию и проверим что получим на выходе:


$("#test").stripTagsFromVal();
console.log('text: ',$("#test").val());


В консоле будет: text: alert, что означает что из всей конструкции осталось только безобидное имя функции alert.

Модифицировать даный пример, для конкретного вашего случая, не составит проблем. Для отладки регулярных выражений пользуйтесь замечательным online отладчиком Rubular

пятница, 14 августа 2009 г.

Firefox 3.5.2 первые впечатления

Новая версия ФФ должна была порадовать мировое сообщество ощутимым увеличением скорости, я сравнивал новый ФФ, Google Chrome, Opera 9.64. Визуально быстрее всех работает Опера, тестирование на выполнение JScript выиграл Google Chrome.

Firefox 3.5.2 работает быстрее своих старых версий, как и обещалось. Но, особого увеличения скорости я как юзер не увидел.. Виной тому являеся то, что я использую большое количество плагинов - что является как достоинством так и не достатком ФФ.

Хочу добавить - сейчас нельзя говорить что ФФ, Хром или Опера круче всех, потому что они постоянно тянутся друг за другом и это неможет не радовать. Конкуренция - двигатель прогресса.

Firefox 3.5.2 выгодно отличается поддержкой HTML 5, но думаю скоро это будет повсеместный стандарт среди броузеров.

Отдельно скажу о IE8 - он стал более адекватным чем 7 версия, пропала часть сугубо ИЕшных "приколов", стал быстрее. Но... По скорости он всеравно отстат от своих конкурентов. Да и для любого разработчика веб-приложений нет более ненависного броузера...

суббота, 1 августа 2009 г.

Радио РОКС - 102.4 FM

Здесь транслируют как современный рок, так и хиты 70-90 годов. Слушайте и получайте удовольствие!

Вывод WMZ без потерь и процентов

Если правильно все сделать, потери при выводе могут равнятся 0%! Если не сильно получится- то не больше 2%. Для того чтобы сэкономить деньги и не платить процент за снятие с карты читайте как правильно прикрутить свой webmoney акаунт к банковской платежной карте в этой статье.

Ваш успех (при правильном выборе БПК - банковской платежной карты) будет всецело зависеть от правильной ставки на бирже webmoney exchanger. Как правильно переводить валюту через нее написано тут.

Важно всегда помнить, чем быстрее вы хотите вывести деньги, тем больше у вас будут потери. Хорошо накатаная схема, позволяет поменять WMZ на другой валютный эквивалент webmoney примерно за 1 день. Для вывода на БПК потребуется тоже от 1 до 3 банковских дней. Чаще всего только 1. Таким образом можно за 2-3 дня вывести WMZ, совершенно без напрягов, рисков и потерь (зависит от того как вы играете на бирже))

пятница, 24 июля 2009 г.

Вывод Webmoney через УкрСоцБанк

Для вывода электронных денег в Украине используется сервис banking.ukrgarant.com, который позволяет:

- переводить деньги на банковскую карту
- перевод на банковский счет
- через систему банковских переводов «Аваль-Експрес» (комиссия банка 1.5% )
- через систему банковских переводов СОФТ от АКБ «Укрсоцбанк» (комиссия банка 1%)

Вам надо знать, что такие услуги возможны только для WMU - вебмани-гривен. Если вы имеете единицы Webmoney в другой валюте, то их надо будет обменять на WMU. Как правильно и без потерь это сделать - описано здесь.

Наиболее удобным способом является перевод на банковскую платежную карту (БПК). Именно из этих соображения я и опишу данный способ.

В качестве банка в котором открыть счет и сделать БПК - я выбрал УкрСоцБанк.

Причины выбора следующие:

- Банк считается одним из самых стабильных в Украине, так как входит в UnitCreditGroup. Это сообщество банков владеет общими финансовыми резервами, что означает меньшую вероятность банкротства и большую вероятность того, что в случае нового кризиса деньги вернут.
- комиссия за снятие денег составляет 0%, что позволяет свести к минимуму потери при выводе электронных денег на карту, а также при последующем их обналичивании.

Сервис в банке на достойном уровне, по сравнению с Приватом я очень удивился, так как при оформлении карты (VISA ELECTRON), ручку брал в руки всего пару раз - надо было расписаться возле галочек. Карта оформляется 10 дней, немного долго, но учитывая тот факт, что ваши денежные затраты при этом равняются 0 гривен - вполне нормально.

После того как карта получена, не забудьте взять в банке реквизиты, так как на сервисе banking.ukrgarant.com вам будет необходимо их указать.

Вы должны прикрепить свою БПК к вашему WMID (WebMoney identificator), для этого надо указать банковские реквизиты и подождать сутки (проверку).

В поле реквизитов Назначение укажите: ваш личный счет, ФИО, ИИН. Писать тут что-то еще - не стоит.

После успешного прикрепления БПК, сделайте перевод небольшой суммы для проверки. Деньги обычно приходят через сутки. После первого платежа на banking.ukrgarant.com нужно указать, что он прошел успешно.

Теперь все. Пользуйтесь своей картой для вывода.

Суммарная комиссия составляет:

0.8% за перевод webmoney + 1% за перевод на карту = 1.8%

вторник, 21 июля 2009 г.

Особенности обмена webmoney на электронной бирже Webmoney Exchanger

Часто возникает проблема обмена между WebMoney валютами. Например - для перевода WMZ на карту, необходимо сначала перевести их в WMU, с наименьшей потерей на курсе. Для таких операций существует сервис Webmoney Exchanger.

Обменять деньги можно тремя способами:

1) поменять непосредственно по курсу биржи. Но этот подход заведомо убыточный, так как курс обмена(берется среднее значение) здесь ОЧЕНЬ невыгодный и заниженный (особенно по сравнению с банками).

2) Вы можете КУПИТЬ WMU за свои кровные WMZ. Тут стоит рассказать немного о особенностях биржи. Вы выбираете направление по которому будете смотреть лоты, получаете их список.

Список отсортирован таким образом, что сверху (БЕЗ РАЗНИЦЫ ПО НАПРАВЛЕНИЮ ОБМЕНА) всегда будут самые выгодные предложения для вас как ПОКУПАТЕЛЯ.

Теперь вам необходимо выбрать нужную сумму(причем лот чтоб находился повыше в списке) и можно на нем кликнуть.

Далее вас перебросит на страницу оплаты вашей покупки. Подвоха тут никакого нету, можете смело переводить деньги и буквально сразу нужная сумма упадет вам на счет (-0.8% комиссии системы Webmoney).

3) самый выгодный для вас вариант. Необходимо НЕ ПОКУПАТЬ WMU, а совершенно наоборот - продавать свои WMZ.

Для этого понадобится немного времени и терпения. Открываем направление биржи где продают валюту, которая есть у вас на руках. Тут лоты отсортированы по возрастанию курса.

Смотрим по какому курсу продают например 10 лот(или можно взять середину списка), запоминаем курс.

Далее, создаем свою новую заявку и выставляем курс, который запомнили (такой курс обычно бывает даже выше, чем вы смогли бы обменять наличные в банке), оплачиваем заявку.

Оплачиваем заявку - означает буквальным образом, что мы ПЕРЕВОДИМ эту сумму на счет биржи (то-есть у нас со счета сумма снимается).
Продавец всегда может отозвать деньги назад - но потеряет 0.8% которые взяла Вебмани.

Таким образом, продавец всю сумму отдает в общий банк биржи - и как только появится встречная заявка по покупке - покупатель сразу получит деньги, как только оплатит свою заявку. Такой механизм делает работу на бирже безопасной, исключая возможность махинаций со стороны продавцов.

На протяжении всего времени, что ваш лот находится в системе, он постоянно меняет свой рейтинг в списке, так как верхние лоты покупаются и появляется определенное количество новых лотов, более дешевые чем ваши(по курсу) соответственно будут повыше, а более дорогие - пониже.

В целом, медленно но уверенно, ваш лот поднимается вверх (терять на курсе и ставить самую дешевую цену, обычно хочет меньшее количество людей, которым срочно надо деньги). Изначально выбранное стартовое место в списке - самый. важный параметр, от которого зависти - будет ваш лот подниматься или опускаться. - поэтому не жадничайте. И в один прекрасный момент - ваша заявка (лот) окажется самой выгодной и будет куплена. Деньги автоматом падают вам на счет, как только оплачена встречная заявка.

Талант игры на бирже, как-раз и заключается в том - чтобы подороже продать свою валюту. Если сильно завысить курс, то есть вероятность что лот будет пару дней не продан и курс валюты может легко упасть, что еще больше усугубит вашу ситуацию. Тогда нужно изменить курс заявки (бесплатно) к более приемлимому.

Информация для покупателей: встречную заявку отозвать уже нельзя - как только она оплачено совершается транзакция обмена лота покупателя и продавца, что есть необратимая операция.

P.S. Не пользуйтесь услугами менял! Не теряйте до 8% на их жадности!
Сделайте себе банковскую платежную карту, играйте на бирже - и ваши потери при выводе денег будут минимальными! Платить посреднику - глупо, лучше разобраться один раз с особенностями работы Webmoney и банковских переводов, и навсегда избавится от подобных проблем.


четверг, 16 июля 2009 г.

Передача JS массива Ajax $.post запросом. Send JS array to server using JQuery $.post method

Часто возникает проблема передачи массива (сформированного средствами JavaScript) Ajax запросом, используя библиотеку JQuery и ее методы $.get, $.post

На самом деле задача доволно несложная:

ids_list = array(1,2,3,4,5);
$.post("/controller/action",
   {'ids[]': ids_list},
   function(result){
     //обработка данных которые вернул сервер
  }
);

Достаточно указать что вы передаете массив, с помощью скобок [].

Хотя, существуют реализации, с использованием join() и прочих "ручных" методов преобразование массива в его JSON аналог, но пример показанный выше намного проще и элегантнее.

понедельник, 22 июня 2009 г.

Связи в моделях. Один ко многим

В предыдущей статье обсуждалась проблема связывания 2 моделей, как "1 к 1". Наша сегодняшняя тема - как связать модели один ко многим.

На самом деле, все не сложнее чем связь 1 к 1, потому чтобы не вдаваться в детали, сразу приведу пример:

class Order < ActiveRecord::Base
   belongs_to :user
end

class User < ActiveRecord::Base
   has_many :orders
end

Здесь описываем связь, когда каждый пользователь может иметь несколько заказов.

Как и в предыдущем примере, связь belongs_to в модели Order показывает что каждый ее экземпляр, может относится только к одному экземпляру модели пользователей (User).

В модели User есть новое для нас условие связи has_many :orders , которое, буквально дословно, означает, что каждый экземпляр класса (модели) User - имеет несколько экземпляров класса (модели) Order.

Вот впринципе и все описание. Теперь осталось рассмотреть несколько примеров использования :

Пример только в целях демонстрации, не используйте его для подсчета суммы:
user = User.find(id)
total = 0;
user.orders.each do |order|
   total += order.price
end

Правильно посчитать сумму можно так:
total = user.orders.sum(:price)

Мы легко можем оперировать свойством orders в цикле, где множественное число имени свойства показывает нам, что мы имеем дело с коллекцией экземпляров order, для конкретного экземпляра user.

Рассмотрим еще один пример, который продемонстрирует, как можно добавлять новые заказы пользователю, а также то, как их при этом создавать даже "на лету":

new_order = Order.create(:price => 150.00)
user.orders << new_order

или еще проще:

user.orders << Order.create(:price => 100.00)

Где "<<" - операция добавления нового значения в массив (коллекцию)

На первый взгляд может показаться, что Рельса не очень гибкая и довольно громоздкая. Но на самом деле все не так - после того, как я опишу все связи моделей, я посвящу несколько статей нестандартным способам описания связей.

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

пятница, 19 июня 2009 г.

Связи в моделях. Один к одному

Я давно хотел написать серию публикаций о связях в моделях, все не доходили руки. Этой статьей я начну серию статей которая раскроет суть взаимодействия моделей в Ruby on Rails.
Связь один к одному
Если у читателей был опыт разработки с использованием БД, то вероятно вам уже известно, как создавать SQL запросы на выборку из разных таблиц. В Ruby on Rails все выглядит немного более привлекательно. Так как Active Record является полноценной ORM, то вполне понятно, что работа со связанными таблицами может быть гораздо удобнее.

Как уже вы знаете, каждой таблице в БД, в ROR(ActiveRecord) сопоставляет одноименный обьект (модель), правда стоит учитывать, что имя таблицы должно быть во множественном числе, в отличии от имени обьекта(модели). Также для доступа к каждому полю таблицы существует одноименный метод в модели (Например таблица users содержит поле name, то в модели оно доступно, как obj.name, где obj - объект класса User). Потому и говорят что таблица "мапится" моделью - что и есть основным принципом ORM. Детальнее тут.

Кроме этого, как уже мною было описано ранее,в публикациях касающихся Модели, она содержит также - множество дополнительных функций, предназначенных для обработки полученных данных (всевозможные выборки тут и детальнее тут, валидация, а также связи между моделями).

Так как вместо таблиц мы используем модели, то нам хотелось бы написать что-то типа такого:
user.address.street - где User и Adress єкземпляры классов соответствующих моделей, а street - свойство экземпляра класса адреса. Заманчиво, не правда ли?

Для того, чтобы написать подобное, использем SQL:

SELECT addresses.name
FROM addresses INNER JOIN users on users.id = addresses.user_id
нам бы пришлось выполнить запрос и достать из него нужное поле... Как по мне, то немного неудобно.

Так как же, все-таки, изменить модели, чтобы мы могли так ими оперировать?
Именно это и является темой данной статьи. Давайте рассмотрим запрос на SQL более детально, так как нам надо выяснить для себе некоторые моменты.

Для того, чтобы связать две таблицы как "1 к 1", нам необходимо выяснить для себя - какая таблица является родительской, а какая подчиненной.

Оказывается, это довольно-таки просто: рассмотрим 2 строку запроса, а именно условие обьеденения
users.id = address.user_id
Таблица Addresses содержит первичный ключ таблицы Users, что автоматически делает ее подчиненной.

Сформулируем словами связи между таблицами: таблица Users содержит таблицу Addresses с одной стороны, таблица Addresses относится к таблице Users - с другой стороны. Все вполне логично. Теперь посмотрим, как все выше сказанное реализовать в моделях RoR:

class Address < ActiveRecord::Base
   belongs_to :user
   # дальше идет код который описывает управление данными модели
end
class User < ActiveRecord::Base
   has_one :address
   # дальше идет код который описывает управление данными модели
end

В самом начале описания модели, мы указываем как она связана с другими моделями. Для реализации связи "1 к 1", достаточно "сказать" модели User - что она has_one (содержит один) Address, а Address в свою очередь belongs_to(содержит) User. Вот и все!

Что мы получили в результате
С этого момента модели являются связанными между собой, и при построении запросов типа: user.address.street ActiveRecord автоматически будет добавлять связи.

Теперь возможно, используя связи, пользоваться подчиненной моделью (таблицей) как частью таблицы родителя(свойством модели родителя), ну думая о том, что на самом деле данные разнесены по разным таблицам.

Имеем полное право вычитать адресные данные пользователя:

User.address.find(:all)

street = User.find_by_id(id).address.street

пятница, 16 января 2009 г.

Перегенерить кеш для движка PHPFox

Недавно столкнулся с проблемой - как перегенерить весь кеш для сайта написаного на движке PHPFox?

Для того чтобы перегенерить весь кеш - необходимо удалить все содержимое папки кеша. Это бывает просто необходимым если вам надо задать другие значения константам для конфига которые хранятся в БД.

Например размер  максимально возможного для загрузки файла хранится в таблице phpfoxtest_sys_sett в переменной max_upload_size. Она в свою очередь доступна из приложения как App::getSetting('max_upload_size'). 

Именно для изминения значения этой константы мне и пришлось перегенерить весь кеш.

Для того чтобы довить новую переменную языка (тестовую константу которая имеет перевод на нужный язык) вам надо добавить ключ с переводами в таблицу phpfoxtest_language_phrase, а потом удалить файл languages_1.php в /file/cache/ 

CodeIgniter Partials

В контроллере Игнайтера создаем небольшой приватный метод:

private function template($data,$page='index'){
  $data['content'] = $this->load->view('controller_name/'.$page, $data, true);
  $data['menu'] = $this->load->view('shared/menu', array(), true);
  $this->load->view('shared/layout', $data);
 }
}

Мы собираем партиалы используя возможность метода load не только отображать страницу, но и сохранять ее в переменной если последним параметром передан TRUE.


Использовать метод нужно так:

function view()
{
  $data['title'] = $this->lang->line('title');
  $data['heading'] = $this->lang->line('heading');
  $this->template($data,'view');
}

Для того, чтобы достать партиалы в главном шаблоне нужно сделать так:


echo $content
echo $menu


Только надо вставить вызовы в нужное место cверстанного шаблона(вставка php кода в Html).

пятница, 9 января 2009 г.

Introduction to CodeIgniter

CodeIgniter - очень мощный и удобный ПХП фреймворк для создания веб-проектов. Постараюсь делится своими впечатлениями и расказывать о его возможностьях в последующих статьях, посвященных ему. 

Почему имеено CodeIgniter?

Мой любимый яык программирования безусловно только руби, но... программист должен писать на любом языке. Все же,  я хотел найти фреймворк для ПХП похожый на родные Релсы. Таковым на данный момент является CodeIgniter

Основные его особенности:
  • MVC архитектура и жесткая структура папок (точно так же как и в рельсах)
  • Встроен PHP аналог ActiveRecord, не такой мощный, это факт, но довольно удобный. При желании использовать что-то помощнее, не трудно подключит полноценный ORM - Doctrine.
  • Удобный роутер урлов( Аля-рельсы).
  • Удобное подключение БД.
  • Легкость настройки всех параметров фреймворка в одном месте (главный контролер, автозагрузка хелперов и т.д.)
  • Большой набор хелперов
  • Очень богатая и подробная ДОКУМЕНТАЦИЯ
  • Богатый набор компонентов и плагинов (например отличный пейдженатор).
  • Простое расширение функционала посредством установки компонент или расширения имеющихся
  • И самое главное для меня, все лежит почти там где и в рельсах, и по возможности реализованы особенности рельсов.
Но все же, ето далеко не Рельса... Сама мощь и красота руби делает рельсы рельсами. 

НИКОГДА Не забывайте ВЕЛИКОГО  НАСТАВНИКА, ибо он раскрывал и раскрывает новичкам и професионалам удивительный мир Ruby on Rails.