Duration 37:53
16+
Play
Video

RailsConf 2019 - Rethinking the View Layer with Components by Joel Hawksley

Joel Hawksley
Software Engineer at GitHub
  • Video
  • Table of contents
  • Video
RailsConf 2019
May 1, 2019, Minneapolis, USA
RailsConf 2019
Request Q&A
Video
RailsConf 2019 - Rethinking the View Layer with Components by Joel Hawksley
Available
In cart
Free
Free
Free
Free
Free
Free
Add to favorites
9.64 K
I like 0
I dislike 0
Available
In cart
Free
Free
Free
Free
Free
Free
  • Description
  • Transcript
  • Discussion

About speaker

Joel Hawksley
Software Engineer at GitHub

Joel is a software engineer at GitHub. He has worked on projects including 2FA reminders and the Content Attachments API. He now leads Project Paper Cuts, incorporating feedback from members of the community into GitHub.

View the profile

About the talk

RailsConf 2019 - Rethinking the View Layer with Components by Joel Hawksley

While most of Rails has evolved over time, the view layer hasn’t changed much. At GitHub, we are incorporating the lessons of the last decade into a new paradigm: components. This approach has enabled us to leverage traditional object-oriented techniques to test our views in isolation, avoid side-effects, refactor with confidence, and perhaps most importantly, make our views first-class citizens in Rails.


Share

All right. Good morning, everyone. My name is Joel. And I work and get him. The creativity is the ability to imagine something new. It is not the ability to create something out of nothing but to generate new ideas by combining changing or reapplying existing ideas. Today we're going to do just that. We're going to take ideas from react. And reapply them in my house. And in doing so we're going to take a template that is hard to test thoroughly. Is it possible to audit with code coverage tools? Makes it difficult to reason about data flow. Enfield basic Ruby code standards

3 factored into a new experimental addition to rails called action view component. That is tested thoroughly and isolation. Is audited with code coverage tools? Only received it out and needs. unpause the code standards of the Ruby language It also happens to be over 200 times faster to past. The first what even is a view? user functions the input data and returned HTML So how is the rails relayer evolved over the years? The reality is that has been pretty stable Real Steel ships with ARB like it in 2005. In 2012 rails for at a turbo links

and in 2016 rails 5 at API mode. In the last couple of years the winds have begun to change. I think it's telling the dhh said when rails 5 was released rails is not only a great choice when you want to build a full stack application that uses server-side rendering of HTML templates, but also great companion for the new crop of client-side JavaScript or native applications to just needs the back into speed chase on The realities of the history of the Rails view layer is one of most of us moving away from her.

What is the weather look like in GitHub? We're still using yarrabee. So why isn't it have a single page app like everything else these days? The main reason I forgot to enhancement swell JavaScript makes our user experience more pleasant most of our app works without it. So why do we do this? There's a couple reasons. The first one is performance. For most of us are lucky to be using modern powerful devices. A lot of our new users are in developing countries, which means they're using low-power netbooks Chromebooks or tablets, which can buckle under have you

done the script. Another reason is browser support since we don't need JavaScript to run our site. We can simply turn it off for older browsers that are harder to develop for which makes our job description easier and cheaper to maintain So, how do we do it? We do with a couple tears of JavaScript bundles. Our first bundle for fully supported browsers just have normal normal JavaScript. It was second to Europe polyfills for those that need it and then for unsupported browsers, we only serve a smaller set of polyphenols. When we defecate browsers, we move them to these three tiers

support. What is Progressive enhancement look like in practice? Take for example posting a new comment on an issue, but the comment button we have some JavaScript intercepts The Click. And then an Ajax request return returns Domino's for the sidebar comment form in Timeline. We then inject ease resultant of the page using p Jack switch if you guys aren't familiar with it is basically turbulance. What's great about this is that if JavaScript is turned off. We just make a normal request and reload the page.

What is it like to work on music app? So I recently work on adding sticky headers to poor request an issue Pages the ship's I believe in January. So part of that project, I got to know this little piece of our user interface that we called issue badge really well. That we use that she bout to display the status of issues and poor class. We use it about a dozen or two places throughout the application. It's part of our design system that we call primer, which you can think of is basically our own version of bootstrap. Before we dig into that too much. Let's talk about our data model.

So when we're talking about issues and pork West a pork last it's just an issue with an Associated pork West object. So all pull requests are issues, but not all issues are poor. What are the issue bad for this parcel? I'll give you a second to read it. So depending on the state of the poor Quest or issue. We're under an icon label and color. These together display the state of the issue of power Quest. Just wanting to know more about the behavior of this view. I figured why don't we just delete it and see what happens? satisfied

