Slide 1

Slide 1 text

Reinvesting in Ruby RubyConf AU 2017

Slide 2

Slide 2 text

Hello!

Slide 3

Slide 3 text

@timriley

Slide 4

Slide 4 text

Present Ruby Different Ruby Future Ruby

Slide 5

Slide 5 text

Present Ruby

Slide 6

Slide 6 text

We’ve come a long way

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

Rails 1.0: Party like it's one oh oh! Posted by David December 13, 2005 @ 09:02 PM

Slide 11

Slide 11 text

No content

Slide 12

Slide 12 text

Ruby 2.4.0 released (ruby-lang.org) 1. Android adopts JRuby: “It’s Java without the Java” (android.com) 2. Why Atom chose Ruby (atom.io) 3. Rubocop saved my life (medium.com) 4.

Slide 13

Slide 13 text

No content

Slide 14

Slide 14 text

No content

Slide 15

Slide 15 text

rails new rails new rails new rails new rails new rails new rails new rails new rails new rails new rails new rails new rails new rails new rails new rails new rails new rails new rails new rails new rails new rails new rails new rails new rails new rails new rails new rails new rails new rails new rails new rails new rails new rails new rails new rails new rails new rails new rails new rails new rails new rails new rails new rails new rails new rails new rails new rails new rails new rails new rails new

Slide 16

Slide 16 text

Nope

Slide 17

Slide 17 text

Challenges

Slide 18

Slide 18 text

Competition

Slide 19

Slide 19 text

Competition

Slide 20

Slide 20 text

Consolidation

Slide 21

Slide 21 text

No content

Slide 22

Slide 22 text

Transition

Slide 23

Slide 23 text

@timriley I have just realised I do not remember the last ruby I wrote — @johnbarton

Slide 24

Slide 24 text

Complacency

Slide 25

Slide 25 text

Tools

Slide 26

Slide 26 text

Culture

Slide 27

Slide 27 text

Ruby has real worth

Slide 28

Slide 28 text

Open source communities are non-exclusive

Slide 29

Slide 29 text

Open source communities are like sharks; they need to keep moving forward. — Matz

Slide 30

Slide 30 text

Reconsider Ruby

Slide 31

Slide 31 text

Ruby moves slowly

Slide 32

Slide 32 text

We’ll all get nice improvements, eventually

Slide 33

Slide 33 text

Ruby is ours

Slide 34

Slide 34 text

Ruby evolves

Slide 35

Slide 35 text

Ruby is evolving

Slide 36

Slide 36 text

Different Ruby

Slide 37

Slide 37 text

dry-rb

Slide 38

Slide 38 text

No content

Slide 39

Slide 39 text

No content

Slide 40

Slide 40 text

Code time!

Slide 41

Slide 41 text

Functional Ruby

Slide 42

Slide 42 text

Functions as values Immutability Avoid side effects

Slide 43

Slide 43 text

Functional objects

Slide 44

Slide 44 text

class CreateArticle def call(input) output = some_action output end end

Slide 45

Slide 45 text

class CreateArticle def call(input) output = some_action output end end

Slide 46

Slide 46 text

Separate data from behaviour

Slide 47

Slide 47 text

class CreateArticle attr_reader :repo def initialize(repo) @repo = repo end def call(input) repo.create(input) end end

Slide 48

Slide 48 text

# Initialize once create_article = CreateArticle.new(repo) # Reuse many times create_article.(title: "Hello Melbourne") create_article.(title: "Hello World!”)

Slide 49

Slide 49 text

# Initialize once create_article = CreateArticle.new(repo) # Reuse many times create_article.(title: "Hello Melbourne") create_article.(title: "Hello World!”)

Slide 50

Slide 50 text

12 gems, 15 minutes, 1 blog.

Slide 51

Slide 51 text

One stack of many

Slide 52

Slide 52 text

GET /articles

Slide 53

Slide 53 text

Routing & HTTP Object dependency management Views Data modelling 1 2 3 4

Slide 54

Slide 54 text

Routing & HTTP dry-web-roda GET /articles 1

Slide 55

Slide 55 text

Routing & HTTP dry-web + roda GET /articles 1

Slide 56

Slide 56 text

$ gem install dry-web-roda $ dry-web-roda new blog

Slide 57

