Slide 1

Slide 1 text

! Hello!!! Welcome to the second day of Ruby Conf Nairobi 2019. Welcome! Thank you for coming for my talk.

Slide 2

Slide 2 text

Prathamesh Sonpatki Pune Part of Ruby community Rails issues team. Marathon Kenya beat west indies in Pune in 1996. It was big upset.

Slide 3

Slide 3 text

Prathamesh Sonpatki Memory.ai timelyapp.com Work at Memory. AI Powered tools to solve the problem of abuses of time and help everyone produce meaningful work.

Slide 4

Slide 4 text

Prathamesh Sonpatki prathamesh.tech Blog on Rails, Ruby Emacs

Slide 5

Slide 5 text

Prathamesh Sonpatki Pokemon Go! Become my friend, meet me after my talk and let’s catch some pokemon.

Slide 6

Slide 6 text

" NaiRuby First of all I would like to say Thank you!

Slide 7

Slide 7 text

Bollywood Chef story about singing

Slide 8

Slide 8 text

We don’t do that here We don’t do it in real life. It is only in movies. Discussion about movies at speaker dinner.

Slide 9

Slide 9 text

prathamesh.tech/movies @_cha1tanya So On popular demand list of movies you should watch, You can tweet me and I will add that movie to the list.

Slide 10

Slide 10 text

State of the art of managing assets in new Rails world Alright, let’s talk about the actual topic now.

Slide 11

Slide 11 text

Rails 6 Rails 6 is just around the corner. The release candidate two was released just two days back. As this is a major release, it is packed with so many interesting features.

Slide 12

Slide 12 text

Rails 6 Action Text for rich text editor support Action MailBox for accepting emails in our web app

Slide 13

Slide 13 text

Rails 6 Multi database support which is extracted from Github Parallel testing support

Slide 14

Slide 14 text

We are going to talk today about a significant change which impacts the way we manage our frontend code. It is about webpacker.

Slide 15

Slide 15 text

The relationship between Rails and JavaScript has been like Tom and Jerry. They don’t love each other fully but also have to make peace with each other because they have to live together.

Slide 16

Slide 16 text

Rails 1 RJS How many of you have used Rails 1? It had a Ruby to JS template system where you write Ruby which generates JavaScript which is evaled by browser in a response to an Ajax request.

Slide 17

Slide 17 text

Rails 3.1+ Asset Pipeline In Rails 3.1 asset pipeline was introduced to solve the problem of managing assets.

Slide 18

Slide 18 text

The release notes rightly mentioned that time that now JavaScript and other assets are integral part of the application.

Slide 19

Slide 19 text

Asset pipeline The asset pipeline provided a framework to concatenate and minify or compress JavaScript and CSS assets.

Slide 20

Slide 20 text

Asset pipeline It also added the ability to write these assets in other languages and pre-processors such as CoffeeScript, Sass and ERB.

Slide 21

Slide 21 text

Asset pipeline It allowed assets in your application to be automatically combined with assets from other gems. Remember jquery-rails or bootstrap-rubygem or underscore-rails?

Slide 22

Slide 22 text

BUT there is always but

Slide 23

Slide 23 text

But the web development landscape was changing extremely fast. JavaScript was the driving force for almost all of the web applications.

Slide 24

Slide 24 text

Node js was initially released in 2009. It allowed us to write server side javascript

Slide 25

Slide 25 text

React was released in 2013 and changed everything, almost!

Slide 26

Slide 26 text

Interestingly webpack was released before React! In 2012.

Slide 27

Slide 27 text

ES6 which made writing javascript much simpler was difficult to use in Rails apps by default.

Slide 28

Slide 28 text

Rails was also catching up. Sprockets was able to understand ES6 syntax though it was experimental.

Slide 29

Slide 29 text

Asset pipeline People used different workaround to take advantage of latest JS ecosystem. Using CDN builds was one option. Using rails-assets.org with bower was one option that used for some time. There were also unofficial integrations with browserify or webpack

Slide 30

Slide 30 text

Asset pipeline Sprockets was able to understand ES6 syntax though it was experimental.

Slide 31

Slide 31 text

Asset pipeline But it was always going to be behind the JavaScript train. It was like we are wrapping the JavaScript world in a Ruby gem and the Javascript world just exploded.

Slide 32

Slide 32 text

Asset pipeline But it was always going to be behind the JavaScript train. It was like we are wrapping the JavaScript world in a Ruby gem and the Javascript world just exploded.

Slide 33

Slide 33 text

let’s flip the problem. Instead of fighting with JS let’s embrace it. That’s what Rails team did. To understand how they did it we need to understand what sprockets does for us.

Slide 34

Slide 34 text

