Decyphering Rails 3

  • 1,283 views
Uploaded on

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

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

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

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

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

More in: Technology , Design
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
1,283
On Slideshare
0
From Embeds
0
Number of Embeds
0

Actions

Shares
Downloads
17
Comments
0
Likes
4

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. Deciphering Rails 3 with Gregg Pollack
  • 2. 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
  • 3. Deciphering Rails 3 Method Compilation Microkernel Architecturealias_method_chain vs super ActiveSupport::Concern Catch/Throw in Bundler
  • 4. let’s test t he water
  • 5. 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
  • 6. evalhash = {name => Toaster, location => Moscow}hash.each_pair do |key, value| eval <<-RUBY @name = Toaster @#{key} = value RUBYend @location = Moscowputs @name + @locationToasterMoscow
  • 7. 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
  • 8. 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
  • 9. Me thod Com pilation
  • 10. 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
  • 11. 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
  • 12. 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
  • 13. [: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
  • 14. 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!
  • 15. ruby-profUsing Method MissingUsing Method Compliation
  • 16. Yehuda’s first commitrails/actionpack/lib/abstract_controller/metal/mime_responds.rb rails/actionpack/lib/abstract_controller/collector.rb
  • 17. 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
  • 18. 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?
  • 19. 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
  • 20. Me thod Com pilation
  • 21. let’s test t he water
  • 22. 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
  • 23. nel Arch itectureM icroker
  • 24. AbstractController A refactor of ActionController in ActionPack Provides  A  Low  Level  Interface  for   making  Customized  Controller  Base  classes. Carl Lerche
  • 25. 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
  • 26. 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
  • 27. Microkernel Design Pattern AbstractController Assigns Callbacks Collector Helpers Layouts Logger Rendering Translation ViewPaths
  • 28. 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
  • 29. 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
  • 30. 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
  • 31. 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)
  • 32. 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
  • 33. 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
  • 34. 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
  • 35. 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
  • 36. 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
  • 37. nel Arch itectureM icroker
  • 38. 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
  • 39. let’s test t he water
  • 40. 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
  • 41. d_chain vs superalia s_metho
  • 42. 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”
  • 43. 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”
  • 44. 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”
  • 45. AbstractController A refactor of ActionController in ActionPack Provides  A  Low  Level  Interface  for   making  Customized  Controller  Base  classes. Carl Lerche
  • 46. 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
  • 47. 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 ...
  • 48. thod_chain vs supera lias_me
  • 49. upport:: ConcernActiveS
  • 50. 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
  • 51. 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
  • 52. 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
  • 53. 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
  • 54. 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
  • 55. 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
  • 56. 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
  • 57. 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 ...
  • 58. 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
  • 59. hrow in B undlerCatch/T
  • 60. Dependency Resolution Depth  First  Search dependencies paperclip searchlogic shoulda sqlite3 aws-s3 activerecord Conflict mocha rake-compiler
  • 61. Control Flow if   method  return elsif   method  invocation else unless for raise  ..  rescue while case catch  ..  throw
  • 62. 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"
  • 63. Dependency Resolution Depth  First  Search dependencies paperclip searchlogic shoulda sqlite3 aws-s3 activerecord Conflict mocha rake-compiler
  • 64. 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
  • 65. hrow in B undlerCatch/T
  • 66. Deciphering Rails 3 Method Compilation Microkernel Architecturealias_method_chain vs super ActiveSupport::Concern Catch/Throw in Bundler
  • 67. 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
  • 68. 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/
  • 69. http://RailsBest.com
  • 70. Slides => http://bit.ly/railstoasterСпасибо @GreggPollack Gregg@EnvyLabs.com http://envylabs.com