Decyphering Rails 3
Upcoming SlideShare
Loading in...5
×

Like this? Share it with your network

Share

Decyphering Rails 3

  • 1,706 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,706
On Slideshare
1,706
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