Asset pipeline Essentially sprockets does two things for us. Managing all of the dependencies in our code and then packaging them so that a single file can be deployed after compiling and running all the post processing tools.

Slide 35

Slide 35 text

Asset pipeline Dependency management Essentially sprockets does two things for us. Managing all of the dependencies in our code and then packaging them so that a single file can be deployed after compiling and running all the post processing tools.

Slide 36

Slide 36 text

Asset pipeline Dependency management Modularization Essentially sprockets does two things for us. Managing all of the dependencies in our code and then packaging them so that a single file can be deployed after compiling and running all the post processing tools.

Slide 37

Slide 37 text

Dependency management JavaScript world had support for package management using npm.

Slide 38

Slide 38 text


 # app/assets/javascripts/application.js //= require moment-rails //= require jquery-rails //= require underscore-rails In theory everyone should be using //= require statement at the top of their file to declare the dependencies of their file, but in my experience this starts breaking down in big projects and people rely on certain things always being available. We are basically relying on globals to be available so that the concatenation works.

Slide 39

Slide 39 text

$ npm i moment Npm - package manager Slow unreliable Can/was shoot you in the foot

Slide 40

Slide 40 text

Remember deleting node_modules and then regenerating will break your app? It was a mess

Slide 41

Slide 41 text

Facebook was working on a package manager around that time, it was released as yarn. Yarn tried to solve problems caused by npm like lockfile, reliability.

Slide 42

Slide 42 text

yarn Rails 5.1+ Rails app and sprockets were able to understand package.json Yarn had proper lock file like bundler and it was created facebook to solve the unreliability with npm. Though npm improved in latter releases.

Slide 43

Slide 43 text

Modularization code organized in multiple files to a user's web browser. We also want to use third party libraries like jQuery. The way bundling JavaScript works in Sprockets is that it concatenates all the files together, relying on the programmer to assign and reference global variables in each file,

Slide 44

Slide 44 text

# app/assets/javascripts/ application.js //= require jquery-rails

Slide 45

Slide 45 text

# app/assets/javascripts/make_coffee.js $(“#coffe_maker”).click( {} ); jQuery is available globally. In theory everyone should be using //= require statement at the top of their file to declare the dependencies of their file, but in my experience this starts breaking down in big projects and people rely on certain things always being available. We are basically relying on globals to be available so that the concatenation works.

Slide 46

Slide 46 text

There are some problems with this. Whether to use third party libs directly in script tags or in vendor files or in my bundle? I

Slide 47

Slide 47 text

Not possible to write unit tests as everything relies on global scope. Not easy to use new hot things like Typescript

Slide 48

Slide 48 text

Modules What we need is ability to create modules and use them with each other to manage different parts of code. But having module system is not the only challenge, browsers also need to understand it. And we know that browsers heavily rely on global objects. Let’s see an example.

Slide 49

Slide 49 text

// version.js module.exports = { version: ‘1.0’ } This simple code shows us how modules can be defined. This works fine in server side code. But to make this work with browsers we need tools that will resolve this require or import calls and make browsers understand them.

Slide 50

Slide 50 text

// version.js module.exports = { version: ‘1.0’ } // application.js var config = require(‘./version.js’) console.log(config.version) We can use the module like this.

Slide 51

Slide 51 text

That’s where web pack comes into the picture. Webpack gives us ability to modularize the code. It also implements a way to translate JavaScript code that doesn't work in any web browser to JavaScript code that works in most web browsers.

Slide 52

Slide 52 text

There are other alternatives like browserify as well but web pack can do much more.

Slide 53

Slide 53 text

https://2018.stateofjs.com/other-tools/ Webpack is clear winner here.

Slide 54

Slide 54 text

Webpack has tons of configurations to do lot of things. So it is hard to use and hard to understand. But nothing in Webpack “just works”—you have to provide configuration for Webpack's most basic features to work.

Slide 55

Slide 55 text

Make Webpack Rails friendly This means we'll be spending a lot of time in documentation and a lot of time making decisions that have nothing to do with our users or the problems we're trying to solve for them. As a consequence, it is fairly complex to configure from scratch making it somewhat of an odd choice for Rails, which promotes convention over configuration. Webpacker fills the gap.

Slide 56

Slide 56 text

That’s why we need webpacker. It is glue between webpack and webpacker.

Slide 57

Slide 57 text

The gem introduces some conventions and abstracts away a default configuration to make it easier to get up-and-running with

Slide 58

Slide 58 text

It also forms bridge between web pack and rails app so that we can include the output generated by webpack in the Rails app as we will see shortly.

Slide 59

Slide 59 text

rails new my_app —webpack Rails 5.1+ When it was introduced in Rails 5.1, We can create a new Rails app with web pack to manage the JavaScript code like this.

Slide 60

Slide 60 text

