Skip to content

aleksandrzen/ror_junior

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

20 Commits
 
 

Repository files navigation

Ruby on Rails Juniors

Введение в профессиональную разработку на Ruby on Rails

Начало работы

Последовательность команд для установки Ruby on Rails на Ubuntu:

sudo apt-get update -y
sudo apt-get install build-essential bison openssl libreadline6 libreadline6-dev curl git-core zlib1g zlib1g-dev libssl-dev libyaml-dev libsqlite3-0 libsqlite3-dev sqlite3 libxml2-dev libxslt-dev autoconf libc6-dev nodejs -y
curl -L get.rvm.io | bash -s stable
source ~/.rvm/scripts/rvm
rvm install 2.1.1
rvm use 2.1.1 --default
gem install rails --no-rdoc --no-ri

После этого становится доступной команда создания нового rails-приложения rails new {applicaion name}. Затем необходимо перейти в каталог созданного приложения и запустить rails-сервер командой rails s. После этого приложение становится доступным в браузере на localhost:3000.

Пример создания приложения first_app и запуска его сервера:

rails new first_app
cd first_app
rails s

Чтобы запустить сервер на другом порте, необходимо указать номер порта с ключом -p:

rails s -p 5555

запустит сервер на порте 5555, и в браузере приложение нужно будет искать на localhost:5555.

Getting started: приступая к работе

В этом практическом задании рассмотрено большое количество очень разных вопросов, объединенных, тем не менее, плотным смысловым единством (состоящим, пожалуй, в том, без всех этих вещей ни с одним rails-приложением нельзя сделать почти ничего):

  • Bundler - пакетный менеджер Rails;
  • язык описания представлений (views) Slim;
  • Twitter Bootstrap для Asset pipeline;
  • контроллер статических страниц;
  • введение в роутинг;
  • первое знакомство с хелперами.

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

Устанавливаем slim — в Gemfile добавляем

gem 'slim-rails'

В каталоге приложения запускаем команду

bundle install

Впрочем, install — это наиболее востребованная операция Bundler'a, которая делается им по умолчанию, поэтому bundle install, как правило сокращается просто до

bundle

Это нужно делать всегда после обновления Gemfile, больше не будем на этом останавливаться.

Теперь мы можем создавать вьюхи на slim'e (Постоянно держа под рукой его мануал). Кроме того, теперь, когда мы выбрали язык описания представлений, можно установить Twitter Bootstrap.

Читаем документацию в readme по ссылке (привыкаем к этому, это теперь на всю профессиональную жизнь в Rails), осмысливаем и делаем только то, что нам нужно:

Добавляем в Gemfile:

gem 'therubyracer'
gem 'less-rails'
gem 'twitter-bootstrap-rails'

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

rails generate bootstrap:install less

Теперь можно бегло глянуть application.css и application.js, чтобы примерно понять, как работает Asset pipeline, а позже обязательно внимательно прочитать документацию.

Тем не менее, не отвлекаемся сильно и создаем главный layout приложения (здесь и далее: команда rails всегда выполняется в каталоге приложения):

rails g bootstrap:layout application fluid

Удаляем старый ужасный application.html.erb из /app/views/layouts/, и, чтобы увидеть значительные изменения, создаем контроллер статических страниц, воспользовавшись диалогом RubyMine или выполнив команду

rails g controller Page home index about info test

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

Теперь пора познакомиться с системой автоматической сборки кода Rake и запросить у нее список обслуживаемых приложением путей (здесь и далее: команда rake точно так же всегда запускается в каталоге приложения):

rake routes

Теперь на localhost:3000 нам открыты все пути, перечисленные в rake routes! Если не открыты, запустите сервер и не забывайте его включать и выключать по необходимости — это тоже очень важный навык.

Однако в самом корне localhost:3000 пока еще стартовая страница по умолчанию.

В routes.rb (который находится в папке /config приложения) пишем

root 'page#home'

или, для большего соответствия с полным синтаксисом routes.rb

root to: 'page#home'

Работать будет и так, и так — делайте выбор из ваших синтаксических предпочтений. С помощью команды root можно направить корневой адрес приложения любому действию (action) любого контроллера, указав их в (желательно одинарных) кавычках в формате :controller#:action.

Полностью команда настройки статического пути выглядит так:

