Talks

Easily Create Interactive UIs in Pure Ruby

Ruby Unconf 2019

00:00:02.610 Are you ready for the next talk? I want to introduce our next speaker.
00:00:09.510 His topic is Mate Stack UI, and I want to give a huge shout-out to him.
00:00:16.500 Thank you! Ok, hi everyone.
00:00:24.439 I want to introduce you to Mate Stack UI. If you don't know what Mate Stack is, I'm going to change that during this talk.
00:00:31.649 The goal is to rapidly create a dynamic web UI in pure Ruby. I pitched it this morning, and I'm going to give a longer version of the pitch right now.
00:00:37.829 First of all, I'm Jonas. I studied mechanical engineering and taught myself software engineering during my studies.
00:00:43.230 I fell in love with software engineering, but if I take a moment, you might wonder why that is.
00:00:51.329 I didn't study computer science, so I have a very high-level view on software engineering.
00:00:56.820 You might have noticed that I'm a co-founder of Base Mate, a software service agency.
00:01:03.120 I'm the creative mastermind, and that's why I wanted to introduce Mate Stack today.
00:01:10.800 Now, I have some questions. Please raise your hands: Who of you loves to write HTML?
00:01:18.030 Ok, who loves to write CSS?
00:01:27.030 And who loves to create a JavaScript mess for some dynamic behavior?
00:01:39.150 Oh yeah, that's quite a few hands! And who has to implement Ruby business logic?
00:01:48.810 Exactly, it's a tough situation.
00:01:54.360 I didn't expect so many hands for HTML and CSS! So, why this talk?
00:02:05.190 I should have looked there; by the way, this is my first talk in front of an audience with this type of microphone.
00:02:11.640 Let's have a look at how a Rails developer faces the challenge of creating a dynamic user interface.
00:02:17.560 We'll examine how much Ruby can be used while facing this challenge.
00:02:29.980 We model the database, use Active Record, and it's beautiful.
00:02:35.530 We love it! The definition and the API are fantastic; many people adore them.
00:02:43.230 We use controllers, such as the application controller, and everything just works.
00:02:49.270 The code looks nice and is highly maintainable.
00:02:54.840 In my opinion, it's also very beautiful.
00:03:02.080 We use gems like Devise to set up authentication; we all know that.
00:03:08.080 You can connect it to your controller, and the architecture is clean and maintainable.
00:03:16.830 It is stunning.
00:03:23.050 Then, personally, I use Pundit for authorization. There are multiple gems for that.
00:03:32.050 We can handle more than just authentication; we also manage authorization beautifully.
00:03:37.680 And we define routes according to what Rails has to offer, which is also beautiful.
00:03:46.750 You can accomplish a lot and perform many magic tasks, saving time and delivering applications quickly.
00:03:55.470 Everything looked good until now, but then the view layer comes into play.
00:04:01.750 That's at least my reaction when I start implementing the view layer.
00:04:12.580 So, you essentially have two options: you can write .erb or humble views.
00:04:18.940 You can manage to avoid writing proper Ruby while adding some JavaScript for dynamic behavior.
00:04:30.400 However, this method often leads to a poorly structured view layer.
00:04:38.520 At least it's part of your Rails application.
00:04:44.139 Alternatively, you can use a modern approach and create a standalone JavaScript application.
00:04:52.060 You can leverage one of the major JavaScript frameworks.
00:04:57.550 In this scenario, Rails is often used just as a JSON API.
00:05:05.349 You handle all the rendering in your standalone JavaScript application.
00:05:12.039 This leads to having a well-structured view layer since they offer good software design.
00:05:20.770 You don't want to say anything negative about that.
00:05:26.800 This approach is well-structured, but it means you have to write a lot of JavaScript.
00:05:33.009 This is where the problem begins because I don't enjoy that.
00:05:38.469 The most important consideration is maintaining two different applications: your Rails back-end and your JavaScript front-end.
00:05:46.029 You have to align these systems and create APIs, which often leads to losing functionality.
00:05:52.569 You may find yourself managing authentication and authorization across both systems.
00:06:00.990 You even have translations living in the back-end, and you need to duplicate them somehow on the front-end.
00:06:07.990 In my opinion, there are many disadvantages to having two different applications, two repositories, and two test suites.
00:06:13.860 We don't like either approach, obviously. If I say 'we,' I mean me, my colleagues, and some people I know.
00:06:21.389 The first approach, the HTML/ERB or Slim option, feels too limited.
00:06:28.650 The second approach, creating a standalone JavaScript application, is deemed too complex.
00:06:35.710 This gap in between is why we created Mate Stack.
00:06:46.670 I would like to show you real working code to give you an idea of how we want to fill that gap.
00:06:53.980 Now, I'm switching to my editor. From now on, I encourage you to ask questions right away.
00:07:03.419 If I'm going too fast, it's because everything looks perfectly natural to me. So let me know if you don't understand anything.
00:07:10.210 Ok, nice. We have a not styled app here.
00:07:16.560 On purpose, I haven't added any styles because we're focusing on functionality right now, not about Bootstrap or Material Design.
00:07:22.020 So, let's have a look at what Mate Stack looks like. I'm going to switch to the editor.
00:07:30.889 Is that ok for everyone? I can make it a bit bigger.
00:07:39.300 Better? Still a bit too small? Ok, I think it's fine.
00:07:44.520 Alright, cool. So, we can see part of the page here.
00:07:50.789 Actually, don’t care too much about what this is on top. As you can see, it's a Ruby class.
00:07:57.210 We should have a look at the response part. Mate Stack has the concepts of a page, a component, and an app.
00:08:03.539 These are the three major building blocks, and we will see what each one entails.
00:08:08.789 At the moment, we're looking at the page class, which has a response function.
00:08:13.889 In this response function, the page orchestrates components.
00:08:20.669 In this case, we describe how the user interface should look.
00:08:26.550 If we switch back, we see this output: 'Hello from page one.' This is, obviously, the first output we orchestrated.
00:08:32.849 This is a heading component, similar to an h2 HTML tag. This is our component call.
00:08:39.240 We have a div; we know what that is, and if we wrap plain text output... hello from page one.
00:08:46.380 The first thing we see here is that we are not writing HTML; we're writing Ruby in a Ruby class.
00:08:53.160 You can imagine we could put a lot of Ruby on top of it. Now we're going to do just that.
00:09:01.660 But that's just the first major component.
00:09:05.910 We describe the user interface in Ruby.
00:09:12.660 The next thing to examine is that there's more on this page.
00:09:18.810 There's obviously some kind of layout, like a title and a navigation bar.
00:09:24.370 So a page may have a layout, and we called it the Mate Stack app.
00:09:31.759 The Mate Stack app can contain multiple pages, but at its core, an app is just a layout for a page.
00:09:38.410 The term 'app' sounds fancier.
00:09:44.710 Again, we have different classes at play here, and now we have the app class.
00:09:50.080 We're applying the same principle that we have a response function orchestrating components.
00:09:57.370 We can have an h1 tag with the text 'My App,' followed by a navigation part with transition components.
00:10:04.210 These are special Mate Stack components you'll see what they do. You might expect what they will do.
00:10:10.950 Again, we declare our user interface with some buttons.
00:10:18.980 Here we have six buttons, each leading to different routes.
00:10:25.680 We also have a main part, a content area for the page, which behaves like a Rails layout.
00:10:32.700 It has a yield like Rails layouts do.
00:10:39.400 What you just saw is the first page with this content filled into the layout.
00:10:46.100 Again, that's just the basics. Mate Stack serves static content, but magic happens now.
00:10:53.500 If I click this button, it may not be easy to see, but there is no page reload.
00:11:00.470 It internally switches out these two pages.
00:11:09.750 We'll quickly have a look at page two.
00:11:15.370 Page two, as we introduced here, uses a prepare statement. This should be done before rendering.
00:11:21.100 So we initialize an array of strings and orchestrate some sub-components.
00:11:28.929 As you can see, they are just HTML tags familiar to you, but implemented in Ruby.
00:11:35.570 We have a list, and we iterate through this array to display the content.
00:11:42.460 The main thing is that the transition between these two pages is done through Mate Stack magic.
00:11:49.640 No JavaScript is involved on our part.
00:11:56.100 This is critical for us as it's a classic requirement for a dynamic user interface.
00:12:02.370 You want to handle page transitions with animations and loading bars. Normally, this would require writing JavaScript.
00:12:10.860 But because it's a classic requirement, we don't want to write JavaScript.
00:12:18.430 We want to define the behavior in a Ruby class where it can just happen automatically.
00:12:25.900 This is one of the main concepts of Mate Stack: dynamic page transitions.
00:12:32.570 I can show you how that looks if you take a look at the network.
00:12:39.450 Just to wait, I will switch to page one.
00:12:47.670 As I switch, I just get the content back from the server for page one.
00:12:54.800 Mate Stack's JavaScript, which is based on Vue.js, takes this content and displays it in the area.
00:13:02.209 If you can imagine, that's the same structure.
00:13:09.639 Ok, questions up to this point?
00:13:16.339 Until now, we have seen two major things: HTML written in a Ruby class and dynamic page transitions.
00:13:23.060 These are based on some magic, and Mate Stack ships them automatically.
00:13:30.519 Any questions? Is everything clear?
00:13:35.950 Cool. There was a question, right?
00:13:42.090 Just a quick question: is this compatible with other Ruby-based frameworks like Sinatra, or is it mostly a Rails gem?
00:13:48.420 Currently, it's built for Rails, but I know that this can be a requirement, and we are looking to port it to other Ruby frameworks.
00:13:56.540 At the moment, I started with Rails because we do client projects there and validate our ideas in our daily business.
00:14:03.880 So I didn't want to build something that doesn't work in reality.
00:14:08.070 That's why we built a Rails engine; you just plug it into your Rails application.
00:14:15.640 Then you get these functionalities out-of-the-box. We use that in our production client systems.
00:14:21.990 Ok, Mate Stack has a lot more to offer. We will see a bit of that right now.
00:14:29.300 Now we're going to dynamically transition to page three without a page reload!
00:14:35.950 And here it is, page three! Let's see what happens next.
00:14:43.390 There’s a button and a date string, so if I click it, you'll see a new string.
00:14:50.380 And some kind of notification will appear. Let's see how this is implemented on my third page.
00:14:58.360 Once again, we orchestrate components in this page. We have our action component.
00:15:07.530 This component gets a configuration, which is defined in a Ruby function.
00:15:14.130 This Ruby function returns a hash informing the action component what to do.
00:15:20.550 We want to perform a post request to this path, which is just a Rails route.
00:15:27.240 If it succeeds, we want to emit this event. If it fails, we want to submit this event.
00:15:34.790 This is the event configuration. The action expects to visualize the action.
00:15:41.550 We could actually put any component here, but we decide to place a button with the text 'Click Me'.
00:15:49.240 So that's why you saw 'Click Me,' which obviously performs post requests.
00:15:55.860 Now, what happens after I clicked it will be the re-rendering of this part of the UI, showing the state string.
00:16:02.899 This occurs because I told this part of the UI to asynchronously re-render if an event is received.
00:16:08.750 We saw that the event is emitted if the action succeeded.
00:16:15.500 So, obviously, the controller behind this succeeded, allowing the event to be emitted.
00:16:22.350 The component visualizes this state string that gets pre-rendered.
00:16:29.260 We do something similar, but with a different approach on this segment of the UI.
00:16:35.670 There’s also a swing component, but now we declare it to show on this event.
00:16:41.610 It will hide after two thousand milliseconds.
00:16:48.950 This is why we could see the notification for two seconds after the action succeeded.
00:16:55.880 This leads to dynamic feedback, which we describe with a few lines of Ruby.
00:17:02.900 No JavaScript is involved in this process.
00:17:09.090 This is also a classic requirement for a dynamic user interface.
00:17:16.290 We want to respond dynamically to back-end reactions.
00:17:23.020 If the action would have failed, we would have seen the notification appearing for two seconds.
00:17:30.360 This is a new concept we just introduced. Any questions up to here?
00:17:36.480 Yes, please wait.
00:17:43.640 I'm just wondering what 'pg' stands for?
00:17:50.180 Oh, sorry. That's a paragraph tag because, in HTML, it's just 'p'.
00:17:57.810 But in Ruby, we couldn't use that directly.
00:18:02.430 So we used 'pg.' Sorry for that! We implement around twenty classic HTML tags, including div, ul, li, table, and spans.
00:18:09.710 PG corresponds to a paragraph tag.
00:18:14.670 Does the event system support more data payload in the event?
00:18:22.900 Yes, it does, but I think I can’t cover it right now.
00:18:30.440 But it is fully supported.
00:18:36.760 I think I have to hurry up; I have ten minutes left, and I'd like to show you much more.
00:18:43.300 Now it’s going to become more interesting. We have a classic requirement: user input.
00:18:50.090 Let's see what happens here. Hello!
00:18:56.170 This was done without a page reload, and part of the UI is re-rendered.
00:19:02.770 I submitted this form using my return key. I'll try to input that again.
00:19:09.150 Oh, it's not working! I see a notification, but then it disappears.
00:19:15.670 You can imagine what that was.
00:19:18.330 The reason is I have a model with validations for title presence and uniqueness.
00:19:25.220 Of course, I tried to create the same instance of the dummy model with the same title.
00:19:31.730 Active Record was unhappy about that, and the validation error messages were dynamically included in the UI.
00:19:38.440 Let's examine how this is implemented because it's another classic requirement.
00:19:45.300 We have again our page prepared, representing a new instance of the dummy model for our form.
00:19:51.700 We have some headings here, and now we focus on a form component.
00:20:00.700 It makes a component component, and it's configured again.
00:20:07.640 This configuration is stored in this method.
00:20:14.160 The form for the dummy model instance specifies what to do.
00:20:20.020 It should perform a post against this path with the input.
00:20:26.830 Similar to the action component, it expects success and failure events.
00:20:33.480 We tell the form that it should ask for the title, mark it as text, and set the placeholder.
00:20:39.820 We make it need a paragraph for spacing, but I don’t want to decide anything here.
00:20:47.050 Finally, for form submission, we want to visualize this action as a button.
00:20:54.240 This is what we see here: an input button.
00:21:00.740 Now, Mate Stack performs the magic with validation from Active Record.
00:21:05.770 Since it wasn’t unique, we see our validation error in the UI.
00:21:12.030 This is just a form input.
00:21:18.810 We have a list that displays each dummy model title for this iteration.
00:21:24.280 This is paired with a sink that reacts on the form's success event.
00:21:31.030 This is why this part is re-rendered after successfully submitting the form with a new dummy model included.
00:21:37.480 Just one more thing here, and then I’ll take some questions.
00:21:44.220 On the right side, we see page five, and on the left side, there's still page four.
00:21:50.640 Let's take a look at this part of the UI.
00:21:57.760 There are two different browser sessions, and there are some broadcasters involved.
00:22:05.740 So, Action Cable is involved. The great part is that it's very easy to implement a reactive user interface.
00:22:12.590 Let's check out page five. We see some headings and another component.
00:22:19.220 Again, this is a swing component that just displays a list of dummy models.
00:22:25.850 We instruct the sync component to rerender upon receiving an event.
00:22:34.060 It's a sync rerender based on an event, and no JavaScript is involved.
00:22:41.410 The only thing we need to manage is creating a form.
00:22:47.520 We create a form action with our parameters. Everything is classic Rails.
00:22:55.120 If there's an error, we respond with an error code. If not, we broadcast that the model was created.
00:23:00.440 This is the event that sync was waiting for to render.
00:23:08.059 At the moment, we can broadcast this to a specific channel.
00:23:15.860 That’s it; it's achievable with no JavaScript involved.
00:23:22.500 We are now looking at a WebSockets-based user interface.
00:23:28.650 Now, that's hopefully nice!
00:23:35.170 Wait a second; I need to address this.
00:23:40.640 We just saw Mate Stack utilizing components in action.
00:23:47.160 This library is well documented and tested to show you how to utilize its configuration.
00:23:55.210 If this isn't enough, you might want to create your own components.
00:24:02.690 I'm not going to go into detail about that now, but you can create your own custom components.
00:24:11.760 Mate Stack is based on UJS, so it abstracts UJS and Rails communication.
00:24:19.800 As a result, Mate Stack comes with a lot of UJS under the hood.
00:24:26.200 If you want to extend Mate Stack, you should write a Vue.js component.
00:24:32.100 Place it next to the core components.
00:24:38.180 Let’s quickly look at the sixth page, showing core components in action.
00:24:46.070 We have a heading core component and a custom demo component.
00:24:53.290 Remember: 'custom' is the namespace you need to create your own components.
00:25:00.270 The custom demo component uses Mate Stack's Ruby templating.
00:25:08.230 You simply add Ruby and Vue.js directives.
00:25:15.080 Here’s how your custom component will look.
00:25:22.220 It's still a classic UJS component.
00:25:28.930 If you click this button, it triggers an API call.
00:25:36.640 It is just classic Vue.js—nothing too fancy.
00:25:42.480 It just iterates through the received data.
00:25:48.860 This is how you could extend Mate Stack. There are really no limits!
00:25:58.220 However, the intention was that around 80 to 90 percent of classic web-driven data-driven UIs.
00:26:05.520 They can be orchestrated using these core components.
00:26:12.740 For highly specific behaviors, you simply create your own components.
00:26:18.760 Place them next to all the core components without making a mess.
00:26:29.480 In our opinion, it’s well architecture overall.
00:26:35.920 Now, you might ask how this connects through routes to controllers.
00:26:43.350 You just use classic routes—there's nothing different here.
00:26:49.790 First page, second page, whatever. You construct your controllers as you wish.
00:26:55.679 You have your actions, and you put this helper function in your class.
00:27:03.020 This tells the Rails action to use the Mate Stack class to render the result.
00:27:09.190 That’s how everything is interconnected.
00:27:15.370 We don’t touch anything else from Rails because we love Rails.
00:27:20.680 We wanted the option to add a new view layer on top of everything else Rails has to offer.
00:27:27.930 You can do this progressively, action by action, deciding whether you want to respond with a Mate Stack page.
00:27:36.320 That's it for now! Any questions are welcome.
00:27:43.480 Actually, it's 3 AM, so maybe we can have just one or two questions.
00:27:49.570 Do you have any benchmarks for how fast it renders pages in comparison with ERB or Slim?
00:27:55.260 As of now, it’s slower. We don't have benchmarks yet.
00:28:02.490 So far, we primarily focused on creating a working concept and plan to optimize it in the future.
00:28:09.340 We'll look into server-side caching and performance optimizations.
00:28:15.590 In many applications, client budget is the crucial factor rather than performance.
00:28:21.790 But we want to optimize performance as well. Thank you!
00:28:30.060 Which parts of Trailblazer are you using?
00:28:38.920 Unfortunately, you don’t directly touch that.
00:28:45.780 Trailblazer cells are very effective but sometimes too complicated.
00:28:54.820 We want to offer a much easier approach.
00:29:02.180 Finally, just a quick survey: Who here is interested in using this approach?
00:29:10.480 Wow, thank you very much! This is encouraging!
00:29:16.009 If you like what you saw, please visit GitHub and give us a star.
00:29:23.640 Look for Base Mate or Mate Stack; it's open source, so try it out.
00:29:31.149 Feel free to create issues; it's not perfect yet version 0.6.