Slide 1

Slide 1 text

Views, from the top RubyConf AU 2019

Slide 2

Slide 2 text

@timriley

Slide 3

Slide 3 text

@timriley @icelab

Slide 4

Slide 4 text

@timriley @icelab dry-rb

Slide 5

Slide 5 text

@timriley @icelab dry-rb Hanami

Slide 6

Slide 6 text

dry-rb Hanami

Slide 7

Slide 7 text

dry-rb Hanami +

Slide 8

Slide 8 text

Views, from the top

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

No content

Slide 12

Slide 12 text

Views, from the top

Slide 13

Slide 13 text

11am meeting: Views

Slide 14

Slide 14 text

Server-rendered views

Slide 15

Slide 15 text

Things are out of hand

Slide 16

Slide 16 text

Controllers Helpers Templates Past and present:

Slide 17

Slide 17 text

Controllers Helpers Templates Past and present:

Slide 18

Slide 18 text

No content

Slide 19

Slide 19 text

No content

Slide 20

Slide 20 text

Controllers Helpers Templates

Slide 21

Slide 21 text

Controllers Helpers Templates

Slide 22

Slide 22 text

Controllers Helpers Templates Too many responsibilities

Slide 23

Slide 23 text

Controllers Helpers Templates Too many responsibilities

Slide 24

Slide 24 text

Controllers Helpers Templates Too many responsibilities Disorganised, mostly gross

Slide 25

Slide 25 text

Controllers Helpers Templates Too many responsibilities Disorganised, mostly gross Decorators?

Slide 26

Slide 26 text

Controllers Helpers Templates Too many responsibilities Disorganised, mostly gross Decorators? Friction, limited

Slide 27

Slide 27 text

Controllers Helpers Templates Too many responsibilities Disorganised, mostly gross Decorators? Friction, limited

Slide 28

Slide 28 text

Controllers Helpers Templates Too many responsibilities Disorganised, mostly gross Decorators? Friction, limited Cluttered, too much logic

Slide 29

Slide 29 text

It’s too hard to write good view code

Slide 30

Slide 30 text

Good view code

Slide 31

Slide 31 text

Good view code

Slide 32

Slide 32 text

Let’s build a better view system

Slide 33

Slide 33 text

dry-view

Slide 34

Slide 34 text

pssssst! dry-view already exists! This guy is just “acting” "

Slide 35

Slide 35 text

dry-view requirements

Slide 36

Slide 36 text

dry-view requirements • Views as objects

Slide 37

Slide 37 text

class Show < Dry::View end

Slide 38

Slide 38 text

class Show < Dry::View end view = Show.new

Slide 39

Slide 39 text

class Show < Dry::View attr_reader :article_repo def initialize(article_repo:) @article_repo = article_repo end end view = Show.new(repo: article_repo)

Slide 40

Slide 40 text

class Show < Dry::View attr_reader :article_repo def initialize(article_repo:) @article_repo = article_repo end end view = Show.new(article_repo: repo)

Slide 41

Slide 41 text

dry-view requirements • Views as objects • Explicit template locals

Slide 42

Slide 42 text

class Show < Dry::View config.template = "articles/show" expose :article do |slug:| article_repo.find_by_slug(slug) end end view = Show.new(article_repo: repo)

Slide 43

Slide 43 text

class Show < Dry::View config.template = "articles/show" expose :article do |slug:| article_repo.find_by_slug(slug) end end view = Show.new(article_repo: repo)

Slide 44

Slide 44 text

class Show < Dry::View config.template = "articles/show" expose :article do |slug:| article_repo.find_by_slug(slug) end end view = Show.new(article_repo: repo)

Slide 45

Slide 45 text

dry-view requirements • Views as objects • Explicit template locals • Templates!

Slide 46

Slide 46 text

/ Template written in Slim h1 = article.title

Slide 47

Slide 47 text

view = Show.new(…) view.call( slug: "together-breakfast", ).to_s # => "

Together breakfast

Slide 48

Slide 48 text

view = Show.new(…) view.call( slug: "together-breakfast", ).to_s # => "

Together breakfast

Slide 49

Slide 49 text

view = Show.new(…) view.call( slug: "together-breakfast", ).to_s #=> "

Together breakfast

"

Slide 50

Slide 50 text

dry-view requirements • Views as objects • Explicit template locals • Templates!

Slide 51

Slide 51 text

