Статьи

Пометка с нуля в Rails

Значок ценника

Теги являются своего рода категориями, которые описывают часть информации (контент) и позволяют пользователю снова искать ее. В этом руководстве вы узнаете, как создать простую систему тегов с нуля в Rails.

Это руководство предполагает базовые знания Ruby on Rails. Я постараюсь сделать это простым.

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

инструменты

  • Рельсы 4.1.4
  • Ruby 2.0.1
  • Фонд 5

Начиная

Исходный код этого приложения можно найти в этом хранилище.

Сначала создайте свой проект Rails:

rails new TaggingTut 

Затем добавьте драгоценный камень для рельсов фундамента, удалите драгоценный камень turbolinks . Запустите bundle install .

Мы будем использовать sqlite, базу данных по умолчанию, используемую Rails

Удалите эту строку из app / assets / javascript / application.js. , так как мы удалили камень с turbolinks .

 //= require turbolinks 

Создание тегов

Создайте модель для тегов с одним атрибутом name :

 rails g model tag name:string 

Мы будем индексировать атрибут name ( index: true ), чтобы ускорить наш поиск по этим тегам. Я рекомендую этот урок для использования индексов в ассоциациях Rails.

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

  • Во-первых : используйте ассоциацию has_and_belongs_to_many . Это создаст таблицу соединения в базе данных, но не будет сгенерированной модели для соединения. Таким образом, вы не сможете добавлять проверки или любые другие атрибуты в объединение.

  • Второе : используйте has_many, through которого требуется создать модель для таблицы соединения. Этот способ предпочтителен в большинстве случаев, поэтому мы будем его использовать.

Создание моделей «Пост» и «Пометка», а также

 rails g model post author:string content:text rails g model tagging post:belongs_to tag:belongs_to 

После создания этих моделей запустите rake db:migrate .

Теперь мы создадим ассоциации между постами и тегами через ActiveRecord следующим образом:

приложение / модели / post.rb

 has_many :taggings has_many :tags, through: :taggings 

приложение / модели / tag.rb

 has_many :taggings has_many :posts, through: :taggings 

app / models / tagging.rb будет сгенерирован так:

 belongs_to :post belongs_to :tag 

Теперь у нас есть соединение между сообщениями и тегами через таблицу объединения тегов.

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

Модель Post имела два атрибута, author и content , которые также определены в текущей форме. Атрибут для all_tags также будет добавлен к данным формы. В Rails 4 добавьте нужный (виртуальный, в данном случае) атрибут, используя строгие параметры. Виртуальные атрибуты очень просты, в этом случае они определены как методы получения и установки. Функция strip предназначена для удаления пробелов

приложение / модели / post.rb

 def all_tags=(names) self.tags = names.split(",").map do |name| Tag.where(name: name.strip).first_or_create! end end def all_tags self.tags.map(&:name).join(", ") end 

Функция all_tags будет настроена для отображения всех тегов, разделенных запятыми.

Перед созданием контроллера и представлений установите Zurb Foundation:

 rails g foundation:install 

Теперь настройте контроллер и представления для отображения постов, включая все теги. Создайте app / controllers / posts_controller.rb , набрав:

 rails g controller posts index create 

Задайте строгие параметры следующим образом, включая наш виртуальный атрибут для хранения всех тегов, введенных через представление:

 private def post_params params.require(:post).permit(:author, :content, :all_tags) end 

Метод permit создает белый список параметров, которые необходимо передать. Подробнее о сильных параметрах читайте здесь

Давайте создадим форму с текстовым полем для тегов. Мы создадим пост, используя AJAX. Это довольно просто:

приложение / просмотров / сообщений / _new.html.erb

 <div class="row text-center"> <%= form_for(Post.new, remote: true) do |f| %> <div class="large-10 large-centered columns"> <%= f.text_field :author, placeholder: "Author name" %> </div> <div class="large-10 large-centered columns"> <%= f.text_area :content, placeholder: "Your post", rows: 5 %> </div> <div class="large-10 large-centered columns"> <%= f.text_field :all_tags, placeholder: "Tags separated with comma" %> </div> <div class="large-10 large-centered columns"> <%= f.submit "Post", class: "button"%> </div> <% end %> </div> 

__remote: true__ — это атрибут, который указывает форму, которая будет отправлена ​​через AJAX, а не через обычный механизм отправки браузера.

После создания нашего поста, create перенаправит на действие index и просмотрит существующие посты.

приложение / контроллеры / posts_controller.rb

 def index @posts = Post.all end 

приложение / просмотров / сообщений / index.html.erb

 <div class="row"> <div class="large-8 columns"> <%= render partial: "posts/new" %> </div> </div> 

Не забудьте разобраться с маршрутами.
конфиг / routes.rb

 root 'posts#index' resources :posts, only: [:create] 

Добавьте несколько очень простых стилей в представление следующим образом:
Приложение / активы / таблицы стилей / posts.css.scss

 .tags-cloud { margin-top: 16px; padding: 14px; } .top-pad { padding: 25px; } .glassy-bg{ box-shadow: 0px 3px 8px -4px rgba(0,0,0,0.15); background: white; border-radius: 4px; padding-bottom: 12px; } .mt{ margin-top: 10px; } .mb{ margin-bottom: 10px; } .pt{ padding-top: 10px; } .pb{ padding-bottom: 10px; } 

Запусти rails s и посмотрим что у нас получится.
Form for post

К сожалению, нет сообщений! Мы никогда не писали действие create .

