рдлреЗрд╕рдмреБрдХ, vkontakte рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рд░реЗрд▓ рдЕрдиреБрдкреНрд░рдпреЛрдЧреЛрдВ рдореЗрдВ рдкреНрд░рдорд╛рдгреАрдХрд░рдг
рдпрд╣ рд▓реЗрдЦ рдЖрдкрдХреЛ рдмрддрд╛рдПрдЧрд╛ рдХрд┐ vkontakte рдФрд░ facebook рд╕реЛрд╢рд▓ рдиреЗрдЯрд╡рд░реНрдХ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рд░реЗрд▓ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдореЗрдВ рд╕рд░рд▓ рдкреНрд░рдорд╛рдгреАрдХрд░рдг рдХреИрд╕реЗ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, omniauth, omniauth-facebook, omniauth-vkontakte рд░рддреНрди рдЗрд╕рдореЗрдВ рдорджрдж рдХрд░рддреЗ рд╣реИрдВред рд╕рд╛рдордЧреНрд░реА рд╢реБрд░реБрдЖрдд рдХреЗ рд▓рд┐рдП рдбрд┐рдЬрд╝рд╛рдЗрди рдХреА рдЧрдИ рд╣реИред рдпрджреНрдпрдкрд┐ рдпрд╣ рдПрдХ рдкреНрд░рд╢рд┐рдХреНрд╖рдг рдЕрдиреБрдкреНрд░рдпреЛрдЧ рд╣реЛрдЧрд╛, рд╣рдо рдЗрд╕реЗ рдкреВрд░рд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЯреНрд╡рд┐рдЯрд░-рдмреВрдЯрд╕реНрдЯреНрд░реИрдк-рд░реЗрд▓ рд░рддреНрди рдХреЗ рд╕рд╛рде рдмреВрдЯрд╕реНрдЯреНрд░реИрдк рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВред
рдврд╛рдВрдЪрд╛
рд╣рдо рдПрдХ рдирдпрд╛ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдмрдирд╛рддреЗ рд╣реИрдВ (рдореИрдВ рдХрдорд╛рдВрдб рд╕реЗ рдкрд╣рд▓реЗ рдмрдВрдбрд▓ рдирд┐рд╖реНрдкрд╛рджрди рдХреЛ рдЫреЛрдбрд╝ рджреВрдВрдЧрд╛):
rails new authproviders
рд╣рдо рд░рддреНрди рдореЗрдВ рдЖрд╡рд╢реНрдпрдХ рд░рддреНрди рд▓рд┐рдЦрддреЗ рд╣реИрдВ
source 'https://rubygems.org' gem 'rails', '3.2.3' gem 'sqlite3', :group => :development gem 'pg', :group => :production group :assets do gem 'sass-rails', '~> 3.2.3' gem 'coffee-rails', '~> 3.2.1' gem 'uglifier', '>= 1.0.3' end gem 'jquery-rails' gem 'haml-rails' gem 'twitter-bootstrap-rails' gem 'devise' gem 'omniauth' gem 'omniauth-facebook' gem 'omniauth-vkontakte'
рд╣рдо рд╕реНрдерд╛рдкрдирд╛ рдХрд░рддреЗ рд╣реИрдВ:
bundle install --without production
рдЖрдЗрдП, рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдХреЛ рдереЛрдбрд╝рд╛ рдЫреЛрдЯрд╛ рдХрд░реЗрдВ, рдмреВрдЯрд╕реНрдЯреНрд░реИрдк рд▓рд╛рдЧреВ рдХрд░реЗрдВ:
rails g bootstrap:layout application fixed
рд╕рд╛рд░реНрд╡рдЬрдирд┐рдХ рдирд┐рд░реНрджреЗрд╢рд┐рдХрд╛ рд╕реЗ index.html рдХреЛ рд╣рдЯрд╛рдирд╛ рди рднреВрд▓реЗрдВ,
рдпрджрд┐ application.html.erb рдлрд╝рд╛рдЗрд▓ рдмрдиреА рд░рд╣рддреА рд╣реИ, рддреЛ рдЙрд╕реЗ рднреА рд╣рдЯрд╛ рджреЗрдВред
рд╣рдо рдПрдХ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдореЙрдбрд▓ рдмрдирд╛рддреЗ рд╣реИрдВ, рдЬрд╣рд╛рдБ url рдкреНрд░реЛрдлрд╛рдЗрд▓ рдкреЗрдЬ рдХрд╛ рдкрддрд╛ рд╣реИ:
rails g scaffold User username:string nickname:string provider:string url:string
рдХреЙрдиреНрдлрд╝рд┐рдЧрд░ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░ рдХрд░реЗрдВ:
rails generate devise:install rails generate devise User rake db:migrate
рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдореЙрдбрд▓ рдореЗрдВ omniauthable рдореЙрдбреНрдпреВрд▓ рдЬреЛрдбрд╝реЗрдВ:
class User < ActiveRecord::Base
рдкреНрд░рддреНрдпреЗрдХ рдкреНрд░рджрд╛рддрд╛ рдХреЗ рдХрд╛рд░реНрдпреЛрдВ рдХреЗ рд╕рд╛рде рдХреЙрд▓рдмреИрдХ рдирд┐рдпрдВрддреНрд░рдХ рдмрдирд╛рдПрдВ
rails g controller Users::OmniauthCallbacks facebook vkontakte
рдбреЗрд╡рд┐рд╕ рд╕реЗ рдЗрд╕реЗ рдкреНрд░рд╛рдкреНрдд рдХрд░рдирд╛ :: OmniauthCallbacksController
class Users::OmniauthCallbacksControllerController < Devise::OmniauthCallbacksController def facebook end def vkontakte end end
рдорд╛рд░реНрдЧреЛрдВ рдореЗрдВ .rb рд╣рдо рд░рд╛рдЙрдЯрд┐рдВрдЧ рд▓рд┐рдЦрддреЗ рд╣реИрдВ:
Authproviders::Application.routes.draw do devise_for :users, :controllers => { :omniauth_callbacks => "users/omniauth_callbacks" } resources :users, :only => [:index, :destroy] root :to => 'users#index' end
рд╕рд╛рдорд╛рдЬрд┐рдХ рдиреЗрдЯрд╡рд░реНрдХ (user_omniauth_authorize_path (: facebook) рдФрд░ user_omniauth_authorize_path (: vkontakte)) рдХреЗ рд▓рд┐рдВрдХ рдХреЛ рд▓рд┐рдВрдХ html.haml рдЯреЗрдореНрдкрд▓реЗрдЯ, рдкреВрд░реНрдг рд╡реАрдбрд┐рдпреЛ рдХреЛрдб рдореЗрдВ рдЬреЛрдбрд╝реЗрдВ:
!!! 5 %html(lang="en") %head %meta(charset="utf-8") %meta(name="viewport" content="width=device-width, initial-scale=1.0") %title= content_for?(:title) ? yield(:title) : "Authproviders" = csrf_meta_tags / Le HTML5 shim, for IE6-8 support of HTML elements /[if lt IE 9] = javascript_include_tag "http://html5shim.googlecode.com/svn/trunk/html5.js" = stylesheet_link_tag "application", :media => "all" %link(href="images/favicon.ico" rel="shortcut icon") %link(href="images/apple-touch-icon.png" rel="apple-touch-icon") %link(href="images/apple-touch-icon-72x72.png" rel="apple-touch-icon" sizes="72x72") %link(href="images/apple-touch-icon-114x114.png" rel="apple-touch-icon" sizes="114x114") %body .navbar.navbar-fixed-top .navbar-inner .container %a.btn.btn-navbar(data-target=".nav-collapse" data-toggle="collapse") %span.icon-bar %span.icon-bar %span.icon-bar %a.brand(href="#") Authproviders .container.nav-collapse %ul.nav - if user_signed_in? %li= link_to "#{current_user.username} (#{current_user.provider})", current_user.url %li= link_to "Sign out", destroy_user_session_path, :method => :delete .container .content .row .span9 = yield .span3 .well.sidebar-nav %h3 Providers %ul.nav.nav-list - if !user_signed_in? %li= link_to "Sign in with Facebook", user_omniauth_authorize_path(:facebook) %li= link_to "Sign in with Vkontakte", user_omniauth_authorize_path(:vkontakte) %footer %p ┬й Company 2012 = javascript_include_tag "application"
рдЪрд▓реЛ рдкрдВрдЬреАрдХреГрдд рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛рдУрдВ рдХреЛ рдкреНрд░рджрд░реНрд╢рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛рдУрдВ / index.html.haml рдЯреЗрдореНрдкрд▓реЗрдЯ рдХреЛ рдареАрдХ рдХрд░рддреЗ рд╣реИрдВ
- model_class = User.new.class %h1=t '.title', :default => model_class.model_name.human.pluralize %table.table.table-striped %thead %tr %th= model_class.human_attribute_name(:username) %th= model_class.human_attribute_name(:nickname) %th= model_class.human_attribute_name(:provider) %th= model_class.human_attribute_name(:sign_in_count) %th= model_class.human_attribute_name(:created_at) %th=t '.actions', :default => t("helpers.actions") %tbody - @users.each do |user| %tr %td= link_to user.username, user.url %td= user.nickname %td= user.provider %td= user.sign_in_count %td= user.created_at %td = link_to t('.destroy', :default => t("helpers.links.destroy")), user_path(user), :method => :delete, :confirm => t('.confirm', :default => t("helpers.links.confirm", :default => 'Are you sure?')), :class => 'btn btn-mini btn-danger'
рд╕реБрдирд┐рд╢реНрдЪрд┐рдд рдХрд░реЗрдВ рдХрд┐ рд╕рдм рдХреБрдЫ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИ рдФрд░ рд╕рдмрд╕реЗ рджрд┐рд▓рдЪрд╕реНрдк рдХреЗ рд▓рд┐рдП рдЖрдЧреЗ рдмрдврд╝реЗрдВ:
rails s
рдлреЗрд╕рдмреБрдХ
рд╣рдо рдкреГрд╖реНрда
https://developers.facebook.com/apps рдкрд░ рдЬрд╛рддреЗ рд╣реИрдВ рдФрд░ рдПрдХ рдирдпрд╛ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдмрдирд╛рддреЗ рд╣реИрдВ
(рд╕рд╛рдЗрдЯ url рдореЗрдВ рдХрд┐рд╕реА рд▓реЛрдХрд▓рд╣реЛрд╕реНрдЯ рдкрд░ рдбрд┐рдмрдЧрд┐рдВрдЧ рдХреЗ рд▓рд┐рдП рдЖрдк
рд▓реЛрдХрд▓рд╣реЛрд╕реНрдЯ рд▓рд┐рдЦ рд╕рдХрддреЗ рд╣реИрдВ: 3000)
Devise.rb рдЖрд░рдВрднреАрдХрд░рдг рдлрд╝рд╛рдЗрд▓ рдореЗрдВ, рдкрдВрдХреНрддрд┐ рдЬреЛрдбрд╝реЗрдВ:
config.omniauth :facebook, 'APP_ID', 'APP_SECRET'
'APP_ID', 'APP_SECRET' рд╣рдо рдПрдХ рдирдпрд╛ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдмрдирд╛рддреЗ рд╕рдордп рдЬрд╛рд░реА рдХрд┐рдП рдЧрдП рдорд╛рдиреЛрдВ рдореЗрдВ рдмрджрд▓ рдЬрд╛рддреЗ рд╣реИрдВред
рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдореЙрдбрд▓ рдореЗрдВ, рдлреЗрд╕рдмреБрдХ рд╡рд┐рдзрд┐ рдЬреЛрдбрд╝реЗрдВ, рдЬреЛ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреЛ рдЙрд╕рдХреЗ рдкреГрд╖реНрда рдХреЗ рдкрддреЗ рдкрд░ рдЦреЛрдЬреЗрдЧрд╛, рдпрджрд┐ рдпрд╣ рдорд╛рдорд▓рд╛ рдирд╣реАрдВ рд╣реИ, рддреЛ рдПрдХ рдирдпрд╛ рдмрдирд╛рдПрдВ (рд╕реБрд░рдХреНрд╖рд╛ рдХреЗ рджреГрд╖реНрдЯрд┐рдХреЛрдг рд╕реЗ рд╕рдмрд╕реЗ рдЕрдЪреНрдЫрд╛ рд╕рдорд╛рдзрд╛рди рдирд╣реАрдВ рд╣реИ, рд▓реЗрдХрд┐рди рдпрд╣ рд╕рд┐рд░реНрдл рдПрдХ рдЙрджрд╛рд╣рд░рдг рд╣реИ):
def self.find_for_facebook_oauth access_token if user = User.where(:url => access_token.info.urls.Facebook).first user else User.create!(:provider => access_token.provider, :url => access_token.info.urls.Facebook, :username => access_token.extra.raw_info.name, :nickname => access_token.extra.raw_info.username, :email => access_token.extra.raw_info.email, :password => Devise.friendly_token[0,20]) end end
рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛рдУрдВ рдореЗрдВ: OmniauthCallbacksController рдирд┐рдпрдВрддреНрд░рдХ, рд╕рд╛рде рд╣реА рдлреЗрд╕рдмреБрдХ рд╡рд┐рдзрд┐ рдЬреЛрдбрд╝реЗрдВ:
def facebook @user = User.find_for_facebook_oauth request.env["omniauth.auth"] if @user.persisted? flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Facebook" sign_in_and_redirect @user, :event => :authentication else flash[:notice] = "authentication error" redirect_to root_path end
Vkontakte
рд╕рдВрдкрд░реНрдХ рдХреЗ рд▓рд┐рдП, рдкреНрд░рдХреНрд░рд┐рдпрд╛ рд╕рдорд╛рди рд╣реИ:
http://vk.com/developers.phpрдПрдХ рдЖрд╡реЗрджрди рдмрдирд╛рдПрдБ:
Devise.rb рдЖрд░рдВрднреАрдХрд░рдг рдлрд╝рд╛рдЗрд▓ рдореЗрдВ рд▓рд╛рдЗрди рдЬреЛрдбрд╝рдирд╛ рди рднреВрд▓реЗрдВ:
config.omniauth :vkontakte, 'APP_ID', 'APP_SECRET'
рд╕реНрдерд╛рдиреАрдпрд╣реЛрд╕реНрдЯ рдкрд░ рд╕рдВрдкрд░реНрдХ рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдирд╛ рдЕрдзрд┐рдХ рдХрдард┐рди рд╣реЛ рдЧрдпрд╛, рдЖрдк рдЗрд╕ рдЪреАрдЬрд╝ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ:
https://github.com/progrium/localtunnelрдмрдВрдбрд▓ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рд╕реНрдерд╛рдкрдирд╛ рдХреЗ рдмрд╛рдж, рдкрд╣рд▓реА рд╢реБрд░реБрдЖрдд рдореЗрдВ рд╣рдо рдХреБрдВрдЬреА рд▓реЛрдб рдХрд░рддреЗ рд╣реИрдВ:
localtunnel -k ~/.ssh/id_rsa.pub 3000
рдлрд┐рд░ рд╣рдо рд╕реБрд░рдВрдЧ рд▓реЙрдиреНрдЪ рдХрд░рддреЗ рд╣реИрдВ, рдпрд╣ рд╣рдореЗрдВ рдкрддрд╛ рджреЗрдЧрд╛ рдХрд┐ рдЖрд╡реЗрджрди рдХрд╣рд╛рдВ рдЙрдкрд▓рдмреНрдз рд╣реЛрдЧрд╛ (рд╣рдо рдЗрд╕реЗ рд╕рдВрдкрд░реНрдХ рдореЗрдВ рд╣рдереМрдбрд╝рд╛ рдХрд░рддреЗ рд╣реИрдВ), рдЙрд╕рдХреЗ рдмрд╛рдж рд░реЗрд▓
localtunnel 3000 This localtunnel service is brought to you by Twilio. Port 3000 is now publicly accessible from http://4v9p.localtunnel.com ... rails s
рд╣рдо рдлреЗрд╕рдмреБрдХ рдХреЗ рд╕рдорд╛рди рдореЙрдбрд▓ рдФрд░ рдирд┐рдпрдВрддреНрд░рдХ рдХреЗ рд▓рд┐рдП vkontakte рд╡рд┐рдзрд┐ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВ, (рдореИрдВ рдзреНрдпрд╛рди рджреЗрддрд╛ рд╣реВрдВ рдХрд┐ рд╕рдВрдкрд░реНрдХ рдЬрд╛рдирдХрд╛рд░реА рд╕реБрд░рдХреНрд╖рд╛ рдиреАрддрд┐ рдХреЗ рдЕрдиреБрд╕рд╛рд░, рдореЗрд▓ рдкрддреЗ рдирд╣реАрдВ рджрд┐рдП рдЧрдП рд╣реИрдВ, рдЗрд╕рд▓рд┐рдП рд╕рддреНрдпрд╛рдкрди рдХреЗ рд╕рд╛рде рд╕рдВрдШрд░реНрд╖ рдирд╣реАрдВ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдмрдирд╛рддреЗ рд╕рдордп, рдореИрдВ рдлрд╝реЙрд░реНрдо рдХреЗ рд╕рд░реЛрдЧреЗрдЯ рдкрддреЗ рдмрдирд╛рддрд╛ рд╣реВрдВ: рдбреЛрдореЗрди + @ vk.com) :
class User < ActiveRecord::Base devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable, :omniauthable attr_accessible :email, :password, :password_confirmation, :remember_me attr_accessible :nickname, :provider, :url, :username def self.find_for_facebook_oauth access_token if user = User.where(:url => access_token.info.urls.Facebook).first user else User.create!(:provider => access_token.provider, :url => access_token.info.urls.Facebook, :username => access_token.extra.raw_info.name, :nickname => access_token.extra.raw_info.username, :email => access_token.extra.raw_info.email, :password => Devise.friendly_token[0,20]) end end def self.find_for_vkontakte_oauth access_token if user = User.where(:url => access_token.info.urls.Vkontakte).first user else User.create!(:provider => access_token.provider, :url => access_token.info.urls.Vkontakte, :username => access_token.info.name, :nickname => access_token.extra.raw_info.domain, :email => access_token.extra.raw_info.domain+'@vk.com', :password => Devise.friendly_token[0,20]) end end end
рдирд┐рдпрдВрддреНрд░рдХ рдХреЛрдб:
class Users::OmniauthCallbacksController < ApplicationController def facebook @user = User.find_for_facebook_oauth request.env["omniauth.auth"] if @user.persisted? flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Facebook" sign_in_and_redirect @user, :event => :authentication else flash[:notice] = "authentication error" redirect_to root_path end end def vkontakte @user = User.find_for_vkontakte_oauth request.env["omniauth.auth"] if @user.persisted? flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Vkontakte" sign_in_and_redirect @user, :event => :authentication else flash[:notice] = "authentication error" redirect_to root_path end end end
рдмреЗрд╢рдХ, DRY рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ, рдЗрд╕ рдХреЛрдб рдХреЛ рд╕рд╛рдорд╛рдиреНрдпреАрдХреГрдд рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред
рдЖрд╡реЗрджрди рд╣реА:
рдирд┐рд╖реНрдХрд░реНрд╖
рдЬреИрд╕рд╛ рдХрд┐ рд╣рдордиреЗ рджреЗрдЦрд╛, рдлреЗрд╕рдмреБрдХ рдФрд░ рд╕рдВрдкрд░реНрдХ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдкреНрд░рдорд╛рдгреАрдХрд░рдг рдХреЗ рд╕рд╛рде рд░реЗрд▓ рдкрд░ рдПрдХ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдмрдирд╛рдирд╛ рдмрд╣реБрдд рд╕рд░рд▓ рд╣реИред
рдПрдХ рд╡рд░реНрдХрд┐рдВрдЧ рдбреЗрдореЛ рдпрд╣рд╛рдБ рд╣реИ:
http://authproviders.herokuapp.com/рдЙрджрд╛рд╣рд░рдг рдХреЛрдб:
https://github.com/mystdeim/Authprovidersрдпрд╣рд╛рдВ рдХреБрдЫ рдЙрдкрдпреЛрдЧреА рд▓рд┐рдВрдХ рджрд┐рдП рдЧрдП рд╣реИрдВ:
- http://railsapps.github.com/rails-examples-tutorials.html - рдбреЗрдореЛ рдЕрдиреБрдкреНрд░рдпреЛрдЧреЛрдВ рдореЗрдВ рдЬреНрдпрд╛рджрд╛рддрд░ рд╡рд╕реАрдпрдд рдФрд░ рдЕрдкрд╡рд┐рддреНрд░рддрд╛ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ
- http://www.communityguides.eu/articles/11 рд╡рд┐рднрд┐рдиреНрди рдкреНрд░рджрд╛рддрд╛рдУрдВ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдПрдХ рд╡реНрдпрдХреНрддрд┐ рдХреЗ рдХрдИ рдЦрд╛рддреЛрдВ рдХреЗ рд╕рдВрдпреЛрдЬрди рдХреЗ рд▓рд┐рдП рдПрдХ рдЕрдЪреНрдЫрд╛ рд╕рдорд╛рдзрд╛рди рд╣реИ (рдпрд╣ рдЖрд╡рд╢реНрдпрдХ рд╣реИ рдХрд┐ рдПрдХ рд╣реА рдореЗрд▓ рд╣реЛ, рджреБрд░реНрднрд╛рдЧреНрдп рд╕реЗ, рд╕рдВрдкрд░реНрдХ рдХреЗ рд╕рд╛рде рдЪрд╛рд▓ рдХрд╛рдо рдирд╣реАрдВ рдХрд░реЗрдЧреА)
PS рдпрд╣ рдореЗрд░рд╛ рдкрд╣рд▓рд╛ рд▓реЗрдЦ рд╣реИ, рдХрд▓рдо рдХреА рдПрдХ рдкрд░реАрдХреНрд╖рд╛, рдЖрдк рдХрд╣ рд╕рдХрддреЗ рд╣реИрдВред
рдпреВрдкреА: рдХреЗрдИ рдФрд░ рдЖрдИрдбреА рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдХреА рдХреБрдВрдЬреА рдХреЛ рдЕрд▓рдЧ рд░рдЦрдирд╛ рдмреЗрд╣рддрд░ рд╣реИ рддрд╛рдХрд┐ рд╡реЗ рдЧрд▓рддреА рд╕реЗ рд░рд┐рдкреЙрдЬрд┐рдЯрд░реА
http://habrahabr.ru/post/142128/#comment_4757653 рдкрд░ рди рдЧрд┐рд░реЗрдВ