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

Sitemap на Ruby on Rails

Пару дней назад озадачился генерированием файла sitemap.xml на Ruby on Rails для пользовательских торговых площадок. Приведу упрощенный пример без блогов, форумов и специальных страниц. В файле Sitemap будут url для:

  • индексной страницы магазина (index)
  • страницы описания (about)
  • страницы контактов (contact)
  • всех страниц товаров (item)


Контроллер (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> является обязательным; остальные теги являются дополнительными.

  • loc – адрес страницы;
  • lastmod – дата последнего обновления;
  • changefreq – частота изменения страницы;
  • priority – приоритет.


Главная страница магазина (index) и страницы товаров (item) являются наиболее важными страницами, поэтому имеют более высокий приоритет. Страница описания и страница контактов, имхо уже «на интерес». Следовательно, имеют приоритет более низкий.


Индексная страница обновляется довольно часто за счет добавления новых товаров и выбывания товаров из листинга. Поэтому обновляется ежедневно (daily).


Страницы товаров тоже обновляются довольно часто. За счет добавления отзывов, комментариев клиентов. Но эта информация не особо важна. Спокойно могу задать им еженедельное (weekly) обновление.


Информация о магазине и контактах, как правило, изменяется крайне редко, и обновлять ее стоит не чаще, чем раз в месяц (monthly).


Успехов на рельсах! © dotrb.blogspot.com


Читать дальше »

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

Yahoo Samsung Internet@TV


Компания Samsung совместно с крупнейшим порталом Yahoo объявили о сотрудничестве по разработке нового интернет-сервиса “Internet@TV - Content Service”, который будет запущен весной 2009. Для этого отдельные модели телевизоров Samsung HDTV будут оснащены встроенным програмным комплектом Yahoo! Widget Engine, который позволит зрителям осуществлять интерактивный просмотр программ при помощи набора “TV Widgets”.

TV Widgets дадут возможность управлять пользователям интернет-контентом и расширить возможности просмотра телевизионных каналов за счет программ и каналов в сети Интернет. Сервис, разработанный с использованием Javascript и XML позволит осуществлять поиск по видеороликам, просматривать видеоконтент и участвовать в работе социальных сетей и приложений. Таким образом обыкновенный телевизор превратится в интерактивное устройство для глобальной сети.

Набор TV Widgets позволит работать также с такими порталами как с Flickr, Yahoo! News, Yahoo! Weather и Yahoo! Finance и просматривать видео на ряде других сервисолв, к примеру USA TODAY, YouTube, eBay и Showtime Networks.
Читать дальше »

вторник, 23 декабря 2008 г.

Использование в XSLT атрибутов XML-элементов

Не так давно я рассказывал как в XSLT создавать элементы и атрибуты. Теперь опишу как использовать в XSLT атрибуты XML-элементов.

collection Item elementНаш нештатный дизайнер посчитал недостаточным, что ссылка изображения элемента и ссылка названия элемента ведут на страницу описания. Он решил еще добавить для этой цели специальную кнопку “details”. А аяксовую ссылку “add to cart” заменить графической кнопкой.

XML: фрагмент кода


<item-list>
...
<item id="50">
<item-name>
<a href="/store/item/50.xml">Crime and Punishment</a>
</item-name>
<item-description>Dostoevsky's first wife died in 1...</item-description>
<item-image>
<a href="/store/item/50.xml">
<img alt="Dostoevsky_crime_and_punishment" class="wide" src="/image/name/0000/0187/187/wide/Dostoevsky_Crime_And_Punishment.jpg?1220530414"/>
</a>
</item-image>
<item-price>$22.00</item-price>
<add-to-cart>
<a href="#" onclick="new Ajax.Request('/cart/ajax_check_item/50?buynow=0', {asynchronous:true, evalScripts:true}); return false;">add to cart</a>
</add-to-cart>
</item>
...
</item-list>


В шаблоне XSLT для атрибута href кнопки “details” я использовал соответствующий атрибут дочернего XML-элемента item-name/a: {item-name/a/@href}. А для атрибута onClick кнопки “add to cart” -- соответствующий атрибут элемента add-to-cart/a: {add-to-cart/a/@onclick}.

XSLT: фрагмент кода


<xsl:template match="item">
<div class="item indexItem">
<table border="0" cellspacing="0" cellpadding="0">
<tr>
<td height="111" valign="top" width="148" class="thumb"><xsl:copy-of select="item-image/*" /></td>
<td valign="top"><p class="title">
<xsl:copy-of select="item-name/*" />
</p>
<p class="description"><xsl:value-of select="item-description" /></p>
<p class="price"><xsl:value-of select="item-price" /></p></td>
</tr>
<tr>
<td colspan="2"><div class="border">
<a href="{item-name/a/@href}" class="floatLeft"><img src="/images/templates/template-2/images/btn_details.gif" alt="details" title="show item details" /></a>
<a href="#" onclick="{add-to-cart/a/@onclick}" class="floatLeft"><img src="/images/templates/template-2/images/btn_add_cart.gif" alt="add to cart" title="add item to cart" /></a>
</div></td>
</tr>
</table>
</div>
</xsl:template>


Таким вот образом. Даже проще, чем использование <xsl:element> и <xsl:attribute>, как в статье XSLT: создание элементов и атрибутов.
Читать дальше »

вторник, 25 ноября 2008 г.

XSLT создание элементов и атрибутов

Еще одна «боевая» задача -- как в XSLT создавать элементы и задавать им атрибуты. Например, есть комплексная форма (<FORM>) или remote-форма (AJAX). И не хочется в XSLT-шаблонах лишний раз нагружать юзера конструкциями вроде:
<form onsubmit="new Ajax.Updater('paypal',
'/mystore/ajax_update_paypal',
{asynchronous:true, evalScripts:true,
onComplete:function(request){ajax_refresh_options()},
parameters:Form.serialize(this)});
return false;" method="post" action="/mystore/ajax_update_paypal">

