Если вы какое-то время создавали приложения на Rails, вы, вероятно, заметили папку с названием concerns .  Эта папка создается внутри каталогов app/controllers и app/models при каждом создании нового приложения Rails.  Я думал, что это было бесполезно до недавнего времени, когда мы должны были использовать это на работе. 
  В этом коротком уроке я хочу показать вам, как использовать силу concerns . 
  Мы собираемся создать мини-приложение для твитов в Rails, я назову его Twik .  В приложении у нас будет два TwitsController .  Один контроллер будет пространством имен под admin (так что его действия доступны только администраторам), а другой будет в обычном пространстве имен Rails.  Мы поделимся функциональностью, используя ActiveSuppourt::Concern чтобы убедиться, что мы ActiveSuppourt::Concern принцип СУХОЙ 
  Посмотрев, как это работает для контроллеров, я покажу вам, как использовать ActiveSupport::Concern в моделях. 
Давайте начнем.
Настройка приложения
Создайте приложение на Rails:
 rails new twik -T 
  Добавьте следующие gems в свой Gemfile : 
 ... gem 'devise' gem 'bootstrap-sass' 
  Теперь в bundle install ваши драгоценные камни. 
Запустите команду для установки Devise:
 rails g devise:install 
  Теперь давайте создадим нашу модель Admin : 
 rails g devise Admin 
Переименуйте app / assets / stylesheets / application.css в app / assets / stylesheets / application.scss и вставьте следующее:
 #app/assets/stylesheets/application.scss @import "bootstrap-sprockets"; @import "bootstrap"; 
  Перейдите в app / assets / javascripts / application.js и добавьте строку над последним require : 
 ... //= require bootstrap-sprockets (ADD THIS) //= require_tree . 
Создайте файл app / views / layouts / _navigation.html.erb и вставьте следующее:
 #app/views/layouts/_navigation.html.erb <nav class="navbar navbar-default"> <div class="container-fluid"> <!-- Brand and toggle get grouped for better mobile display --> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar-collapse" aria-expanded="false"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <% if admin_signed_in? %> <%= link_to "Twik", admin_twits_path, class: "navbar-brand" %> <% else %> <%= link_to "Twik", root_path, class: "navbar-brand" %> <% end %> </div> <div class="collapse navbar-collapse" id="navbar-collapse"> <ul class="nav navbar-nav navbar-right"> <li><%= link_to 'Home', root_path %></li> <% if admin_signed_in? %> <li><%= link_to 'My Account', edit_admin_registration_path %></li> <li><%= link_to 'Logout', destroy_admin_session_path, :method => :delete %></li> <% else %> <li><%= link_to 'Login', new_admin_session_path %></li> <% end %> </ul> </div> </div> </nav> 
Теперь отредактируйте app / views / layouts / application.html.erb, чтобы он выглядел так:
 #app/views/layouts/application.html.erb <!DOCTYPE html> <html> <head> <title>Twik</title> <%= csrf_meta_tags %> <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %> </head> <body> <%= render "layouts/navigation" %> <div class="container-fluid"> <%= yield %> </div> </body> </html> 
Контроллеры и проблемы Twits.
Мы хотим написать как можно меньше кода, чтобы мы не повторяли ничего. Чтобы достичь этого, мы будем использовать проблемы, чтобы разделить действия в обоих контроллерах. Смущенный? Сделайте следующее:
-   Создайте свою модель Twit: 
rails g model Twit tweet:text. -   Создайте свой TwitsController: 
rails g controller TwitsController. - Перейдите в app / controllers / Концерны и создайте файл twitable.rb , вставив следующее:
 
  #app/controllers/concerns/twitable.rb module Twitable extend ActiveSupport::Concern included do before_action :set_twit, only: [:show, :edit, :destroy, :update] end def index @twits = Twit.all end def new @twit = Twit.new end def show end def create @twit = Twit.new(twit_params) if @twit.save flash[:notice] = "Successfully created twit." redirect_to @twit else flash[:alert] = "Error creating twit." render :new end end private def twit_params params.require(:twit).permit(:tweet) end def set_twit @twit = Twit.find(params[:id]) end end 
Теперь давайте рассмотрим код выше.
  Использование extend ActiveSupport::Concern сообщает Rails, что мы создаем проблему.  Код внутри included блока будет выполняться везде, где включен модуль.  Это лучше всего для включения сторонних функций.  В этом случае мы получим ошибку, если before_action записано вне included блока.  На этом этапе мы можем включить наш модуль Twitable в контроллеры, которым необходимо такое поведение. 
  После этого наш TwitsController очень TwitsController : 
 #app/controllers/twits_controllers.rb class TwitsController < ApplicationController include Twitable end 
Нам нужен каталог администратора для размещения контроллера для администраторов:
 mkdir app/controllers/admin touch app/controllers/admin/twits_controllers.rb 
Теперь вставьте этот код в файл, который вы только что создали:
 #app/controllers/admin/twits_controllers.rb class Admin::TwitsController < ApplicationController include Twitable def edit end def update if @twit.update_attributes(twit_params) flash[:notice] = "Successfully updated twit." redirect_to admin_twit_path else flash[:alert] = "Error creating twit." render :edit end end def destroy if @twit.destroy flash[:notice] = "Successfully deleted twit." redirect_to twits_path else flash[:alert] = "Error deleting twit." end end end 
