monkey patching in ruby

22
Monkey Patching in Ruby by Anton Sakovich @ Ottawa Ruby Feb 24, 2015

Upload: anton-sakovich

Post on 05-Aug-2015

124 views

Category:

Technology


0 download

TRANSCRIPT

Monkey Patchingin Ruby

by Anton Sakovich

@ Ottawa Ruby

Feb 24, 2015

What is monkey patching?Modification of an existing class at runtime with an intent tomodify or extend existing functionality.

Guerilla Patching

Gorilla Patching

Monkey Patching

Reopening classesclass Person  attr_accessor :children  def initialize(name)    @name = name    @children = []  endend

# some code omitted

class Person  def <<(child)    children << child  endend

bob   = Person.new('Bob')paul  = Person.new('Paul')julie = Person.new('Julie')

bob << paulbob << julie

puts bob.children.count# 2

Example: merging nested hashesh1 = {  first_name: "Bob",  personal_data: { age: 30 }}h2 = {  last_name: "Smith",  personal_data: { weight: 160 }}

Age data is lost on simple mergeputs h1.merge(h2)# { first_name: "Bob",#   personal_data: { weight: 160 },#   last_name: "Smith"# }

ActiveSupport gem adds a method to merge nested hashesrequire 'active_support/core_ext/hash/deep_merge'puts h1.deep_merge(h2)# { first_name: "Bob",#   personal_data: { age: 30, weight: 160 },#   last_name: "Smith"# }

The new method is added by the Hash classreopeningclass Hash  def deep_merge(other_hash, &block)    # some code  end  def deep_merge!(other_hash, &block)    # some code  endend

Example: custom extensions - Rails# lib/core_extensions.rbclass DateTime  def weekday?    !sunday? && !saturday?  endend

The patch can be wrapped in a module# lib/core_extensions/date_time/business_days.rbmodule CoreExtensions  module DateTime    module BusinessDays      def weekday?        !sunday? && !saturday?      end    end  endend

and the module gets mixed inDateTime.include CoreExtensions::DateTime::BusinessDays

Example: overriding methodsclass Array  alias_method :old_to_s, :to_s

  def to_s    stars = "\n" << '*' * 40 << "\n"    "#{stars}#{old_to_s}#{stars}"  endend

array = [1, 2, 3]puts array.to_s# ****************************************# [1, 2, 3]# ****************************************

Example: scoped patching in Ruby 2+module Starrable  refine Array do    alias_method :old_to_s, :to_s    def to_s      stars = "\n" << '*' * 40 << "\n"      "#{stars}#{old_to_s}#{stars}"    end  endendclass Logger  using Starrable  def self.debug(x)    puts x.to_s  endend

Logger.debug [4, 5, 6]# ****************************************# [4, 5, 6]# ****************************************

and gems for Excelspreadsheets

Axlsx AxlsxStyler

Sample worksheet

With Axlsx

With Axlsx + AxlsxStylerrequire 'axlsx_styler'axlsx = Axlsx::Package.newworkbook = axlsx.workbookworkbook.add_worksheet do |sheet|  sheet.add_row  sheet.add_row ["", "Product",  "Category",    "Price"]  sheet.add_row ["", "Butter",   "Dairy",       4.99   ]  sheet.add_row ["", "Bread",    "Baked Goods", 3.45   ]  sheet.add_row ["", "Broccoli", "Produce",     2.99   ]  sheet.column_widths 5, 20, 20, 20

  # using AxlsxStyler DSL  sheet["B2:D2"].add_style b: true  sheet["B2:B5"].add_style b: true  sheet["B2:D2"].add_style bg_color: "95AFBA"  sheet["B3:D5"].add_style bg_color: "E2F89C"  sheet["D3:D5"].add_style alignment: { horizontal: :left }  sheet["B2:D5"].add_border  sheet["B3:D3"].add_border [:top]endworkbook.apply_stylesaxlsx.serialize "grocery.xlsx"

Axlsx allows to retrieve an array of cells withsheet["B2:D5"]

This allows to for easy implementation of style applications,such as

sheet["B2:D2"].add_style b: truesheet["B2:D5"].add_bordersheet["B3:D3"].add_border [:top]

AxlsxStyler: a patch to the Array (for now)module AxlsxStyler  module Array    def add_style(style)      validate_cells      each do |cell|        cell.add_style(style)      end    end    def add_border(edges = :all)      validate_cells      selected_edges(edges).each do |edge|        add_border_at(edge)      end    end    # ...  endend

require 'axlsx'require 'axlsx_styler/version'require 'axlsx_styler/array'require 'axlsx_styler/axlsx_extensions'

# adding the patchesArray.send           :include, AxlsxStyler::ArrayAxlsx::Workbook.send :include, AxlsxStyler::Axlsx::WorkbookAxlsx::Cell.send     :include, AxlsxStyler::Axlsx::Cell

Credits

Russ Olsen, Eloquent Ruby, Addison-Wesley 2011David A. Black, The Well-Grounded Rubyist, 2nd ed.,Manning 2014

http://www.justinweiss.com/blog/2015/01/20/3-ways-to-monkey-patch-without-making-a-mess

Thank you!