Slide 1

Slide 1 text

module WhyIsNobody using Refinements end # lazyatom.com/refinements

Slide 2

Slide 2 text

Disclaimer This is not a sales pitch for refinements. I am not going to show you how they are amazing and will solve all your programming problems. They might be good… but also they might be bad. They might even be terrible! Using refinements might make your application implode, or make you smell really bad, or lower your credit rating, or make your cat regard you with an even stronger level of apathetic disdain. Please do not hold me responsible if use of refinements leaves you with an unexplainable rash, or an overwhelming sense of ennui. Other side effects may include: syntax itch, nil nausea, const-ipation, drowsiness, and global interpreter lockjaw.

Slide 3

Slide 3 text

What are refinements? A mechanism to change the behaviour of an object in a limited and controlled way

Slide 4

Slide 4 text

module Shouting refine String do def shout self.upcase + "!" end end end

Slide 5

Slide 5 text

module Shouting refine String do def shout self.upcase + "!" end end end class Thing using Shouting "Hello".shout # => "HELLO!" end

Slide 6

Slide 6 text

module Shouting refine String do def shout self.upcase + "!" end end end class Thing using Shouting "Hello".shout # => "HELLO!" end "Hello".shout # => NoMethodError: undefined method 'shout' for "Oh no":String

Slide 7

Slide 7 text

module TexasTime refine Time do def to_s if hour > 12 "Afternoon, y’all!" else super end end end end

Slide 8

Slide 8 text

module TexasTime refine Time do def to_s if hour > 12 "Afternoon, y’all!" else super end end end end class RubyConf using TexasTime Time.parse("12:00").to_s # => "2015-11-16 12:00:00" Time.parse("14:15").to_s # => "Afternoon, y’all!" end

Slide 9

Slide 9 text

module TexasTime refine Time do def to_s if hour > 12 "Afternoon, y’all!" else super end end end end class RubyConf using TexasTime Time.parse("12:00").to_s # => "2015-11-16 12:00:00" Time.parse("14:15").to_s # => "Afternoon, y’all!" end Time.parse("14:15").to_s # => "2015-11-16 14:15:00"

Slide 10

Slide 10 text

using ( a developer’s guide )

Slide 11

Slide 11 text

class Thing "hello".shout # => "HELLO!" end using Shouting

Slide 12

Slide 12 text

class Thing "hello".shout # => "HELLO!" end using Shouting

Slide 13

Slide 13 text

class Thing def greet "hello".shout end end Thing.new.greet # => "HELLO!" using Shouting

Slide 14

Slide 14 text

class Thing def greet "hello".shout end end Thing.new.greet # => "HELLO!" using Shouting

Slide 15

Slide 15 text

class Thing def greet "hello" end end Thing.new.greet using Shouting .shout # => NoMethodError

Slide 16

Slide 16 text

class Thing using Shouting end class Thing "hello".shout # => NoMethodError end

Slide 17

Slide 17 text

class Thing using Shouting class OtherThing "hello".shout # => "HELLO!" end end

Slide 18

Slide 18 text

class Thing using Shouting class OtherThing < Thing "hello".shout end end # => NoMethodError

Slide 19

Slide 19 text

class Thing using Shouting class OtherThing < Thing "hello".shout end end # => "HELLO!"

Slide 20

Slide 20 text

class Thing using Shouting class OtherThing "hello".shout # => "HELLO!" end end

Slide 21

Slide 21 text

class Thing using Shouting end class Thing::OtherThing "hello".shout # => NoMethodError end

Slide 22

Slide 22 text

class Thing using Shouting def run yield end end Thing.new.run do "hello".shout end # => NoMethodError

Slide 23

Slide 23 text

Lexical Scope

Slide 24

Slide 24 text

file: thing.rb class Thing using Shouting "hello".shout end "goodbye".shout

Slide 25

Slide 25 text

file: thing.rb class Thing using Shouting "hello".shout end "goodbye".shout Top Level

Slide 26

Slide 26 text

file: thing.rb class Thing using Shouting "hello".shout end "goodbye".shout Top Level

Slide 27

Slide 27 text

file: thing.rb class Thing using Shouting "hello".shout end "goodbye".shout Top Level A

Slide 28

Slide 28 text

file: thing.rb class Thing using Shouting "hello".shout end "goodbye".shout Top Level Parent none A Parent Top Level

Slide 29

Slide 29 text

file: thing.rb class Thing using Shouting "hello".shout end "goodbye".shout Top Level Parent none A Parent Top Level

Slide 30

Slide 30 text