<!-- Еще туева хуча полей -->

</form>

Это как номер раз. Второе -- когда самому нужно сверстать несколько таких шаблонов для одного и того же XML, так это и нерационально, и самому рехнуться можно. И третье: В Rails лучше использовать рельсовые методы, а не то, что получается у них на выходе.

Приведу пару простеньких примеров.

Пример номер «раз»

Фрагмент .rxml


# Form:Buy Now
xml.tag!("form-buy_now") do
xml.action("/cart/add/#{@item.id}")
xml.name("add_to_cart")

xml.tag!("button-buynow"){
xml << ( link_to_remote image_tag("/images/buttons/button_buynow.png", :class => "button", :alt => "buy now"), :url => {:controller => "cart", :action => "ajax_check_item", :id => @item.id, :buynow => 1 } )
}
end

Фрагмент XSLT


<xsl:element name="form" >
<!-- form: attributes -->
<xsl:attribute name="action" >
<xsl:value-of select="action" />
</xsl:attribute>
<xsl:attribute name="name" >
<xsl:value-of select="name" />
</xsl:attribute>

<!-- form: fields -->
<xsl:copy-of select="button-buynow/*" />
</xsl:element>

Пример номер «два»

Фрагмент .rxml


xml.tag!("store-search"){
#Form
xml.tag!( "form", :action => url_for({:controller=>"/search", :action=>"index"}), :name=>"searchform", :method => "get" ){
xml.tag!("search_by"){ xml << hidden_field_tag(:search_by_seller,1) }
xml.tag!("seller"){ xml << hidden_field_tag(:seller, @store.name) }

xml.tag!("submit"){
xml << image_submit_tag("/images/template/button_search.gif", {:alt => "search", :class => "imgbutton"})
}
}
#End: Form
}

Фрагмент XSLT


<xsl:template match="store-search/form">
<xsl:element name="form">
<!-- form: attributes -->
<xsl:attribute name="action" >
<xsl:value-of select="@action" />
</xsl:attribute>
<xsl:attribute name="name" >
<xsl:value-of select="@name" />
</xsl:attribute>
<xsl:attribute name="method" >
<xsl:value-of select="@method" />
</xsl:attribute>
<!-- form: elements -->
<xsl:copy-of select="search_by/*" />
<xsl:copy-of select="seller/*" />
<table class="compact">
<tr><td align="center" height="30">
<input name="text" id="text" class="field" value="" type="text" />
</td></tr>
<tr><td align="center">
<xsl:copy-of select="submit/*" />
</td></tr>
</table>
</xsl:element>
</xsl:template>

Вот такие конструкции :).
Читать дальше »

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

XSLT вывод элементов в группах