Slide 57 text

$ gem install dry-web-roda $ dry-web-roda new blog

Slide 58

Slide 58 text

. ├── Gemfile ├── README.md ├── Rakefile ├── apps │ └── main │ ├── lib │ │ └── main │ │ └── views │ │ └── welcome.rb │ ├── system │ │ ├── boot │ │ │ └── view.rb │ │ ├── boot.rb │ │ └── main │ │ ├── application.rb │ │ ├── container.rb │ │ ├── import.rb │ │ ├── transactions.rb │ │ ├── view_context.rb │ │ └── view_controller.rb

Slide 59

Slide 59 text

class Blog::Application < Dry::Web::Roda::Application route do |r| r.get "articles" do r.view "articles.index" end end end

Slide 60

Slide 60 text

class Blog::Application < Dry::Web::Roda::Application route do |r| r.get "articles" do r.view "articles.index" end end end

Slide 61

Slide 61 text

class Blog::Application < Dry::Web::Roda::Application route do |r| r.get "articles" do r.view "articles.index" end end end

Slide 62

Slide 62 text

Object dependency management dry-system GET /articles 2

Slide 63

Slide 63 text

r.view "articles.index" Blog::Container["views.articles.index"] # # lib/blog/views/articles/index.rb

Slide 64

Slide 64 text

r.view "articles.index" Blog::Container["views.articles.index"] # # lib/blog/views/articles/index.rb

Slide 65

Slide 65 text

r.view "articles.index" Blog::Container["views.articles.index"] # # lib/blog/views/articles/index.rb

Slide 66

Slide 66 text

r.view "articles.index" Blog::Container["views.articles.index"] # # lib/blog/views/articles/index.rb

Slide 67

Slide 67 text

require $LOAD_PATH

Slide 68

Slide 68 text

Views dry-view GET /articles 3

Slide 69

Slide 69 text

class Index < Blog::ViewController configure do |config| config.template = "articles/index" end end

Slide 70

Slide 70 text

class Index < Blog::ViewController configure do |config| config.template = "articles/index" end end

Slide 71

Slide 71 text

Object dependency management dry-auto_inject GET /articles 2

Slide 72

Slide 72 text

class Index < Blog::ViewController configure do |config| config.template = "articles/index" end include Blog::Import[repo: "blog.repositories.articles"] expose :articles do repository.listing end end

Slide 73

Slide 73 text

class Index < Blog::ViewController configure do |config| config.template = "articles/index" end include Blog::Import[repo: "repositories.articles"] expose :articles do repository.listing end end

Slide 74

Slide 74 text

class Index < Blog::ViewController configure do |config| config.template = "articles/index" end include Blog::Import[repo: "repositories.articles"] expose :articles do repo.listing end end

Slide 75

Slide 75 text

Data modelling dry-types & dry-struct GET /articles 4

Slide 76

Slide 76 text

class Article < Dry::Struct attribute :title, Types::Strict::String attribute :body, Types::Strict::String attribute :published_at, Types::Strict::Time end

Slide 77

Slide 77 text

class Article < Dry::Struct attribute :title, Types::Strict::String attribute :body, Types::Strict::String attribute :published_at, Types::Strict::Time end

Slide 78

Slide 78 text

class Article < Dry::Struct attribute :title, Types::Strict::String attribute :body, Types::Strict::String attribute :published_at, Types::Strict::Time end

Slide 79

Slide 79 text

ol - articles.each do |article| li = article.title r.view “articles.index”
  1. Hello Melbourne
  2. Hello World

Slide 80

Slide 80 text

ol - articles.each do |article| li = article.title r.view "articles.index"
  1. Hello Melbourne
  2. Hello World

Slide 81

Slide 81 text

ol - articles.each do |article| li = article.title r.view "articles.index"
  1. Hello Melbourne
  2. Hello World

Slide 82

Slide 82 text

ol - articles.each do |article| li = article.title r.view "articles.index"
  1. Hello Melbourne
  2. Hello World

Slide 83

Slide 83 text

ol - articles.each do |article| li = article.title Blog::Container["views.articles.index"].()
  1. Hello Melbourne
  2. Hello World

Slide 84

Slide 84 text

class Blog < Dry::Web::Application route do |r| r.get "articles" do r.view "articles.index" end end end