and I pushed it up to RCI. Annabelle cast so how could this be? The reality is that things are I mean if she's a special saw aliens talk yesterday a little different in the way we use rail to get hub. You have is a rails monolith. They just turned 11 years old. foot talk about scale we have over 200 controllers not including or API. We have over 550 models not including concerns of which there's about 1,500. 8 / 3700 views so how might the scale affect our approach to testing reviews? So right now our main way of exercising review code is through controller tests that

we set up the rent a car reviews. And then our test weed it takes 6 seconds to run one of these not including any setup. Set another way that's one minute to run a suite of 10 cases. So does anyone here know how long the Jeopardy theme song is? The 30 seconds. There we go. So imagine listening to it twice every time you want to run a suite of 10 cases that are scale. This just isn't sustainable. I believe this problem is a symptom of several shortcomings in the rails. We liar. When I ask my

local Ruby group this question, the number one response is data flow. A common data flow error. We're probably all familiar with is a good all done + 134. We accidentally generate an expensive query in a few. Example code also have some data flow issues. Super Bowl request an issue. We need from each object. These are active record objects. We'd be touching their entire set of attributes when we met and Tackle. I need one or two for each object. In addition, it's unclear where the poor request in issue variables are coming from making it difficult

to reuse this partial with any amount of confidence. Another problem is that unit testing fuse is in a common practice and rails rails encourages us to test our views through integration in system tests, which are expensive. This is especially painful for partials. Like our issue badge is they often end up being tested for each of the views. They're included in which leads to duplication of tests. And I would say really cheap is the benefit of reusing a partial in the first place. another promise measuring code coverage either simple Cub or coveralls

supportive Yukon this combined with the friction of writing tests put their views in a real blind spot compared to the rest of her coat. Another weakness is the lack of a method signature. I'd like a map to Declaration on an object needs to not expose the values are expected to receive. pokebattler example What data does this mean you need to render? Does it need a pork roast? Does it need an issue? Should I be able to passing both? What about neither? What is value passing as

locals, or do they come from helper? Rihanna interviews regularly fail even the most basic standards of code quality. We expect out of our birthing classes. So it's taking a look at that example code. So this was a method on a class what aspects might be objective and I thought of you. Besides it being a super long method and I can think of a couple. Where is octagon defined? Where does the class attribute value come from it kind of feels like a magic spring? Where

are poor quest in issue coming from? The reality is that we regularly do things in our templates that we would never do in a ruby class. Surgery cap rails views are difficult to test and those tests are impossible to audit with code coverage tools out. There. They are. And make it difficult to reason. Data flow. an implicit method signatures and often feel basically because standards the reality. Is that the existing rails view layer. Is a second-class citizen these days? So with all these shortcomings perhaps it isn't

much of a surprise, but a new way of building views to taken hold in our community. react three components A component encapsulate a piece of user interface making easy to reuse. They just one way of writing hello world in a react component. three components at a minimum employment or under method that returns HTML And then argument passed to a component are assigned to the props object, which is accessible within methods on the component. But here's an example of what the issue badge might look like as react component.

Like our template the component renders an icon and label wrapped in a state-specific CSS class. Another dimension of Architecture is types. The proptypes library allows react components to express some expectations about the data they receive So in this case, we are expecting an issue with the is closed bouillon to always be provided and a pork roast is sometimes be provided. And if so would that is closed is merged and is draft bullion's. What's great about this is that we can then reference that is closed

Boolean on issue without fear Hazard type check will guarantee that it is present. Another advantage of react as how simplifies data flow. Bypassing values interviews instead of objects reacting to urges us to write functions without side effects. Another great thing about react. That's how it usually components can be tested in isolation. Futaba logo sample component Act Here's an example test that renders the component directly and then it starts against the output. What's great is that the test runs without touching the database for the

controller layer, which means that it's really really fast. Set a recap react as components that render HTML. types that give us confidence in our inputs simplify data flow I might wait texting and isolation. Which is really too bad because not compatible with our Progressive enhancement architecture that we used to get high. But what if there is a way we can incorporate some of the benefits of react and giraffes? So before we start doing some more factoring, that's right,

because you got to make sure that we're not going to break anything. So it made it look like to test review. Tony's case we're doing three things said in class meme icon and label. Put a start. Let's have some traditional controller tops for each state will start with a test for the open issue batch will certainly of The Craft class name icon and label. And I love you like I did earlier and see what happens. Repelling text which means we can actually do something Factory.

