пятница, 30 января 2015 г.

Шаблонизатор haml вместо erb

В веб-фреймворке Ruby on Rails есть отличная возможность подключить новый шаблонизатор haml вместо имеющегося стандартного erb-шаблонизатора. Для этого достаточно в Gemfile проекта добавить строчку:  
gem "haml"

Так выглядит erb-шаблон для отображения товаров - index.html.erb:
<h1>Listing products</h1>
<% if notice %>
  <p id="notice"><%= notice %></p>
<% end %>

<table class="products">
<% @products.each do |product| %>
  <tr class="<%= cycle('list_line_odd', 'list_line_even') %>">
    <td>
      <%= image_tag(product.image_url, class: 'list_image') %>
    </td>
    <td class="list_description">
      <dl>
        <dt><%= product.title %></dt>
        <dd><%= truncate(strip_tags(product.description),
               length: 80) %></dd>
      </dl>
    </td>
    <td class="list_actions">
      <%= link_to 'Показать', product %><br/>
      <%= link_to 'Ред.', edit_product_path(product) %><br/>
      <%= link_to 'Удалить', product, method: :delete,
                  data: { confirm: I18n.t('app.delete_confirm') } %>
    </td>
  </tr>
<% end %>
</table>
<br />
<%= link_to 'New product', new_product_path %>

А вот тот же шаблон с использованием haml - index.html.haml:
%h1 Listing products
- if notice
  %p#notice= notice
%table.products
  - @products.each do |product|
    %tr{class: "#{cycle('list_line_odd', 'list_line_even')}"}
      %td
        %p= image_tag(product.image_url, class: 'list_image')
      %td.list_description
        %dl>
          %dt= product.title
          %dd= truncate(strip_tags(product.description), length: 80)
      %td.list_actions
        = link_to 'Показать', product
        %br/
        = link_to 'Ред.', edit_product_path(product)
        %br/
        = link_to 'Удалить', product, method: :delete, data: { confirm: I18n.t('app.delete_confirm') }
%p= link_to 'New product', new_product_path

Если вы используете в работе Ruby on Rails, использование шаблонизатора haml позволит вам сократить количество кода в ваших шаблонах и сделает их более читаемыми. Поначалу может показаться, что нет смысл учить чью-то придумку, ведь есть старый добрый html. Однако в больших проектах кода много и его читаемость можно улучшить применив этот шаблонизатор. Кроме того, правила haml-шаблонизатора весьма просты, освоить их можно минут за 20, проделав самостоятельно примеры, приведенные на официальном сайте шаблонизатора haml.

Также есть аналогичный шаблонизатор - slim, синтаксис которого еще лучше. Однако, у нас в проекте используется haml, поскольку "так исторически сложилось".

понедельник, 1 декабря 2014 г.

Определить первый родительский класс

В языке программирования Ruby каждый объект имеет как минимум один родительский класс. Однако большинство объектов имеют в цепочке наследования несколько классов. Каждый дочерний класс может переопределить метод, определенный в родительском.
Требуется написать метод
def location_in_hierarchy(object, method)
...
end
который будет принимать объект и метод и возвращать класс, в котором данный метод был впервые определен.

Сериализация и десериализация объекта класса в Ruby

Объект в языке Ruby можно представить в виде строки. Также можно преобразовать строку обратно в объект. Для этого необходимо подключить библиотеку yaml.
# encoding: utf-8
require "yaml"

class Car
  def initialize(model, speed)
    @model = model
    @speed = speed
  end

  # Десереализуем в объект
  def self.deserialize(yaml_string)
    YAML::load(yaml_string)
  end
  
  # Объект сериализуем
  def serialize
    YAML::dump(self)
  end

  def to_s
    "Car: [model = #{@model}, speed = #{@speed}]"
  end
end

audi = Car.new('Audi', 400)
yaml = audi.serialize
puts "yaml выглядит так:"
p yaml
puts "Yaml можно обратно преобразовать в объект\n: #{Car.deserialize(yaml)}"
puts

bmw = Car.new('BMW', 600)
mazda = Car.new('Mazda', 120)
cars = [bmw, mazda]

puts "Мы можем даже сериализовать массивы: #{cars}"
Программа выведет:
yaml выглядит так:
"--- !ruby/object:Car\nmodel: Audi\nspeed: 400\n"
Yaml можно обратно преобразовать в объект
: Car: [model = Audi, speed = 400]