rails new my_app Rails 6 In Rails 6, that’s it. Webpack is enabled by default.

Slide 61

Slide 61 text

rails webpacker:install create config/webpacker.yml Copying webpack core config create config/webpack create config/webpack/development.js create config/webpack/environment.js create config/webpack/production.js create config/webpack/test.js When web packer is installed, it sets up common conventions and configurations for different environments and default options for web pack.

Slide 62

Slide 62 text

yarn add v1.17.3 info No lockfile found. [1/4] Resolving packages... ⠁ @rails/actioncable@^6.0.0-alpha warning @rails/webpacker > postcss-preset-env > postcss- color-functional-notation > postcss-values-parser > [email protected]: I wrote this module a very long time ago; you should use something else. [2/4] Fetching packages... [3/4] Linking dependencies... [4/4] Building fresh packages... success Saved lockfile. success Saved 602 new dependencies. info Direct dependencies !" @rails/[email protected] !" @rails/[email protected] !" @rails/[email protected] !" @rails/[email protected] #" [email protected] It also sets up required npm packages using yarn. This includes all packages that Rails itself depends on.

Slide 63

Slide 63 text

Yay!

Slide 64

Slide 64 text

app/javascript It also creates a new directory app/javascript which hosts our javascript code now. Earlier it was app/assets/javascripts now it is app/javascript. You might be wondering why the javascript in the directory name instead of assets or say javascripts?

Slide 65

Slide 65 text

Because it is part of the application written in JavaScript the language as DHH says here. It is very significant statement as it acknowledges javascript part of the application as equal partner in the relationship.

Slide 66

Slide 66 text

app/javascript But Also the name suggests, The web packer is only meant to manage our JavaScript code not the other assets like font/images. They are still managed by asset pipeline like before. In that way, we still have assets pipeline enabled in Rails 6 but only for non-javascript assets.

Slide 67

Slide 67 text

