How to rename a file with file_column plugin.
Собственно, понадобилось… Время от времени изменять имя файла, загруженного с помощью file_column. Например так: stylesheet.css → stylesheet_1399194798.css.
Вот, что получилось.
Читать дальше »How to rename a file with file_column plugin.
Собственно, понадобилось… Время от времени изменять имя файла, загруженного с помощью file_column. Например так: stylesheet.css → stylesheet_1399194798.css.
Вот, что получилось.
Читать дальше »
Многие программисты уже используют Google Wave как средство командной разработки. В этом отношении, кстати, весьма полезный инструмент. Можно поделиться фрагментом сырого кода, прокомментировать или исправить проблемный код, предложить свой вариант. Рано или поздно каждый разработчик задает вопрос: «Как подключить подсветку синтаксиса в Google Wave»? Не так давно я нашел простое и удобное решение.

Проект TurnKey Linux представил подборку из 40 готовых минималистичных сборок Ubuntu 8.04.3 LTS для быстрого развертывания в Cloud-окружениях Amazon EC2 или для использования в качестве гостевых ОС в системах виртуализации VMware, Xen HVM, KVM или VirtualBox.
И в частности, – полностью работоспособную сборку с Ruby on Rails. Которая помимо стандартного ядра TurnKey, включает в себя следующее:
Сборка представлена в виде VM (221MB) и ISO-образа (195MB).
Узнал на OpenNews.
Есть хорошая рекомендация, а точнее – правило: Таблицы в HTML документе использовать только по прямому назначению. А именно – для отображения табличной информации. «Трудо-часы – трудо-доллары», «дебет-кредит-итого» и подобных структурированных данных. Все остальное, как говорится, – «от лукаваго».
Недавно наш новый командный tech writer начал доводить до ума проектный FAQ и попросил помочь ему избавиться от таблиц в разметке документа. Раз уж мы пытаемся уйти от «табличной верстки» в сторону «верстки дивной», то хорошо бы чтоб новые таблицы не появлялись там, где их не ждут. За несколько минут набросал ему HTML+CSS код для двухколоночного параграфа.
Код HTML: табличная верстка (было)
<table width="100%" border="0"><tbody>
<tr>
<td valign="top" width="50%">
<strong>Method 1 - By logging into your ***** account</strong>:
<ol><li>Go to My Account > My Account Page. This displays the Profile page.</li>
<li>In the Profile page, locate the My Account section.</li>
<li>Click on the Edit button. Enter the new Password as requested in the correct places.</li>
<li>Click on the Save button.</li></ol>
</td>
<td width="50%">
<img src="/images/change-password.gif" alt="change your password" height="202" width="212" hspace="12" align="right" border="0">
</td>
</tr></tbody></table>
Код HTML+CSS: дивная верстка (стало)
<div style="width:50%; float:left;">
<strong>Method 1 - By logging into your ***** account</strong>:
<ol><li>Go to My Account > My Account Page. This displays the Profile page.</li>
<li>In the Profile page, locate the My Account section.</li>
<li>Click on the Edit button. Enter the new Password as requested in the correct places.</li>
<li>Click on the Save button.</li></ol>
</div>
<img style="float:right; margin:0 0 0 12px; border-width:0;" src="/images/change-password.gif" alt="change your password" height="202" width="212" />
<div style="clear:both;"></div>
Разумеется, впоследствии я выделил стили для элементов в классы CSS и отправил писателю шаблонный HTML для использования.
Надеюсь, это окажется полезным. Успехов!
Изначально так сложилось, что для контроля версий я использовал Tortoise SVN. Но после аврального переезда на новую систему пришлось подыскивать новый SVN клиент, поскольку работа с консоли меня никак не прельщала. Выбор пал на RapidSVN. Привыкший к удобству «Тортойза», я не ожидал подвоха... и первый же коммит (svn commit) подарил много радостей всей команде. В итоге я задался вопросом как заставить SVN игнорировать выбранные файлы?
Команда svn propsetsvn propset свойство маска_файлов директория
Например, находясь в директории конфигурации проекта trunk/config:
svn propset svn:ignore development.* environments
Рекомендуется по возможности использовать другую команду svn propedit
svn propedit svn:ignore environments
В открывшемся редакторе свойств, можно задать несколько масок (каждая с новой строки):
development.*
test.rb
Для удаления свойств со всей директории используют команду svn propdel
svn propdel svn:ignore environments
Проделав необходимые манипуляции и жмакнув кнопку “Commit” с отвисшей челюстью проводил отправленный в базу «проигнорированный» development.rb
Йопт! В чем дело? Почему она игнорирует мое игнорирование?!! :)

Минут пять покурив SVN Book, я нашел причину. Оказывается, поддержка масок пропуска файлов действует только на unversioned файлы.
На данный момент остановился на варианте коммита отдельных директорий:
svn commit app public -m "fixes ticket #1234"
На тему другой системы контроля версий – Git
Бонус для RSS подписчиков – SVN wallpaper

