• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
Decyphering Rails 3
 

Decyphering Rails 3

on

  • 1,580 views

Код ядра Rails был существенно улучшен с выпуском Rails 3, в основном из-за использования эффективных паттернов ...

Код ядра Rails был существенно улучшен с выпуском Rails 3, в основном из-за использования эффективных паттернов проектирования. Мы разберем некоторые из ключевых изменений, которые привели к улучшению качества кода, и на их примере научимся применять такие техники к своему собственному коду.

Вот некоторые из таких техник:

Компилирование методов vs method_missing
Микроядерная архитектура
alias_method_chain vs super
ActiveSupport::Concern
Catch/Throw в Bundler

Слушатели намного улучшат свои знания о некоторых сложных паттернах проектирования в Ruby и станут лучше разбираться во внутренностях Rails 3.

Statistics

Views

Total Views
1,580
Views on SlideShare
1,580
Embed Views
0

Actions

Likes
4
Downloads
17
Comments
0

0 Embeds 0

No embeds

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    Decyphering Rails 3 Decyphering Rails 3 Presentation Transcript

    • Deciphering Rails 3 with Gregg Pollack
    • asi c to r edi# B ded an d R _s r ovi er er e .to # p end pe) = typ R ty in e =( e "] # t_t yp Typ ten te nt- con [" Con def d ers hea end "] ype ype t_t t-T on ten on ten f c [" C source de rs ade ng the he end raid of readi ion ion "] be af at at loc Loc Don’t def [" d ers hea rl e nd (u rl) = u on= n"] a ti ca tio loc "Lo def s[ derDon’t be afraid of making it your own hea end us tus ) s tat sta def ta tus co de( @_s at us_ tu s) ls .st end sta Uti =( :: l] a tus R ack [va st = : def t a tus ? val @_s ) ch) val ea end od y =( to ?(: ns e_b po nd_ e spo . res
    • Deciphering Rails 3 Method Compilation Microkernel Architecturealias_method_chain vs super ActiveSupport::Concern Catch/Throw in Bundler
    • let’s test t he water
    • Basics def city return @city endclass SimplePrint attr_accessor :city def city=(val) def initialize(val) @city = val self.city = val end end def method_missing(meth) s = SimplePrint.new("moscow") puts "#{meth} #{city}" endend s.toaster> s = SimplePrint.new("moscow") => #<SimplePrint:0x000001020013d0 @city="moscow">> s.toastertoaster moscow => nil
    • evalhash = {name => Toaster, location => Moscow}hash.each_pair do |key, value| eval <<-RUBY @name = Toaster @#{key} = value RUBYend @location = Moscowputs @name + @locationToasterMoscow
    • instan ce_eval class Test t = Test.new def print t.print puts "Hello" Hello end end Test.new.instance_eval do t = Test.new print t.instance_eval do end def print2 Hello puts "Hello2" b6 0> end 0 0124 t :0 x1 end r# <Tes Test.new.print2 rin t 2’ fo eth od ‘p t.print2 d m u nd efine r: dErro Hello2 M etho No
    • c lass_eval class Test class Test end end class Test Test.class_eval do def print def print puts "Ruby5" puts "Ruby5" end end end end Test.new.print Test.new.print Ruby5 Ruby5
    • Me thod Com pilation
    • s = SimplePrint.new [:push_it, :pop_it, :top_it] outputs.push_its.pop_it => “called push_it”s.top_it => “called pop_it” => “called top_it”class SimplePrint attr_accessor :actions def initialize(arr) self.actions = arr end def print(meth) puts "called #{meth}" end def method_missing(meth) print(meth) if actions.include? meth endend
    • s = SimplePrint.new [:push_it, :pop_it, :top_it]30000.times do s.push_it s.pop_it s.top_itend Using Method Missing 5 Seconds
    • s = SimplePrint.new [:push_it, :pop_it, :top_it]class SimplePrint class BetterPrint attr_accessor :actions def initialize(arr) def initialize(arr) arr.each do |meth| self.actions = arr end instance_eval <<-RUBY def #{meth} def print(meth) print("#{meth}") puts "called #{meth}" end end RUBY def method_missing(meth) end print(meth) if actions.include? meth end endend def print(meth) puts "called #{meth}" end end
    • [:push_it, :pop_it, :top_it]class BetterPrint def initialize(arr) def push_it arr.each do |meth| print("push_it") end instance_eval <<-RUBY def #{meth} print("#{meth}") end def pop_it RUBY print("pop_it") end end end def print(meth) def top_it puts "called #{meth}" print("top_it") end endend
    • s = SimplePrint.new [:push_it, :pop_it, :top_it]30000.times do s.push_it s.pop_it s.top_itend Using Method Missing 5 Seconds Using Instance Eval 3.5 Seconds 30 % Faster!
    • ruby-profUsing Method MissingUsing Method Compliation
    • Yehuda’s first commitrails/actionpack/lib/abstract_controller/metal/mime_responds.rb rails/actionpack/lib/abstract_controller/collector.rb
    • ActiveSupport Callbacks Refactored for Speed Used in ActionPack, Test::Unit, and ActionController before_filter :authenticate after_filter :fetch_extra_data around_filter :transaction skip_filter :admin_only, :except => [:new] 10x Faster with method compilation
    • method compliation class PostsController < ApplicationController layout custom end layout :symbol layout proc{ |c| c.logged_in? ? "member" : "non-member" } layout false layout nil Are  you  a  string,  symbol,  proc,  boolean,  or  nil?
    • method compliation rails/actionpack/lib/abstract_controller/layouts.rb layout_definition = case _layout when String _layout.inspect when Proc define_method :_layout_from_proc, &_layout "_layout_from_proc(self)" when false nil when true raise ArgumentError when nil name_clause end self.class_eval <<-RUBY, __FILE__, __LINE__ + 1 def _layout #{layout_definition} end private :_layout RUBY
    • Me thod Com pilation
    • let’s test t he water
    • includin g modules module PrintMeth def print puts "Ruby5" end end As Instance Method As Class Method class Test class Test include PrintMeth extend PrintMeth end end Test.new.print Test.print Ruby5 Ruby5
    • nel Arch itectureM icroker
    • AbstractController A refactor of ActionController in ActionPack Provides  A  Low  Level  Interface  for   making  Customized  Controller  Base  classes. Carl Lerche
    • Old ActionController Stack Microkernel Design Pattern Cookies ActionDispatch S Routeression Layout s Logger MimeRes ponds s Cal lback Rendere r url_for http_ auth helpers
    • Old ActionController Stack Microkernel Design Pattern Cookies Assigns AbstractController Sessio Layout Callbacks n s Collector Logger MimeRes Helpers ponds Layouts Logger ks ac Callb Renderinge R nderer Translation url_for ViewPaths http_ auth helpers
    • Microkernel Design Pattern AbstractController Assigns Callbacks Collector Helpers Layouts Logger Rendering Translation ViewPaths
    • The MicroKernel rails/actionpack/lib/abstract_controller/base.rb module AbstractController class Base attr_internal :response_body attr_internal :action_name abstract! # Calls the action going through the entire action dispatch stack. def process(action, *args) def controller_path def action_methods private def action_method?(name) def process_action(method_name, *args) def _handle_action_missing def method_for_action(action_name) end end
    • AbstractController::BaseThe microkernel with the bare minimum needed for dispatching Assigns before_filter :authorized Callbacks after_filter :send_email Collector around_filter :benchmark helper :custom Helpers helper_method :current_user layout custom Layouts layout :symbol Logger layout false layout nil Rendering Translation ViewPaths Modules that can be included to add functionality on top of base
    • Microkernel Design Pattern ActionController::Metal << AbstractController::Base Assigns Base Callbackscontains just enough code to get a valid Collector Rack application from a controller Helpers Layouts Logger Rendering Translation ViewPaths ActionMailer::Base << AbstractController::Base
    • Microkernel Design Pattern ActionController::Metal << AbstractController::Baseapp/controllers/hello_controller.rb class HelloController < ActionController::Metal def index self.response_body = "Hello World!" end endconfig/routes.rb match hello, :to => HelloController.action(:index)
    • ActionController::Base Microkernel Design Pattern rails/actionpack/lib/action_controller/metal.rb module ActionController class Metal < AbstractController::Base abstract!request def self.call(env) action(env[...][:action]).call(env) end def self.action(name, klass = ActionDispatch::Request) middleware_stack.build do |env| new.dispatch(name, klass.new(env)) end end def dispatch(name, request) @_request = request @_env = request.env @_env[action_controller.instance] = self process(name) response ? response.to_a : [status, headers, response_body] end end end
    • Cookies Exceptions Flash ActionController::Metal Helpers Redirecting Renderingredirect_to post_url(@post) Responder UrlForclass PeopleController < ApplicationController ..... (lots more) respond_to :html, :xml, :json def index @people = Person.find(:all) Assigns respond_with(@people) end Callbacksend Collector Helpers AbstractController::Base Layouts Logger Rendering Translation ViewPaths
    • Microkernel Design Patternapp/controllers/hello_controller.rb class HelloController < ActionController::Metal include ActionController::Rendering append_view_path "#{Rails.root}/app/views" def index render "hello/index" end endapp/controllers/hello_controller.rbclass HelloController < ActionController::Metal include ActionController::Redirecting include Rails.application.routes.url_helpers def index redirect_to root_url endend
    • ActionController::Base Microkernel Design Patternrails/actionpack/lib/action_controller/base.rbmodule ActionController class Base < Metal abstract! MODULES = [ AbstractController::Layouts, AbstractController::Translation, module ActionController Cookies, Helpers, Flash, module Helpers HideActions, Verification, UrlFor, RequestForgeryProtection, include AbstractController::Helpers Streaming, Redirecting, ... RecordIdentifier, Rendering, Renderers::All, Instrumentation, ConditionalGet, AbstractController::Callbacks, RackDelegation, Rescue SessionManagement, ] Caching, MimeResponds, MODULES.each do |mod| PolymorphicRoutes, include mod ImplicitRender, end
    • includes ActionController::Base abstract ActionController Namespace Cookies Exceptions Flash Helpers ActionController::Metal Redirecting Rendering abstract Responder UrlFor ..... (lots more) AbstractController Namespace Assigns Callbacks Collector HelpersAbstractController::Base Layouts Logger abstract Rendering Translation ViewPaths
    • nel Arch itectureM icroker
    • asi c to r edi# B ded an d R _s r ovi er er e .to # p end pe) = typ R ty in e =( e "] # t_t yp Typ ten te nt- con [" Con def d ers hea end "] ype ype t_t t-T on ten on ten de f c rs [" C he ade end "] ion ion source at at loc Loc ing the def [" ers f read d hea fraid o rl rl) = u t be a e nd (u n"] Don’ ti on= tio loc a ca s[ "Lo def der hea end us tus ) s tat sta def ta tus co de( @_s at us_ tu s) ls .st end sta Uti =( :: l] a tus R ack [va st = : def t a tus ? val @_s ) ch) val ea end od y =( to ?(: ns e_b po nd_ e spo . res
    • let’s test t he water
    • Both instance and class methods module PrintMeth class Test def self.included(base) include PrintMeth end ? base.class_eval do base.extend(ClassMethods) extend ClassMethods end Test.new.print_podcast end Test.print_company def print_podcast Ruby5 puts "Ruby5" Envy Labs end module ClassMethods def print_company puts "Envy Labs" end end end
    • d_chain vs superalia s_metho
    • Can’t modify parent class class GenericUser mr def name name_ without_ "Gregg Pollack" end end module Mr def name_with_mr na me "Mr. " + name_without_mr endclass User < GenericUser include Mr def self.included(base)end base.class_eval do alias :name_without_mr :nameputs User.new.name alias :name :name_with_mr end end output=> “Gregg Pollack” end output wanted=> “Mr. Gregg Pollack”
    • Can’t modify parent class class GenericUser mr def name name_ without_ "Gregg Pollack" end end module Mr def name_with_mr na me "Mr. " + name_without_mr endclass User < GenericUser include Mr def self.included(base)end base.class_eval do alias :name_without_mr :nameputs User.new.name alias :name :name_with_mr end end output=> “Gregg Pollack” end output wanted alias_method_chain :name, :mr=> “Mr. Gregg Pollack”
    • class GenericUser def name "Gregg Pollack" end end module Mr def name "Mr. " + superclass User < GenericUser end include Mrend endputs User.new.name output=> “Gregg Pollack” output wanted=> “Mr. Gregg Pollack”
    • AbstractController A refactor of ActionController in ActionPack Provides  A  Low  Level  Interface  for   making  Customized  Controller  Base  classes. Carl Lerche
    • ActionController::Base Microkernel Design Patternrails/actionpack/lib/action_controller/base.rbmodule ActionController class Base < Metal abstract! MODULES = [ AbstractController::Layouts, AbstractController::Translation, module ActionController Cookies, Helpers, Flash, module Helpers HideActions, Verification, UrlFor, RequestForgeryProtection, include AbstractController::Helpers Streaming, Redirecting, ... RecordIdentifier, Rendering, Renderers::All, Instrumentation, ConditionalGet, AbstractController::Callbacks, RackDelegation, Rescue SessionManagement, ] Caching, MimeResponds, MODULES.each do |mod| PolymorphicRoutes, include mod ImplicitRender, end
    • Using Superrails/actionpack/lib/abstract_controller/helpers.rb module AbstractController module Helpers # Returns a list of modules, normalized from the acceptable kinds of # helpers with the following behavior: def modules_for_helpers(args) ...rails/actionpack/lib/action_controller/metal/helpers.rb module ActionController helper : module Helpers all def modules_for_helpers(args) args += all_application_helpers if args.delete(:all) super(args) end ...
    • thod_chain vs supera lias_me
    • upport:: ConcernActiveS
    • class MyBar module MyCamp cattr_accessor :year def self.included(klass) include MyCamp klass.class_eval do self.year = "2010" def self.unconference extend ClassMethods ? puts title + " " + year end end end end module ClassMethods MyBar.unconference def title "BarCamp" endBarCamp 2010 end=> nil end
    • module MyCamp module MyCamp extend ActiveSupport::Concern def self.included(klass) klass.class_eval do included do self.year = "2010" self.year = "2010" extend ClassMethods end end module ClassMethods end def title "BarCamp" module ClassMethods end def title end "BarCamp" end end endend
    • When a module with ActiveSupport::Concern gets included into a class, it will:1. Look for ClassMethods and extend them module MyCamp extend ActiveSupport::Concern class MyBar include MyCamp module ClassMethods } def title end "BarCamp" end end end
    • When a module with ActiveSupport::Concern gets included into a class, it will:2. Look for InstanceMethods and include them module MyCamp extend ActiveSupport::Concern class MyBar include MyCamp module InstanceMethods } def title end "BarCamp" end end end
    • When a module with ActiveSupport::Concern gets included into a class, it will:3. class_eval everything in the include block module MyCamp extend ActiveSupport::Concern class MyBar include MyCamp } included do self.year = "2010" end end end
    • 4. All included modules get their included hookrun on the base classmodule MyFoo extend ActiveSupport::Concern } included do self.planet = "Earth" end class MyBarend include MyCamp endmodule MyCamp extend ActiveSupport::Concern include MyFoo Previously you had to do } class MyBar included do self.year = "2010" include MyFoo,MyCamp endend end
    • ActionController::Base Microkernel Design Patternrails/actionpack/lib/action_controller/base.rbmodule ActionController class Base < Metal abstract! MODULES = [ AbstractController::Layouts, AbstractController::Translation, module ActionController Cookies, Helpers, Flash, module Helpers HideActions, Verification, UrlFor, RequestForgeryProtection, include AbstractController::Helpers Streaming, Redirecting, ... RecordIdentifier, Rendering, Renderers::All, Instrumentation, ConditionalGet, AbstractController::Callbacks, RackDelegation, Rescue SessionManagement, ] Caching, MimeResponds, MODULES.each do |mod| PolymorphicRoutes, include mod ImplicitRender, end
    • rails/actionpack/lib/abstract_controller/helpers.rb module AbstractController module Helpers extend ActiveSupport::Concern } included do class_attribute :_helpers delegate :_helpers, :to => :self.class self._helpers = Module.new ../action_controller/base.rb end module ActionController class Base < Metalrails/actionpack/lib/action_controller/metal/helpers.rb include Helpers module ActionController module Helpers extend ActiveSupport::Concern include AbstractController::Helpers } included do class_attribute :helpers_path self.helpers_path = [] end ...
    • upport::C oncern ActiveS1. Look for ClassMethods and extend them2. Look for InstanceMethods and include them3. class_eval everything in the include block4. All included modules get their included hookrun on the base class
    • hrow in B undlerCatch/T
    • Dependency Resolution Depth  First  Search dependencies paperclip searchlogic shoulda sqlite3 aws-s3 activerecord Conflict mocha rake-compiler
    • Control Flow if   method  return elsif   method  invocation else unless for raise  ..  rescue while case catch  ..  throw
    • Control Flow catch(:marker) do puts "This will get executed" throw :marker puts "This will not get executed" end begin .. This will get executed => nil raise .. catch(:marker) do rescue puts "This will get executed" .. throw :marker, "hello" end puts "This will not get executed" end This will get executed => "hello"
    • Dependency Resolution Depth  First  Search dependencies paperclip searchlogic shoulda sqlite3 aws-s3 activerecord Conflict mocha rake-compiler
    • Inside Bundler Bundler/lib/bundler/resolver.rb when resolving a requirement retval = catch(requirement.name) do resolve(reqs, activated) end when activated gem is conflicted to go to initial requirement parent = current.required_by.last || existing.required_by.last debug { " -> Jumping to: #{parent.name}" } throw parent.name, existing.required_by.last.name when gem is not activated, but it’s dependencies conflict parent = current.required_by.last || existing.required_by.last debug { " -> Jumping to: #{parent.name}" } throw parent.name, existing.required_by.last.name
    • hrow in B undlerCatch/T
    • Deciphering Rails 3 Method Compilation Microkernel Architecturealias_method_chain vs super ActiveSupport::Concern Catch/Throw in Bundler
    • asi c to r edi# B ded an d R _s r ovi er er e .to # p end pe) = typ R ty in e =( e "] # t_t yp Typ ten te nt- con [" Con def d ers hea end "] ype ype t_t t-T on ten on ten f c [" C source de rs ade ng the he end raid of readi ion ion "] be af at at loc Loc Don’t def [" d ers hea rl e nd (u rl) = u on= n"] a ti ca tio loc "Lo def s[ derDon’t be afraid of making it your own hea end us tus ) s tat sta def ta tus co de( @_s at us_ tu s) ls .st end sta Uti =( :: l] a tus R ack [va st = : def t a tus ? val @_s ) ch) val ea end od y =( to ?(: ns e_b po nd_ e spo . res
    • Creative Commons name author URLConstruction Time Again v1ctory_1s_m1ne http://www.flickr.com/photos/v1ctory_1s_m1ne/3416173688/Microprocesseur Stéfan http://www.flickr.com/photos/st3f4n/2389606236/chain-of-14-cubes.4 Ardonik http://www.flickr.com/photos/ardonik/3273300715/(untitled) squacco http://www.flickr.com/photos/squeakywheel/454111821/up there Paul Mayne http://www.flickr.com/photos/paulm/873641065/Day 5/365 - Night Terrors Tom Lin :3= http://www.flickr.com/photos/tom_lin/3193080175/Testing the water The Brit_2 http://www.flickr.com/photos/26686573@N00/2188837324/High Dive Zhao Hua Xi Shi http://www.flickr.com/photos/elephantonabicycle/4321415975/
    • http://RailsBest.com
    • Slides => http://bit.ly/railstoasterСпасибо @GreggPollack Gregg@EnvyLabs.com http://envylabs.com