Slide 85

Slide 85 text

GET /articles

Slide 86

Slide 86 text

Routing & HTTP dry-web-roda Object dependency management dry-system & dry-auto_inject Views dry-view Data modelling dry-types & dry-struct 1 2 3 4

Slide 87

Slide 87 text

POST /articles

Slide 88

Slide 88 text

Validation Success & error handling 1 2

Slide 89

Slide 89 text

class MyApp < Dry::Web::Roda::Application route do |r| r.get "articles" do r.view "articles.index" end r.post "articles" do r.resolve "operations.create_article" do |create| create.(r[:article]) end end end end

Slide 90

Slide 90 text

class MyApp < Dry::Web::Roda::Application route do |r| r.get "articles" do r.view "articles.index" end r.post "articles" do r.resolve "operations.create_article" do |create| create.(r[:article]) end end end end

Slide 91

Slide 91 text

class MyApp < Dry::Web::Roda::Application route do |r| r.get "articles" do r.view "articles.index" end r.post "articles" do r.resolve "operations.create_article" do |create| create.(r[:article]) end end end end

Slide 92

Slide 92 text

class MyApp < Dry::Web::Roda::Application route do |r| r.get "articles" do r.view "articles.index" end r.post "articles" do r.resolve "operations.create_article" do |create| create.(r[:article]) end end end end

Slide 93

Slide 93 text

r.resolve "operations.create_article" Blog::Container["operations.create_article"] # # lib/blog/operations/create_article.rb

Slide 94

Slide 94 text

r.resolve "operations.create_article" Blog::Container["operations.create_article"] # # lib/blog/operations/create_article.rb

Slide 95

Slide 95 text

r.resolve "operations.create_article" Blog::Container["operations.create_article"] # # lib/blog/operations/create_article.rb

Slide 96

Slide 96 text

r.resolve "operations.create_article" Blog::Container["operations.create_article"] # # lib/blog/operations/create_article.rb

Slide 97

Slide 97 text

class CreateArticle def call(input) result = Validation::ArticleSchema.(input) end end

Slide 98

Slide 98 text

class CreateArticle def call(input) result = Validation::ArticleSchema.(input) end end

Slide 99

Slide 99 text

Validation dry-validation POST /articles 1

Slide 100

Slide 100 text

module Validation ArticleSchema = Dry::Validation.Form do required(:title).filled(min_size?: 3) required(:body).filled required(:published).filled(:bool?) required(:published_at).filled(:time?) end end

Slide 101

Slide 101 text

module Validation ArticleSchema = Dry::Validation.Form do required(:title).filled(min_size?: 3) required(:body).filled required(:published).filled(:bool?) required(:published_at).filled(:time?) end end

Slide 102

Slide 102 text

module Validation ArticleSchema = Dry::Validation.Form do required(:title).filled(min_size?: 3) required(:body).filled required(:published).filled(:bool?) required(:published_at).filled(:time?) end end

Slide 103

Slide 103 text

module Validation ArticleSchema = Dry::Validation.Form do required(:title).filled(min_size?: 3) required(:body).filled required(:published).filled(:bool?) required(:published_at).filled(:time?) end end

Slide 104

Slide 104 text

module Validation ArticleSchema = Dry::Validation.Form do required(:title).filled(min_size?: 3) required(:body).filled required(:published).filled(:bool?) required(:published_at).filled(:time?) end end

Slide 105

Slide 105 text

input = { "title" => "Hello Melbourne", "body" => "Why I always forget my umbrella", "published" => "1", "published_at" => "2017-02-09 09:50" } ArticleSchema.(input).to_h { title: "Hello Melbourne", body: "Why I always forget my umbrella" published: true, published_at: 2017-02-09 09:50:00 +1100 }

Slide 106

Slide 106 text

input = { "title" => "Hello Melbourne", "body" => "Why I always forget my umbrella", "published" => "1", "published_at" => "2017-02-09 09:50" } ArticleSchema.(input).to_h { title: "Hello Melbourne", body: "Why I always forget my umbrella" published: true, published_at: 2017-02-09 09:50:00 +1100 }

Slide 107

Slide 107 text