Entry point Webpack entry point is where web pack looks to start building the bundle The convention here is that all webpack entry points should be placed in the app/*javascript/*packs folder and the modules can be placed inside app/*javascript folder in any way you like.

Slide 68

Slide 68 text

app/javascript/packs Webpack entry point is where web pack looks to start building the bundle The convention here is that all webpack entry points should be placed in the app/*javascript/*packs folder and the modules can be placed inside app/*javascript folder in any way you like.

Slide 69

Slide 69 text

app/javascript/packs/admin.js app/javascript/packs/client.js Separate packs for separate pages and web pack will create bundles automatically. We don’t need to include them in config.assets_precompile list

Slide 70

Slide 70 text

app/javascript/packs/application.js Rails by default creates application pack. This is equivalent to application.js created with sprockets. // This file is automatically compiled by Webpack, along with any other files // present in this directory. You're encouraged to place your actual application // logic in a relevant structure within app/javascript and only use these pack files to // reference that code so it'll be compiled. This configuration is provided by webpacker by default.

Slide 71

Slide 71 text

The webpacker way Let’s see how we can organize code with webpacker

Slide 72

Slide 72 text

app/javascript/util #"" index.js Let’s say we have our common JavaScript code in a util directory.

Slide 73

Slide 73 text

# app/javascript/packs/application.js import 'common/util'; We can include it in our application pack like this. This will tell webpack to load it while preparing the bundle.

Slide 74

Slide 74 text

Rendering it on page The next step is making sure that we include the bundle generated by web pack in our HTML page.

Slide 75

Slide 75 text

<%= javascript_pack_tag 'application', 'data-turbolinks-track': ‘reload' %> We can tell Rails to include the bundle generated by webpack by using a pack tag. This helper method is provided by webpacker and takes care of inserting the JavaScript into our page similar to javascript_link_tag

Slide 76

Slide 76 text

stylesheet_pack_tag Also comes with this tho optional.

Slide 77

Slide 77 text

Yay! We are using modern frontend JavaScript finally. We are no longer lacking behind.

Slide 78

Slide 78 text

Webpacker provides out of the box integrations with most of the popular JS frameworks.

Slide 79

Slide 79 text

bundle exec rails webpacker:install:react Webpacker provides out of the box integrations with most of the popular JS frameworks.

Slide 80

Slide 80 text

bundle exec rails webpacker:install:vue Webpacker provides out of the box integrations with most of the popular JS frameworks. It supports elm, vue, stimulus.

Slide 81

Slide 81 text

Webpacker provides out of the box integrations with most of the popular JS frameworks. It supports elm, vue, stimulus.

Slide 82

Slide 82 text

Existing app What about existing apps tho?

Slide 83

Slide 83 text

Webpacker makes webpack work along side assets pipeline.

Slide 84

Slide 84 text

gem ‘webpacker’, ‘~> 4.0’

Slide 85

Slide 85 text

bin/rails webpacker:install

Slide 86

Slide 86 text

+ So if you have existing legacy code, then you can start adding new code under app/javascript and keep old code as it is.

Slide 87

Slide 87 text

+ Or you can start migrating some of the legacy code to webpack and then remove it from assets pipeline.

Slide 88

Slide 88 text

Moving deps What about existing apps tho?

Slide 89

Slide 89 text

gem “momentjs-rails" //= require momentjs

Slide 90

Slide 90 text

moment().calendar()

Slide 91

Slide 91 text

import moment from ‘moment'; moment().calendar() Need to include it everywhere moment is used. Because global scope does not work.

Slide 92

Slide 92 text

Say NO to Global scope!

Slide 93

Slide 93 text

Say NO to Global scope! I will say it once more.

Slide 94

Slide 94 text

Moving application code You can avoid it if possible, it will continue to work!

Slide 95

Slide 95 text

app/assets/javascripts/hello.js Need to include it everywhere moment is used. Because global scope does not work.

Slide 96

Slide 96 text

app/javascript/hello.js Need to include it everywhere moment is used. Because global scope does not work.

Slide 97

Slide 97 text

# app/javascript/packs/application.js import ‘hello’ Need to include it everywhere moment is used. Because global scope does not work.

Slide 98

Slide 98 text

Working with directories

Slide 99

Slide 99 text

require_tree ‘./components’

Slide 100

Slide 100 text

require_context It allows you to pass in a directory to search, a flag indicating whether subdirectories should be searched too, and a regular expression to match files against.

Slide 101

Slide 101 text

Exposing globals Globals from packages as well as from your code When you move one module from

Slide 102

Slide 102 text

# app/javascript/hello.js export default Hello; Let’s say we moved hello module to webpack.

Slide 103

Slide 103 text

# app/javascript/hello.js export default Hello; # app/javascript/packs/application.js import Hello from ‘./hello.js’; But hello is only available when you import it.

Slide 104

Slide 104 text

# app/assets/javascripts/greeting.js App.Hello.sayHello() But then some of the legacy code still uses it.

Slide 105

Slide 105 text

Deployment Webpacker makes deployment mostly seamless.

Slide 106

Slide 106 text

bundle exec rails assets:precompile Rails provides a rake task to precompile assets from asset pipeline. Webpacker adds webpacker:compile task to the assets:precompile task. It gets run every time you run assets:precompile.

Slide 107

Slide 107 text

brew install yarn Yarn will be needed on the server we are trying to deploy, Nodes as well Heroku does this out of the box. So it just works. It also supports docker, cloud9, capistrano

Slide 108

Slide 108 text

Tools

Slide 109

Slide 109 text

Hot module replacement Live reload with dev-server refresh driven development

Slide 110

Slide 110 text

./bin/webpack-dev-server Webpacker provides a script to run web pack Dev server. This enables Live code reloading

Slide 111

Slide 111 text

▶ ./bin/webpack-dev-server ℹ 「wds」: Project is running at http://localhost:3035/ ℹ 「wds」: webpack output is served from /packs/ ℹ 「wds」: Content not from webpack is served from /Users/prathamesh/ Projects/scratch/blog/public/packs ℹ 「wds」: 404s will fallback to /index.html ℹ 「wdm」: Hash: f8c6a1b1755dc32681d6 Version: webpack 4.36.1 Time: 3240ms Built at: 07/23/2019 3:13:42 PM Asset Size Chunks Chunk Names js/application-2d8c7ac24fdd30370b2e.js 504 KiB application [emitted] application js/application-2d8c7ac24fdd30370b2e.js.map 568 KiB application [emitted] application manifest.json 364 bytes [emitted] ℹ 「wdm」: Compiled successfully. Compiles on demand,

Slide 112

Slide 112 text

Fancy features

Slide 113

Slide 113 text

Source maps Enabled by default in production

Slide 114

Slide 114 text

Tree shaking Tree shaking is fancy term for dead code elimination. Enabled by default by webpacker.

Slide 115

Slide 115 text

Code splitting Code splitting is one of the most compelling features of webpack. This feature allows you to split your code into various bundles which can then be loaded on demand or in parallel. It can be used to achieve smaller bundles and control resource load prioritization which, if used correctly, can have a major impact on load time. This also works with web packer as it allows multiple entry points.

Slide 116

Slide 116 text

Rails has fully embraced js for js Time for you to invest some time in learning webpack. It is not one to one mapping between assets and webpack. When to use webpacker and when to use sprockets. What web pack brings to the table Modularization

Slide 117

Slide 117 text

asante prathamesh.tech/webpacker @_cha1tanya

Slide 118

Slide 118 text

asante prathamesh.tech/webpacker prathamesh.tech/movies @_cha1tanya