Мы можем даже сериализовать массивы: [Car: [model = BMW, speed = 600], Car: [model = Mazda, speed = 120]]

пятница, 21 ноября 2014 г.

Ruby on Rails показывает пустую страницу после деплоя на locum.ru

Если говорить о деплое Ruby on Rails на хостинг locum.ru, то это отображение пустой страницы при переходе по url проекта может быть вызвана рядом причин.

1. Попробуйте перезапустить мастер-процесс unicorn проекта, зайдя в админку locum и нажав на кнопку "Перезапустить проект" в настройках проекта.

2. Если по-прежнему по обращению по url проекта отображается пустая страница, то проверьте файл: config/secrets.yml:
production:
  secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>

Вы определяли на сервере locum значение этой переменной?

3. Определим переменную SECRET_KEY_BASE на сервере locum. Для этого зайдем на сервер и добавим ее в ~/.bashrc:
    $ ssh hosting_kenny@fluorine.locum.ru
    $ vim ~/.bashrc

В редакторе vim откроется указанный файл. Добавьте в конец строку:
# Содержимое ОБЯЗАТЕЛЬНО заключите в ДВОЙНЫЕ квычки
export SECRET_KEY_BASE="AAAAB ...I5t9YYbX"

Содержимое этой переменной можно сгерерировать у себя на ПК. Для этого нужно перейти в корневую папку своего проекта и запустить команду:
    $ rake secret
f400fdd1ad5fb822012c3a09a8b5f7871f9e35105198b5d69
d7623167934b810c011397ae921800e234405bddbf05bbc8
1f5f8730f06a5a443c4bb784b499705

4. Проверим, видит ли Ruby установленную переменную. Откроем новое окно терминала и зайдем на удаленный сервер locum. Запустим интерактивную консоль ruby (irb):
    $ ssh hosting_kenny@fluorine.locum.r
    $ irb
    irb(main):002:0> ENV['SECRET_KEY_BASE']

Вы должны увидеть значение, которое вы установили этой переменной.
Через админку хостинга locum перезапустите проект.
После этого данная переменная станет доступна в окружении сервера, и вы не будете видеть пустой страницы, когда перейдете по url своего проекта.

Если данный способ вам не помог, просто откройте файл config/secrests.yml и определите secret_key_base для среды production непосредственно:

production:
  secret_key_base: edb5d003ae05f ..... da17d5bc

четверг, 20 ноября 2014 г.

Урок по созданию git-репозитория на хостинге locum

На хостинге locum.ru можно создать приватный git-репозиторий для своих проектов. В этом уроке мы пошагово покажем, как это сделать.

Предупреждение:

1.Подключимся по ssh на удаленный сервер locum:
    $ ssh hosting_kenny@fluorine.locum.ru

Здесь hosting_kenny - наше имя пользователя на сервере, fluorine.locum.ru - адрес сервера.

2. Перейдем в папку git, расположенную в домашней директории на удаленном сервере (если этой папки нет, создайте ее):
    $ cd ~/git

3. В папке ~/git создадим папку blog (совпадает с названием нашего проекта в админке) и инициируем git-репозиторий нашего проекта:
    $ mkdir ~/git/blog.git
    $ cd ~/git/blog.git
    $ git init --bare

Данной командой мы создаем "голый" репозиторий (без рабочего каталога). По соглашению папки "голых" репозиториев оканчиваются на .git. С этого момента у нас для проекта blog есть удаленный репозиторий.

4. На своем ПК перейдем в папку своего проекта, инициируем там локальный репозиторий и добавим ссылку на созданный удаленный репозиторий.
    $ ~/my_projects/blog
    $ git init
    $ git remote add origin ssh://hosting_kenny@fluorine.locum.ru/home/hosting_kenny/git/blog.git

/home/hosting_kenny/git/blog.git - это абсолютный путь к нашему репозиторию на хостинге. Последней командой мы добавили в наш локальный репозиторий удаленный и назвали его origin.

5. У себя на ПК сделаем начальный коммит и отправим все изменения в наш удаленный git-репозиторий на сервере locum:
    $ git add .
    $ git commit -m "Initial commit"
    $ git push -u origin master

   
Флаг -u (--set-upstream) позволяет установить соответствие между нашим репозиторием и удаленным. Это впоследствии позволит нам использовать короткие команды: git push.

Создание соединения с удаленным сервером по ssh

Мы рассмотрим создание ssh-соединения с удаленным ПК в ОС Linux (Ubuntu 14.04 х64).