get '{:path}', to: ':controller#:action', as: {:alias}

Рассмотрим на примере:

get 'page', to: 'page#index', as: :page

Все примерно понятно и знакомо, но зачем же alias? Снова запустим rake routes и найдем все элиасы в первой колонке таблицы, озаглавленном Prefix. Теперь в application.html.slim (который, мы помним, в /app/views/layouts) найдем ссылки в шапке сайта и поправим их в соответствии со структурой нашего приложения:

li = link_to 'Pages index', :page

Первый и, пожалуй, самый важный встреченный нами хелпер (ruby-функция rails, генерирующая html-код на основании переданных параметров) link_to применяется, очевидно, для описания ссылок и первым параметром принимает текст или html-код отображения ссылки, вторым — адрес, на который будет указывать ссылка. Далее можно будет указать дополнительные параметры, как то style: или class:, но об этом позже. Адрес ссылки можно задать, помимо уже известного, еще двумя способами:

li = link_to 'Pages index', page_path

и

li = link_to 'Pages index', page_url

Встроенные методы rails {:prefix}_path и {:prefix}_url создадут соответственно относительный от корня сайта адрес страницы и абсолютный url, включающий адрес сайта. На локалхосте разницу, конечно, не заметить, но знать это надо. Вот мы и разобрались с префиксами роутов.

Продолжаем. В routes.rb пишем что-то вроде

get 'any/other/page/anywhere/at/your/site', to: 'page#info', as: :info

в application.html.slim

li = link_to 'Cool page', :info

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

Теперь самое время заменить ужасную html-ную ссылку на главную страницу, которая, ко всем прочим недостаткам, из коробки twitter bootstrap gem еще и не работает (видимо, по каким-то серьезным идейным соображениям). Меняем

a.brand href="#"{:app_name}

на

= link_to 'Practice', :root, class: %i[brand]

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

Еще об одном хелпере — content_for. В application.html.slim есть многообещающее

title = content_for?(:title) ? yield(:title) : "Practice"

Как же задать этот title? (использованный здесь тернарный оператор, конечно же, всем знаком) В любой взятой наугад вьюхе, например, app/views/page/index.html.slim, лучше всего в самой первой строчке, пишем:

- content_for :title
  | Проникновенный и точный заголовок индексной страницы по умолчанию

И это, собственно, все. Сравниваем с заголовками вьюх, где content_for :title не определен и озаряемся пониманием.

Для закрепления поменяем последнюю ссылку в сайдбаре на

li = content_for?(:sidebar) ? yield(:sidebar) : link_to("Link 3", "/path3")

Соответственно, во вьюхе, в которой мы хотим заменять эту ссылку, пишем, например:

- content_for :sidebar
  - link_to 'Home Sweet Home', :root, style: 'color:green'

Вот, собственно, и весь content_for — второй хелпер, рассмотренный в этом курсе. Их еще много, и пора переходить к следующему занятию, где мы поближе познакомимся со странными двоеточими в начале некоторых слов, имеющими прямое отношение к важным различиям между символами и строками в ruby, и, возможно, даже с процентными литералами.

Можно пока остальные ссылки в лэйауте приложения допилить и все content_for снести. Ну, или, наоборот, добавить, если эффект понравился.

Getting continued: немного Ruby и FizzBuzz-тест (beginner & NIGHTMARE)

В ходе этой небольшой, но важной работы мы

  • сформулируем самое общее представление о Ruby как о языке программирования;
  • узнаем самые важные вещи об объектной модели Ruby;
  • выделим самый важный класс объектов не только Ruby, а, возможно, всей Computer science;
  • разберемся наконец с разницей между символами и строками в Ruby;
  • сделаем простейшую реализацию принципа duck typing;
  • слегка прикоснемся к магии Ruby — доброй и не очень;
  • немного затронем регулярные выражения и процентные литералы;
  • а также проверим свои навыки постановки и решения прикладных задач.

Запустим рельсовую консоль (здесь и далее: в каталоге приложения rails c) и запишем — вот прямо подряд:

class String
def palindrome?
self == self.reverse
end
end

Проверяем:

'deified'.palindrome?
'olol'.palindrome?
'ololo'.palindrome?

