About five months ago, I tied up with the zend framework and moved onto rails. Then he began to rewrite his project
www.okinfo.ru . Now it is already finished and sloccount showed that the number of lines in the project decreased from 15,000 to 4,000. My familiar php developers asked for a success story and eventually this article was born. In it, I will describe how it was, as well as talk a little about my transition to ruby.
Prehistory
Having worked as the main developer of
www.starlook.ru for about two years, I reached a point where you understand that it is time to move on. The architecture of the application is well-established, scaling is inherent, the code as a whole is fine and in general the whole development process was clear and debugged. In fact, we continued to increase the functionality and maintain already written. At this time for interest, I began to look closely at django and rails. Despite the fact that the python as a language I liked, but that django was not impressed. And with the rails and cut the situation was completely different. It can be called love at first sight. And literally after a month of study, a letter came to My Circle offering a vacancy for a ruby ββdeveloper, although it was an accident, since sending for some reason thought that I know ruby. In general, I decided that this was fate, and after passing a bunch of interviews, they still took me to
chop for a new place.
What for?
After getting acquainted with the rails, it was very difficult to continue writing a project on ZF, too much had to be done by hand and practically for each task (tags, trees, deploy, migrations, etc.) to write our own solutions. Given that there is not much free time on the project, it was decided to rewrite it, killing two birds with one stone. Firstly, to simplify your development and support, and secondly, to tighten the knowledge of the relevant technologies.
')
Setting up the environment
rvm.beginrescueend.com - installation of several versions of ruby ββand gemset support.
ru.wikipedia.org/wiki/RubyGems - gems
sudo apt-get install rubygems
gem install rails
rails s
, . .
.
. tail -f log/development.log (http://habrastorage.org/storage/29abc841/af70206c/2794fbe5/5d1f434f.png). , , , , ..
rails c
irb . . .

netbeans vim.
REST
habrahabr.ru/blogs/webdev/38730guides.rubyonrails.org/routing.html, . REST .
:
pastebin.com/x0bA3siH:
namespace :admin do
resources :brands do
resources :systems
end
end
rake routes :
pastebin.com/mPSvzkAJ. :
admin_brand_systems_path(@brand) #
admin_brand_systems_url(@brand) #
. Zend_Controller_Router_Route_Hostname.
, . rails3 , :
# ZF
match "/company/:id" => redirect("/companies/%{id}")
ZF Zend_Rest_Controller Zend_Rest_Route, , :
$restRoute = new Zend_Rest_Route($front, array(), array(
'api',
'backlog' => array('task'),
));
$router->addRoute('rest', $restRoute);
, .
guides.rubyonrails.org/action_controller_overview.html:
pastebin.com/TYhYAWEQ:
pastebin.com/seJzhYMq. ZF, , .
( ) β ApplicationController ( ). . .
(ThinkingSphinx)
.
www.okinfo.ru.
, ,
DSL.
define_index do
indexes title
indexes body
indexes sub_categories(:title)
has created_at
has taggings(:tag_id), :as => :tag_ids, :facet => true
has companies_sub_categories :sub_category_id
has addresses :city_id
has :calculator_page # :without => {:calculator_page => 0}
has :site
has :reviews_count
end
sphinx, . :
rake ts:rebuild
:
Company.search ββ
sphinx, . thinking-sphinx , . , .
ActiveRecord
guides.rubyonrails.org/association_basics.htmlguides.rubyonrails.org/active_record_querying.htmlclass Company < ActiveRecord::Base
acts_as_taggable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable, :trackable
belongs_to :image
has_many :weekly_newsletters, :class_name => 'Notice', :conditions => { :notice_type => 'weekly_newsletter' }
has_many :addresses
has_many :sub_categories, :through => :companies_sub_categories
validates :site,
:uniqueness => true,
:format => { :with => URI::regexp(%w(http https)) },
:allow_blank => true
validates :title, :presence => true, :uniqueness => true
validates :twitter_page, :format => { :with => URI::regexp(%w(http https)) },
:allow_blank => true
scope :by_rating, order('ratings_sum DESC')
scope :with_logo, where('image_id IS NOT NULL').includes(:image)
. . , orm .
State Machine
github.com/pluginaweek/state_machine/ .. .
# ActiveRecord model
state_machine :state, :initial => :moderate do
state :off, :human_name => ''
state :moderate, :human_name => ' '
state :published, :human_name => '' do
validates :uri, :presence => true
end
event :publish, :human_name => '' do
transition :off => :published
transition :moderate => :published
end
event :off, :human_name => '' do
transition :published => :off
end
end
(Rake, cron)
ru.wikipedia.org/wiki/Rakerake. , ,
pastebin.com/MavWT7JA. , . rake ruby. php β
pake.
#
namespace :app do
namespace :notice do
desc 'Weekly newsletter'
task :weekly_for_company => :environment do
Company.state.find_each do |company| # find_each batch
last_notice = company.weekly_newsletters.last
next if last_notice && (Time.now - last_notice.created_at < 1.week)
notice = company.weekly_newsletters.create
notice.save
CompanyMailer.weekly_newsletter(company).deliver
sleep(1)
end
end
end
end
:
rake app:notice:weekly_for_company
php, . .
Deploy (capistrano)
github.com/capistrano/capistrano/wikihabrahabr.ru/blogs/webdev/110021. (
):
cap deploy:migrations
, , , , , . .
(Bundler)
gembundler.combundler. . , , ( ) . . :
pastebin.com/50pe40Sb.
β Ancestry (use Materialized path)
β breadcrumbs_on_rails
β graticule
β acts-as-taggable-on
β paperclip
β foreigner
β rspec, factory_girl_rails, rcov
β capistrano, capistrano-ext, capistrano_colors
(Devise)
github.com/plataformatec/devise: Omniauthable, Confirmable, Recoverable, Registerable, Rememberable, Trackable, Timeoutable, Validatable, Lockable, Encryptable, Database Authenticatable, Token Authenticatable.
. . .
(Simple_Form)
github.com/plataformatec/simple_formZF, Django, . . Zend_Form ( zend_form ).
<%= simple_form_for(resource, :url => company_registration_path) do |f| %>
<%= f.input :email %>
<%= f.input :password %>
<%= f.input :password_confirmation %>
<%= f.association :forma, :label_method => :title %>
<%= f.input :title %>
<%= f.association :sub_categories, :label_method => :title %>
<%= f.simple_fields_for :address do |address| %>
<%= address.association :city, :as => :select, :collection => cities(@company.address),
:input_html => { :id => "city_id" } %>
<%= address.input :street %>
<%= address.input :house, :required => false %>
<% end %>
<%= f.submit '' %>
<% end %>
.
(Rspec, FactoryGirl, Rcov)
habrahabr.ru/blogs/testing/52929github.com/thoughtbot/factory_girl:
pastebin.com/3tEctqmT:
pastebin.com/VmawXxbcFactoryGirl , , , . ( ). assertβ.
php. , .
guides.rubyonrails.org/action_mailer_basics.htmlrusrails.ru/action-mailer-basicsrails . rails g mailer MailerName. . . : CompanyMailer.after_registration(@company).deliver
. Rails environment , ZF, : , , env. dev ( ) .
( ):
describe ColumnistMailer do
describe "after_registration" do
let(:mail) { ColumnistMailer.after_registration }
it "renders the headers" do
mail.subject.should eq("After registration")
mail.to.should eq(["to@example.org"])
mail.from.should eq(["from@example.com"])
end
it "renders the body" do
mail.body.encoded.should match("Hi")
end
end
end
github.com ( ) β .
newrelic.com β .
hoptoadapp.com/pages/home β . , php. ( ), β , , . , . , , . . , must have. js.
github.com/jdpace/errbit (Errbit is an open source, self-hosted error catcher. It is Hoptoad API compliant so you can just point the Hoptoad notifier at your Errbit server if you are already using Hoptoad).
php
github.com/rich/php-hoptoad-notifier. . , .
, , . , .