1. Проверим, установлен ли у нас в системе ssh. Откроем окно терминала (рекомендуем использовать Terminator) и введем команду:
    $ ssh
Должно вывестись примерно это:   
    usage: ssh [-1246AaCfgKkMNnqsTtVvXxYy] [-b bind_address] [-c ciphe...

Если возникла ошибка, вам нужно установить ssh:
    $ sudo apt-get install ssh

2. Попытаемся установить соединение с удаленным компьютером:
    $ ssh hosting_kenny@fluorine.locum.ru

Здесь hosting_kenny - имя пользователя на сервере, а fluorine.locum.ru - адрес сервера. Укажите свои имя пользователя и адрес сервера.
Скорее всего, удаленный сервер потребует от нас пароль. И так каждый раз, пока мы не настроим ssh-соединение при помощи ключей.

3. Проверим, есть ли у нас на ПК сгенерированные приватный/публичный ключи:
ls -al ~/.ssh
Эта команда выводит файлы папки .ssh домашней директории нашего пользователя ОС. К примеру, это могут быть такие файлы:
  • authorized_keys - перечень публичных ключей пользователей, которым разрешено подключаться к нашему ПК по ssh.
  • id_rsa - приватная часть ключа (как правило, защищается от посторонних посредством пароля)
  • id_rsa.pub - публичная часть ключа

Если файлов id_rsa.pub и id_rsa у вас на ПК нет, то сгенерируем их:
    $ ssh-keygen -t rsa -C "your_email@example.com"

4. Теперь, когда у нас есть файл id_rsa.pub, поместим его содержимое на удаленный сервер в директорию ~/.ssh/authorized_keys. Выполним у себя на ПК команду:
    $ cat ~/.ssh/id_rsa.pub | ssh hosting_kenny@fluorine.locum.ru 'cat >> ~/.ssh/authorized_keys'
Эта команда добавляет ваш публичный ключ к списку авторизованных ключей удаленного сервера. При этом у нас потребуют ввести пароль.

Теперь мы можем свободно подключаться к удаленному серверу при помощи ssh, забыв о вводе паролей.

Кстати, сгенерированный публичный ключ id_rsa.pub можно также добавить к вашему аккаунту на github.com, чтобы свободно осуществлять соединение с ним.

Простой проект на Ruby on Rails

Создадим на своем локальном компьютере простой проект, который потом мы разместим на хостинге locum посредством Capistrano.

1. Убедимся, что у нас на локальном ПК используется версия Ruby 2.1.5:
    $ rvm use 2.1.5
    $ ruby -v
    ruby 2.1.5p273 (2014-11-13 revision 48405) [x86_64-linux]

2. Проверим, какая версия rails используется:
    $ rails -v
    Rails 4.1.8
Если возникла ошибка, то вам нужно установить Ruby on Rails для выбранной в rvm версии ruby:
    $ gem install rails

3. Создадим заготовку Rails-проекта и запустим сервер разработки:
    $ rails new blog
    $ cd blog
    $ rails s
Перейдите в браузере по адресу: http://127.0.0.1:3000/ Там вы должны будете увидеть приветствие Welcome aboard.

4. Остановим сервер разработки и сгенерируем контроллер posts:
    $ rails g controller posts index
При этом будет создан ряд файлов.

5. Сгенерируем модель Post:
    $ rails g model Post title:string{100} text:text
Будут созданы:
  • модель app/models/post.rb
  • файл миграции db/migrate/20141120101447_create_posts.rb
6. На основе созданного файла миграций создадим для модели Post в базе данных таблицу. Для этого запустим миграцию:
    $ rake db:migrate

7. Для активации CRUD для модели Post отредактируем файл config/routes.rb:
Rails.application.routes.draw do
  root 'posts#index'
  resources :posts
end

8. Запустим сервер разработки и перейдем на страницу отображения статей:
    $ rails s
Перейдем по адресу: http://127.0.0.1:3000/.
Здесь должен отображаться список названий статей. Но у нас пока нет ни одной статьи, поэтому создадим форму для их добавления.

9. Форма добавления должна располагаться по адресу: http://localhost:3000/posts/new
При переходе мы получим ошибку: The action 'new' could not be found for PostsController.
Для ее устранения добавим экшен new в файл app/controllers/posts_controller.rb:
class PostsController < ApplicationController
  def index
  end

  def new
    @post = Post.new
  end
end

10. Если еще раз попробуем перейти по адресу из п.9, то получим ошибку об отсутствии шаблона: Missing template posts/new, application/new with {:locale=>[:en], :formats=>[:html], :variants=>[], :handlers=>[:erb, :builder, :raw, :ruby, :coffee, :jbuilder]}. Searched in: * "/home/yesnik/learn/rails_projects/blog/app/views"
Для ее устранения создадим файл app/views/posts/new.html.erb:
<h3>Добавить новый пост</h3>

<%= form_for :post, url: posts_path do |f| %>
  <p>
    <%= f.label :title, "Заголовок" %><br>
    <%= f.text_field :title %>
  </p>
  <p>
    <%= f.label :text, "Содержание" %><br>
    <%= f.text_area :text %>
  </p>
  <p>
    <%= f.submit "Создать" %>
  </p>
<% end %>

После этого при переходе по адресу из п.9 мы увидим форму добавления поста.

11. Если попытаться создать пост, мы получим ошибку:
The action 'create' could not be found for PostsController
Для ее устранения добавим экшен create в файл app/controllers/posts_controller.rb.
class PostsController < ApplicationController
  def index
  end

  def new
    @post = Post.new
  end

  def create
   @post = Post.new(post_params)
   @post.save
   # После сохранения делаем редирект на новую статью
   redirect_to @post
  end

  def show
   @post = Post.find(params[:id])
  end

  private
  def post_params
   params.require(:post).permit(:title, :text)
  end
end

12. Однако добавив экшен и отправив форму, мы обнаружим еще одну ошибку: The action 'show' could not be found for PostsController. Для ее устранения нам понадобится в файл контроллера добавить метод show, а также создать шаблон для показа поста app/views/posts/show.html.erb:
<p><%= link_to "К списку постов", posts_path %></p>
<h3><%= @post.title %></h3>
<p><%= @post.text %></p>

13. Теперь после добавления поста происходит редирект на страницу его просмотра (show). Нажав ссылку "К списку постов", мы переходим на страницу, где должны показываться названия всех постов. Чтобы они отобразились, отредактируем экшен index в файле контроллера app/controllers/posts_controller.rb:
class PostsController < ApplicationController
  def index
    @posts = Post.all
  end
  # ...
end

Также отредактируем шаблон app/views/posts/index.html.erb:
<h3>Список постов</h3>
<ul>
<% @posts.each do |post| %>
  <li>
   <%= link_to post.title, post_path(post) %> 
   <small>от <%= post.created_at.strftime "%d.%m.%Y в %H:%M" %></small>
   <%= link_to "ред.", edit_post_path(post) %>
   <%= link_to "[X]", post_path(post), method: :delete, 
    data: {confirm: "Точно удалить пост?"} %>
  </li>
<% end %>
</ul>
<%= link_to "Добавить пост", new_post_path %>

14. Если вы теперь перейдете по ссылке http://localhost:3000/posts , то увидите ссылки ред. и [Х] рядом с названием каждой статьи. Для того, чтобы они заработали, добавим экшены edit и destroy в контроллер app/controllers/posts_controller.rb:
class PostsController < ApplicationController

  def edit
    @post = Post.find(params[:id])
  end

  def destroy
    @post = Post.find(params[:id])
    @post.destroy
    # После удаления поста редирект на список постов
    redirect_to posts_path
  end
  # ...
end

Теперь при нажатии на [X] произойдет удаление поста.

15. Чтобы заработало редактирование содержимого поста, добавим шаблон app/views/posts/edit.html.erb:
<h3>Редактирование поста</h3>
<%= form_for @post do |f| %>
 <p>
  <%= f.label :title, "Заголовок" %><br>
  <%= f.text_field :title %>
 </p>
 <p>
  <%= f.label :text, "Содержание" %><br>
  <%= f.text_area :text %>
 </p>
 <p>
  <%= f.submit "Сохранить" %>
 </p>
<% end %>

Т.к. posts мы сделали ресурсом в config/routes.rb, то Rails сам определит нужный экшен.

16. Если отредактировать статью и сохранить, возникнет ошибка: The action 'update' could not be found for PostsController. Для ее устранения добавим экшен update в контроллер app/controllers/posts_controller.rb:
class PostsController < ApplicationController

  def update
    @post = Post.find(params[:id])
      if @post.update(post_params)
        redirect_to @post
      else
        render 'edit'
    end
  end
  # ...
end

Поздравляем! Теперь у вас есть приложение на Ruby on Rails, в котором можно добавлять, редактировать и удалять посты.