So what might a component look like in the real world? And I think would make it it would make sense to make it a class like everything else in Ruby. foot caught badge inside the issues module And how might we call it interview? The rails way would be to use the existing render syntax. So see if we can get our first test to pass. The first one method chart component that returns to open issue badge from our partial. Call HTML is render that react uses is a loaded word in action View and will run or test.

This is interesting. It looks like action view render doesn't like being passed or component imagine that. footage of how to handle it now short of 14 rails and changing the original definition of axon view base render. naked Dora monkey patch will redefine render. Symphony pops are component because their components is Kim out method. undefined method octagon remember I could have you comment about about not knowing where that occupy Medicaid from now or code

is asking us the same question. So back in a component, let's tell it where to find it. Go Runner Taxi. Now it looks like it can't find the CSS we're looking for. I wonder what are component is rendering at this point. It looks a little something like this the skates HTML. So I might be something you something like HTML safe here. I think it might be a better idea to just try and reuse the rails rendering Pipeline and all the safety guarantees it against us. So first, let's move our template into a method called template.

And then in our HTML method will run our template through action view zrb. Template Handler this effectively mirrors how regular Ruth templates are compiled and then executed in the be later today. Search for an artist again. Time to ship it, right? black people the next test this work clothes issue. We should have a red background a closed icon and close the label. Flotrack I can't find the red CFS class as we haven't handled this case yet. Let's go back to our template. Already passing an issue here.

The first we're going to need to update our monkey patch to take the argument for passing in to render and passing them into an initializer on our component. Memo needs to find component in this case. If you can remember back to the slide about the data model we're going to let pork recipe now is not all tissues have chloroplasts. Now that we have an issue. We can render it. We can reference it in our template. Sophie Runner new text screen something interesting just happened there.

We gave ourselves an interface for a view. Which means no more implicit arguments. And just like that. We're making even more progress on our coat of you. The next let's take the rest of our partial and just drop it into our component and see how the rest of the controller Test 2. We're still green. But you know something doesn't feel quite right about the pamphlet the first 2/3 handle various pull request States or the last third candle issue state. It really seems that we have two components here. Not one.

So since the view that calls are component knows whether it is dealing with a pork roast or an issue. How about we split this out into two components and let the view pick which one to use You know the time so bad. Based on whether the issue has a poor Quest we can render either the poor request badge or the issue patch. We're going to do a couple things. We need to update our conditional. Look for both components. Gloucester countertops They're still green.

Circle back to Arkadelphia Remember that comment about magic strings? both our issue badge andar por Casa badge remembering that same state UI element from our design system. So why do we make that a component? Source it UIL really just has one option color. So if it was a component. That would be a single argument. football. What state within inside the primer module? Enemalta need to update our monkey patch to handle yet another component. Do you know this is getting a little weird? Perhaps are missing an abstraction here. Or really trying to say with this line of code is MI dealing with one of

these new components. I think it might be time for a parent class. McCall action view component So take a new parent class and update our existing components to inherit from it. Then we can simplify the conditional in a monkey patch to instead just check if the argument is a subclass of action view component. Now that we have a parent class for a components. We talked to do some reducing duplication. So an easy Canada is are HTML method, which has doesn't have any component specific logic. Let's move that to ask if you can come in.

I was checking artist we're still green. Go back to build a new component. You know, this one's a little different. We're passing a content is a block. So stop by running a test. Now we could probably just rely on our existing controller test for this. Would it be nice if we could touch this new nasty component directly by itself? The react Artest were able to render a component directly and then insert against the resulting age came out and ideally we'd be able to do the same and

Rouse render the component directly and start against the resulting age came out. All we would need is a way to render template in line. Which is pretty easy to do with application controller. We can render our template in the same code path is normal view in the park the result in nokogiri, which will make those assertions a lot easier. Search Runner cast now. Looks like something that expects the class is receiving a hash. And it looks like our test helper is actually the blame here passing a hash into render. So is it turns out action views render method except

a couple different types of arguments? Three new go back to our monkey patch and update our conditional to make sure we're dealing with with a class. And then what's 300 * that's a lot closer to what we looking for. We are expecting our content to be rendered. We just got an empty string. So think about how we might make this work. When were passing content into our component, what were effectively saying is render this block in the context of the current View and then wrap the result in the component. So how made it look?

The first we're going to need to update our monkey patch took up the block argument. And then we need to update our vendor step. The first instantiate the component and then if a block has been passed render in the context of the current View and we'll do this using actions use capture Helper and then we'll find a result to an accessor on the component. At that point our component will know about the content so we can render it. Now that we're expecting all components to have this content accessor. Let's go back to action view component and declare it there. Just a

