 I'm David Koontz of Happy Camper Studios, the Phoenix Ruby User Group, and we're here talking about GUI development in our favorite language, Ruby, which may be sort of a strange connection for some people. So, quick question. Who here has done GUI development before? Like in any language? Okay. So, almost everyone here. Okay. So, tangential question. Who here gets paid to do Ruby? Who here gets paid to do Ruby not doing web? Nice. Okay. A few hands. Better than last year's Ruby Conf, I'd say, where there's like four hands, two of which were like James Britt and myself, of Happy Camper Studios. So, what we're going to be talking about this week is this problem of, if there's tons of web Ruby development, why isn't there an equal amount of desktop Ruby development? So, we've got a couple issues that may be preventing us. Some people would say, well, the desktop is dead. We don't need it. We have the web now. Everything's moved to the web. Or Ruby's a bad language. It doesn't interface well with the operating system. It's not a C or a C++, so it just doesn't really work. It's not Rails. Therefore, I'm not interested. A whole slew of reasons of why we haven't seen a lot of Ruby desktop application development. The hard to distribute one has probably been the biggest problem for a long period of time. If you have an app, and it's a Ruby app, and you can't guarantee that your person you're distributing it to has Ruby, that's a big problem. You can use some of the Ruby to Ruby, or I'm sorry, Ruby to EXE type packages, and I saw there was a talk on Crate today, so that might be a pretty good option. But we're going to see that there's actually a bunch of options today that you may not have known about. So, the truth is commercial GUI application development in Ruby is doable today, right now. You don't have to wait for the future. The future is here now, and it's available to you. These are some requirements that I would look at. If I wanted to develop an actual commercial product, I want to actually spend money to build something that I expect people to pay me for. So, whatever I'm using, it's got to be writable, basically pure Ruby. It's got to work on multiple platforms, and you've got to be able to distribute it easily. If it's some weird, crazy, oh, go download this library over here, install Ruby first, that's never going to fly. Your customers aren't going to do that. Finally, something that doesn't get talked about a lot is your code is just script files, right? They're just .rb files, and anyone can just open those up, and if you have your little license key check, oh, we'll just go and delete that line, and now there's no license key check anymore, right? So the idea of being able to secure your Ruby code somehow is sort of important, I think, if you're doing a commercial application. It doesn't matter on the web so much, because obviously they can't see, well, hopefully, unless you've done something horrible along with your security, they will never be able to see your source code. But it does stop app, they've got the whole package, so you've got to be thinking sort of a little bit more along those lines. So monkey bars is a library, not a framework, it's a library that solves the problems that were mentioned here using swing, which is a Java library, and JRuby, which is what gives us access to that Java library. So we are going to be in JRubyland a bit here, but allow me to explain why Java. I know there's a lot of people who, you hear the word Java, and the instinct of reflection is to shrink back and tear. So my question is, why not Java? I mean, it's natural, I guess, in the Ruby community to say Java. But why is that? I mean, years of EJBs, like Jam, Daniel's Throat, or something like that? But we're not talking web, we're talking desktop app development. So it's actually a whole different world. And a point that I think is missed a lot is that anytime you deal with any GUI toolkit, TK, GTK, QT, Fox, WX Widgets, whatever you want, that was not written in Ruby. That was written in C, or C++, or Objective C, or whatever it is that your GUI framework is written in. So you're always dealing in a foreign language. Now, most of the time it's C, and the Ruby community accepts C, so why not accept Java? And especially, if we're talking about having some sort of foreign library that we're gonna integrate with, why not pick the one that has really, really good Ruby integration? And the truth is, in JRuby, you have fantastic integration with the target language. If you have a Java library and you hit that with Ruby, there are no bindings. There's no extra layer to get in your way. If there's a Java object, you instantiate it, you call it completely seamless. So that's a pretty good reason, no bindings, no extra layer to get in your way and sort of muck things up. Well, JRuby happens to be the fastest Ruby implementation, so that's kind of cool, our apps run really fast, always nice. And while we're at it, beyond just swing, there's like a Hojillion Java libraries out there for whatever you run into that you need. We need a library to be an FTP server, and the Ruby one doesn't quite do this weird obscure SFTP thing or whatever. Here's like 18 Java ones to do. Okay, there we go. Pick that off, slap a Ruby layer on top. It looks pretty now. We're good to go. So that's a pretty big benefit when you're doing this commercially. So the question I would pose to you is, if you're still unconvinced, JRuby, Java, spawn the devil, can your Ruby do game engines, GUI frameworks, enterprise-y stuff? This is useful. I mean, you get paid to do this stuff, right? So I care that I have access to this because people will pay me to do this with Ruby. The fact that it's using a Java library somewhere down the chain is completely irrelevant to me, because I'm never going to see it. I'm going to wrap it up in a nice little Ruby layer, and it's gone. But I get paid to do it. The point is, it's possible now. So if C extensions are OK when I Java, I think that's a pretty fair assessment. I mean, everyone seems OK with C extensions, for the most part. Why not Java extensions? Same thing. So why swing? I don't know if anyone here has experience in Java development, but there's two big families of Java GUI frameworks, Swing and SWT, which was written by IBM. So we chose Swing. So why swing? Well, it's been around a long time. It's kind of been put through the paces a bit, and it's got a lot of tooling support built up around it. One nice thing is this is an app that we built for a client, still in our active development. They're an engineering company, so they have very dense, complex, graphical forms. So it's an MDI, they request an MDI. A bunch of things. Those graphs over there are nested components inside each other in multiple layers deep. And you could lay this out by hand, I guess, or with some builder syntax thing. I wouldn't want to. And then the client comes back and says, oh, yeah, this thing doesn't line up quite right. We need you to move this over. Or we want these things grouped. Like we want those demod check marks. We want those aligned with the reset peak, not over here on the left. Like little tweaks. They come back to this all the time. And to do that by hand, I would be maddened by that to have to go in and find where in this big block of code is the little thing that sets the alignment left right. Versus I click on it, I click the drop down, align right, and it snaps over, and we're good. Move on. So tooling support is very important for me as a developer and as a business owner that's making commercial desktop apps. Because I want to be able to come in and make quick tweaks. And also, you can visualize. You can just design the thing, show them, is this right? They go, uh-huh. I mean, it's right then, of course. I won't be right tomorrow. But it's right then. And you go, OK. And then you go wire it up to actually do useful things. So you have this rapid prototyping capability that you don't really have if all your everything is hand written by a nice Ruby syntax. As nice as that is for you as a Ruby developer, it doesn't really give you that rapid prototyping kind of effect. So monkey bars, again, it's a library in our framework. And it's hopers for making swing much more palatable, much easier to use. And it's also sort of an opinion on how you should structure your app. So we're not going to ram anything down your throat, but we do give you a lot of things that make your life easier if you just kind of do what we set up for you to do. In terms of we have generators and that kind of stuff. So monkey bars is not an abstraction layer for swing. This is probably the biggest like, whoa, what's going on here? It does not just take away swing completely so that you don't ever have to deal with it. We looked into that early and decided that was not possible. We couldn't actually do that. Because there's too many swing components, and there's all kinds of third-party swing components we've never heard of that you're going to want to use. So if those aren't wrapped up, then you're going to be like fairies in unicorns' land. And then it's like, oh, Java swing land. And there's this huge disparity between the two. So we took the middle road of we're going to wrap up things that we can consistently wrap up. And the rest is left to you. So the downside, you have to know swing to some degree. You have to understand what's going on. But you would have had to have known that anyways. No amount of abstraction is going to save you from that. And we're not a self-contained toolkit. We're not an all-in-one-shoes bundling thing or a library. You could drop this into an existing Java project, like Java swing project. And one by one, start migrating your forms away from Java to Ruby. And eventually, you'll just be all Ruby. So this is made to work with existing swing projects or Greenfield projects. Either way, it'll work. So what we're going to do is we're just going to go through and write hello world and just kind of see how it works. And then we'll talk about the internals of what's actually going on here. So here I have just empty project directory. And I'm going to use the monkey bars command line tool. This is installed when you install the gem. And we'll create a hello world project. Can you guys see this? Is this big enough? Little bigger. Little bigger. Is that better? Good? So it created the directory hello world, and it created the project structure. And if we go in here, you can see that we've got a rake file, a lib directory, a source directory, and task directory. And while we're in here, we're going to install this little helper tool called RAR. And we'll get back to what this does. This helps us interface better with our tools, specifically NetBeans. So NetBeans is a Java development tool. And it expects things like a Java main file. Well, RAR gives you that. And basically, it turns around and calls your Ruby main file so that you can kind of Javaize any Ruby app. It does more than that that we'll see later. The next thing we need to do is, no, we don't want to do that. We need a couple of libraries. So we're going to go here into our lib. So like I said, it's sort of an opinion on how you should do things. And one of those opinions is how you structure apps. So we have a lib directory. Inside the lib directory, you have a Java and a Ruby directory, which is where you put your libraries. So under our lib Java directory, that's where we put the monkey bars jar for you. And you're then expected to go have some form of JRuby. We don't know what form of JRuby you're going to have. So you need to grab that. And for our purposes today, we're going to be using a swing layout extension, the swing layout manager. So we copy those libraries over. OK, some jar files, drop them in here. Now we're good to go. Now we can go ahead and create our project. So I'm in NetBeans here. I don't know if everyone's seen NetBeans. It's got Ruby editing support. Fairly good Ruby editing support. This is what we use as our day-to-day editor, for the most part. We're just going to go create a new project. And this project is called Hello World. And we just have to tell it about our source directory. The Ruby Java integration in NetBeans is Soso. When you want to do a mix mode project, the NetBeans team is aware of this. They're working on it. But there's nothing that says you have to use NetBeans. I just like it because it gives you a pretty good GUI editor and a Ruby editor in the same package. So you have to be switching between apps all day long. So there's nothing to say you couldn't do your GUI design in anything that you feel like. OK, and then we're just going to let NetBeans be aware of these so that it can run. So now you can see that Monkeybars has generated a couple files for us. It's got our main file, which does things like make sure that if you're on OS 10, it's going to put the tool bar actually up at the bar up at the top, this kind of area up here, versus on the window, which is what Swingoot does by default. So we have a couple of defaults that kind of make the system a little smarter. We have some error handling that's kind of built in here. And down at the bottom, we have this nice little section, your app goes here. So we'll do this, and we'll run it, and ask us which main file we want to run. Which Java main file? Well, that's where the RAR file comes in. So there you go. Run that one. It's just going to run our main file that's under source. It's going to turn around and run that. And there we go. If you notice down here, we have Hello World. So we're good. We're running. Now we can get to the business of writing our actual, our GUI, the actual stuff we're interested in. So monkeybars has a bunch of helper scripts, one of which is, and they're all rake tasks, because we figured why I have a bunch of binaries when you have rake. So we just made them rake tasks. And you can see that the first task here is generate. RAR adds a bunch of tasks, but the one we're interested in right now is generate. And so we want to generate all meaning model view and controller, and we want to pop it under source greeter. So that's going to go and generate our greeter model view and controller. So everything in monkeybars is model view and controller, like these sets of three, similar to what you would get in the Rails world. So we've got our model view and controller. And over here to pop up, there we go. And they're very, very simple files. Our controller just says, hey, that's my model, that's my view, and what to do when I close. The model is completely empty, because we don't know what your model is. It doesn't inherit from anything. You bring your own model, do whatever you want to do with it. And the view is asking for a Java class. Which Java class am I associated with? So let's give it a Java class. So we want a new J frame. That's the real base level swing component. And we have this window that's way too big. And we're just going to go toss a label on here. And we'll call this label. We'll give it a name, so scrunched. We're just going to name this variable message. And then we're just going to toss a button on here. As you can see, it all docks nicely and everything. OK, so we're going to have the basic idea of this app is there's just a whole world, so we've got a little message. You display initial message, you click the button, the message changes, as simple as we can get. So we've got our greeter class now. And we need to name our button here. We'll name him improve. So we've got a label name message and a button name improve. And now we can tell our greeter about the class. So that's the class name that we created over here. OK. So now what we need to do, the way monkey bar is going to work is the drawer is going to load. It's going to instantiate its model and its view. And then the view is going to get its data from the model. OK, so we need some data to display in that text field. So we'll go over to our model and we'll create a message accessor and then we'll create a initial value. OK, swing is OK. So pretty straightforward, right? So what we expect is when we run this, for that label to have the message, swing is OK. That's what we expect. And to get this all kicked off, we have to go back to our main program here. And we have to require. And then we can tell our controller, our greeter controller, that we want it to pop up and show. And the way you do that is you tell it to open. So everything is basically a singleton when it comes to controllers. So grab the controller, ask it to open itself. And the perils of life coding has given us what should already be on the path. Oh goodness. I'm not following my script properly. Oh. Yes, NetBeans has interesting concepts of the keys that it maps. Shift F11 does not play well with OS 10. OK, so here we have our label. That was a NetBeans bug, by the way. It just had to clean and rebuild, and it cleared it out. So here we have our J label. We click the improve button. Oh, I'm sorry. We have our J label. And nothing's happened yet. So it's showing up, but nothing's in there. And the reason that is is because there's nothing to connect the view and the model yet. So what we have to do is we have to tell the view that its message.text should come from the model, and it should come from the message property on the model. So that's how we're connecting them. We're saying, hey, view, go grab that value out of the model, and put it into the text property of the message object, the message instance variable. So there we go. We have swing is OK. It's showing up. Yes? I'm going to change the value to be very over-contrast for the text object. Yes. Can we change the colors? This should be a white. Is that better? OK, sorry about that. So we're mapping from the model, the message eye of the model, to the text property of the message object. And as you saw, it showed up. And while we're in here, we're just going to tell the view when it starts up to move to the center. Just toss that in there. So it should look nice. Pops up right in the middle. OK, so swing is OK. Now we want to improve it. So we have to have some way to say, when that button is pressed, I want to go do something. And the way we do that is, in the controller, we simply create a method. If you remember, we named the instance variable improve. That was the name of the button. So we have a method called improve. And then the action. So in swing, it's action performed is like the generic something happened to me action. So we say, improve action performed. And when you click on the button, this method gets called, all auto magically. So we're doing all the event handler registration. Everything is done by monkey bars. So then what we can do is we can just tell that model, well, you have new message now, right? And then when we're done, we're done here. We have to tell the view to update itself. Because unlike other MVC systems, the view isn't constantly pulling. You tell the view when it should update. And that allows you to batch things up and not have these weird flicker redraw issues. So we've got our button. We click it. Boom, monkey bars is awesome. So we didn't have to do any of the normal swing event handler registration, nested interclasses, implementing interfaces, all that blah, blah, blah. That's just gone. Simply by saying, name of component underscore name of action. And there you go. So there's Hello World. It's not as simple as some frameworks that would just write it all in one file. But it does give you a nice separation. Go ahead. The very cute. OK, so the question was, what is this referring to? So this is referring to the message instance variable on your view. So we named the label message. And text is a property of that. So what this is effectively going to call when it runs this is it's going to call message.text equals model.message. That's effectively what's going to happen. Every time you say update view, that's going to be triggered. OK, so that hopefully gives you a very general sense of how monkey bars is kind of working. Miles uses controllers. They all do separate things. And move on. OK, so monkey bars is kind of MVC-ish, as you can see. Specifically, it's different from normal MVC in the GUI sense of normally the view and the controller can talk to each other. And everyone can kind of talk to everyone to a degree. They all have their own responsibilities, but there's a lot of talking to each other that goes on. And monkey bars is very different in this regard in that the control and the view don't talk to each other directly. They talk through monkey bars. So the controller can set and get things on for the model. The view doesn't write to the model, but it can read from the model. And then monkey bars sort of orchestrates back and forth. So it's kind of a minor change, but it has some big ramifications as we get down the line. So by keeping the controller and the view separate, that buys us some flexibility, buys us a little bit more work on the front end, but it buys us some good things on the back end. So normally you would just say, you'd have a reference to your view, and you'd just call something on the view. View dot, whatever name of your button, dot text, and you'd just set it. Instead, what we do is we put a value into the model and then say, hey monkey bars, go tell the view to update itself based on the model's values. We separate those things out. So the controller doesn't tell the view. Instead, the controller tells the model and then asks monkey bars to tell the view to update itself. Again, a little bit more work, but what this buys us is it makes it very hard for you to do a lot of view logic in your controller, because you're going to have to put it in the model first. So it helps you sort of mentally divide where your things should live. And it also is going to help us in testing later. So as you saw, event handlers. Those are done a bit different. What would normally be done as typically in the most of the Ruby eyes versions of Java frameworks and whatnot that I've seen has been something kind of like this. You'd have some button. You would call some sort of add name of handler. And then inside there you would have an action listener, which only has one method, action performed. But you could have others like a mouse listener that has three or four methods. And you would go ahead and you'd write your methods in there, or you would attach a block per handler that you want to do, different ways of doing it. Instead, we just say, you've got this method. And by naming convention, we know how to attach it to the correct component on your view, because you named my button is the same on both ends. Yes? Sorry, so can you pass a block to an event and have that block to fire for the event this far? The question is, can you pass a block to an event? No. Well, I mean, yes, you could if you wanted to wire that in yourself. The way monkey bars works is you always write a method, or you call a method define handler, and pass it the name of the event you want to do. Basically, you could say define handler, my button action performed, and pass a block to that. So yes, you do get some flexibility that allows you to say, register a handler on an object from outside that object. You have a parent object spawning like a child window. It can register a handler for when this window closes. And then it can grab the data back from that form, or something like that. So it's possible. But typically, you do it like this. And the benefit there is, now you have a method to test. Whereas if you're passing blocks over the place, it makes it other actually really difficult to test your handlers, because they're just blocks. They were never bound to any name. So you have no reference to them to test them later. Sort of that grows out of your projects as they get bigger. So basically, what monkey bars are doing here is the heavy lifting of attaching all the appropriate listeners to the appropriate objects, based on conventions again. So what's special about this? Why do we go to all this extra work? Well, the nice thing is, because the controller doesn't hold a reference to the view, it means that, and also your model's a plain Ruby object. So both of those things means that testing is actually kind of easy, because you're never going to call view.something. You're never going to make that direct call, which means you never have to mock out this view ugly, really kind of nasty view layer. Instead, you put stuff in the model, and then monkey bars gets the model to the view. So all you have to test is that you put something in the model, and it was there when your controller was done, which is trivial, because you provided your own model. So it's whatever you want. I mean, and mocking that out is very easy. So unit testing in the GUI app. I don't know. A lot of people here had done GUI development before. Has anyone unit tested their GUI apps before? Was it fun? Or was it like, oh, I'm going to use Java Reflection and feel really dirty? I mean, typically, my experience has not been a lot of fun for most of the stuff. OK, so let's take a look at writing a unit test, and just see what that would look like. And by the way, if you have any questions, feel free to just jump in and yell at me. So create a new file. We'll call this Greeter Controller Test. We'll just do test unit, just real straightforward, test unit. And we need a bunch of things here. If you were doing this for real, of course, you would have your rake task set up, all your environment, and all that. We're just going to do it manually here. So it would require a couple of files to get monkey bars kind of off the ground. So we can use our real model, because it doesn't do anything spiffy, like it doesn't hit the database when you instantiate or anything like that. If yours did, you would need to provide a mock model. OK, so we require a test unit. Requiring Java is Jay Ruby's way of saying, I'm going to make it possible to call into Java libraries, which obviously we are. OK, and we're just going to create a little mock view class here. It doesn't need to do anything. It's just so that we can instantiate it, because the controller is going to try to instantiate the view when it loads. OK, so we have a class, and we'll make it Greeter Controller Test. And it, of course, inherits from test unit, test case. And we're going to need to do a few things. Eventually, we're, of course, going to call our method and validate that the model has the correct value set in it. So the model, when it initializes, it's going to have the value, swings OK. After we call the method, it should have the message, but monkey bars is awesome. And that's all we need to test. Does the model have that value when we're done? So we need to create a method here, improve. Let me know if you want to see anything specific. Right here, we're just going to override the view that's being used, so it doesn't actually go and try to create a real view. So that's where our mock view comes in. We just tell it, hey, use my little mock view for your view. It's never going to be called. We're never going to call UpdateView or anything like that. So now we can instantiate a new Greeter Controller. And then we can go ahead and be slightly dirty and go ask it for its model object. So we're going to use the send to grab out the model. So it's a private method, but we're unit testing it. So that's probably OK. So we grab out the model so that we can check it when it's done. And now we can go ahead and call that method. So we call the method, improve action performed. And when we're done, we should, if everything works right, we should see that this text over here is, in fact, what is in our model. So we're going to assert that those are equal and pretty straightforward. Any questions on this, what we're doing here? We run our unit test and start, finished. There we go. No problem. So we didn't have to instantiate our view because monkey bars allow you to just turn that part off. And because you're never talking directly to the view, you're never going to have a view dot something to mock out. All you have to do is your controller is only calling things on your model, which means your model is very easy to mock. It's usually just a structurally kind of dumb data holding object. So it's very easy to check it at the end to make sure that it, in fact, got the right data set on it. So we can unit test our GUIs for the most part. Unit testing the view part would be more difficult if you wanted to actually validate that the actual field had the value put into it when you called update view. You could do that, certainly. Although those are the kinds of errors that are very easy to see because they're going to be right in front of your face. You're going to see that the value did not get propagated to the component. And you're going to cache that really quick. The harder ones are the logical errors. Hey, you did this big calculation, and you put a value into your model, and it was the wrong value. Or you grabbed the wrong record out of the database or something like that. OK. So the main features of monkey bars beyond putting a conceptual framework on you are saying you have models using controllers that are separate. They're easy to wire up in the sense of like monkey bars will auto wire a controller to its view in terms of handling the events. It's the idea that you've got generators that will get you started. So if you remember, we ran a generator right at the beginning. So we ran a generator. Well, there's the monkey bar generator that we ran initially, monkey bar space hello world, so that gets your project going. And then you've got these generators for each of your model view controller sets. And you could, of course, instead of all, you could do model view or controller if you just want to generate one of those. So you've got the niceties of that kind of framework-ish in that sense. Something that would be very more in-depth than we can get into here is that you do have the ability to create, say, a component that is a Java form and model view and control that go with it. And then nest that inside something else. So as an example, you can create a panel, fairly complicated. And that's actually what we're doing here. What we're actually doing up here is this whole thing right here is a component. And we can actually open that up in the designer and see it as a component, like this. And then it's got a model view and controller backing it for its code. And then you can dynamically insert it in, which we do, depending on what things are checked and what is actually physically present. This communicates with a piece of hardware satellite modem. So depending on what's actually physically installed in the machine, you get the proper one showing up. And that's all dynamically done. We have other examples of those where you could have a list of items that I can show you where it can dynamically grow. And the nice thing is then all the logic for this is completely self-contained, just in its own little set of model view controller. They can be independently unit tested. So that makes it very nice for segmenting your app out. We have helpers for things like, I don't know if anyone's running this situation. During the event handler, you start doing something and it takes more than a second. And all of a sudden the GUI is non-responsive and they won't repaint. If you drag it, it gets all torn and everything. And that's because most GUI frameworks only have one thread working total at all, which means if you're doing something while that thread is working, it's not repainting. So we have helpers to allow you to very easily say, while we're painting, do and then your stuff. And it will continue to cause the GUI to repaint. So you can do long-running tasks and not lock up your GUI in a very simple way, without having to spawn off background threads and that kind of stuff. So again, Monkey Barge is all about taking the pain out of doing with GUI app development. It's not going to completely shield you from it. It's not going to take all that away. It's going to hopefully take the really sharp edges off wherever we can. And then we have helper methods. You saw move to center, for example. Really simple method. Just toss it in there, now you're form-centered. Surprisingly enough, Swing doesn't have anything like that. And most GUI frameworks that I've seen don't have something like that. So lots of little helpers to do those sorts of things. OK, that's our way. So that gives us an app that's writable in Peer Ruby. We wrote Peer Ruby, even though we were interfacing with Swing. It is cross-platform. We'll show you that in just a second. So it's not necessarily easy to distribute yet. That's still a problem that we have. So what we want is something that builds on any platform and has nice front-ends. We don't want them to have to double-click a jar. They might not know how to do that. Or they might not understand that that's the app. On Windows, they want to see an EXE. On OS X, they want to see a .app. That's what they want, because we need that. So there's this library, RAR. And it was a library that was built to package. That's all it does. It packages JRuby apps up so that you can distribute them. Pretty straightforward. So let's take a look at taking our app and packaging it up. So back over here, if you remember those rake tasks that we had earlier, you had all these RAR tasks. And the interesting ones here are bundle app and bundle EXE. Those are the two that are going to help us out. So you can, if you want, to just jar everything up. So that's the first step. Take all my Ruby code, toss it in a jar file that can be double-clicked on. And any Java user could just take it and use it. And they wouldn't even know it's Ruby. It's just like, it's just a jar file. OK, you can hand it to them. That works. And as you can see, it gave us the very aptly named changeme.jar. This is from our configuration file. Obviously, you can go in there and tweak that out. So that's the first step. And now we can go ahead and we can bundle, for example, an app. So the next step would be take that jar file and put a nice front loader on it. Put an app, kind of wrapper around it. I don't know if you are aware of this, but an app file is really just a directory. So that's actually pretty trivial on a S10. So here's our hello world and our package. And you can see, here's classes. This is where it compiled everything. Jar, this is where it stuck your app when you're done. And we can double-click on this and get, it's an executable jar, so you can just double-click on it and it will run pretty straightforward. And again, this could be, I mean, this is just double-clicking on a jar. So you could, at this point, hand it to someone else on Windows or Solaris or whatever. And they could double-click on the jar as long as their OS knows how to launch Java to handle a jar, you're golden. You've now distributed your app, your desktop app that you wrote in Ruby. But that's not quite good enough, so we have an app file, .app. And now when we double-click on it, we get the nice change me and the bar up here shows up down here. It's a real Mac app and it obviously still works. Okay, so that's two parts of it. The third part is Windows distribution because if you're distributing a desktop app, you probably are distributing for Windows at some point. So here we go, we generate change me.exe and you have to figure me out, I forgot to do one thing. There's a current bug in JRuby that we failed to report in time for 115, they're aware of it, it's gonna be fixed for 116. In the meantime, on Windows only, you do have to go in and manually put your sub-directories, like all under source. If you notice, we didn't have to, when we were running it, we didn't have to put greeter on the load path, you could just say greeter controller, require greeter controller, you didn't have to say require greeter slash greeter controller, that's because monkey bars puts everything under your source directory, it auto-loads it on your load path for you. That fails on Windows, so we have to do that ourselves. So there we go, we add greeter to the load path, now we're okay. So small, small price to pay. So we've bundled this up, gives us our exe and now we can come over here to Windows. You can see there's an exe inside here and what I'm going to do is, there it is. So on Windows, change me, swing is okay, but monkey bars is awesome. So from one platform, thank you. So from one platform, you can generate native looking executables for all platforms, like right there. Yes, go ahead. Have you considered, I mean, it's all Java, like Java Web Start integration? The question was Web Start integration. Yes, we have. We spent a lot of time on Web Start integration and found a lot of places where Web Start simply falls down, especially with the integration that Jerry, we've had problems with that. So Web Start is still a, we would love for someone to come along and give us a patch that makes that all just work, but that is a problem area. So we sort of said, Web Start is nice, native integration, sort of native installers and whatnot is probably better. So that's where we focused our attention. Go ahead. I'm not a Java guy and I don't know swing. Do you recommend any particular books to get up on swing, specifically for monkey bars? Okay, so the question is, you're not a Java guy, you don't know swing, how do you get up on swing? Specifically swing. There's no like, in my opinion, super awesome game started on swing book. There is a lot of online documentation. So the SunSight for swing, there is a lot of documentation there and they have what are called trails. So they have these little kind of like step-by-step paths through getting started with swing, various things. And that's gonna be in Java, unfortunately, and you're gonna have to sort of do a little bit with that. I would actually suggest a book like Headfirst Java by, it's part of the Headfirst series, a very excellent book. They do cover swing in some degree in that book and that would give you enough of the feel, the flavor of swing and what it's doing to allow you to just look at the APIs and once you're at that point, all you have to do is basically be able to read the API and know how it translates over to monkey bars. And there's also documentation on the monkey bar site that helps you get started if you know a little bit about swing, how you would make those connections. Yes, go ahead. Does this make this like approach? I remember, although I tried to get my swing days, but there was like a jay-data table or something, some pretty complicated model classes for the grids in swing. Does any of this take away any of that complexity? Okay, so the question is, there's some fairly complicated models that back the views. When I say view, I mean the graphical component that's drawn and they have some fairly complicated things. Does this take that away? No, it doesn't. So that would be a very specific component to wrap up and we have talked about creating, we have a monkey bars extras project now and we're talking about putting in wrappers for those kinds of things, like very specific wrappers for specific things. We ran into, we had a j, what was it, tree frame where it was like a tree on one side or tree table. So it was a tree on one side and then a table on the other side and that was a fairly complicated thing to use. So we have talked about that. We accept any kind of wrapper that you'd want to submit. We're very open to that. We've been working on stabilizing monkey bars and making it really rock solid and have not gotten too much to the nice to haves, the kind of peripheral things, but all that is possible. I mean, that's absolutely doable. Were there any other questions? Yeah, go ahead. I have one other quick question. Is any of this between J3D and monkey bars nicely packaged up for a front-end enterprise Linux here? I mean, what are the questions? Is this interface between JRuby and monkey bars, or? Is it packaged up nicely in an RBM or a Red Hat? So is it an RBM? No, it's a jar file. On the website, you'll find a gem to install it. They'll give you the command line stuff and there's just a straight jar file. So if you want to just drop that on a project, you can't, there is no RBM because then that means someone would have to maintain the RBM and I don't run Red Hat, so that's not going to be me. But if someone wanted to, it should be fairly easy to do. I've got a couple other questions. Yeah, I can understand if you have a lot of systems that would be difficult. Although if you're just deploying this app, I mean, monkey bars is just going to come with your app. So at that point, you're deploying the app and monkey bars just happens through in the libraries that it uses. So, okay, real quick before we run out of time here. Couple other little things here real quick. So, secureable, that's the last issue. So we can package and we can deploy, but is our code protected? Can they just open the directory and be like, oh, this is what your app's doing. Oh, here's your key, your password to log into your web server, all that fun stuff. So, here's a file, hello.rb, there's the contents, puts hello world. We compile it with jrc. Oh, by the way, all the files that we just, we're using all of the Ruby files that we created, those were actually compiled for us by RAR, you can turn that off, but they were compiled into Java class files before they were stuck into the jar file. So no.rb files went into that jar file that we ran on the multiple platforms, it was all compiled into classes first. And what I'm gonna do is I'm gonna show you what this looks like when you decompile it to see if you could kind of reverse engineer it. So a really common decompiler is JAD, and this is what you get back. So basically what's happening is the class files being turned into something that the internal parser of JRuby can understand. It was not meant for people to look at or to be particularly readable, it was meant for JRuby to consume. So it's not standalone class, it's not like a class that you could then just give to someone else and they could just use in their Java project. But it can be run standalone. And you can see a few things, you can see that right here on this line here, that's where our puts is, so we're registering a call site for the puts method. Okay, that's call site zero. And then we come down here and you can see that, oh, right here we're calling call site zero, we're getting it, we're calling it, and we're calling it with a thing called underscore underscore zero. And we go down a few bit, oh, and there it is. There's underscore underscore zero, that's where hello world is. So if you really wanted to, yes, you could kind of reverse engineer this thing and figure out what's going on, of course you'd have to deal with this, like obviously much at a much higher degree. Not particularly readable. And I can guarantee you that this is far harder to understand than just straight Java that's decompiled. If you take like a commercial off the shelf, Java desktop application and decompile it, you get pretty readable code back almost all the time. Like it's really, really easy to decompile. This is a much higher level of obfuscation in my opinion. So if we can ship commercial Java applications in this economy and environment, then I think we can ship this. I mean, it's easily at the same level. Okay, so we're secureable, we're a pretty easy distribute cross-platform, writable and purely, about the easy distribute. One thing we don't handle is installers because that's very specific to your application and what you want to do. So it's sort of intended that this exe or this .app would have some sort of installer, like on the Mac, your installer is opening up a DMG and drag it over, right? There's nothing to do there. But on Windows, you could use any Windows installer at that point, the wise installer, whatever you want to use, to package it up. And we do that for our app via a rake task. Yes? I don't know much about JRuby, but is it all packaged in, the runtime and everything? So the question is JRuby all packaged in. JRuby was that jar file. So the entire runtime for our app is that file right there. That's the entire Ruby runtime, including standard library, all that fun stuff. Okay, so gem that you include, you would want to unpack because reading a gem out when you don't have gems properly installed is problematic. So what you typically do in that situation is you toss that under lib ruby, you unpack your gems in here, all your gems will be compiled and tossed into your main jar with your app. So we unpack all of our gems. I mean, that's just kind of good to do to unpack your gems anyways, so that you know you're on a stable version and it's not gonna go changing out from underneath you. So that's the preferred method. It would be nice someday to be able to drop the gem file here and have monkey bars and JRuby know how to go into the gem file, but for now, just unpack them, which has worked for us pretty good. Okay, so does it really work? Yeah, it's all nice. You can give us a presentation. Well, yes, it does. We are distributing commercial applications in JRuby written in Java right now. So we make our living off GUI apps in Java and JRuby and you can too, if you want to. Like this is within reach, this is doable. And I'll show you the app that we've been writing. So it is what slower? So the question is, is the interface slower? And the answer is, we have very, very rarely run into a place where our code was the thing being slow versus like redrawing a lot of components and that being slow. That's almost always the place where we're slow. So it's sort of like swing's fault or something along those lines. Sorry, I didn't have this queued up. Let me just go grab it. It is compiled to Java byte code, but it's not standalone Java byte code. So it still has to be run through JRuby, if that makes sense. So here you go. Here's Jotbot. This is the product that we're hoping will make us rich and famous. Very basic time tracking application. It started up, as you see, there's a new icon up here in the system tray, or whatever you want to call it. And basically it sits here and every 15 minutes it pops up and says, what did you do for the last 15 minutes? And you say, I did X. Here it's editable. It wouldn't normally be editable when it had popped up after 15 minutes. You simply tell it what you did. So okay, I was doing this. And there you go, and it's gone. And at the end of the day, you can answer the question, what did I do all day? You now know at 15 minute increments, which is good enough for billing. And we use this for billing. And it just sits there very, very unobtrusively. Every so often it just pops up, hit okay, or type in new message, and it's gone. And as you can see, we have system tray integration. We're here in the system tray. This was 100% Ruby. We did not write a line of Java to make this app work. We use Java libraries, and there's a Java desktop integration package that we use, but we didn't write any Java to make this happen. So this is all very within the realm of possibility. We're using a Java database called H2. It's like a pure Java implementation. It's basically like SQLite, but pure Java. So it works without native drivers and all that fun stuff. Pretty simple, and it was very helpful to our development process. So just to get people excited, if you want it, you can go to getjobot.com slash rubyconf08 and downloadjobot, the pre-beta version. It's not quite beta, it'll be beta next week. Download it, mess around with it. There's a link to a mailing list. Go on there and register with the code rubyconf08. All this is on that site. And when it launches, if you can just do us the favor sending us one feedback email. I liked it, I didn't like it, whatever. Everyone gets free copies. So when it launches, we'll, we're just giving it out. Because we think it's cool that you can write commercial desktop applications in Ruby now, and we think that should be a big thing. Like I would like to see, in three years from now, there's the desktop GUI conference, not just the web conference, you know, the Rails and the Merb Camp. I wanna see desktop Ruby doing awesome things. So here you go, linkybars.org, jruby.org, and raar.rubyforge.org are the three websites for all this information. And any last questions? Yes, go ahead. What about JotBot, does the task, like, bar integration work the same with Windows, Linux, with like, you know, or is it? It's a pretty good process. We had some issues with Ubuntu, older Ubuntu. There are, that is very much getting into desktop specific stuff, the whole integration with the task train, everything. So that varies, unfortunately. You're kind of, we were at the mercy of the library we were using.