file: thing.rb class Thing using Shouting "hello".shout end "goodbye".shout Top Level Parent none A Parent Top Level

Slide 31

Slide 31 text

file: thing.rb class Thing using Shouting "hello".shout end "goodbye".shout Top Level Parent none Refinements empty A Parent Top Level Refinements Shouting

Slide 32

Slide 32 text

file: thing.rb class Thing using Shouting "hello".shout end "goodbye".shout Top Level Parent none Refinements empty A Parent Top Level Refinements Shouting

Slide 33

Slide 33 text

file: thing.rb class Thing using Shouting "hello".shout end "goodbye".shout Top Level Parent none Refinements empty A Parent Top Level Refinements Shouting

Slide 34

Slide 34 text

Top Level Parent none Refinements empty A Parent Top Level Refinements Shouting file: thing.rb class Thing using Shouting "hello".shout end "goodbye".shout

Slide 35

Slide 35 text

Top Level Parent none Refinements empty A Parent Top Level Refinements Shouting file: thing.rb class Thing using Shouting "hello".shout end "goodbye".shout # => “HELLO!”

Slide 36

Slide 36 text

Top Level Parent none Refinements empty A Parent Top Level Refinements Shouting file: thing.rb class Thing using Shouting "hello".shout end "goodbye".shout

Slide 37

Slide 37 text

file: thing.rb class Thing using Shouting "hello".shout end "goodbye".shout Top Level Parent none Refinements empty A Parent Top Level Refinements Shouting

Slide 38

Slide 38 text

file: thing.rb class Thing using Shouting "hello".shout end "goodbye".shout Top Level Parent none Refinements empty A Parent Top Level Refinements Shouting

Slide 39

Slide 39 text

Top Level Parent none Refinements empty A Parent Top Level Refinements Shouting file: thing.rb class Thing using Shouting "hello".shout end "goodbye".shout

Slide 40

Slide 40 text

Top Level Parent none Refinements empty A Parent Top Level Refinements Shouting # => NoMethodError file: thing.rb class Thing using Shouting "hello".shout end "goodbye".shout

Slide 41

Slide 41 text

file: thing.rb using Shouting class Thing "hello".shout end "goodbye".shout Top Level Parent none Refinements Shouting A Parent Top Level Refinements Shouting

Slide 42

Slide 42 text

Top Level Parent none Refinements Shouting A Parent Top Level Refinements Shouting file: thing.rb using Shouting class Thing "hello".shout end "goodbye".shout

Slide 43

Slide 43 text

Top Level Parent none Refinements Shouting A Parent Top Level Refinements Shouting file: thing.rb using Shouting class Thing "hello".shout end "goodbye".shout # => “HELLO!” # => “GOODBYE!”

Slide 44

Slide 44 text

Refinements are activated in current and nested lexical scopes

Slide 45

Slide 45 text

file: thing.rb class Thing using Shouting end class Thing "hello".shout end

Slide 46

Slide 46 text

file: thing.rb class Thing using Shouting end class Thing "hello".shout end Top Level Parent none Refinements empty

Slide 47

Slide 47 text

file: thing.rb class Thing using Shouting end class Thing "hello".shout end Top Level Parent none Refinements empty A Parent Top Level Refinements Shouting

Slide 48

Slide 48 text

file: thing.rb class Thing using Shouting end class Thing "hello".shout end Top Level Parent none Refinements empty A Parent Top Level Refinements Shouting B Parent Top Level Refinements empty

Slide 49

Slide 49 text

file: thing.rb class Thing using Shouting end class Thing "hello".shout end Top Level Parent none Refinements empty A Parent Top Level Refinements Shouting B Parent Top Level Refinements empty

Slide 50

Slide 50 text

same class ≠ same scope

Slide 51

Slide 51 text

file: thing.rb class Thing using Shouting end class Other < Thing "hello".shout end

Slide 52

Slide 52 text

file: thing.rb class Thing using Shouting end class Other < Thing "hello".shout end Top Level Parent none Refinements empty A Parent Top Level Refinements Shouting B Parent Top Level Refinements empty

Slide 53

Slide 53 text

class hierarchy ≠ scope hierarchy

Slide 54

Slide 54 text

different file = different scope

Slide 55

Slide 55 text

file: thing.rb class Thing using Shouting def greet "hello".shout end end Thing.new.greet

Slide 56

Slide 56 text

file: thing.rb class Thing using Shouting def greet "hello".shout end end Thing.new.greet Top Level Parent none Refinements empty A Parent Top Level Refinements Shouting

Slide 57

Slide 57 text