input = { "title" => "Hello Melbourne", "body" => "Why I always forget my umbrella", "published" => "1", "published_at" => "2017-02-09 09:50" } ArticleSchema.(input).to_h { title: "Hello Melbourne", body: "Why I always forget my umbrella" published: true, published_at: 2017-02-09 09:50:00 +1100 }

Slide 108

Slide 108 text

input = { "title" => "Hello Melbourne", "body" => "Why I always forget my umbrella", "published" => "1", "published_at" => "2017-02-09 09:50" } ArticleSchema.(input).to_h { title: "Hello Melbourne", body: "Why I always forget my umbrella" published: true, published_at: 2017-02-09 09:50:00 +1100 }

Slide 109

Slide 109 text

input = { "title" => "Hello Melbourne", "body" => "Why I always forget my umbrella", "published" => "1", "published_at" => "2017-02-09 09:50" } ArticleSchema.(input).to_h { title: "Hello Melbourne", body: "Why I always forget my umbrella" published: true, published_at: 2017-02-09 09:50:00 +1100 }

Slide 110

Slide 110 text

ArticleSchema.("published" => "1").messages { :title => [ "is missing", "size cannot be less than 3"], :body => ["is missing"], :published_at => ["is missing"] }

Slide 111

Slide 111 text

ArticleSchema.("published" => "1").messages { :title => [ "is missing", "size cannot be less than 3"], :body => ["is missing"], :published_at => ["is missing"] }

Slide 112

Slide 112 text

class CreateArticle include Blog::Import[repo: "repositories.articles"] def call(input) validation = Validation::ArticleSchema.(input) if validation.success? result = repo.create(validation) Article.new(result) end end end

Slide 113

Slide 113 text

class CreateArticle include Blog::Import[repo: "repositories.articles"] def call(input) validation = Validation::ArticleSchema.(input) if validation.success? repo.create(validation) end end end

Slide 114

Slide 114 text

class CreateArticle include Blog::Import[repo: "repositories.articles"] def call(input) validation = Validation::ArticleSchema.(input) if validation.success? repo.create(validation) end end end

Slide 115

Slide 115 text

Success & failure handling dry-monads & dry-matcher POST /articles 2

Slide 116

Slide 116 text

def call(input) validation = Validation::ArticleSchema.(input) if validation.success? result = articles.create(validation) Right(result) end end

Slide 117

Slide 117 text

def call(input) validation = Validation::ArticleSchema.(input) if validation.success? result = articles.create(validation) Right(result) end end

Slide 118

Slide 118 text

def call(input) validation = Validation::ArticleSchema.(input) if validation.success? result = articles.create(validation) Right(result) else Left(validation) end end

Slide 119

Slide 119 text

r.post do r.resolve "operations.create_article" do |create| create.(r[:article]) m.success do r.redirect "/articles" end m.failure do |validation| r.view "articles.new", validation: validation end end end end

Slide 120

Slide 120 text

r.post do r.resolve "operations.create_article" do |create| create.(r[:article]) do |m| m.success do r.redirect "/articles" end m.failure do |validation| r.view "articles.new", validation: validation end end end end

Slide 121

Slide 121 text

r.post do r.resolve "operations.create_article" do |create| create.(r[:article]) do |m| m.success do r.redirect "/articles" end m.failure do |validation| r.view "articles.new", validation: validation end end end end

Slide 122

Slide 122 text

r.post do r.resolve "operations.create_article" do |create| create.(r[:article]) do |m| m.success do r.redirect "/articles" end m.failure do |validation| r.view "articles.new", validation: validation end end end end

Slide 123

Slide 123 text

r.post do r.resolve "operations.create_article" do |create| create.(r[:article]) do |m| m.success do r.redirect "/articles" end m.failure do |validation| r.view "articles.new", validation: validation end end end end

Slide 124

Slide 124 text

POST /articles

Slide 125

Slide 125 text

Validation dry-validation Success & error handling dry-monads & dry-matcher 1 2

Slide 126

Slide 126 text

dry-rb

Slide 127

Slide 127 text

dry-rb

Slide 128

Slide 128 text

Change

Slide 129

Slide 129 text

New feature Email articles to subscribers

Slide 130

Slide 130 text

dry-transaction

Slide 131

Slide 131 text

Blog::Transactions.define do |t| t.define "transactions.create_article" do step :create, with: "operations.create_article" step :notify, with: "operations.notify_subscribers" end end