Смотрим и делаем ряд важных выводов:

  • Ruby — один из самых объектно ориентированных языков. Объекты здесь везде;
  • Ruby way — это всегда вызов метода объекта на объекте через точку;
  • в Ruby можно дополнять описания встроенных системных классов;
  • в Ruby принято дополнять имена проверяющих функций вопросительным знаком (привет content_for?!);
  • к слову: методы с восклицательным знаком, как правило, изменяют объект, на котором они вызваны;
  • метод возвращает последнее упомянутое в коде значение, return нужно использовать только тогда, когда код сложный и есть вероятность возникновения путаницы.

А также несколько не очень важных:

  • рельсовая консоль красиво расставляет отступы;
  • а в случае использования гема pry-rails еще и делает красивую подсветку синтаксиса.

Кто еще не поставил pry-rails, ставит:

gem 'pry-rails'

в Gemfile, конечно. Здесь и далее.

Осваиваем конструкцию диапазона (a..b), итератор each, однострочный блок { |variable| code(variable) } и команду вывода puts:

def count_to(limit)
  (1..limit).each { |i| puts i }
end

Вызов, само собой:

count_to(10)

или даже

count_to 10

Это есть такой в Ruby синтаксический сахар — передача аргументов методу без скобок. Удобно иногда, хотя лучше этим не увлекаться — от такого сахара по сути правильный код иногда вдруг внезапно перестает работать (обратите внимание, что в примере с content_for :sidebar аргументы link_to пришлось взять в скобки, и неспроста).

Еще немного:

def count_to(limit)
  (1..limit).each do |i|
    print i
  end
end

Это, соответственно, многострочный блок и команда print.

Чтобы чуть облагородить вывод команды print, освоим интерполируемые строки (это которые в двойных кавычках, в отличие от неинтерполируемых в одинарных — знакомая во многих языках практика). К слову о культуре кода, следите постоянно за тем, чтобы в двойных кавычках обязательно происходила какая-нибудь интерполяция. Если ее нет, одинарные кавычки обязательны. Код читать будет намного удобнее.

Итак:

def count_to(limit)
  (1..limit).each { |i| print "#{i}\n" }
end

Сформируем массив значений:

def count_to(limit)
  (1..limit).map { |i| i }
end

Вот еще красивая практика для сокращения записи одного подвида тернарных операторов:

['Fizz'][0]
['Fizz'][1]
['Fizz'][100500]

Все это значит, что в Ruby первый индекс массива — 0, а обращение к элементу массива с несуществующим индексом не выдает ошибки, а только nil — отсутствующее значение. Разовьем мысль:

"#{['Fizz'][0]}"
"#{['Fizz'][100500]}"

К слову. Метод blank? проверяет, является ли строка пустой. Еще к слову: если вы хотите вызвать какой-либо метод на объекте, генерируемом каким-либо оператором, возьмите этот оператор в скобки.

Вот и все. Теперь вы готовы к тому, чтобы почти мгновенно написать красивое однострочное решение для FizzBuzz-теста на Ruby:

Определить функцию от двух аргументов, возвращающую массив целых чисел от одного аргумента до другого, обработанных так:

  • если число делится без остатка на 3, вернуть «Fizz»;
  • если число делится без остатка на 5, вернуть «Buzz»;
  • если и на 3 и на 5, конечно же, «FizzBuzz»;
  • если число не делится ни на 3, ни на 5, вернуть само число.

Подсказка 1: Первый аргумент не обязательно меньше второго.

Подсказка 2: Остаток от деления i на 3 — это i%3. Можно пользоваться справочниками по операторам, циклам и ветвлениям в Ruby.

Подсказка 3: Ни сейчас, ни когда-либо еще не используйте for {:variable} in {:array}. Это совсем не Ruby way, не создает локальной области видимости переменных, и вообще некрасиво.

Интересный пример реализации

def fizz_buzz_test (a,b)
  ((a<b) ? (a..b) : (b..a)).map {|elem| {0 => elem, elem%3 => 'Fizz', elem%5 => 'Buzz', elem%15 => 'FizzBuzz'}[0]}
end

Когда тест готов, синтаксически совершенен и абсолютно работоспособен, наступает время опустить скучные модификации вроде FizzBuzzEazzCozzDezz и сделать все вовсе по-взрослому. На NIGHTMARE мы будем делать этот тест:

  • принимая массив делителей как аргумент функции;
  • и представляя числа как произведения своих делителей и остатка деления.