file: thing.rb class Thing using Shouting def greet "hello".shout end end Thing.new.greet Top Level Parent none Refinements empty A Parent Top Level Refinements Shouting Thing#greet Lexical scope A

Slide 58

Slide 58 text

file: thing.rb class Thing using Shouting def greet "hello".shout end end Thing.new.greet Top Level Parent none Refinements empty A Parent Top Level Refinements Shouting Thing#greet Lexical scope A

Slide 59

Slide 59 text

file: thing.rb class Thing using Shouting def greet "hello".shout end end Thing.new.greet Top Level Parent none Refinements empty A Parent Top Level Refinements Shouting Thing#greet Lexical scope A

Slide 60

Slide 60 text

Top Level Parent none Refinements empty A Parent Top Level Refinements Shouting Thing#greet Lexical scope A file: thing.rb class Thing using Shouting def greet "hello".shout end end Thing.new.greet

Slide 61

Slide 61 text

Top Level Parent none Refinements empty A Parent Top Level Refinements Shouting Thing#greet Lexical scope A file: thing.rb class Thing using Shouting def greet "hello".shout end end Thing.new.greet # => “HELLO!”

Slide 62

Slide 62 text

methods are evaluated using the lexical scope at their definition

Slide 63

Slide 63 text

file: thing.rb class Thing using Shouting def run yield end end Thing.new.run do "hello".shout end # => NoMethodError

Slide 64

Slide 64 text

file: thing.rb class Thing using Shouting def run yield end end Thing.new.run do "hello".shout end Top Level Refinements empty A Parent Top Level Refinements Shouting Thing#greet Lexical scope A

Slide 65

Slide 65 text

file: thing.rb class Thing using Shouting def run yield end end Thing.new.run do "hello".shout end Top Level Refinements empty A Parent Top Level Refinements Shouting Thing#greet Lexical scope A Main#block Lexical scope Top level

Slide 66

Slide 66 text

blocks are evaluated using the lexical scope at their definition

Slide 67

Slide 67 text

Refinements use lexical scope

Slide 68

Slide 68 text

You get a new lexical scope when 1. Entering a different file 2. opening a class or module definition 3. running code from a string using “eval”

Slide 69

Slide 69 text

Refinement principles 1. activated within current and nested scopes 2. scope hierarchy is not the same as class hierarchy 3. different files get different top level scopes 4.methods are evaluated using lexical scope at their definition 5. blocks are evaluated using lexical scope at their definition

Slide 70

Slide 70 text

Let’s use refinements!

Slide 71

Slide 71 text

1. Monkey patching

Slide 72

Slide 72 text

class String def camelize self.split("_").map(&:capitalize).join end end "ruby_conf_2015".camelize # => "RubyConf2015"

Slide 73

Slide 73 text