Slide 132

Slide 132 text

Blog::Transactions.define do |t| t.define "transactions.create_article" do step :create, with: "operations.create_article" step :notify, with: "operations.notify_subscribers" end end

Slide 133

Slide 133 text

Blog::Transactions.define do |t| t.define "transactions.create_article" do step :create, with: "operations.create_article" step :notify, with: "operations.notify_subscribers" end end

Slide 134

Slide 134 text

Blog::Transactions.define do |t| t.define "transactions.create_article" do step :create, with: "operations.create_article" step :notify, with: "operations.notify_subscribers" end end

Slide 135

Slide 135 text

create_article.(r[:article]) do |m| m.success do r.redirect "/articles" end m.failure :create do |errors| r.view "articles.new", validation: errors end m.failure :notify do |error| # ... end end

Slide 136

Slide 136 text

create_article.(r[:article]) do |m| m.success do r.redirect "/articles" end m.failure :create do |errors| r.view "articles.new", validation: errors end m.failure :notify do |error| # ... end end

Slide 137

Slide 137 text

create_article.(r[:article]) do |m| m.success do r.redirect "/articles" end m.failure :create do |errors| r.view "articles.new", validation: errors end m.failure :notify do |error| # ... end end

Slide 138

Slide 138 text

create_article.(r[:article]) do |m| m.success do r.redirect "/articles" end m.failure :create do |errors| r.view "articles.new", validation: errors end m.failure :notify do |error| # ... end end

Slide 139

Slide 139 text

Change

Slide 140

Slide 140 text

Isolated testing

Slide 141

Slide 141 text

Simplicity

Slide 142

Slide 142 text

Clear separation of concerns

Slide 143

Slide 143 text

Clear flow of data

Slide 144

Slide 144 text

Design around boundaries

Slide 145

Slide 145 text

rom-rb

Slide 146

Slide 146 text

Design for the domain

Slide 147

Slide 147 text

Ruby, melting pot

Slide 148

Slide 148 text

A new kind of Ruby

Slide 149

Slide 149 text

A change-positive architecture

Slide 150

Slide 150 text

A change-positive app is maintainable

Slide 151

Slide 151 text

A change-positive app is sustainable

Slide 152

Slide 152 text

A change-positive app is joyful

Slide 153

Slide 153 text

dry-rb

Slide 154

Slide 154 text

dry-rb Icelab

Slide 155

Slide 155 text

Levelling up

Slide 156

Slide 156 text

Look Sandi Metz in the eye again

Slide 157

Slide 157 text

Levelling up

Slide 158

Slide 158 text

Better developers, better executing team

Slide 159

Slide 159 text

Ship without guilt

Slide 160

Slide 160 text

This is our future Ruby

Slide 161

Slide 161 text

Future Ruby
 for small teams

Slide 162

Slide 162 text

Future Ruby
 for large organisations

Slide 163

Slide 163 text

Let's say every company gets three innovation tokens. You can spend these however you want, but the supply is fixed. — Dan McKinley

Slide 164

Slide 164 text

Optimize globally

Slide 165

Slide 165 text

Deliver faster

Slide 166

Slide 166 text

Future Ruby
 for learners

Slide 167

Slide 167 text

Future Ruby
 for learners

Slide 168

Slide 168 text

Future Ruby
 in OSS

Slide 169

Slide 169 text

Hanami

Slide 170

Slide 170 text

Hanami dry-validation &
 rom-rb

Slide 171

Slide 171 text

No content

Slide 172

Slide 172 text

Trailblazer dry-validation &
 dry-container/auto_inject

Slide 173

Slide 173 text

More to Ruby than you think

Slide 174

Slide 174 text

A healthy future for Ruby is diverse

Slide 175

Slide 175 text

if not us then "who?" end

Slide 176

Slide 176 text

Curiosity

Slide 177

Slide 177 text

Criticism

Slide 178

Slide 178 text

Cooperation

Slide 179

Slide 179 text

Cooperation

Slide 180

Slide 180 text

Celebration

Slide 181

Slide 181 text

Curiosity Criticism Cooperation Celebration!

Slide 182

Slide 182 text

Reinvest in Ruby

Slide 183

Slide 183 text

No content

Slide 184

Slide 184 text

Thank you!