То есть, все просто. fizzBuzzNigthmare(1,100,3,5) должна нам выдавать '3x2' на 6, '5x2' на 10, '3x5' на 15 и так далее. Отложим пока фильтрацию ввода и сделаем пока чистую реализацию алгоритма, предполагая, что в массиве делителей не будет неприятных неожиданностей. Однако обязательно подумаем об этом позже.

А сейчас необходимо освоить несколько технических моментов как Ruby, так и Rails.

Прием массива как аргумента функции с помощью * (заодно раскроем тему точек с запятой в Ruby):

def checkArrayArg(a, b, *c)
  puts a; puts b; puts c.inspect
end

Проверяем:

checkArrayArg(1,100,-1,0,1,2,3,5,7,4,10)

Это Ruby, здесь просто. А вот в рельсах есть MVC с роутингом, здесь все сложнее — чтобы получить такой же REPL в rails-приложении, нужно подергать и роутинг, и контроллеры, и вьюхи.

Вернемся к чуть более простому случаю — приему обычных параметров из url.

В routes.rb добавляем

get 'test/:a/:b', to: 'page#test'

В page_controller.rb:

def test
  @a, @b = params[:a], params[:b]
end

В page/test.html.slim:

p = @a
p = @b

Вот и все, наслаждаемся

С массивом в качестве аргумента все до обидного аналогично и тривиально: В routes.rb:

get 'test/:a/:b/*c', to: 'page#test'

В page_controller.rb:

def test
  @a, @b, @c = params[:a], params[:b], params[:c].split('/')
end

В page/test.html.slim:

p = @a
p = @b
p = @c.inspect

Можно принять несколько массивов из одного запроса, используя разделитель:

В routes.rb:

get 'test/:a/:b/*c/div/*d', to: 'page#test'

В page_controller.rb:

def test
  @a, @b, @c, @d = params[:a], params[:b], params[:c].split('/'), params[:d].split('/')
end

В page/test.html.slim:

p = @a
p = @b
p = @c.inspect
p = @d.inspect

Распилив пришедший параметр по разделителю методом split, мы более явно, чем в случае с одиночным параметром, убедились в том, что в параметрах запроса все значения приходят строками. Чтобы получить из них целые числа, следует воспользоваться методом to_i (to integer). Кстати, есть еще to_s для приведения к строкам, to_f к числам с точкой. Есть много других типов и методов для приведения к ним, и их можно будет освоить самостоятельно.

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

def test
  @a, @b, @c = params[:a].to_i, params[:b].to_i, params[:c].split('/').map(&:to_i)
end

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

Вот, кстати, дополнительная задача для будущих суровых well-grounded рубистов:

Добавить в системный класс Fixnum описание метода fizzBuzz, который будет возвращать результат FizzBuzz-преобразования и вот так вот красиво работать через лямбду:

(1..100).map(&:fizzBuzz)

У кого получилось, может считать себя прикоснувшимся к доброй магии Ruby.

Однако нам потребуется еще несколько инструментов. Осваиваем:

Выбрать элементы массива, на которых условие выполняется:

[2,3,5,7,11].select { |i| 55%i == 0 }

Посчитать размер массива:

[2,3,5,7,11].select { |i| 55%i == 0 }.size

Удалить элементы массива, если условие выполняется:

[-1,0,1,2,3,5,7,11,4,6,10].delete_if { |i| i < 2 }

Красиво вывести массив через разделитель:

[2,3,5,7,11,].join('x')

И вообще, Array — один из важнейших объектов не только Ruby, но и всей Computer Science вообще. Насколько это возможно близкое знакомство с документацией класса Array в высшей степени рекомендуется. То, что в списке классов Array стоит первым, поверьте, не случайность.

Однако, еще немного инструкций по мелочи.

Найти наибольший обший делитель:

55.gcd(99)

Еще вот прямо совсем подсказка:

a = 1024
5.times { a /= 2 }
puts a

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

gem 'better_errors'
gem 'binding_of_caller'

Вот и все. Теперь вас не остановит даже FizzBuzz тест на NIGHTMARE. А внимательные читатели уже готовы сделать и фильтрацию ввода тоже.