/app/.bundle/gems/ruby/2.1.0/gems/activesupport-4.2.1/lib/active_support/ inflector/methods.rb:261:in `const_get': wrong constant name Admin/adminHelper (NameError) from /app/.bundle/gems/ruby/2.1.0/gems/activesupport-4.2.1/lib/active_support/ inflector/methods.rb:261:in `block in constantize' from /app/.bundle/gems/ruby/2.1.0/gems/activesupport-4.2.1/lib/active_support/ inflector/methods.rb:259:in `each' from /app/.bundle/gems/ruby/2.1.0/gems/activesupport-4.2.1/lib/active_support/ inflector/methods.rb:259:in `inject' from /app/.bundle/gems/ruby/2.1.0/gems/activesupport-4.2.1/lib/active_support/ inflector/methods.rb:259:in `constantize' from /app/.bundle/gems/ruby/2.1.0/gems/activesupport-4.2.1/lib/active_support/ core_ext/string/inflections.rb:66:in `constantize' from /app/.bundle/gems/ruby/2.1.0/gems/actionpack-4.2.1/lib/ abstract_controller/helpers.rb:156:in `block in modules_for_helpers' from /app/.bundle/gems/ruby/2.1.0/gems/actionpack-4.2.1/lib/ abstract_controller/helpers.rb:144:in `map!' from /app/.bundle/gems/ruby/2.1.0/gems/actionpack-4.2.1/lib/ abstract_controller/helpers.rb:144:in `modules_for_helpers' from /app/.bundle/gems/ruby/2.1.0/gems/actionpack-4.2.1/lib/action_controller/ metal/helpers.rb:93:in `modules_for_helpers'

Slide 74

Slide 74 text

Monkey-patching Refinements breaks API expectations cannot affect distant code makes code harder to debug leaves lexical clue when active

Slide 75

Slide 75 text

2. Managing API changes

Slide 76

Slide 76 text

# Ruby 1.9 "hello".chars # => # # Ruby 2.0+ "hello".chars # => ["h", "e", "l", "l", "o"]

Slide 77

Slide 77 text

module OldChars refine String do def chars; each_char; end end end "hello".chars # => ["h", "e", "l", "l", “o"] using OldChars "hello".chars # => #

Slide 78

Slide 78 text

3. DSLs

Slide 79

Slide 79 text

describe "Ruby" do it "should bring happiness" do developer = Developer.new(uses: "ruby") developer.should be_happy end end

Slide 80

Slide 80 text

describe "Ruby" do it "should bring happiness" do developer = Developer.new(uses: "ruby") developer.should be_happy end end

Slide 81

Slide 81 text

module RSpec refine Object do def should(expectation) expectation.satisfied_by?(self) end end end using RSpec 123.should eq 123 # => true false.should eq true # => false

Slide 82

Slide 82 text

4. Internal access control

Slide 83

Slide 83 text

module UserAdmin refine User do def purge! user.associated_records.delete_all! user.delete! end end end # in app/controllers/admin_controller.rb class AdminController < ApplicationController using UserAdmin def purge_user User.find(params[:id]).purge! end end

Slide 84

Slide 84 text

So… why is nobody using refinements?

Slide 85

Slide 85 text

No content

Slide 86

Slide 86 text

No content

Slide 87

Slide 87 text

“Because they’re just bad.”

Slide 88

Slide 88 text

YOU ARE EVERYTHING THAT IS WRONG WITH EVERYTHING

Slide 89

Slide 89 text

“ermm — are they? Why do you think that?”

Slide 90

Slide 90 text

“Because they’re just bad.”

Slide 91

Slide 91 text

awesome (adj.) — inspiring awe. Credit: pfala/Flickr

Slide 92

Slide 92 text

$ gem install awesome # this is not awesome

Slide 93

Slide 93 text

http://blog.honeybadger.io/benchmarking-ruby- refinements/ “TL;DR: Refinements aren’t slow. Or at least they don’t seem to be slower than ‘normal’ methods”

Slide 94

Slide 94 text

1. Lack of understanding?

Slide 95

Slide 95 text

No content

Slide 96

Slide 96 text

No content

Slide 97

Slide 97 text

No content

Slide 98

Slide 98 text

2. Adding using everywhere is a giant pain in the ass?

Slide 99

Slide 99 text

3. Rails (and Ruby) doesn't use them

Slide 100

Slide 100 text

$ fgrep 'refine ' -R rails | wc -l # => 0

Slide 101

Slide 101 text

$ fgrep 'refine ' -R ruby-2.2.3/ext | wc -l # => 0

Slide 102

Slide 102 text

4. Implementation quirks?

Slide 103

Slide 103 text

using Shouting "hello".shout # => “HELLO" "hello".respond_to?(:shout) # => false "hello".send(:shout) # => NoMethodError ["how", "are", "you"].map(&:shout) # => NoMethodError

Slide 104

Slide 104 text

5. Refinements solve a problem that nobody has?

Slide 105

Slide 105 text

6. The rise of Object-Oriented Design?

Slide 106

Slide 106 text

class Shouter def initialize(string) @string = string end def shout @string.upcase + "!" end end shouted_hello = Shouter.new("hello") shouted_hello.shout # => "HELLO!"

Slide 107

Slide 107 text

I don’t know. 1. Lack of understanding 2. Adding using everywhere is a pain in the ass 3. Rails (and Ruby) doesn’t use them 4. Implementation quirks 5. They solve a problem that nobody has 6. Object-oriented design

Slide 108

Slide 108 text

I don’t know. 1. Lack of understanding 2. Adding using everywhere is a pain in the ass 3. Rails (and Ruby) doesn’t use them 4. Implementation quirks 5. They solve a problem that nobody has 6. Object-oriented design 7. Because other people have told us that they are “bad”.

Slide 109

Slide 109 text

Psssst! This isn’t a talk about refinements.

Slide 110

Slide 110 text

Sharing opinions is good

Slide 111

Slide 111 text

Blindly adopting opinions is… less good

Slide 112

Slide 112 text

Dude, globes suck.

Slide 113

Slide 113 text

Awesome! Sucks.

Slide 114

Slide 114 text

“I think you’ll find it’s a bit more complicated than that.”

Slide 115

Slide 115 text

Explore for yourselves

Slide 116

Slide 116 text

Thanks! James Adam @lazyatom lazyatom.com/refinements