Довольно давно сталкивался с задачкой как в XSLT сделать вывод элементов коллекции в группах по X элементов. Решил поделиться решением :).

Например, есть магазин (@store) и в нём товары (@items). Нужно с помощью XSLT выводить их в 2, 3 или 4 колонки.
@store = Store.find(params[:id])
@items = @store.items

Вот так я генерирую XML

Фрагмент файла index.rxml


# Store items
xml.tag!("item-list") do
@items.each do |item|
xml.tag!("item", :id => item.id) do
xml.tag! "name" do
xml << (link_to truncate(item.name, 20), :controller => "store", :action => "item", :id => item.id)
end
xml.tag! "description", truncate(item.description, 36)
xml.tag! "price", item.price
end
end
end


В файле XSLT задаю параметр, в котором будет задаваться размер группы <xsl:param name="group-size" select="'3'" />, чтобы легче в-последствии было его изменять.

А примерно вот так выглядит файл XSLT трансформации.

Фрагмент файла index.xsl


<xsl:param name="group-size" select="'3'" />

...

<xsl:apply-templates select="//item-list" />

<xsl:template match="item-list">
<table class="item-gallery">
<xsl:apply-templates select="item[position() mod $group-size = 1]" mode="row" />
</table>
</xsl:template>

<!-- Строка -->
<xsl:template match="item" mode="row">
<tr>
<xsl:apply-templates select=". | following-sibling::item[position() &lt; $group-size]" />
</tr>
</xsl:template>

<!-- Ячейка -->
<xsl:template match="item">
<td>
<table border="0" cellpadding="0" cellspacing="0">
<tr><td align="left" colspan="2"><strong><xsl:copy-of select="item-name/*" /></strong></td></tr>
<tr><td align="left" colspan="2"><xsl:value-of select="item-description" /></td></tr>
<tr><td align="left"><strong>price:</strong></td><td align="right"><xsl:value-of select="item-price" /></td></tr>
</table>
</td>
</xsl:template>

Вуа-ля! Элементы выводятся в строки по 3.
Читать дальше »

среда, 12 ноября 2008 г.

Использование CDATA в Ruby on Rails

В одном из предыдущих постов я рассказывал о том, что такое CDATA и для чего она нужна. Теперь приведу пару примеров использования CDATA в XML, сваренном на Ruby on Rails.

Если у вас есть некоторый текст, который не должен быть проанализирован XML-парсером, просто определяем его как CDATA соответствующей командой cdata! или cdata_section. Более того, в CDATA можно включать и partials.

Пример с cdata!


xml.cdata!("Этот текст не будет проанализирован XML-парсером")
#=> <![CDATA[Этот текст не будет проанализирован XML-парсером]]>

Пример с cdata_section


cdata_section("<Это бля не тег!>")
# => <![CDATA[<Это бля не тег!>]]>

Ну и как определять рельсовые partials как CDATA.

Пример с partial


xml.cdata!(
render( :partial => "javascript" )
)

Читать дальше »

вторник, 11 ноября 2008 г.

file_column и ImageMagick в XSLT-шаблонах

Решил предоставить пользователю выбор размеров изображении в шаблоне XSLT.

Исходные: К любому сообщению пользователь может прикрепить одно изображение. Само изображение хранится в разных версиях (используем file_column + ImageMagick). И вот и решил я: раз уж юзер может использовать собственные XSLT-шаблоны, нужно дать ему возможность выбирать и размеры изображений (версию).
Вот примерно так у меня хранятся изображения.

Модель image.rb


class Image < ActiveRecord::Base
belongs_to :imageable, :polymorphic => true
VALID_EXTENSIONS = ["gif", "jpeg", "jpg", "png"]
file_column :name,
:magick => {:versions => {
:pic => {:crop => "1:1", :size => "33x33!", :attributes => {:quality => 90 },
:transformation => Proc.new { |image| image.strip! }},
:mini => {:crop => "1:1", :size => "50x50!", :attributes => {:quality => 90 },
:transformation => Proc.new { |image| image.strip! }},
:middle => {:size => "500x375>",:attributes => {:quality => 90 },
:transformation => Proc.new { |image| image.strip! }},
:thumb => {:crop => "148:111", :size => "148x111!", :attributes => {:quality => 90 },
:transformation => Proc.new { |image| image.strip! }}
}
}
validates_file_format_of :name, :in => ["gif", "png", "jpg", "GIF", "PNG", "JPG"]
end


