Slide 1

Slide 1 text

LAZY, LAZY, LAZY, ALL THE THINGS!

Slide 2

Slide 2 text

WHAT IS LAZY PROGRAMMING?

Slide 3

Slide 3 text

IT DOES NOT MEAN LETTING AN IDE WRITE CODE FOR US

Slide 4

Slide 4 text

IT DOES NOT MEAN ASKING SIRI TO DO DATABASE MIGRATIONS

Slide 5

Slide 5 text

IT IS NOT WRITING CODE ON A GLOOMY MONDAY WITH A CUP OF COFFEE

Slide 6

Slide 6 text

WHAT IS IT THEN ?

Slide 7

Slide 7 text

IT CAN BE A SIMPLE EXPRESSION

Slide 8

Slide 8 text

OR, A DESIGN PATTERN

Slide 9

Slide 9 text

PERHAPS, EVEN A LANGUAGE SEMANTIC

Slide 10

Slide 10 text

WHY AM I TELLING YOU THIS ?

Slide 11

Slide 11 text

No content

Slide 12

Slide 12 text

LAZINESS IS THE QUALITY THAT MAKES YOU GO TO GREAT EFFORT TO REDUCE OVERALL ENERGY EXPENDITURE. Larry Wall BECAUSE,

Slide 13

Slide 13 text

YOU HAVE TO BE LAZY TO BE A RUBY PROGRAMMER Yukihiro ‘Matz’ Matsumoto AND,

Slide 14

Slide 14 text

LET’S BE LAZY

Slide 15

Slide 15 text

1. LAZY EVALUATION 2. LAZY LOADING 3. LAZY INHERITANCE

Slide 16

Slide 16 text

LAZY EVALUATION THE EXPRESSION

Slide 17

Slide 17 text

DELAYING EVALUATION OF AN EXPRESSION UNTIL IT'S REQUIRED

Slide 18

Slide 18 text

GIVING US THE ABILITY TO ABSTRACT THE CONTROL FLOW

Slide 19

Slide 19 text

EXAMPLE

Slide 20

Slide 20 text

ABSTRACTING THE CONTROL FLOW PART 1 - THE PLEDGE INITIALISE AN OBJECT ▸All we need to do is initialise an object ▸But we need to check if its nil or not ▸Seriously, how hard can that be?

Slide 21

Slide 21 text

ABSTRACTING THE CONTROL FLOW PART 1 - THE TURN INITIALISE AN OBJECT if @foo.nil? @foo = :bar end

Slide 22

Slide 22 text

ABSTRACTING THE CONTROL FLOW PART 1 - THE PRESTIGE INITIALISE AN OBJECT, LAZILY @foo ||= :bar

Slide 23

Slide 23 text

THAT WAS SIMPLE ENOUGH

Slide 24

Slide 24 text

NOW, A MORE USEFUL EXAMPLE

Slide 25

Slide 25 text

ABSTRACTING THE CONTROL FLOW PART 2 - THE PLEDGE FETCHING AN AUTHENTICATION TOKEN ▸Communicate with an API ▸Fetch an Authentication Token ▸Use it

Slide 26

Slide 26 text

ABSTRACTING THE CONTROL FLOW PART 2 - THE TURN SEND A REQUEST, AND SAVE THE TOKEN require 'restclient' class Authenticator attr_reader :auth_token def auth_token url res = RestClient.get(url) @auth_token = parse_response_for_token(res) end def parse_response_for_token response # parsing logic here, returns a hash end end

Slide 27

Slide 27 text

ABSTRACTING THE CONTROL FLOW PART 2 - THE PRESTIGE SEND A REQUEST, AND SAVE THE TOKEN, LAZILY require 'restclient' class Authenticator attr_reader :auth_token def auth_token url @auth_token ||= (res = RestClient.get(url) && parse_response_for_token(res)) end def parse_response response # parsing logic here, returns a hash end end

Slide 28

Slide 28 text

THIS LOOKS SUSPICIOUSLY LIKE CACHING

Slide 29

Slide 29 text

CACHING = LAZINESS

Slide 30

Slide 30 text

MIND = BLOWN!

Slide 31

Slide 31 text

No content

Slide 32

Slide 32 text

No content

Slide 33

Slide 33 text

ANOTHER EXAMPLE

Slide 34

Slide 34 text