Практически каждый разработчик имеет собственные предпочтения в выборе сторонних компонентов и библиотек, используемых в приложении. Не так давно мне попался такой топ-лист плагинов и gem-ов для разработки на Ruby on Rails.
Существует множество плагинов, которые мы используем для специальных нужд, как то генерирование PDF или обработка вложений (attachment). Однако это список наших избранных плагинов, которые мы используем практически в каждом проекте.
Это был список лучших 10 Rails плагинов и gem-ов от Глена Вандербурга (Glenn Vanderburg), опубликованный им в своем блоге: 10 Must-Have Rails Plugins and Gems (2009 Edition).
«Если кобыла сдохла, слезь с нее!» Именно так я вчера и поступил. А что еще делать? Прошел месяц (!!!), а «отмашки» все нет, равно как нет и хоть каких-нибудь комментариев или прогнозов.
В общем, «откладывать дальше уже некуда» © Goblin. Тем более коммитить (Commit – предавать) тему надо по-любому, пусть и в «альфе». Во-первых: уже «уплочено», во-вторых ее все равно на рассмотрение предоставить надо. А там уже решать, поскольку одной сменой лэйаута (layout – макет) дело не обойдется. Одних аяксовых (AJAX — «асинхронный JavaScript и XML») функциональностей нужно будет переделать на полтора ведра с ковшиком.
Предупредил народ и вечером таки закоммитил изменения в SVN репозиторий. Нехрена им “unversioned” болтаться. Кстати, RapidSVN отнюдь не порадовал.
Вот так. Буду искать новый проект.
К сожалению, это не единственная «дохлая кобыла», с которой мне придется таки слезть.
Читать дальше »Важная новость от Blogger Buzz! Команда Blogger запустила несколько расширений для Gadget API, которые позволят разработчикам ваять более релевантные, симпатичные и улыбчивые гаджеты для Blogger.
Каждый блог на данной платформе это контейнер гаджетов. Более того, благодаря Friend Connect, это еще и OpenSocial гаджет-контейнер. Это означает, что как разработчик ты можешь использовать social APIs для создания более удобных инструментов для блоггеров и их аудитории.
В добавок к использованию социальных инструментов, гаджеты теперь могут получать доступ к фидам (feeds) сообщений и комментариев блога через новые JSON APIs. Например, используя эти данные, ты можешь создать гаджет карты, на которой будешь отмечать географию сообщений, или гаджет популярных сообщений.
Недавно разработчики опросили пользователей, какие гаджеты для Blogger были бы наиболее востребованными. На основе сотен откликнувшихся был сформирован список пожеланий.
Когда ты создаешь гаджет для Blogger, он становится доступным миллионам активных блоггеров. Просто отправь свой гаджет нам, и в течении нескольких минут он появится в директории гаджетов Blogger где пользователи могут просматривать, настраивать и добавлять твой гаджет к своим блогам.
Лично я считаю, что это отличная новость. Не ковыряй в носу, начни создавать свои гаджеты прямо сейчас.
В продолжение программы по обновлению и дополнению «теоретического багажа» в данном посте разбираю парадигму ООП. Эта статья в первую очередь, – шпаргалка для себя. Поэтому постараюсь избежать излишней детализации. Дополнительные детали, на мой взгляд, «жизненно необходимы» лысеющим теоретикам, да судорожно содрогающимся студентам на экзаменах. ..Сдаваемых лысеющим теоретикам.
Объектно-ориентированное программирование (ООП) — парадигма программирования, в которой основными концепциями являются понятия объектов и классов.
Класс — это тип, описывающий устройство объектов. Понятие «класс» подразумевает некоторое поведение и способ представления. Понятие «объект» подразумевает нечто, что обладает определённым поведением и способом представления. Говорят, что объект — это экземпляр класса. Класс можно сравнить с чертежом, согласно которому создаются объекты. Обычно классы разрабатывают таким образом, чтобы их объекты соответствовали объектам предметной области.
Класс является моделью ещё не существующей сущности, объекта.
Объект — сущность, появляющаяся при создании экземпляра класса.
Абстрактные методы и классы
При построении сложной иерархии, для обеспечения полиморфизма программисты часто вынуждены вводить методы в классы верхнего уровня, даже если эти методы ещё не определены для этой специфической абстракции. Здесь можно было бы оставить пустые методы, но многие ОО языки предлагают такой специфический механизм, как определение абстрактных методов, то есть методов без реализации. Классы, имеющие хотя бы один абстрактный метод, часто называется абстрактным классом.
Множественное наследование и интерфейсы
Некоторые ОО языки допускают наследование более чем одному базовому классу. Другие языки позволяют вам наследовать только от одного класса, но дополнительно позволять вам наследовать также от многочисленных интерфейсов или чисто абстрактных классов, то есть классов, состоящих только из виртуальных функций.
Объектно-ориентированный подход заключается в следующем наборе основных принципов:
Как уже говорилось выше, в современных объектно-ориентированных языках программирования каждый объект является значением, относящимся к определённому классу). Класс представляет собой объявленный программистом составной тип данных, имеющий в составе:
Взаимодействие объектов в абсолютном большинстве случаев обеспечивается вызовом ими методов друг друга.
Инкапсуляция обеспечивается следующими средствами:
Почитать по теме:
Результат выполнения еще одного «тестового задания». ..И в который раз затрудняюсь с выбором кавычек. Выкладываю коды здесь, вдруг да когда пригодится. Использовался фреймворк Prototype. Сам текст задания, возможно, выложу позже.

document.opener (main.html);document.opener;