matter of taking our component. And updating the template to render the value of that content accessor. Post tracking artist go back to green. But I don't have contact. What about selling the color? So let's start by adding a color argument initializer. What values do we need to handle? If you look at our design system documentation. It looks like we can specify three green red and purple. Otherwise the component defaults to cry, but she probably seen as our new feature draft poor

class. Flakka back to work on it. Let's capture those relationships in a constant. This gives us a clear mapping between the default value and not applying CSS class and the color values and their respective CSS class. What's great? Is that the keys of our hash also represent the entirety of the values? We should allow for the color argument. So, how come you enforce this in our component? Let's start with a test. So we'll start that when passing a color. We're not expecting chartreuse. An

error will be raised and it will be raised with a message in a format. That should be suspiciously familiar top. fliptroniks Homemaker fails so how many intercolor is one of the expected values? Where in rails so this is a problem and it's called active model validations. Sabaton or component? We can use an inclusion validation to check that color is one of the keys in our concept. Make this work will need to find an attribute reader. An action view component needs include active model validation. And then that's all that's left is to go back to a monkey pouch. And a

deceptive. They are compounded before I render it. soprano Jessica Now it's out of tests to make sure make sure we're saying the right CSS class based on the color. NFL previously, we just had the CFS class hard-coded in or template. But now that we can be sure that color is one of the keys are hash. We can safely use the hash look up the correct CSS class. And use it in our tablet. Or I can bring. Ghostek note to look at that documentation. I think we might have missed something here. We're supposed to have a title attribute.

The reality is that for most of the components in our design system CSS isn't the only interface there's a lot of things that we have to take into account like first for accessibility reasons, for example But this is something original partial never accounted for so let's make sure that doesn't happen again. It'll do that with the past. The past is in an empty title and expect a validation error. I will make sure it fails. Then it's just a matter of adding and presents validation for the title attribute and right back to green. associate Harkin tour tots are doing

missing keyword title. I think we might have just caught a regression. Never updated our consumers of this new primer State component to pass on that new required title argument. So that the title attribute? Go back to green. Go to come to dataflow you're mainly concerned with arbues unintentionally queering or database. But what if we could avoid passing an active record objects all together that would eliminate that rest, right? food start with a shoe patch Right now we're passing an issue

object, which is active record. The only thing we're doing with it is calling the clothes predicate method. Since you can probably imagine our issues are issue models interface is much more than just this one method. But yeah, we're passing an entire object just to get this one value. Sophie look at the very abbreviated implementation of issue. You can just see that this close predicate method is just checking whether the value of state is closed. What minor component look like if we pass in the state value instead of the whole issue object?

So first, we need to update the initialize method to accept Estate Value instead of issue object. And out of validation for the possible values of State. But I'm looking at her template what if we could extract eat branch of this to just be derived from the value of a state. We can still Express the relationship between the state and the combination of color icon name and label. And then we can take our template. Extract the values from the constant instead of having nearly duplicate branches of our template. So, it's Runner test. We're still green.

But what about our poor cost component? I might be a couple if I could record. Looking at the template we're relying on three predicate methods merged closed and drafts. So can we pass an estate value like we did for the issue component? Looking at another abbreviated model things aren't so simple as they were for the issue model while we do have a state value whether the poor Quest is a draft or not is independent of that value. In fact, it's just stored as a Boolean in our schema. Which means we're going to need both of these values to rent

or component correctly. So start with an x. What's lucky luckily enough for ice when you have any easy way to unit Pastore components? Send his first one will start that when you pass in those State and is draft values that we're under the correct label title attribute in icon. What's my name? It looks like our component is still expecting the old argument. Let's go updated. The first one need to update the initializer to accept the state and is draft values instead of the poor request object. Then let's take our template. Extract the title

color Arctic on name and label into methods and I'll spare you the details of those right now. It was interesting here to take a good look at this component. Is that it's really similar to a reactant walk up. It's almost uncanny. 50 heart health fair back to green So remember how we act encourage simple data flow minimizing side effects. Bypassing values into are components instead of objects for seeing similar benefits. Remember how we couldn't we were unable to get code coverage

reports for abuse? Are dental lab here now has a perfect score in simple cuff and perhaps more importantly if we dig into the report we can see that all the branching exercise and those methods I spared you. The details have is being exercised. So what are you waiting for? Let's ship it crazy, right? So as it turns out we already have these components that we bring today have been in production since March. And as of last week, we're also rendering repository topics and language badges with action to come out. What do we learn from using his architecture

in production? As we Implement these components in numerous places places throughout our views be exposed several cases. We forgot to set that title attribute. Much like the test failure we've been to earlier. Rihanna is that by using standard Ruby constructs, like required arguments. We've been able to enforce the interfaces of our components and as a result not have a single standardized reusable implementation. What is UI components that makes it easier for engineers to work with our design system. What about performance?