Модель post.rb


class Post < ActiveRecord::Base
...
has_one :image, :as => :imageable, :dependent => :destroy
...
end

Ну это так... были вводные данные. Теперь перейдём непосредственно к XML/XSLT.

XML: blog.rxml


xml.tag!("posts") do
@posts.each do |post|
xml.tag!("post", :id => post.id){
xml.tag!("title") do
xml << link_to(post.title, :controller => "blog", :action => "post", :id => post.id)
end
xml.tag!( "created_at", post.created_at.strftime("%b %d, %Y") )
if @image = post.image
xml.tag!("image"){
xml.tag!("src-wide", url_for_file_column("image", "name", "wide") )
xml.tag!("src-thumb", url_for_file_column("image", "name", "thumb") )
xml.tag!("src-middle", url_for_file_column("image", "name", "middle") )
}
end #image
xml.message(post.message)
}
end
end

Ну и, собственно XSLT-темплейт

XSLT: blog.xsl


<xsl:template match="posts">
<xsl:apply-templates select="post" />
</xsl:template>

<xsl:template match="post">
<div class="blog-post" id="blog-post{@id}">
<h2><xsl:copy-of select="title/*" /></h2>
<span class="highcolor2"><xsl:value-of select="created_at" /></span><br />
<div class="blog-body">
<xsl:apply-templates select="image" />
<p><xsl:value-of select="message" /></p>
</div>
</div>
</xsl:template>

<xsl:template match="post/image">
<xsl:element name="img">
<xsl:attribute name="src">
<xsl:value-of select="src-thumb" />
</xsl:attribute>
<xsl:attribute name="class">thumb</xsl:attribute>
</xsl:element>
</xsl:template>

Вуа-ля! Теперь если пользователь захочет отображать изображения другого размера, ему просто нужно будет заменить <xsl:value-of select="src-thumb" /> например на <xsl:value-of select="src-middle" />.
Читать дальше »

XML термин CDATA

Элемент CDATA содержит в себе текстовые данные, которые не должны быть проанализированы XML-парсером.


Дополнение: по-умолчанию, парсер воспринимает их как PCDATA.


Такие символы как "<" и "&", содержащиеся в тексте, вызовут ошибку XML.


Символ "<" XML-парсер интерпретирует как начало нового элемента. А символ "&" – как начало сущности.


Некоторые текстовые данные, например JavaScript, могут содержать множество символов "<" или "&". Чтобы избежать ошибок, такие блоки должны быть определены как CDATA.


Всё внутри секции CDATA игнорируется XML-парсером.


Секция CDATA начинается с "<![CDATA[" и заканчивается "]]>".


Пример использования CDATA


Читать дальше »

суббота, 8 ноября 2008 г.

DoS уязвимость в REXML

Найдена DoS уязвимость в библиотеке REXML, включенной в стандартную библиотеку Ruby. Так называемая "XML entity explosion" техника атаки может применяться для удаленного отключения любого приложения, парсящего пользовательский XML с использованием REXML.

Большинство Rails-приложений будут уязвимы, поскольку Rails парсит XML предоставляемый пользователем, используя REXML по умолчанию.

Атакующий может вызвать отказ от обслуживания (DoS) заставив REXML парсить документ, содержащий рекурсивно вложенные сущности, например:

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE member [
<!ENTITY a "&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;">
<!ENTITY b "&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;">

<!ENTITY c "&d;&d;&d;&d;&d;&d;&d;&d;&d;&d;">
<!ENTITY d "&e;&e;&e;&e;&e;&e;&e;&e;&e;&e;">

<!ENTITY e "&f;&f;&f;&f;&f;&f;&f;&f;&f;&f;">
<!ENTITY f "&g;&g;&g;&g;&g;&g;&g;&g;&g;&g;">

<!ENTITY g "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx">
]>
<member>
&a;
</member>

Подробнее на английском: DoS vulnerability in REXML
Читать дальше »

DOCTYPE ENTITY средствами ruby-xml

Была задача: в генерируемом рельсами XML получить нечто похожее:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE body [
<!ENTITY reg "&#174;">
<!ENTITY mdash "&#8212;">
<!ENTITY nbsp "&#160;">
<!ENTITY ldquo "&#8220;">
<!ENTITY rdquo "&#8221;">
<!ENTITY pound "&#163;">
<!ENTITY trade "&#8482;">
<!ENTITY euro "&#8364;">
<!ENTITY yen "&#165;">
<!ENTITY copy "&#169;">
]>
...