приложение / контроллеры / posts_controller.rb

 def create @post = Post.new(post_params) respond_to do |format| if @post.save format.js # Will search for create.js.erb else format.html { render root_path } end end end 

Этот фрагмент создает новую публикацию с параметрами, указанными пользователем, проверяя ее действительность и возвращая результат. Поскольку форма отправляется с AJAX, формат ответа — js .

Теперь нам нужно создать файл create.js.erb для хранения javascript, который будет запускаться после создания поста:
приложение / просмотров / сообщений / create.js.erb

 var new_post = $("<%= escape_javascript(render(partial: @post))%>").hide(); $('#posts').prepend(new_post); $('#post_<%= @post.id %>').fadeIn('slow'); $('#new_post')[0].reset(); 

Этот код частично отображает вновь созданное сообщение, функция prepend позволяет отображать его поверх старых сообщений с эффектом fadeIn .

Создайте партиал, который будет отображать каждое сообщение:

приложение / просмотров / сообщений / _post.html.erb

 <%= div_for post do %> <div class="large-12 columns border border-box glassy-bg mt pt"> <strong><%= h(post.author) %></strong><br /> <sup class="text-muted">From <%= time_ago_in_words(post.created_at)%></sup><br /> <div class="mb pb"> <%= h(post.content) %> </div> <div class="tags"> <%=raw post.all_tags %> </div> </div> <% end %> 

Прежде чем мы проверяем вывод, изменим представление index чтобы оно содержало частичное для сообщений:

приложение / просмотров / index.html.erb

 <div class="row mt pt"> <div class="large-5 columns"> <div class="top-pad glassy-bg"> <%= render partial: "posts/new" %> </div> </div> <div class= "large-7 columns" id="posts"> <%= render partial: @posts.reverse %> </div> </div> 

Сообщения будут располагаться в обратном порядке сверху вниз, то есть наиболее свежие введенные сообщения будут первыми.

На этом этапе у нас есть сообщения с тегами, которые хранятся в базе данных с использованием двух таблиц, тегов и тегов . Таблица тегов сохраняет связь между сообщениями и тегами. Вот как выглядят наши посты:

posts

Поиск по тегам

В этом разделе мы создадим поиски на основе области по имени тега.

Создайте метод класса с именем tagged_with(name) который будет принимать имя указанного тега и искать связанные с ним сообщения.

приложение / модель / post.rb

 def self.tagged_with(name) Tag.find_by_name!(name).posts end 

Создайте переменную экземпляра, содержащую результаты на контроллере.

приложение / контроллеры / posts_controller.rb

 def index if params[:tag] @posts = Post.tagged_with(params[:tag]) else @posts = Post.all end end 

Добавьте маршрут get для хранения имени тега и укажите на метод posts_controller#index :

конфиг / routes.rb

 get 'tags/:tag', to: 'posts#index', as: "tag" 

После этого измените теги каждого сообщения на ссылки на метод index:

приложение / просмотров / _post.html.erb

 <%=raw tag_links(post.all_tags)%> 

tag_links(tags) — это вспомогательный метод, который будет содержать логику преобразования тегов в ссылки.

приложение / хелперы / posts_helper.rb

 def tag_links(tags) tags.split(",").map{|tag| link_to tag.strip, tag_path(tag.strip) }.join(", ") end 

Ура! Теперь у нас есть поиск на основе тегов для наших сообщений!

tag-based search

Облако тегов

Давайте сгенерируем одно из этих классных облаков тегов, основанное на подсчете количества вхождений каждого тега во всех сообщениях.

Сначала создайте метод для подсчета всех тегов, связанных с сообщениями:

приложение / модели / tag.rb

 def self.counts self.select("name, count(taggings.tag_id) as count").joins(:taggings).group("taggings.tag_id") end 

Этот запрос группирует совпадающие tag_ids из таблицы присоединения тегов и подсчитывает их.

Мы будем tag_cloud их в соответствии с их количеством, создав вспомогательный метод tag_cloud который принимает результат вызова функции tag_cloud и CSS-классов.

приложение / хелперы / posts_helper.rb

 def tag_cloud(tags, classes) max = tags.sort_by(&:count).last tags.each do |tag| index = tag.count.to_f / max.count * (classes.size-1) yield(tag, classes[index.round]) end end 

Этот вспомогательный метод получит тег с максимальным количеством. Затем он зацикливается на каждом теге для расчета index который выберет класс CSS на основе округленного значения. Затем пройденный блок будет выполнен.

Нам нужно добавить стили для разных размеров следующим образом:

Приложение / активы / tags.css.scss

 .css1 { font-size: 1.0em;} .css2 { font-size: 1.2em;} .css3 { font-size: 1.4em;} .css4 { font-size: 1.6em;} 

Не забудьте добавить *= require tags в application.css .

Наконец, добавьте код для отображения тегов в представлении и примените к ним CSS-классы.

приложение / просмотров / сообщений / index.html.erb

 <div class="tags-cloud glassy-bg"> <h4>Tags Cloud</h4> <% tag_cloud Tag.counts, %w{css1 css2 css3 css4} do |tag, css_class| %> <%= link_to tag.name, tag_path(tag.name), class: css_class %> <% end %> </div> 

Проверьте это, наши теги в облаке!
tag cloud

действовать как taggable_on

После этой статьи вы сможете без проблем обработать гем act_as_taggable_on . Вы можете прочитать больше об этом на его репозитории github .

Вывод

Я надеюсь, что это руководство поможет вам понять, что входит в создание базовой системы тегов. Tag, ты это! 🙂