Изображения получены из открытых источников
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html dir='ltr' xmlns='http://www.w3.org/1999/xhtml'>
<head>
<title>Main Window</title>
<link type='text/css' rel='stylesheet' href='public/stylesheets/general.css' />
<script type="text/javascript" src="public/javascripts/prototype.js"></script>
<script type="text/javascript" src="public/javascripts/main.js"></script>
</head>
<body>
<p><input id="btnOpen" type="button" value="Изменить..." onclick="return btnOpen_onclick();" /></p>
<div id="connections"></div>
</body>
</html>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
<title>DHTML Tab Connection Panel</title>
<script type="text/javascript" src="public/javascripts/prototype.js"></script>
<link type='text/css' rel='stylesheet' href='public/stylesheets/general.css' />
<script type="text/javascript" src="public/javascripts/child.js"></script>
</head>
<body>
<p id="navBar">
<input id="btnAdd" type="button" tabindex="1" value="Добавить" onclick="tabView.addControl(); return false;" />
<a id="aTabPrev" href="#" tabindex="2" onclick="tabView.tabPrev(); return false;">←</a>
<kbd>Ctrl</kbd>
<a id="aTabNext" href="#" tabindex="3" onclick="tabView.tabNext(); return false;">→</a>
</p>
<div id="tabView1" class="tabView">
<div id="tabPanel" class="tabPane">
<div id="tabViewTab_0" class="tabActive" style="left: 0px;" onclick="tabView.tabSelect(0); return false;">
<div class="icon"></div>
<span tabindex="4"><span id="tabText_0" class="tabText">Guest</span>
<a href="#" onclick="tabView.destroy(0); return false;"><img class="close" width="8" height="8" src="public/images/icon_delete.gif" /></a>
</span>
<img class="tab_right" src="public/images/tab_right.gif" />
</div>
</div>
<div class="clear"></div>
<div id="aFormCont" class="aTab">
<form id="aForm" action="">
<input id="index" type="hidden" />
<div>Логин</div>
<div><input id="login" type="text" onchange="tabView.onChange();" /></div>
<div>Пароль</div>
<div><input id="password" type="password" onchange="tabView.onChange();" /></div>
<div><input id="remember_login" type="checkbox" onchange="tabView.onChange();" /><label for="remember_login">Запомнить логин</label></div>
<div><input id="remember_password" type="checkbox" onchange="tabView.onChange();" /><label for="remember_password">Запомнить пароль</label></div>
<div><input id="auto_login" type="checkbox" onchange="tabView.onChange();" /><label for="auto_login">Входить автоматически</label></div>
<p style="text-align:right;">
<input id="button_submit" type="button" value="Подключить" onclick="tabView.onChangeStatus();" />
</p>
</form>
</div>
</div>
</body>
</html>
/* General */
body{
margin:10px;
font-size:0.9em;
}
/* Main */
#connections p {
border:1px solid #919B9C;
padding: 0 0.2em;
width: 500px;
font-family: Trebuchet MS, Lucida Sans Unicode, Arial, sans-serif;
}
/* Child */
a{
color:#F00;
}
.tabView {
width:500px;
height:400px;
}
.tabPane{
height:21px; /* Height of tabs */
border-bottom:1px solid #919b9c;
}
.aTab{
border-left:1px solid #919b9c;
border-right:1px solid #919b9c;
border-bottom:1px solid #919b9c;
font-family: Trebuchet MS, Lucida Sans Unicode, Arial, sans-serif;
padding:5px;
}
.tabPane div{
float:left;
height:100%; /* Height of tabs */
padding-left:3px;
vertical-align:middle;
background-repeat:no-repeat;
background-position:bottom left;
cursor:pointer;
position:relative;
bottom:-1px;
margin-left:0px;
margin-right:0px;
}
.tabPane .tabActive{
background-image:url('../images/tab_left_active.gif');
margin-left:0px;
margin-right:0px;
z-index:10;
}
.tabPane .tabInactive{
background-image:url('../images/tab_left_inactive.gif');
margin-left:0px;
margin-right:0px;
z-index:10;
}
.tabPane .inactiveTabOver{
background-image:url('../images/tab_left_over.gif');
margin-left:0px;
margin-right:0px;
}
.tabPane span{
font-family:arial;
vertical-align:top;
font-size:11px;
padding-left:3px;
padding-right:3px;
line-height:21px;
float:left;
}
.tabPane .tabActive span{
padding-bottom:1px;
line-height:20px;
position: relative;
padding-left: 4px;
padding-right: 12px;
}
.tabPane .tabInactive span{
padding-bottom:1px;
line-height:20px;
position: relative;
padding-left: 3px;
padding-right:10px;
}
.tabPane img{
float:left;
}
.tabPane .tabActive img.tab_right {
background-image:url(../images/tab_right_active.gif);
}
.tabPane .tabInactive img.tab_right {
background-image:url(../images/tab_right_inactive.gif);
}
.tabActive a {
display:none;
}
.icon {
background-image:url(../images/globe_icon.gif);
background-repeat:no-repeat;
background-position:bottom left;
width:15px;
}
img.close {
position: absolute;
top: 6px;
right: 0;
border:0;
}
a img.close {
opacity:.01;
filter: alpha(opacity=1); /* IE */
-moz-opacity: 0.01; /* Firefox */
}
a:hover img.close {
opacity:1;
filter: alpha(opacity=100); /* IE */
-moz-opacity: 1; /* Firefox */
}
.tabText{
min-width:30px;
display:inline-block;
}
.clear {
clear: both;
}
/* Navigation Bar */
#navBar {
width:500px;
text-align:right;
}
#navBar input {
float:left;
}
#navBar a {
text-decoration:none;
}
#navBar kbd {
font-size:larger;
}
/* Form elements */
input[type="text"], input[type="password"] {border:1px solid #919b9c;}
/* for IE */
input { border:expression((this.type=='text')||(this.type=='password') ? '1px solid #919b9c' : ''); }
// <!CDATA[
var pPrefix = "connection_";
var prefixLogin = "login_";
document.addConnection = addConnection;
document.updateConnection = updateConnection;
document.destroyConnection = destroyConnection;
document.updateConnectionStatus = updateConnectionStatus;
/* Open Child document */
function btnOpen_onclick(){
open("child.html", "Child");
}
/* Helpers */
bool2human = function(value){
return (value == true) ? "да" : "нет";
}
humanStatus = function(value){
return (value == true) ? "Подключен" : "Отключен";
}
/* Dynamic template for #connections P.innerHTML */
getTemplate = function(attr){
return 'Логин: <span class="login">'+ attr.login +'</span><br />'+
'Пароль: <span class="password">'+ attr.password +'</span><br />'+
'Запомнить: логин (<span class="remember_login">'+ bool2human(attr.remember_login) +'</span>),'+
' пароль (<span class="remember_password">'+ bool2human(attr.remember_password) +'</span>)<br />'+
'Входить автоматически (<span class="auto_login">'+ bool2human(attr.auto_login) +'</span>)<br />'+
'<span class="status">'+ humanStatus(attr.status) +'</span>';
}
/* functions called by Child document */
function addConnection(attr){
try {
var p1 = document.createElement("p");
p1.id = pPrefix + attr.index;
var template = getTemplate(attr);
p1.innerHTML = template;
$("connections").appendChild(p1);
}
catch (e) {
alert(e.description);
}
}
function updateConnection(attr){
try {
var p1 = $(pPrefix + attr.index);
var template = getTemplate(attr);
p1.innerHTML = template;
}
catch (e) {
alert(e.description);
}
}
function destroyConnection(index){
var p1 = $(pPrefix + index);
Element.remove(p1);
}
function updateConnectionStatus(index, status){
var cssExpr = $$("#"+ pPrefix + index +" .status");
cssExpr[0].innerHTML = humanStatus(status);
}
// ]]>
document.onkeydown = navigateTabs;
function navigateTabs (event){
if (window.event) event = window.event;
if (event.ctrlKey){
switch (event.keyCode ? event.keyCode : event.which ? event.which : null){
case 0x25:
tabView.tabPrev();
break;
case 0x27:
tabView.tabNext();
break;
}
}
}
/* Class: Tab View */
TabView = function(){
this.tabPrefix = "tabViewTab_";
this.tabTextPrefix = "tabText_";
this.tabPanelId = "tabPanel";
this.items = new Array();
this.cnActive = "tabActive";
this.cnInactive = "tabInactive";
this.patternTabActive = "#tabView1 ." + this.cnActive;
this.tabIndexShift = 4; //сдвиг tabindex
this.init = function(){
this.add("Guest", "guest", true, true, false, false);
}
this.destroy = function(index){
if ( $(this.tabPrefix + index) != undefined ){
this.items[index] = null;
this.destroyConnection(index);
Element.remove(this.tabPrefix + index);
}
}
this.add = function(login, password, remember_login, remember_password, auto_login, status){
var index = this.items.length;
var Item = new Object();
Item.index = index;
Item.login = login;
Item.password = password;
Item.remember_login = remember_login;
Item.remember_password = remember_password;
Item.auto_login = auto_login;
Item.status = status;
this.items.push(Item);
this.addConnection(Item);
}
/* Functions for Document.Opener */
this.addConnection = function(obj){
opener.document.addConnection(obj);
}
this.updateConnection = function(obj){
opener.document.updateConnection(obj);
}
this.destroyConnection = function(index){
opener.document.destroyConnection(index);
}
this.updateConnectionStatus = function(index, status){
opener.document.updateConnectionStatus(index, status);
}
/* Helpers */
this.humanStatus = function(value){
return (value == true) ? "Отключить" : "Подключить";
}
this.checkbox2bool = function(value){
return (value == null) ? false : true;
}
/* DOM Functions */
this.addControl = function(){
this.add("", "", false, false, false, false);
var index = this.items.length-1;
var p1 = document.createElement("p");
var div1 = document.createElement("div");
div1.id = this.tabPrefix + index;
div1.className = this.cnInactive;//;
div1.onclick = function(){ tabView.tabSelect(index); return false; };
var div2 = document.createElement("div");
div2.className = "icon";
div1.appendChild(div2);
var span1 = document.createElement("span");
span1.setAttribute("tabindex", index*1 + this.tabIndexShift);
var span2 = document.createElement("span");
span2.id = this.tabTextPrefix + index;
span2.className = "tabText";
span1.appendChild(span2);
var a1 = document.createElement("a");
a1.href = "#";
a1.onclick = function(){ tabView.destroy(index); return false; };
var img1 = document.createElement("img");
img1.className = "close";
img1.setAttribute("width", 8);
img1.setAttribute("height", 8);
img1.setAttribute("src", "public/images/icon_delete.gif");
a1.appendChild(img1);
span1.appendChild(a1);
var img2 = document.createElement("img");
img2.className = "tab_right";
img2.setAttribute("src", "public/images/tab_right.gif");
div1.appendChild(span1);
div1.appendChild(img2);
$(this.tabPanelId).appendChild(div1);
}
this.fillForm = function(obj){
$("index").value = obj.index;
$("login").value = obj.login;
$("password").value = obj.password;
$("remember_login").checked = obj.remember_login;
$("remember_password").checked = obj.remember_password;
$("auto_login").checked = obj.auto_login;
$("button_submit").value = this.humanStatus(obj.status);
}
this.tabSelect = function(index){
//scope for each() function
cnActive = this.cnActive;
cnInactive = this.cnInactive;
if ( $(this.tabPrefix + index) != undefined ){
//off
$$(this.patternTabActive).each( function(elem){
Element.removeClassName(elem, cnActive);
Element.addClassName(elem, cnInactive);
});
//on
Element.removeClassName(this.tabPrefix + index, this.cnInactive);
Element.addClassName(this.tabPrefix + index, this.cnActive);
this.fillForm(this.items[index]);
return true;
}
return false;
}
/* onModyfy functions */
this.onChange = function(){
var index = $F("index");
var Item = this.items[index];
Item.index = index;
Item.login = $F("login");
Item.password = $F("password");
Item.remember_login = this.checkbox2bool( $F("remember_login") );
Item.remember_password = this.checkbox2bool( $F("remember_password") );
Item.auto_login = this.checkbox2bool( $F("auto_login") );
$(this.tabTextPrefix + index).innerHTML = Item.login;
this.updateConnection(Item);
}
this.onChangeStatus = function(){
var index = $F("index");
var Item = this.items[index];
Item.status = (Item.status == true) ? false : true;
this.updateConnectionStatus(Item.index, Item.status);
$("button_submit").value = this.humanStatus(Item.status);
}
/* Navigation through Tabs */
this.tabPrev = function(){
var index = $F("index");
for (var i = index*1-1; i >= this.items.first().index; i--){
if (this.tabSelect(i)){
break;
}
}
}
this.tabNext = function(){
var index = $F("index");
for (var i = index*1+1; i <= this.items.last().index; i++){
if (this.tabSelect(i)){
break;
}
}
}
this.init();
}
// End: Class TabView
// Init tabView
tabView = new TabView();
Event.observe(window, "load", function() {
tabView.fillForm(tabView.items[0]);
});
Вот как-то так :).
Пару дней назад озадачился генерированием файла sitemap.xml на Ruby on Rails для пользовательских торговых площадок. Приведу упрощенный пример без блогов, форумов и специальных страниц. В файле Sitemap будут url для:
Контроллер (store_controller.rb)
def sitemap
@store = Store.find(params[:id])
@items = @store.items.find :all,
:conditions => "available_for_sale = 1",
:order => "updated_at DESC",
:limit => 49997
end
В соответствии с концепцией MVC, процедуру выборки товаров лучше вынести в модель.
В рекомендациях по файлам Sitemap для Google оговаривается, что файл Sitemap может содержать не более 50000 URL. За вычетом страниц индекса, описания и контактов, остается 49997 позиций для URL товаров.
Представление (sitemap.xml.builder)
xml.instruct! :xml, :version => "1.0", :encoding=>"UTF-8"
xml.urlset(:xmlns => "http://www.sitemaps.org/schemas/sitemap/0.9") {
# Index
xml.url {
xml.loc(url_for :controller => :store, :action => :index, :id => @store.id, :format => :xml, :only_path => false)
lastmod = @items.first.updated_at.strftime("%Y-%m-%d") unless @items.nil?
lastmod = lastmod || Time.now.strftime("%Y-%m-%d")
xml.lastmod(lastmod)
xml.changefreq("daily")
xml.priority(1)
}
# About
xml.url {
xml.loc(url_for :controller => :store, :action => :about, :id => @store.id, :format => :xml, :only_path => false)
xml.lastmod(@store.updated_at.strftime("%Y-%m-%d"))
xml.changefreq("monthly")
xml.priority(0.7)
}
# Contact
xml.url {
xml.loc(url_for :controller => :store, :action => :contact, :id => @store.id, :format => :xml, :only_path => false)
xml.lastmod(@store.updated_at.strftime("%Y-%m-%d"))
xml.changefreq("monthly")
xml.priority(0.5)
}
# Items
@items.each do |item|
xml.url {
xml.loc(url_for :controller => 'store', :action => 'item', :id => item.id, :format => :xml, :only_path => false)
xml.lastmod(item.updated_at.strftime("%Y-%m-%d"))
xml.changefreq("weekly")
xml.priority(0.9)
}
end
}
Страницы пользовательских магазинов у меня выводятся в формате XML с (XSL трансформацией) и при генерации url я использую параметр :format => :xml. В стандартном выводе этот параметр не нужен.
Для каждого URL адреса тег <loc> является обязательным; остальные теги являются дополнительными.
Главная страница магазина (index) и страницы товаров (item) являются наиболее важными страницами, поэтому имеют более высокий приоритет. Страница описания и страница контактов, имхо уже «на интерес». Следовательно, имеют приоритет более низкий.
Индексная страница обновляется довольно часто за счет добавления новых товаров и выбывания товаров из листинга. Поэтому обновляется ежедневно (daily).
Страницы товаров тоже обновляются довольно часто. За счет добавления отзывов, комментариев клиентов. Но эта информация не особо важна. Спокойно могу задать им еженедельное (weekly) обновление.
Информация о магазине и контактах, как правило, изменяется крайне редко, и обновлять ее стоит не чаще, чем раз в месяц (monthly).
Успехов на рельсах! © dotrb.blogspot.com