LONG SEQUENCES PART 1 - THE PLEDGE CALCULATE SUM OF FIRST THOUSAND EVEN NUMBERS ▸Iterate 2000 times ▸Check if the number is even ▸Add to a sum if it is.

Slide 35

Slide 35 text

LONG SEQUENCES PART 1 - THE TURN LOOP IT, SUM IT, RETURN IT def sum_of_thousand_even_numbers sum = 0 2001.times do |number| sum += number if number % 2 == 0 end sum end sum_of_even_numbers(1000) => 1001000

Slide 36

Slide 36 text

THAT’S NOT THE RUBY WAY, IS IT ?

Slide 37

Slide 37 text

LONG SEQUENCES PART 1 - THE TURN RANGE IT, SELECT IT, REDUCE IT def sum_of_even_numbers limit (1..limit*2) .select(&:even?) .reduce(:+) end sum_of_even_numbers(1000) => 1001000

Slide 38

Slide 38 text

LONG SEQUENCES PART 1 - THE TURN RANGE IT, SELECT IT, TAKE IT, REDUCE IT def sum_of_even_numbers limit (1..Float::INFINITY) .select(&:even?) .take(limit) .reduce(:+) end

Slide 39

Slide 39 text

LONG SEQUENCES PART 1 - THE TURN RANGE IT, SELECT IT, TAKE IT, REDUCE IT def sum_of_even_numbers limit (1..Float::INFINITY) .select(&:even?) .take(limit) .reduce(:+) end DANG! INFINITE LOOP !

Slide 40

Slide 40 text

LONG SEQUENCES PART 1 - THE PRESTIGE RANGE IT, SELECT IT, TAKE IT, REDUCE IT, LAZILY def sum_of_even_numbers limit (1..Float::INFINITY) .lazy .select(&:even?) .take(limit) .reduce(:+) end sum_of_even_numbers(1000) => 1001000

Slide 41

Slide 41 text

THAT WAS PEDAGOGICAL

Slide 42

Slide 42 text

LET’S GET REAL(WORLD)

Slide 43

Slide 43 text

LAZY SEQUENCES PART 1 - THE PLEDGE STEP WISE PERSON LOOKUP ▸We want to fetch person’s data based on his email ▸Using Third party services like Clearbit ▸And each lookup for data is expensive computationally and monetarily.

Slide 44

Slide 44 text

LAZY SEQUENCES - THE TURN STEP WISE LOOKUP def find_match lookups = [ expensive_phone_lookup(@person.email), expensive_location_lookup(@person.email), expensive_property_lookup(@person.email), ] lookups.each do |lookup| result = @service.match_person(stage) break result if result.success? end end

Slide 45

Slide 45 text

LAZY SEQUENCES - THE PRESTIGE STEP WISE LOOKUP def stages(email) @stages ||= Enumerator.new do |enumerator| # Step 1: Only Phone phone = expensive_phone_lookup(email) enumerator.yield(:email_only, :phone => phone) # Step 2: Only Phone Number location = expensive_location_lookup(email) enumerator.yield(:location_only, :location => location) # Step 3: Only Property property = expensive_property_lookup(email) enumerator.yield(:property, :property => property) # Step 4: All Info enumerator.yield(:all_info, all_info) end end

Slide 46

Slide 46 text

LAZY SEQUENCES - THE PRESTIGE STEP WISE LOOKUP def find_match while stage = stage(@person.email).next result = @service.match_person(stage) break result if result.success? end rescue StopIteration => _ # error handling here end

Slide 47

Slide 47 text

LAZY LOADING A DESIGN PATTERN

Slide 48

Slide 48 text

A DESIGN PATTERN WHERE LOADING OF OBJECTS IS DELAYED UNTIL REQUIRED

Slide 49

Slide 49 text

RUBY HAS PROCS, BUILT TO STORE AND RUN CODE WHEN REQUIRED

Slide 50

Slide 50 text

RUBY HAS MODULES, BUILT TO WRITE AND INJECT CODE WHEN REQUIRED

Slide 51

Slide 51 text

COMBINE THESE TWO AND YOU CAN BUILD PROGRAMS THAT ALLOW LAZY LOADING

Slide 52

Slide 52 text

WE’LL BE LOOKING AT AN EXCELLENT LIBRARY WRITTEN IN RUBY ON RAILS

Slide 53

Slide 53 text

BUT FIRST ! A LITTLE BACKGROUND INFO

