вторник, 10 марта 2015 г.

ArgumentError: comparison of String with Date failed

Что не так со следующим кодом?
# здесь custom_lot_attributes - колонка типа 'hstore' в таблице lots
start_date = lot.custom_lot_attributes['start_date']
if start_date && start_date < Date.today
  # ...
end

При выполнии этого участка кода возникает ошибка:
ArgumentError: comparison of String with Date failed
Это связано с тем, что мы пытаемся сравнить дату со строкой. Из hstore мы получаем строку, поэтому в переменной start_date содержится дата в виде строки, к примеру "2015-03-10".
Для устранения ошибки нужно преобразовать строку в дату. Для этого в Rails есть метод to_date.
Теперь ошибки возникать не будет:

start_date = lot.custom_lot_attributes['start_date'].to_date
if start_date && start_date < Date.today
  # ...
end

А сможете ли вы найти ошибку в этом коде?
start_date = lot.custom_lot_attributes['start_date'].to_date
if start_date && start_date < Date.today
  lot.custom_lot_attributes['start_date'] = Date.today
  lot.custom_lot_attributes['end_date'] = 
    lot.custom_lot_attributes['end_date'] +
    (Date.today - lot.custom_lot_attributes['start_date'])
end

Данный код будет вызывать ошибку:
TypeError: can't convert Rational into String
Причина аналогична предыдущей, мы забыли преобразовать строковое значение, полученное из hstore, в дату.
Вот как должно быть:
start_date = lot.custom_lot_attributes['start_date'].to_date
if start_date && start_date < Date.today
  lot.custom_lot_attributes['start_date'] = Date.today
  lot.custom_lot_attributes['end_date'] = 
    lot.custom_lot_attributes['end_date'].to_date +
    (Date.today - lot.custom_lot_attributes['start_date'].to_date)
end


вторник, 10 февраля 2015 г.

NoMethodError: undefined method `[]' for nil:NilClass

Данная ошибка может возникать в Ruby при попытке обратиться к несуществующему свойству объекта, который почему-то стал nil.
Рассмотрим пример, запустив консоль Rials (rails c):
hash = {a: 1, b: {c: 2}}
=> {:a=>1, :b=>{:c=>2}}

# Получаем элемент с индексом :a
hash[:a]
=> 1

# Получаем элемент с индексом :b
hash[:b]
=> {:c=>2}

# Получаем элемент с индексом :b, а для него - с индексом :c
hash[:b][:c]
=> 2

# Пытаемся получить эл-нт с индексом :zzz, а для него - с индексом :c
hash[:zzz][:c]
NoMethodError: undefined method `[]' for nil:NilClass

Как видно, при попытке получить элемент с индексом :zzz мы получаем nil, и уже для nil мы пытаемся найти элемент с индексом :c, что приводит к ошибке, ведь у nil нет метода [].

Решение:

hash[:zzz].try(:[], :c)

Обратите внимание, что этот способ работает только в Rails. В чистом Ruby он не поможет, там можно использовать трюк с if:
hash[:zzz][:c] if hash[:zzz]
 

пятница, 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.