Сегодня коллега попросил помочь разобраться со странным поведением браузера Chrome.
На странице два элемента <iframe>. В одном чат, в другом ротатор баннеров.
<!-- В этом фрейме работает флешевый чат -->
<iframe id="frame_chat" src="/userplane/chat" frameborder="no"></iframe>
<!-- В этом фрейме сторонний скрипт баннеро-ротатор -->
<iframe id="frame_ads" src="http://third_part_banner_rotator" frameborder="no"></iframe>
Скрипт приводить не буду. Действует так: через каждые 30 секунд создает frameset с фреймами. Во фреймах различные составные рекламные баннеры.
Все браузеры (даже IE!), как и задумано, перезагружают только содержимое ифрейма "frame_ads", а Chrome – страницу целиком.
Перекопано было все, что только можно, по ходу исправлено еще несколько неаккуратностей, а проблема решилась до безобразия просто. Переписал тег второго ифрейма в сокращенной XHTML форме:
<iframe id="frame_ads" src="http://third_part_banner_rotator" frameborder="no" />
И всё заработало :). Сыроват еще «Хром», сыроват...
Многим людям нравится черно-белое фото. Такое фото создает некий эффект «классики», «старомодности», зрелости. Vintage -- одним словом. Вы не знали? -- «раньше небо было голубее, солнце желтее...»*, а PHP и в помине не было.function grayscale($filename){
//Получаем размеры изображения
$img_size = GetImageSize($filename);
$width = $img_size[0];
$height = $img_size[1];
//Создаем новое изображение с такмими же размерами
$img = imageCreate($width,$height);
//Задаем новому изображению палитру "оттенки серого" (grayscale)
for ($c = 0; $c < 256; $c++) {
ImageColorAllocate($img, $c,$c,$c);
}
//Содаем изображение из файла Jpeg
$img2 = ImageCreateFromJpeg($filename);
//Объединяем два изображения
ImageCopyMerge($img,$img2,0,0,0,0, $width, $height, 100);
//Сохраняем полученное изображение
imagejpeg($img, $filename);
//Освобождаем память, занятую изображением
imagedestroy($img);
}function show_grayscale($filename){
$img_size = GetImageSize($filename);
$width = $img_size[0];
$height = $img_size[1];
$img = imageCreate($width,$height);
for ($c = 0; $c < 256; $c++) {
ImageColorAllocate($img, $c,$c,$c);
}
$img2 = ImageCreateFromJpeg($filename);
ImageCopyMerge($img,$img2,0,0,0,0, $width, $height, 100);
//Отдаем полученное изображение браузеру
header("Content-type: image/jpeg");
imagejpeg($img);
imagedestroy($img);
}+) ваше приложение нагружается всего один раз -- когда вы сохраняете изображение;-) на сервере у вас хранятся черно-белые изображения. Цветных вы из них уже не сделаете.+) на сервере у вас хранятся цветные изображения. И вы всегда можете выбрать как их отобразить;-) ваше приложение нагружается каждый раз -- когда вы отдаете черно-белое изображение браузеру.
Код ruby
simple_colors = {
:aliceblue => "f0f8ff",
:antiquewhite => "faebd7",
:aqua => "00ffff",
:aquamarine => "7fffd4",
:azure => "f0ffff",
:beige => "f5f5dc",
:bisque => "ffe4c4",
:black => "000000",
:blanchedalmond => "ffebcd",
:blue => "0000ff",
:blueviolet => "8a2be2",
:brown => "a52a2a",
:burlywood => "deb887",
:cadetblue => "5f9ea0",
:chartreuse => "7fff00",
:chocolate => "d2691e",
:coral => "ff7f50",
:cornflowerblue => "6495ed",
:cornsilk => "fff8dc",
:crimson => "dc143c",
:cyan => "00ffff",
:darkblue => "00008b",
:darkcyan => "008b8b",
:darkgoldenrod => "b8860b",
:darkgray => "a9a9a9",
:darkgreen => "006400",
:darkkhaki => "bdb76b",
:darkmagenta => "8b008b",
:darkolivegreen => "556b2f",
:darkorange => "ff8c00",
:darkorchid => "9932cc",
:darkred => "8b0000",
:darksalmon => "e9967a",
:darkseagreen => "8fbc8f",
:darkslateblue => "483d8b",
:darkslategray => "2f4f4f",
:darkturquoise => "00ced1",
:darkviolet => "9400d3",
:deeppink => "ff1493",
:deepskyblue => "00bfff",
:dimgray => "696969",
:dodgerblue => "1e90ff",
:feldspar => "d19275",
:firebrick => "b22222",
:floralwhite => "fffaf0",
:forestgreen => "228b22",
:fuchsia => "ff00ff",
:gainsboro => "dcdcdc",
:ghostwhite => "f8f8ff",
:gold => "ffd700",
:goldenrod => "daa520",
:gray => "808080",
:green => "008000",
:greenyellow => "adff2f",
:honeydew => "f0fff0",
:hotpink => "ff69b4",
:indianred => "cd5c5c",
:indigo => "4b0082",
:ivory => "fffff0",
:khaki => "f0e68c",
:lavender => "e6e6fa",
:lavenderblush => "fff0f5",
:lawngreen => "7cfc00",
:lemonchiffon => "fffacd",
:lightblue => "add8e6",
:lightcoral => "f08080",
:lightcyan => "e0ffff",
:lightgoldenrodyellow => "fafad2",
:lightgrey => "d3d3d3",
:lightgreen => "90ee90",
:lightpink => "ffb6c1",
:lightsalmon => "ffa07a",
:lightseagreen => "20b2aa",
:lightskyblue => "87cefa",
:lightslateblue => "8470ff",
:lightslategray => "778899",
:lightsteelblue => "b0c4de",
:lightyellow => "ffffe0",
:lime => "00ff00",
:limegreen => "32cd32",
:linen => "faf0e6",
:magenta => "ff00ff",
:maroon => "800000",
:mediumaquamarine => "66cdaa",
:mediumblue => "0000cd",
:mediumorchid => "ba55d3",
:mediumpurple => "9370d8",
:mediumseagreen => "3cb371",
:mediumslateblue => "7b68ee",
:mediumspringgreen => "00fa9a",
:mediumturquoise => "48d1cc",
:mediumvioletred => "c71585",
:midnightblue => "191970",
:mintcream => "f5fffa",
:mistyrose => "ffe4e1",
:moccasin => "ffe4b5",
:navajowhite => "ffdead",
:navy => "000080",
:oldlace => "fdf5e6",
:olive => "808000",
:olivedrab => "6b8e23",
:orange => "ffa500",
:orangered => "ff4500",
:orchid => "da70d6",
:palegoldenrod => "eee8aa",
:palegreen => "98fb98",
:paleturquoise => "afeeee",
:palevioletred => "d87093",
:papayawhip => "ffefd5",
:peachpuff => "ffdab9",
:peru => "cd853f",
:pink => "ffc0cb",
:plum => "dda0dd",
:powderblue => "b0e0e6",
:purple => "800080",
:red => "ff0000",
:rosybrown => "bc8f8f",
:royalblue => "4169e1",
:saddlebrown => "8b4513",
:salmon => "fa8072",
:sandybrown => "f4a460",
:seagreen => "2e8b57",
:seashell => "fff5ee",
:sienna => "a0522d",
:silver => "c0c0c0",
:skyblue => "87ceeb",
:slateblue => "6a5acd",
:slategray => "708090",
:snow => "fffafa",
:springgreen => "00ff7f",
:steelblue => "4682b4",
:tan => "d2b48c",
:teal => "008080",
:thistle => "d8bfd8",
:tomato => "ff6347",
:turquoise => "40e0d0",
:violet => "ee82ee",
:violetred => "d02090",
:wheat => "f5deb3",
:white => "ffffff",
:whitesmoke => "f5f5f5",
:yellow => "ffff00",
:yellowgreen => "9acd32"
}Код ruby: сортировка по насыщенности
ordered_colors = simple_colors.sort{|a,b| ( a[1][0,2].to_i(16)+a[1][2,2].to_i(16)+a[1][4,2].to_i(16) )<=>(b[1][0,2].to_i(16)+b[1][2,2].to_i(16)+b[1][4,2].to_i(16))}sort класса Hash возвращает массив (Array).Пример: hsh.sort -> anArray
h = { "a" => 20, "b" => 30, "c" => 10 }
h.sort » [["a", 20], ["b", 30], ["c", 10]].rhtml):<style type="text/css">
.colorPick {
width:16px; height:16px; float:left;
border:2px solid #FFF;
margin:1px;
}
</style>
<!-- ... -->
<%
ordered_colors.each do |color|
name = color[0]
value = color[1]
%>
<div class="colorPick"
style="background-color: #<%= value %>"
title="<%= name %>">
</div>
<% end %>
id и собственное значение value. В группу же они объединяются по атрибуту name (name у них одинаковый).<input type="radio" name="font-face" id="font-face-georgia" value="Georgia, Times, serif" />
<label for="font-face-georgia" style="font-family: Georgia;" class="font-label">Georgia</label>
<input type="radio" name="font-face" id="font-face-arial" value="Arial, sans-serif" />
<label class="font-label" style="font-family: Arial;" for="font-face-arial">Arial</label>
<input type="radio" name="font-face" id="font-face-courier" value="Courier, monospace" />
<label for="font-face-courier" style="font-family: Courier;" class="font-label">Courier</label>
form) отправляется value переключателя, у которого установлен атрибут checked или checked="checked".var radioGrp = document['forms']['form_name_or_id']['radio_grp_name'];
for(i=0; i < radioGrp.length; i++){
if (radioGrp[i].checked == true){
var radioValue = radioGrp[i].value;
}
}
name или id формы и name группы переключателей.function $RF(el, radioGroup) {
if($(el).type && $(el).type.toLowerCase() == 'radio') {
var radioGroup = $(el).name;
var el = $(el).form;
} else if ($(el).tagName.toLowerCase() != 'form') {
return false;
}
var checked = $(el).getInputs('radio', radioGroup).find(
function(re) {return re.checked;}
);
return (checked) ? $F(checked) : null;
}form (id или object) и radio group name, или посредством radio button (id или object).var value = $RF('radio_btn_id');
var value = $RF('form_id', 'radio_grp_name');$), а подстановку значений переменных -- с ним. Блажь у меня такая.@server constants {
variableName: variableValue;
}
body {
color: $variableName;
}Пример: необработанный HTML
<style type="text/css">
@server constants {
bodyBackgroundColor: #FFF;
bodyFaceColor: #000;
bodyFont: 11px/1.5em Arial, Helvetica, sans-serif;
}
body {
font: $bodyFont;
background-color: $bodyBackgroundColor;
color: $bodyFaceColor;
}
</style>
<style type="text/css"></style>.Ruby-модуль css_ssc_module.rb
def ssc_get_style_contents(pattern, text)
matches = text.scan(pattern)
str_out = matches.to_s
return str_out
end
pattern (regular expression) -- шаблон RegExp по которому будет осуществляться выбор;text (String) -- текст страницы, где будет осуществляться поиск и выборка.<style type="text/css"></style>.pattern внутри самой функции? Это регулярное выражение нам еще раз понадобится при подстановке сгенерированного CSS обратно на страницу. Правильнее будет ее определить только в одном месте.[variableName, variableValue] внутри блока @server constants {}$variableName их значениями variableValue;@server constants {}.Ruby-модуль css_ssc_module.rb
def ssc_process_server_variables(css)
pattern = /@server\s+(?:variables|constants)\s*\{\s*([^\}]+)\s*\}\s*/i
pattern2 = /([^:\}\s]+)\s*:\s*([^;\}]+);/
str_out = css
if ( matches = css.scan(pattern) )
matches.each do |match|
vars = match[0].scan(pattern2)
vars.each{|var, value| css.gsub!("$#{var}", value)}
end
css = css.gsub!(pattern, '')
str_out = css unless css.nil?
end
return str_out
end
css (String) -- текст CSS.Ruby-модуль css_ssc_module.rb
def ssc_serve_styles(text)
pattern = /^\<style\ \b[^>]*>((\s|.)+?)\<\/style\>/i
css = ssc_get_style_contents(pattern, text)
css_out = ssc_process_server_variables(css)
text.gsub!(pattern, "<style type='text/css'>#{css_out}</style>")
return text
end
text (String) -- «сырой» текст страницы.ssc_serve_styles(text), получим следующий код HTML.Пример: обработанный HTML
<style type="text/css">
body {
font: 11px/1.5em Arial, Helvetica, sans-serif;
background-color: #FFF;
color: #000;
}
</style>
@server constant constantName: constantValue;
@server constants {
constantName1: constantValue;
constantName2: constantValue;
constantName3: constantValue;
}@server constants {
linkColor: #003366;
linkDecoration: underline;
}
a {color: linkColor; text-decoration: linkDecoration;}a {color: #003366; text-decoration: underline;}sample.css?variableName=variableValue
$).@server variable $variableName: defaultVariableValue;
@server variables {
$variableName1: defaultVariableValue;
$variableName2: defaultVariableValue;
$variableName3: defaultVariableValue;
}@server variable $bodyBgColor: #333;
body { background-color: $bodyBgColor; }
body { background-color: #333; }xsl:variable. Для HTML этот способ не подходит.<!-- CSS Variables -->
<xsl:variable name="consoleEntityColor" select="'#B00080'" />
<xsl:variable name="consoleStringColor" select="'#7F007F'" />
<xsl:variable name="consoleKeywordColor" select="'#00007F'" />
<xsl:variable name="consoleActionColor" select="'#007F7F'" />
<xsl:variable name="consoleParamColor" select="'#C0A030'" />
<xsl:variable name="consoleCommentColor" select="'#007F00'" />
<xsl:variable name="consoleFont" select="'font-family: Courier New, Courier, monospace; font-size: 10pt'" />
<!-- Additional CSS -->
<style type="text/css">
.console {
width:300px; height:200px; border:1px solid <xsl:value-of select="$consoleActionColor"/>;
overflow:auto;
<xsl:value-of select="$consoleFont"/>;
}
.console .entity {
color: <xsl:value-of select="$consoleEntityColor"/>;
}
.console .string {
color: <xsl:value-of select="$consoleStringColor"/>;
}
.console .keyword {
color: <xsl:value-of select="$consoleKeywordColor"/>;
}
.console .action {
color: <xsl:value-of select="$consoleActionColor"/>;
}
.console .param {
color: <xsl:value-of select="$consoleParamColor"/>;
}
.console .comment {
color: <xsl:value-of select="$consoleCommentColor"/>;
}
</style>
<pre class="console">
<span class="comment"># Store home</span><br />
<span class="keyword">def</span> <span class="action">index</span><br />
<span class="entity">@store</span> = Store.find(params[<span class="param">:id</span>])<br />
<span class="entity">@title</span> = <span class="string">"Home - #{@store.name}"</span><br />
<span class="keyword">end</span>
</pre>
randomNumber.html -- собственно, сама страница с готовой разметкой HTML;randomNumber.css -- «таблица стилей» CSS.
В блоге охотно делюсь примерами как из собственного опыта веб-разработки, коей посвятил уже более 5 лет своей жизни, так и просто интересными находками.
Будь то программирование или верстка, дизайн или настройка шаблонов Blogger – мне интересно все. И так или иначе все это найдет отражение на страницах моего блога.