Slide 54

Slide 54 text

RAILS IS A HUGE PROJECT ! 250,000+ LINES OF CODE 60,000+ COMMITS 3,200+ CONTRIBUTOR S

Slide 55

Slide 55 text

AND HAS A COMPLEX LOADING AND INITIALISING MECHANISM

Slide 56

Slide 56 text

BUT, WITH HIGH COMPLEXITY COMES HIGH BOOT UP TIME

Slide 57

Slide 57 text

SO IN 2010, TO IMPROVE BOOT TIME, RAILS CHANGED ITS INITIALISATION MECHANISM TO LAZY LOADING

Slide 58

Slide 58 text

https://github.com/rails/rails/commit/39d6f9e112f2320d8c2006ee3bcc160cfa761d0a

Slide 59

Slide 59 text

AND THAT’S HOW ACTIVESUPPORT::LAZYLOAD HOOKS WAS BORN

Slide 60

Slide 60 text

ACTIVE SUPPORT LAZY LOAD HOOKS THE TASK OF THIS LIBRARY IS TO ▸Accept blocks of code and store them in a hash. ▸Have a calling mechanism for the stored blocks. ▸Then call those stored blocks when required.

Slide 61

Slide 61 text

ACCEPT BLOCKS AND STORE THEM IN A HASH https://github.com/rails/rails/blob/master/activesupport/lib/active_support/lazy_load_hooks.rb

Slide 62

Slide 62 text

CALL THE HOOKS ONLY WHEN REQUIRED https://github.com/rails/rails/blob/master/activesupport/lib/active_support/lazy_load_hooks.rb

Slide 63

Slide 63 text

EXECUTE THE STORED BLOCKS https://github.com/rails/rails/blob/master/activesupport/lib/active_support/lazy_load_hooks.rb

Slide 64

Slide 64 text

https://github.com/rails/rails/blob/master/activerecord/lib/active_record.rb ActiveRecord

Slide 65

Slide 65 text

https://github.com/rails/rails/blob/master/activerecord/lib/active_record.rb ActiveRecord

Slide 66

Slide 66 text

https://github.com/rails/rails/blob/master/activerecord/lib/active_record/base.rb ActiveRecord::Base

Slide 67

Slide 67 text

https://github.com/rails/rails/blob/master/activerecord/lib/active_record/base.rb ActiveRecord::Base

Slide 68

Slide 68 text

ACTIVE SUPPORT LAZY LOAD HOOKS THE IMPLICATIONS OF THIS LIBRARY ARE ▸By calling the .run_load_hooks method at the end, Rails can call code that depends on ActiveRecord, lazily. ▸This helps Rails load code only when it’s required, which reduces its booting time significantly.

Slide 69

Slide 69 text

EXAMPLE

Slide 70

Slide 70 text

THE CORNERSTONE OF A NUTRITIOUS RAILS APP

Slide 71

Slide 71 text

No content

Slide 72

Slide 72 text

USER AUTHENTICATION

Slide 73

Slide 73 text

CURRENT_USER, REMEMBER?

Slide 74

Slide 74 text

YOU CAN USE THIS OBJECT FOR QUERYING, SCOPING, ET AL; ALL IN THE CONTEXT OF CONTROLLERS AND VIEWS.

Slide 75

Slide 75 text

No content

Slide 76

Slide 76 text

PASSING THE OBJECT TO EVERY MODEL METHOD DOESN’T MAKE SENSE, RIGHT ?

Slide 77

Slide 77 text

THE CURRENT USER - THE PLEDGE ‣ All you need to do is store the current_user in a thread-local variable and use it in your model ‣ Thread.current[:current_user] = @current_user USING CURRENT USER ACROSS THE APP

Slide 78

Slide 78 text

THE CURRENT USER - THE PLEDGE USING CURRENT USER ACROSS THE APP ‣ If you’re using threaded app servers like Thin or Puma, then it’s wise to use the request_store or request_store_rails gem. ‣ RequestStore.store[:current_user] = @current_user

Slide 79

Slide 79 text

SAY YOU HAVE A BLOG APPLICATION AND YOU WANT TO SHOW THE CURRENT USER’S COMMENTS ON A BLOG

Slide 80

Slide 80 text