И поскольку, для генерации XML я использую рельсовый RXML билдер, то и выводить надо было rails-методами. Что такое xml.instruct! и как им пользоваться, я давно знаю, но вот с этими entities была и самая проблема.

Нашел в классе Builder::XmlMarkup метод declare! а хули толку? Там настолько "содержательный" пример применения, что хотелось пристрелить разработчиков документации. Пришлось познать, что есть такое Class: Proc в Ruby.

Собственно, вот что в итоге получилось.

XML DOCTYPE ENTITY средствами ruby on rails (layout.rxml)



xml.instruct! :xml, :version=>"1.0", :encoding=>"UTF-8"

entities = {:nbsp => "&#160;", :copy => "&#169;", :reg => "&#174;", :trade => "&#8482;", :mdash => "&#8212;", :ldquo => "&#8220;",
:rdquo => "&#8221;", :pound => "&#163;", :yen => "&#165;", :euro => "&#8364;"}

xml.declare! :DOCTYPE, "body".intern do |x|
entities.each do |key, value|
x.declare! :ENTITY, "#{key}".intern, "#{value}"
end
end


Пользуйтесь! Не забывайте говорить «спасибо» ;) или «thanks» -- кто как приучен :).
Читать дальше »

среда, 5 ноября 2008 г.

Рендер partial .rhtml в .rxml

Для добавления partial .rhtml в .rxml - документ (с последующим отображением его после XSL-трансформации), проделаем следующее:

XML: фрагмент store.rxml


# partial
xml.tag!("store-description"){
xml << render( :partial => "store_description" )
}

Применим правила XSL-трансформации для вывода фрагмента HTML без окружающих тегов.

XSLT: фрагмент store.xsl


<!-- # Render Partial -->
<xsl:apply-templates select="//store-description" />

<xsl:template match="store-description">
<xsl:copy-of select="node()" />
</xsl:template>
Читать дальше »

вторник, 4 ноября 2008 г.

XSLT обход набора элементов

Для обхода элементов коллекции можно использовать <xsl:for-each>, но лично я предпочитаю использовать шаблоны. Т.е. связку
<xsl:apply-templates> и <xsl:template match="">.

Допустим, есть некий магазин @store с определенным набором товаров
@items = @store.items

Фрагмент кода store.rxml:


# Store items
xml.tag!("items") do
@items.each do |item|
xml.tag!("item", :id => item.id) do
xml.tag! "item-name" do
xml << (link_to truncate(item.name, 20), :controller => "store", :action => "item", :id => item.id)
end
xml.tag! "item-description", truncate(item.description, 100)
xml.tag! "item-price", "#{item.currency}#{item.price}"
end
end # items.each
end


Это сгенерирует следующий XML:


<items>
<item id="13">
<item-name><a href="/customstore/item/13">Lucky Number</a></item-name>
<item-description>Navy lucky number 13</item-description>
<item-price>$12.00</item-price>
</item>
<item id="16">
<item-name><a href="/customstore/item/16">Leather ...</a></item-name>
<item-description>Leather is a material created through the tanning of hides and skins of animals</item-description>
<item-price>$17.00</item-price>
</item>
<item id="75">
<item-name><a href="/customstore/item/75">Adobe Photoshop ...</a></item-name>
<item-description>Lorem ipsum dolor sit amet, conse...</item-description>
<item-price>$23.00</item-price>
</item>
</items>


Применим следующие правила XSL-трансформации:


<xsl:apply-templates select="//items" />

<xsl:template match="items">
<xsl:apply-templates select="item" />
</xsl:template>

<xsl:template match="item">
<div class="item" id="item{@id}">
<h2><xsl:copy-of select="item-name/*" /></h2>
<p><xsl:value-of select="item-description" /></p>
<span class="price"><xsl:value-of select="item-price" /></span>
</div>
</xsl:template>


P.S. Для отображения copy-of item-name ссылкой, используем вывод без родительского тега. Читать дальше »

суббота, 1 ноября 2008 г.

Как установить Ruby XML/ XSLT на Ubuntu

Как установить ruby-xslt на Ubuntu
sudo apt-get install libxslt1-dev libxml-ruby
gem install ruby-xslt
Читать дальше »