Создание постоянных ссылок из строк в Ruby

May 14
2007 18:27 (Ruby on Rails) · English (8,158 views)

Если вы разрабатываете приложение на Ruby on Rails вроде блога, возможно вам понадобится генерировать URLы используя заголовки статей. Это хорошая практика, потому что поисковики обожают ключевые слова в URLах, да и выглядят они более человеко-читабельными. Просто сравните: http://example.com/posts/10 и http://example.com/posts/generating-permalinks-from-string (угу, длинновато, но зато наглядно). А любом случае, это короткая заметка о преобразовании заголовка в постоянную ссылку.

Основная вещь, которую я обожаю в Ruby — это возможность расширять классы своими собственными методами. Я могу просто добавить метод to_permalink любой строке, и затем где угодно использовать что-то вроде @post.title.to_permalink. Это офигенно!

class String
  def to_permalink
    result = strip_tags
    # Preserve escaped octets.
    result.gsub!(/-+/, '-')
    result.gsub!(/%([a-f0-9]{2})/i, '--\1--')
    # Remove percent signs that are not part of an octet.
    result.gsub!('%', '-')
    # Restore octets.
    result.gsub!(/--([a-f0-9]{2})--/i, '%\1')

    result.gsub!(/&.+?;/, '-') # kill entities
    result.gsub!(/[^%a-z0-9_-]+/i, '-')
    result.gsub!(/-+/, '-')
    result.gsub!(/(^-+|-+$)/, '')
    return result.downcase
  end

  private
 
    def strip_tags
      return clone if blank?
      if index('<')
        text = ''
        tokenizer = HTML::Tokenizer.new(self)

        while token = tokenizer.next
          node = HTML::Node.parse(nil, 0, 0, token, false)
          # result is only the content of any Text nodes
          text << node.to_s if node.class == HTML::Text
        end
        # strip any comments, and if they have a newline at the end (ie. line with
        # only a comment) strip that too
        text.gsub(/<!--(.*?)-->[\n]?/m, '')
      else
        clone # already plain text
      end
    end
end

Как это работает? Первое, что вы скорее всего заметили,– это приватный метод strip_tags. Да, я знаю об ActionView::Helpers::TextHelper::strip_tags, и это почти 100% копия версии из Rails (отличие только в том, что мой код всегда возвращает копию исходной строки). Я просто не хочу зависеть от рельсовых библиотек.

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

Естественно, в вашем приложении вы должны проверить коллизии (несколько статей с одинаковыми заголовками должны иметь уникальные постоянные ссылки, например вы можете добавлять числа начиная с 1: hello, hello-1, hello-2 и т.д.). У меня нет желания описывать все сложности, с которыми вы можете столкнуться, это ведь короткая заметка, вы же помните?

Удовольствия ради, вот тесты RSpec для этого метода:

describe 'String.to_permalink from extensions.rb' do
  it 'should replace all punctuation marks and spaces with dashes' do
    "!.@#$\%^&*()Test case\n\t".to_permalink.should == 'test-case'
  end

  it 'should preserve _ symbol' do
    "Test_case".to_permalink.should == 'test_case'
  end
 
  it 'should preserve escaped octets and remove redundant %' do
    'Test%%20case'.to_permalink.should == 'test-%20case'
  end

  it 'should strip HTML tags' do
    '<a href="http://example.com">Test</a> <b>case</b>'.to_permalink.should == 'test-case'
  end

  it 'should strip HTML entities and insert dashes' do
    'Test&nbsp;case'.to_permalink.should == 'test-case'
  end

  it 'should trim beginning and ending dashes' do
    '-. Test case .-'.to_permalink.should == 'test-case'
  end

  it 'should not use ---aa--- as octet' do
    'b---aa---b'.to_permalink.should == 'b-aa-b'
  end
 
  it 'should replace % with -' do
    'Hello%world'.to_permalink.should == 'hello-world'
  end

  it 'should not modify original string' do
    s = 'Hello,&nbsp;<b>world</b>%20'
    s.to_permalink.should == 'hello-world%20'
    s.should == 'Hello,&nbsp;<b>world</b>%20'

    s = 'Hello'
    s.to_permalink.should == 'hello'
    s.should == 'Hello'
  end
end

Прикольно, правда?

5 отзывов на 'Создание постоянных ссылок из строк в Ruby'

Подписаться на комментарии по RSS или TrackBack на 'Создание постоянных ссылок из строк в Ruby'.

1
demjan
сказал 18.05.2007 в 20.59

Да. ))
Интересно сколько займет ресурсов и как будет выглядеть реализация на php в свете недавнего обсуждения на харахабарачтототам

2
сказал 18.05.2007 в 21.30

Да ровно столько же! Все эти войны Something vs. Something another — чистой воды треп людей, которым нечем заняться. При грамотном подходе код в любом языке будет лаконичным и эффективным. Вопрос в компактности и читабельности — это да, целиком ложится на совесть языка.

Вон в комментах к статье о подзапросах я приводил пример кода пхп. Да, задача не повседневная, но классическая, когда надо обработать выборку. И ну никак всего этого уродства не обойти. Можно размазать по коду, это да. Или вынести в отдельный файл и никому не показывать. Но оно останется. Потому что ПХП — это набор знаков препинания, разделенных словами.

3
demjan
сказал 19.05.2007 в 8.57

Аналога этому в не обьектном языке не напишешь:

class String
  def to_permalink
  end
end
'string'.to_permalink

Только вроде такого

function to_permalink ($name) {
}
to_permalink($post->title);
4
сказал 19.05.2007 в 12.41

Ну почему же. В C# 3.0 можно будет писать extension методы:

public static class Extensions
{
    public static String ToPermalink(this String s)
    {
    }
}

'Something'.ToPermalink();

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

5
kelyar
сказал 04.06.2007 в 11.29

а почему просто не переопределить to_param модели?

Оставить отзыв

Вы можете использовать простые теги форматирования HTML (вроде <a>, <ul> and others). Чтобы вставить пример код, используйте <code lang="php">$a = "hello";</code> (поддерживаемые языки: ruby, php, yaml, html, csharp, javascript). Также Вы можете использовать <code>$a = "hello";</code>, синтаксис не будет подсвечен. Если вы не хотите использовать тег <code>, замените символ < на &lt;.

Отправить

 
Copyright © 2005 - 2008, Dmytro Shteflyuk