dry-view requirements • Views as objects • Explicit template locals • Templates!

Slide 52

Slide 52 text

dry-view requirements • Views as objects • Explicit template locals • Templates! SIMPLE templates

Slide 53

Slide 53 text

dry-view requirements • Views as objects • Explicit template locals • Templates! SIMPLE templates

Slide 54

Slide 54 text

h1 = article.title == markdown(article.body)

Slide 55

Slide 55 text

h1 = article.title == article.body_html

Slide 56

Slide 56 text

class Parts::Article < Dry::View::Part def body_html render_markdown(body) end private def render_markdown(str) # … end end

Slide 57

Slide 57 text

class Parts::Article < Dry::View::Part def body_html render_markdown(body) end private def render_markdown(str) # … end end

Slide 58

Slide 58 text

dry-view requirements • Views as objects • Explicit template locals • Simple templates • View logic on decorated values

Slide 59

Slide 59 text

dry-view requirements • Views as objects • Explicit template locals • Simple templates • View logic on decorated values • View facilities are automatic

Slide 60

Slide 60 text

class Show < Dry::View config.template = "articles/show" config.part_namespace = Parts expose :article # => Parts::Article end

Slide 61

Slide 61 text

class Show < Dry::View config.template = "articles/show" config.part_namespace = Parts expose :article # => Parts::Article end

Slide 62

Slide 62 text

dry-view requirements • Views as objects • Explicit template locals • Simple templates • View logic on decorated values • View facilities are automatic • View facilities are integrated

Slide 63

Slide 63 text

h1 = article.title == article.body_html .share-widget[ data-share-url=article.url data-share-title=article.title data-share-body=article.body_preview_text ]

Slide 64

Slide 64 text

h1 = article.title == article.body_html == render(:share_widget, url: article.url, title: article.title, preview_text: article.body_preview_text)

Slide 65

Slide 65 text

class Parts::Article < Dry::View::Part def share_widget render( :share_widget, url: url, title: title, preview_text: body_preview_text, ) end end

Slide 66

Slide 66 text

class Parts::Article < Dry::View::Part def share_widget render( :share_widget, url: url, title: title, preview_text: body_preview_text ) end end

Slide 67

Slide 67 text

h1 = article.title == article.body_html == article.share_widget

Slide 68

Slide 68 text

dry-view requirements • Views as objects • Explicit template locals • Simple templates • View logic on decorated values • View facilities are automatic • View facilities are integrated • View logic on specific templates

Slide 69

Slide 69 text

== render(:related_article, article: article)

Slide 70

Slide 70 text

- show_author = \ defined?(show_author) ? show_author : false - link_prefix = \ defined?(link_prefix) ? link_prefix : "Related: " .related-article a href=article.url = "#{link_prefix} #{article.title}" - if show_author .author …

Slide 71

Slide 71 text

- show_author = \ defined?(show_author) ? show_author : false - link_prefix = \ defined?(link_prefix) ? link_prefix : "Related: " .related-article a href=article.url = "#{link_prefix} #{article.title}" - if show_author .author …

Slide 72

Slide 72 text

- show_author = \ defined?(show_author) ? show_author : false - link_prefix = \ defined?(link_prefix) ? link_prefix : "Related: " .related-article a href=article.url = "#{link_prefix} #{article.title}" - if show_author .author … 4c391850cabe4809a6b6ab1a155529… 16 March 2016 Totally wrote this code. FIXME!

Slide 73

Slide 73 text

class Scopes::RelatedArticle < Dry::View::Scope def show_author? locals.fetch(:show_author, false) end def link_text prefix = locals.fetch(:link_prefix, "Related:") "#{prefix} #{article.title}” end end

Slide 74

Slide 74 text

class Scopes::RelatedArticle < Dry::View::Scope def show_author? locals.fetch(:show_author, false) end def link_text prefix = locals.fetch(:link_prefix, "Related:") "#{prefix} #{article.title}” end end

Slide 75

Slide 75 text

class Scopes::RelatedArticle < Dry::View::Scope def show_author? locals.fetch(:show_author, false) end def link_text prefix = locals.fetch(:link_prefix, "Related:") "#{prefix} #{article.title}” end end

Slide 76

Slide 76 text

class Scopes::RelatedArticle < Dry::View::Scope def show_author? locals.fetch(:show_author, false) end def link_text prefix = locals.fetch(:link_prefix, "Related:") "#{prefix} #{article.title}" end end