Разве это не круто? Нам не нужно повторять наш код. Теперь давайте создадим представления, чтобы мы могли проверить, действительно ли все это работает:
 mkdir -p app/views/admin/twits touch app/views/admin/twits/index.html.erb touch app/views/admin/twits/new.html.erb touch app/views/admin/twits/show.html.erb touch app/views/admin/twits/edit.html.erb touch app/views/twits/new.html.erb touch app/views/twits/show.html.erb touch app/views/twits/index.html.erb 
  Вставьте приведенный ниже код в соответствующие файлы. 
  Admin Twit Редактировать страницу 
 #app/views/admin/twits/edit.html.erb <div class="container-fluid"> <div class="row"> <div class="col-sm-offset-4 col-sm-4 col-xs-12"> <%= form_for @twit, :url => {:controller => "twits", :action => "update" } do |f| %> <div class="form-group"> <%= f.label :tweet %> <%= f.text_field :tweet, class: "form-control" %> </div> <div class="form-group"> <%= f.submit "Update", class: "btn btn-primary" %> <%= link_to "Cancel", :back, class: "btn btn-default" %> </div> <% end %> </div> </div> </div> 
Индексная страница администратора Twit
 #app/views/admin/twits/index.html.erb <div class="container-fluid"> <p id="notice"><%= notice %></p> <h1>Listing Twits</h1> <div class="row"> <div class="col-sm-12 col-xs-12"> <%= link_to "New Tweet", new_admin_twit_path, class: "btn btn-primary pull-right" %> </div> </div> <div class="row"> <div class="col-sm-12 col-xs-12"> <div class="table-responsive"> <table class="table table-striped table-bordered table-hover"> <tbody> <% @twits.each do |twit| %> <tr> <td class="col-sm-8 col-xs-8"><%= twit.tweet %></td> <td class="col-sm-4 col-xs-4"><%= link_to 'Show', admin_twit_path(twit), class: "btn btn-primary" %> <%= link_to 'Edit', edit_admin_twit_path(twit), class: "btn btn-default" %> <%= link_to "Delete", admin_twit_path(twit), class: "btn btn-danger", data: {:confirm => "Are you sure?"}, method: :delete %> </td> </tr> <% end %> </tbody> </table> </div> </div> </div> </div> 
Admin Новая страница Twit
 #app/views/admin/twits/new.html.erb <div class="container-fluid"> <div class="row"> <div class="col-sm-offset-4 col-sm-4 col-xs-12"> <%= form_for @twit do |f| %> <div class="form-group"> <%= f.label :tweet %> <%= f.text_field :tweet, class: "form-control" %> </div> <div class="form-group"> <%= f.submit "Submit", class: "btn btn-primary" %> <%= link_to "Cancel", :back, class: "btn btn-default" %> </div> <% end %> </div> </div> </div> 
Admin Twit Показать страницу
 #app/views/admin/twits/show.html.erb <div> <h2><%= @twit.tweet %></h2> </div> 
Индексная страница Twit
 #app/views/twits/index.html.erb <div class="container-fluid"> <p id="notice"><%= notice %></p> <h1>Listing Twits</h1> <div class="row"> <div class="col-sm-12 col-xs-12"> <%= link_to "New Tweet", new_twit_path, class: "btn btn-primary pull-right" %> </div> </div> <div class="row"> <div class="col-sm-12 col-xs-12"> <div class="table-responsive"> <table class="table table-striped table-bordered table-hover"> <tbody> <% @twits.each do |twit| %> <tr> <td><%= twit.tweet %></td> <td><%= link_to 'Show', twit, class: "btn btn-primary" %></td> </tr> <% end %> </tbody> </table> </div> </div> </div> </div> 
Новая страница Twit
 #app/views/twits/new.html.erb <div class="container-fluid"> <div class="row"> <div class="col-sm-offset-4 col-sm-4 col-xs-12"> <%= form_for @twit do |f| %> <div class="form-group"> <%= f.label :tweet %> <%= f.text_field :tweet, class: "form-control" %> </div> <div class="form-group"> <%= f.submit "Submit", class: "btn btn-primary" %> <%= link_to "Cancel", :back, class: "btn btn-default" %> </div> <% end %> </div> </div> </div> 
Twit Show Page
 #app/views/twits/show.html.erb <div> <h2><%= @twit.tweet %></h2> </div> 
  Теперь запустите сервер Rails, запустив rails server .  Перейдите через ваш сайт, и вы увидите, что все работает отлично.  Ваша кодовая база аккуратна, а контроллеры тонкие, благодаря concern . 
Проблемы в моделях
  ActiveSupport::Concern работает в моделях Rails, как мы видели в контроллерах.  Если вы поймете, что мы сделали выше, вы сможете реализовать это в своих моделях, где это необходимо.  Допустим, у нас есть функция reply в нашем приложении.  С помощью этой функции пользователи могут отвечать на твиты (дух).  Наряду с функцией ответа у нас есть функция голосования, позволяющая пользователям голосовать за твиты и ответы.  Итак, теперь у нас есть три модели: Twit , Reply и Vote . 
  Twit и Reply имеют много votes , поэтому их модели выглядят так: 
 class Twit < ActiveRecord::Base has_many :votes, as: :votable has_many :replies def vote! votes.create end end class Reply < ActiveRecord::Base has_many :votes, as: :votable belongs_to :twits def vote! votes.create end end class Vote < ActiveRecord::Base belongs_to :votable, polymorphic: true end 
Используя заботы, вы можете сделать вещи красивыми и аккуратными. Вот как вы можете это сделать:
 module Votable extend ActiveSupport::Concern included do has_many :votes, as: :votable end def vote! votes.create end end class Twit < ActiveRecord::Base include Votable has_many :replies end class Reply < ActiveRecord::Base include Votable belongs_to :twit end 
Я уверен, что вы согласитесь со мной, что этот путь намного лучше.
Вывод
  Цель этого урока проста.  Я просто хотел показать вам способ ActiveSupport::Concern принципу СУХОЙ, используя ActiveSupport::Concern .  Я надеюсь, что это стоило времени 🙂 