О символах и duck typing

Пора, однако, раз и навсегда разобраться с уже порядком намозолившими глаза символами. Символы — это неизменяемые строки Ruby, имена собственные. Названия полей, методов, всех объектов вообще, ключи в хэшах — всегда символы. Символы очень похожи на строки, и их довольно легко получить друг из друга методами to_s и to_sym соответственно.

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

Берегите память. Если не собираетесь изменять какую-либо строку — так и скажите, что она символ. Когда ваш проект окажется под высокой нагрузкой, будете себе за это очень благодарны.

К практике. Убедимся, что этот пример выдает ошибку:

:some_symbol.to_i

(как хорошо, что у нас все на одной странице, и то, что про ошибку я сказал до приведения кода примера. Заставить этот фрагмент работать, конечно, можно, добавив в определение системного класса Symbol описание метода to_i, но здесь как раз не нужно пользоваться этой восхитительной гибкостью Ruby)

Это означает, к примеру, что такого рода фильтрация с приведением к целым числам

def with_filtering(a,b,*c)
  c.map!(&:to_i)
end

Справится и с числами с точкой, и со строками, а вот одним-разъединственным символом подавится. Что же делать? Так

:some_symbol.to_s.to_i

конечно же, сработает. Но что же нам теперь, все через строки конвертировать? Нет, конечно. Есть способ понять, определен ли какой-либо метод для этого объекта или нет.

class Object
  def integerize!
    self.respond_to?(:to_i) ? self.to_i : self.to_s.to_i
  end
end

Теперь приобщим к этому величию нашу функцию фильтрации:

def with_filtering(a,b,*c)
  c.map!(&:integerize!)
end

Бинго! Теперь наша фильтрация не давится символами, а мы получили опыт использования принципа duck typing. Подробнее о том, что это такое, зачем это нужно и почему это важно, хотя бы здесь и, например, здесь.

Не вся магия одинаково полезна

Определим функцию, которая должна возвращать единицу, так:

def dirty_magic
  def dirty_magic
    def dirty_magic
      def dirty_magic
        def dirty_magic
          def dirty_magic
            def dirty_magic
            1
            end
          2
          end
        3
        end
      4
      end
    5
    end
  6
  end
7
end

И будем вызывать ее до тех пор, пока она не станет устойчиво возвращать единицу.

Сверхгибкий язык программирования Ruby позволяет создавать эффективнейшие средства троллинга отдела тестирования. Если таким образом определить какой-либо экшн какого-либо контроллера, эффект сохранится. Каждый перезапуск сервера будет запускать такой генератор блуждающих неисправностей заново. Очень удобно: спринт стремительно заканчивается, по проекту гуляют блуждающие ошибки, тестировщики седеют, подавая пачки тщательно оформленных багрепортов, а вы спокойно занимаетесь своими делами, зная, что можете решить 90% поставленных вам задач одним движением. Ценнейший для командной работы навык.

Хорошо бы придумать какое-то практическое применение для этого эффекта, которое не связано с вредительством. У меня пока не получилось.

Что здесь произошло: внутри объявления функции dirty_magic мы можем производить объявления функции с тем же названием dirty_magic практически без ограничений. При вызове она будет определять функцию с названием dirty_magic (воздержимся от чересчур метафизичной формулировки «переопределять саму себя») и помимо этого делать что-то еще — в данном случае возвращать ту или иную цифру. Для Ruby (хоть в интерпретируемом, хоть в компилируемом варианте) такая цепочка переопределений не представляет проблемы — у нас всегда есть тем или иным способом определенная функция dirty_magic, которой никто не запрещает определять функцию с названием dirty_magic.

Для нас же это не хорошо и не плохо. Так устроен язык Ruby, и об этих особенностях нужно знать, даже если непонятно, как это применить.

Чуть позже, возможно, через пару дней, здесь появится описание бонусной лабораторной работы по подходам к работе с объектами на Ruby on Rails.

Регулярные выражения и процентные литералы

Предположим, мы уже решили задачу и хотим красиво, по-бутстраповски вывести результаты. Пишем во вьюхе:

- @result.each do |result|
  span class=%i[btn btn-large] class="btn-#{/\A\d+\Z/.match(result) ? 'danger' : 'success'}" style='margin:4px' = result

