Автоматизация создания нового приложения с помощью рейлс-темплейтов

April 22, 2012

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

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

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

Любая автоматизация, это маленькая локальная инновация. И кроме уменьшения сроков выполнения и увеличения качества конкретной задачи у нее есть неочевидный, но очень важный эффект - автоматизация влияет на повседневные шаблоны поведения программиста и на конечный результат. Поясню на примерах. Agile кроме уменьшения сроков и увеличения отзывчивости девелопмента помогает создавать легкие, изящные приложения, которые не получится разработать обычными методами. Гитхаб и рубиджемс помогли нам получить огромное количество интересного кода, который раньше не был бы открытым, даже не из-за желания скрыть свои наработки, а из-за сложности публикации и поддержки своего опен-сорса. Гит показал нам, как здорово создавать ветки и делать маленькие комиты. Раньше нам казалось, что ветки не нужно создавать часто, но оказывается это было не потому-что сама идея веток - бесполезна, а потому-что сложности создания и мержа уничтожали все потенциальные приемущества. Автоматизация (как и выбор правильных инструментов) помогает взглянуть на старые подходы к работе под другим углом, выработать и использовать новые навыки, и, в конечном итоге, получить принципиально другой результат.

Вернемся к теме сегодняшней беседы. В повседневной практике довольно редко приходится создавать новое приложение, обычно это приходится делать только в начале нового проекта. Но в любом случае у каждого программиста сформирован набор гемов и начальных настроек, которые бы он хотел использовать в любом проекте независимо от размера и назначения. У меня тоже есть такой набор, каждый раз я его применял вручную, поэтому гем-файлы всех проектов выглядят чуть-чуть по разному. Но вообще я всегда создавал новый проект с неохотой, потому-что нужно было добавить поддержку хамл, спеки, удалить index.html, настроить базу и сделать еще много всяких маленьких скучных правок. Кроме того я люблю возиться с исходниками рейлс и часто у меня возникает вопрос, как работает та или иная функциональность. Было бы удобно иметь всегда рабочий стенд, на котором можно быстро проверять свои гипотезы. Я пробовал использовать для этого пустой sandbox-проект, но он очень быстро приходил в негодность, плюс иногда еще хочется посмотреть как это работало например в 3.0 или даже в 2.3. Можно конечно еще делать так: создать пустое приложение rails new ..., добавить therubyracer, bundle install, настроить базу, bundle install, фух - не хватает haml, bundle install, опс - хочу автомиграции, я к ним привык - добавить, bundle install - да ну в качель эту идею и рейлс целиком, пусть работают как хотят...

Сейчас я создаю пустое приложение несколько раз за день. Так как, чтобы проверить как работает тот или иной код в чистых рейлс достаточно 3-х простых команд:

rails new demo-app -m <путь-до-темплейта> # у меня есть заготовки для 3.0, 3.1 и 3.2
cd demo-app
rails s   # привычное и прекрасное окружение запущено

# hack, hack, hack

cd .. && rm -fr demo-app

В заключение приведу свой основной темплейт-файл, который я обычно использую:

# Можно использовать просто команду `file`, но в этом случае
# при попытке переписать существующий файл, система спросит:
# "уверенны ли вы?" Конечно уверены! Мы же создаем проект с
# нуля и не хотим лишний раз нажимать ентер.
def file_force(name, content)
  f = File.open(name, 'w')
  f.puts content
  f.close
end

file_force 'Gemfile', <<-CODE
source 'https://rubygems.org'

gem 'rails', '3.2.3'
gem 'pg'

gem 'haml'
gem 'jquery-rails'
gem 'unicorn'

group :assets do
  gem 'sass-rails',   '~> 3.2.3'
  gem 'coffee-rails', '~> 3.2.1'
  gem 'therubyracer', :platform => :ruby
  gem 'uglifier', '>= 1.0.3'
end

group :development do
  gem 'rails-footnotes', git: 'git://github.com/avakhov/rails-footnotes.git', branch: 'custom'
end

group :test do
  gem 'factory_girl_rails'
  gem 'timecop'
  gem 'database_cleaner'
end

group :development, :test do
  gem 'rspec-rails'
end
CODE

file_force '.gitignore', <<-CODE
/.bundle
/log/*.log
/tmp
/public/assets
CODE

file 'README.md', <<-CODE
# Project

TODO: description
CODE

file '.rspec', <<-CODE
--colour
CODE

file 'spec/spec_helper.rb', <<-CODE
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
require 'rspec/autorun'

Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}

RSpec.configure do |config|
  config.before(:suite) do
    DatabaseCleaner.strategy = :transaction
  end

  config.before(:each) do
    DatabaseCleaner.start
  end

  config.after(:each) do
    DatabaseCleaner.clean
  end

  config.include FactoryGirl::Syntax::Methods
end
CODE

file 'spec/factories.rb', <<-CODE
FactoryGirl.define do
  # factory :demo do
  #   name 'name'
  # end
end
CODE

file 'app/controllers/home_controller.rb', <<-CODE
class HomeController < ApplicationController
  def index
  end
end
CODE

file 'app/views/home/index.html.haml', <<-CODE
%h1 Home#index
%p Find me in app/views/home/index.html.haml
CODE

file 'spec/controllers/home_controller_spec.rb', <<-CODE
require 'spec_helper'

describe HomeController do
  it "index" do
    get 'index'
    response.should be_success
  end
end
CODE

# Нам не нужны примеры роутов, мы и так все знаем!
head = File.readlines('config/routes.rb').first
file_force 'config/routes.rb', <<-CODE
#{head.strip}
  root to: 'home#index'
end
CODE

# Скрипт темплейта запускается в директории
# созданного проекта:
FileUtils.rm('public/index.html')
FileUtils.rm('README.rdoc')
FileUtils.rm_rf('test')

# И мы можем запускать даже шелл-команды
system "bundle install"
system "rake db:create db:migrate db:test:prepare"
system "git init"
system "git add ."
system "git ci -amInitial"

После выполения этого темплейта, создается приложение с базой, с главной страницей (можно подключить devise, которому нужен root_path) и зелеными спеками. Можно вызвать команду rails g scaffold post title:string && rake db:migrate и экспериментировать с моделями. Scaffold-генератор создает много файлов стоит сказать, если он нам не понравится мы удалим его с помощью git checkout . && git clean -df, а может создадим чистый проект заново, с темплейтами - это просто!

comments powered by Disqus