THE CURRENT USER - THE PRESTIGE USING CURRENT USER ACROSS THE APP class Blog include Mongoid::Document field :title, type: String field :content, type: String belongs_to :author, class_name: "User" has_many :comments attr_accessor :current_viewer after_initialize Proc.new { |blog| blog.current_viewer = RequestStore.store[:current_user] } def my_comments self.comments.where(user_id: self.current_viewer.id) end end

Slide 81

Slide 81 text

THE CURRENT USER - THE PRESTIGE USING CURRENT USER ACROSS THE APP class Blog include Mongoid::Document field :title, type: String field :content, type: String belongs_to :author, class_name: "User" has_many :comments attr_accessor :current_viewer after_initialize Proc.new { |blog| blog.current_viewer = RequestStore.store[:current_user] } def my_comments self.comments.where(user_id: self.current_viewer.id) end end

Slide 82

Slide 82 text

THE CURRENT USER - THE TURN USING CURRENT USER ACROSS THE APP module CurrentUser class RailTie < Rails::RailTie ActiveSupport.on_load :action_controller do before_action do |c| RequestStore.store[:current_user] ||= c.current_user if c.current_user.present? end end end end

Slide 83

Slide 83 text

THE CURRENT USER - THE TURN USING CURRENT USER ACROSS THE APP module CurrentUser class RailTie < Rails::RailTie ActiveSupport.on_load :action_controller do before_action do |c| RequestStore.store[:current_user] ||= c.current_user if c.current_user.present? end end end end

Slide 84

Slide 84 text

ANOTHER EXAMPLE

Slide 85

Slide 85 text

WE HAVE THE SAME BLOG APPLICATION, BUT WITH A FEW CHANGES

Slide 86

Slide 86 text

class Blog include Mongoid::Document field :title, type: String field :content, type: String belongs_to :author, class_name: "User" attr_accessor :current_viewer after_initialize Proc.new { |blog| blog.current_viewer = RequestStore.store[:current_user] } ActiveSupport.run_load_hooks(:blog, self) end

Slide 87

Slide 87 text

class Blog include Mongoid::Document field :title, type: String field :content, type: String belongs_to :author, class_name: "User" attr_accessor :current_viewer after_initialize Proc.new { |blog| blog.current_viewer = RequestStore.store[:current_user] } ActiveSupport.run_load_hooks(:blog, self) end

Slide 88

Slide 88 text

WE’VE SEPARATED THE COMMENTS FEATURE AS A MODULE

Slide 89

Slide 89 text

module Commentable def self.included(base) base.has_many :comments, as: :commentable base.send(:include, InstanceMethods) end module InstanceMethods def my_comments self.comments.where(user_id: self.current_viewer.id) end # . . . end end

Slide 90

Slide 90 text

ActiveSupport.on_load(:blog) do self.send(:include, Commentable) end

Slide 91

Slide 91 text

NOW WE’VE DECIDED TO ADD FEEDS AND PHOTOS TO THE APP

Slide 92

Slide 92 text

class Feed # feed model details here ActiveSupport.run_load_hooks(:feed, self) end class Photo # feed model details here ActiveSupport.run_load_hooks(:photo, self) end

Slide 93

Slide 93 text

THEY SHOULD SUPPORT COMMENTS AS WELL

Slide 94

Slide 94 text

[:blog, :feed, :photo].each do |post| ActiveSupport.on_load(post) do self.send(:include, Commentable) end end

Slide 95

Slide 95 text

THERE ARE PLENTY OF OTHER LAZY PARADIGMS

Slide 96

Slide 96 text

THERE ARE PLENTY OF OTHER LAZY LANGUAGES

Slide 97

Slide 97 text

ENOUGH LAZINESS TODAY

Slide 98

Slide 98 text

CONCLUSION

Slide 99

Slide 99 text

||= IS A HANDY SHORT CIRCUIT OPERATOR

Slide 100

Slide 100 text

ENUMERATOR::LAZY OFFERS INFINITE AND IMMUTABLE SEQUENCES

Slide 101

Slide 101 text

ACTIVESUPPORT::LAZYLOADHOOKS IS A POWERFUL LIBRARY FOR COMPOSING BEHAVIOUR

Slide 102

Slide 102 text

THANK YOU !

Slide 103

Slide 103 text

AMURA MARKETING TECHNOLOGIES SHAUNAK PAGNIS

Slide 104

Slide 104 text

@_kanuahs shaunakpp shaunak-amura SHAUNAK PAGNIS