Тем, кто знаком с регулярными выражениями, уже почти все понятно. Стоит разве что напомнить, что регулярным выражением можно проверять многострочные последовательности, и \A и \Z означают соответственно начало и конец всей проверяемой последовательности. Привычные ^ и $ работают, конечно, но означают начало и конец строки — возможно, одной из многих. Будьте внимательны.

Процентные литералы позволяют экономить символы на запятых, кавычках, двоеточих и прочей пунктуации, и тем значительно улучшают читаемость кода.

Массив символов:

%i[one two three four five six seven eight nine ten]

Массив строк:

%w[one two three four five six seven eight nine ten]

Знайте и используйте. Подробнее о процентных (и не только процентных) литералах обязательно смотрите здесь.

Однако наилучшим образом процентные литералы смотрятся в хелперах. А именно здесь slim позволяет и вовсе так:

- @result.each do |result|
  span.btn.btn-large class="btn-#{/\A\d+\Z/.match(result) ? 'danger' : 'success'}" style='margin:4px' = result

Миграции, ORM и CRUD в консоли

Чтобы не начинать создавать модели и миграции в пустоте, создадим модель для пользователей и приделаем к ней гем devise. Или приделаем ее к гему devise. Devise gem — открываем, смотрим, ставим, привыкаем — с ним мы теперь надолго.

Там, конечно, все хорошо написано, но здесь все же продублируем:

gem 'devise'
rails generate devise:install
rails generate devise User
rake db:migrate

Что где пишется и выполняется, вы все, конечно, помните. Если нет — нужная информация есть здесь выше или в readme devise.

Выполняем все, читаем логи, обращаем внимание на существенно обогатившийся каталог /db/migrate и файл /db/schema.rb. С этим каталогом и файлом теперь придется работать систематически.

Остальные функции devise оставим пока в покое, чтобы не отвлекаться и снова не растекаться мыслью по всем рельсам. Вместо этого поставим пока

gem 'faker'

Вот его GitHub с дивно интересным readme.

Теперь мы готовы создавать мнимых пользователей нашего приложения с правдоподобными данными. Открываем консоль и убеждаемся в определенности модели User:

User.count

Отлично. Модель определена и содержит 0 записей. Так и должно быть.

Аккуратно создаем нового пользователя и выдаем ему адрес электронной почты:

first_user = User.new
first_user.email = Faker::Internet.email

Пытаемся сохранить:

first_user.save

Что-то не получается. Проверяем, всего ли хватает нашему первому пользователю, проходит ли он валидацию:

first_user.valid?

Действительно, нет. Как узнать, чего не хватает:

first_user.errors.full_messages.each { |i| puts i }

Так намного понятнее. С faker'ом пароль — не проблема совершенно:

first_user.password = Faker::Internet.password

(Что-то какие-то так себе пароли faker создает, без огонька...) Однако теперь это сработает:

first_user.save

Итак, с методами записей new и save мы разобрались. Теперь освоим метод create, который делает и то, и другое сам, по дороге освоив минимальную ruby way автоматизацию:

def create_user
  User.create(email: Faker::Internet.email,
  password: Faker::Internet.password)
end

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

7.times { create_user }

Чтобы они там не скучали.

У нас тоже дел еще полно: все на рельсах для тренировки Twitter пишут, а мы вот структурой данных LiveJournal вдохновимся для разнообразия. Пусть у нас у пользователя будет много постов и комментариев. Но все по порядку — сначала сделаем собственную миграцию, чтобы немного уточнить модель пользователя. Добавим ему имя, фамилию и флажки, админ ли он и не забанен ли он, случаем:

rails g migration AddFnameLnameAdminBannedToUsers fname:string lname:string admin:boolean banned:boolean

или можно воспользоваться генератором RubyMine. Просматриваем единственный сгенерированный файл и восхищаемся достигнутым взаимопониманием. Банзай:

rake db:migrate

Чтобы увидеть изменения из консоли, как правило, достаточно выполнить команду

reload!

Иногда приходится выйти из консоли командой exit или quit и снова зайти: rails c.

Смотрим

User.all

и видим, что все, в общем, в порядке: имя и фамилию всякие анонимусы могут и не указывать, а вот nil в качестве значения admin и banned имеют серьезные шансы в скором времени поломать логику нашего приложения. Это обязательно нужно исправить. Переписываем миграцию:

class AddFnameLnameAdminBannedToUsers < ActiveRecord::Migration
  def change
    add_column :users, :fname, :string
    add_column :users, :lname, :string
    add_column :users, :admin, :boolean, null: false, default: false
    add_column :users, :banned, :boolean, null: false, default: false
  end
end

И переделываем ее rake'ом:

rake db:migrate:redo

User.all подхватывает свершившиеся изменения даже без reload!.

Только вот вывод консоли выглядит что-то чересчур беспорядочным. Чтобы сберечь свое время и повысить уровень профессионализма, совершим героическую эпопею установки и настройки гема hirb:

gem 'hirb'

А вот после бандлера начинаются пляски с бубном. Чтобы заставить hirb работать с pry, создаем конфигурационный файл для pry (в командном интерпретаторе):

nano ~/.pryrc

и запиливаем туда

begin
  require 'hirb'
rescue LoadError
  # Missing goodies, bummer
end

if defined? Hirb
  # Slightly dirty hack to fully support in-session Hirb.disable/enable toggling
  Hirb::View.instance_eval do
    def enable_output_method
      @output_method = true
      @old_print = Pry.config.print
      Pry.config.print = proc do |output, value|
        Hirb::View.view_or_page_output(value) || @old_print.call(output, value)
      end
    end

    def disable_output_method
      Pry.config.print = @old_print
      @output_method = nil
    end
  end

  Hirb.enable
end

Сохраняем, закрываем. Переписывание конфигов hirb — как раз те самые операции, для получения результатов которых нехобходимо именно выходить из консоли и запускать ее заново. Так и делаем. Пытаться понять приведенный выше фрагмент кода лучше в свободное от обучения на курсе время.

Зато User.all теперь смотрится куда как приличнее. А вот если бы еще отображать только нужные колонки — так вообще было бы хорошо. Не вопрос. в /config создаем hirb.yml и оформляем его примерно так:

:output:
  User:
    :options:
      :fields:
        - id
        - email
        - admin
        - banned
        - created_at
        - updated_at

После перезахода в консоль можно наслаждаться отображением User.all, опасно близким к совершенству.

Давайте-ка как-нибудь обзовем всех юзеров:

User.all.each do |user|
  name = Faker::Name.name.split(' ')
  fname, lname = name[-2], name[-1]
  user.update(fname: fname, lname: lname)
end

и настроим отображение соответствующих полей:

:output:
  User:
    :options:
      :fields:
        - id
        - fname
        - lname
        - email
        - admin
        - banned
        - created_at
        - updated_at

На этом знакомство с hirb можно считать исчерпывающим.

Не забудем переопределить генератор юзеров, а то они так анонимусами и останутся:

def create_user
  name = Faker::Name.name.split(' ')
  fname, lname = name[-2], name[-1]
  User.create(fname: fname, lname: lname,
  email: Faker::Internet.email,
  password: Faker::Internet.password)
end

Вернемся к ORM — создадим модель Post:

rails g model Post title:string content:text user:references
rake db:migrate

Просматривая созданные файлы, не устаем удивляться тому, как хорошо работает генератор рельсов — даже belongs_to :user в модель вписал. Теперь только User'у осталось добавить, что у него

has_many :posts

Неужели руками будем посты создавать? Ничуть не бывало, это совсем не ruby way. Вот как надо:

def create_post
  uids = User.all.map(&:id)
  Post.create(title: Faker::Lorem.sentence,
  content: Faker::Lorem.paragraph(2 + rand(8)),
  user_id: uids.sample)
end

И, как обычно:

10.times { create_post }

hirb еще можно под это сконфигурировать:

:output:
  User:
    :options:
      :fields:
        - id
        - fname
        - lname
        - email
        - admin
        - banned
        - created_at
        - updated_at
  Post:
    :options:
      :fields:
        - id
        - title
        - content
        - user_id

А вот и ORM в действии:

User.find(7).posts
Post.find(10).user

Перед уничтожением пользователя обновим модель пользователя:

has_many :posts, dependent: :destroy

Уничтожить юзера с id=3 вместе со всеми его постами:

User.destroy(3)

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

User.find(3).destroy

связанных объектов не удалит.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published