Slide 77

Slide 77 text

.related-article a href=article.url = link_text - if show_author? .author …

Slide 78

Slide 78 text

== scope(:related_article, article: article)

Slide 79

Slide 79 text

== scope(:related_article, article: article).render

Slide 80

Slide 80 text

dry-view requirements • Views as objects • Explicit template locals • Simple templates • View logic on decorated values • View facilities are automatic • View facilities are integrated • View logic on specific templates

Slide 81

Slide 81 text

dry-view requirements • Views as objects • Explicit template locals • Simple templates • View logic on decorated values • View facilities are automatic • View facilities are integrated • View logic on specific templates • Common helpers

Slide 82

Slide 82 text

class Context < Dry::View::Context def initialize(assets:, **args) @assets = assets super(**args) end def asset_path(asset_name) @assets[asset_name] end end

Slide 83

Slide 83 text

class Context < Dry::View::Context def initialize(assets:, **) @assets = assets super end def asset_path(asset_name) @assets[asset_name] end end

Slide 84

Slide 84 text

class Context < Dry::View::Context def initialize(assets:, **) @assets = assets super end def asset_path(asset_name) @assets[asset_name] end end

Slide 85

Slide 85 text

img src=asset_path("header.png") h1 = article.title == article.body_html == article.share_widget

Slide 86

Slide 86 text

class Parts::Article < Dry::View::Part def feature_image_url url = value.feature_image_url url || asset_path("article.png") end end

Slide 87

Slide 87 text

class Context < Dry::View::Context def initialize(assets:, **) @assets = assets super end def asset_path(asset_name) @assets[asset_name] end end

Slide 88

Slide 88 text

dry-view requirements • Views as objects • Explicit template locals • Simple templates • View logic on decorated values • View facilities are automatic • View facilities are integrated • View logic on specific templates • Common helpers

Slide 89

Slide 89 text

View concepts

Slide 90

Slide 90 text

View concepts • View - config, dependencies, exposures

Slide 91

Slide 91 text

View concepts • View - config, dependencies, exposures • Exposures - prepare values

Slide 92

Slide 92 text

View concepts • View - config, dependencies, exposures • Exposures - prepare values • Template & partials - markup

Slide 93

Slide 93 text

View concepts • View - config, dependencies, exposures • Exposures - prepare values • Template & partials - markup • Parts - behaviour on values

Slide 94

Slide 94 text

View concepts • View - config, dependencies, exposures • Exposures - prepare values • Template & partials - markup • Parts - behaviour on values • Scopes - behaviour for templates

Slide 95

Slide 95 text

View concepts • View - config, dependencies, exposures • Exposures - prepare values • Template & partials - markup • Parts - behaviour on values • Scopes - behaviour for templates • Context - baseline rendering environment

Slide 96

Slide 96 text

What have we learnt?

Slide 97

Slide 97 text

Views are complex

Slide 98

Slide 98 text

Views are complex

Slide 99

Slide 99 text

Views are complex View · Exposures Templates & partials Parts · Scopes · Context

Slide 100

Slide 100 text

Minimum viable views

Slide 101

Slide 101 text

Better view code

Slide 102

Slide 102 text

Making the easy thing

Slide 103

Slide 103 text

also the right thing Making the easy thing

Slide 104

Slide 104 text

Separation of concerns

Slide 105

Slide 105 text

Separation of concerns Encapsulation

Slide 106

Slide 106 text

Separation of concerns Encapsulation Immutability

Slide 107

Slide 107 text

Separation of concerns Encapsulation Immutability Testability

Slide 108

Slide 108 text

Better OOP = Better views

Slide 109

Slide 109 text

Better OOP = Better views

Slide 110

Slide 110 text

Better OOP = Joyful views

Slide 111

Slide 111 text

Better apps

Slide 112

Slide 112 text

Better apps Better Ruby

Slide 113

Slide 113 text

Diversity

Slide 114

Slide 114 text

Diversity Flexibility

Slide 115

Slide 115 text

Diversity Flexibility Innovation

Slide 116

Slide 116 text

No content

Slide 117

Slide 117 text

No content

Slide 118

Slide 118 text

dry-view!

Slide 119

Slide 119 text

@timriley dry-rb.org Thank you!

Slide 120

Slide 120 text

@timriley dry-rb.org Thank you! Say hello! Learn more!