Start at Sweet Arkansas road test take about 6 seconds for loading a page and asserting against this content. So what about these new unit test they clocked in at around twenty-five milliseconds run in the same Suite. But / 240 times faster. Set another way AR test that used to take to Jeopardy theme song to run now barely makes it past the first note. The creativity is the ability to imagine something new is not the bility to create something out of nothing but to generate new ideas by

combining changing or reapplying existing ideas. By taking ideas from react and incorporating them into rails to take a note template that was hard to test efficiently. Was impossible to audit with code coverage tools. Made it difficult to reason about data flow. Enfield basic Ruby code standards and created a new way of thinking about the view layer and rails that enables us to write efficient isolated test. That are audited the code coverage tools. only work with the values they need and follow the code standards

of the Ruby language. All these things give us a higher confidence interview layer and perhaps most importantly make it a first-class citizen in rounds. Thank you. I have six minutes for questions if anyone doesn't anyone talk about Funny story didn't see Eileen's talk yesterday. Yeah, so we have a rule I get Hub at sorry. The question the question was do we have any plans to push this into rails? So first of all, whenever we have at this point, it is the default rule of the company that you have to plan. Streaming. That is especially true if you're running a monkey past

its running a production. got Where are the push 2 Upstream? So if you go to my website, there's a demo repo that actually has the exact source code as it's running in production right now for everything. We're up today. There's some small performance tweaks that I didn't have time to cover today. They're very minor though. Sure. So the question was we have thousands of views. Where do you see the pattern making sense vs. Cracked? So I think the most obvious application is using a design system like we have or if

you're using something silly bootstrap something that use over and over and over again that I don't know we care deeply about accessibility. So I didn't consistency in the way rui is rendered is really important in that regard. So we started with our design system. But we're working to actually for the pretty specific goal, which is we have kind of elements throughout our application that are reused and the one we're working on right now is what we call the repo list item. We do it 27 different ways right now. It's literally like if you see a list of repositories that thing so we're

building our way up into that has points. That's why you see us doing repository Topics in the language pouch. We're probably about a month away from rendering all those the same way with this everywhere and they have like complexity with the pattern. There's things on the reactant side that we have yet to try and apply in that regard. I think we'll start to see that as we build the reaper last item though. I'm excited to see what we can discover as we go. Cashing

a question was do the components of forecasting. Yes so that you can inject cashing into this at this point. We do cashing outside of the component. We're rendering inside normally RP templates at this point because we're just doing small in a reusable pieces of rui that would become more important as we work our way up. So the question was how are we doing like say more interactive things? You can pick up. This is like when you click a drop-down in a dynamically load some HTML for example, very lightweight JavaScript. Keep in mind that for a lot of our like a good chunk of our users. They

don't get JavaScript. So the answer to that is as long as minimally as possible because any time we had a feature that requires JavaScript thing as some people aren't compelled to use this. So we do with little sprinkles and ideally we do it in a way that like if you don't have JavaScript enabled there still a way to accomplish that goal. He might not be able to do it as efficiently, you know, if they making a new, and not having a ball page reload, but we don't we really try and stay away from building work clothes that you can only accomplish with JavaScript. So the question was how

he looked into what the bulk of our six second run time is over headed for a test. We have somewhat I think the the short answer to that is that I really try to focus this talk on things that are universally applicable. There's a lot of things if you saw aliens talk yesterday that we do internally that are unique to our code base that are more relics then things that are Benny interest that is part of the overhead. I know part of it is just some specific dependencies we have Call Father grammar check.

Cackle comments for the website

Buy this talk

Access to the talk “RailsConf 2019 - Rethinking the View Layer with Components by Joel Hawksley”
Available
In cart
Free
Free
Free
Free
Free
Free

Access to all the recordings of the event

Get access to all videos “RailsConf 2019”
Available
In cart
Free
Free
Free
Free
Free
Free
Ticket

Interested in topic “IT & Technology”?

You might be interested in videos from this event

September 28, 2018
Moscow
16
166
app store, apps, development, google play, mobile, soft

Similar talks

Sonja Peterson
Senior Software Engineer at ActBlue Technical Services
Available
In cart
Free
Free
Free
Free
Free
Free

Buy this video

Video

Access to the talk “RailsConf 2019 - Rethinking the View Layer with Components by Joel Hawksley”
Available
In cart
Free
Free
Free
Free
Free
Free

Conference Cast

With ConferenceCast.tv, you get access to our library of the world's best conference talks.

Conference Cast
577 conferences
23287 speakers
8705 hours of content