RR Object Oriented Programming in Rails with Jim Weirich
- Published on:
- What is Object Oriented Programming?
- If Ruby or Rails programmers aren't programming in an object oriented way does it matter?
- The goal isn't to do OO for OO's sake.
- Tell, don't ask
- Case switching on the object's class can be refactored to take advantage of Polymorphism
- Law of Demeter: Am I allowed to know this?
- Single Responsibility Principle
- Ask "What does this object do?" rather than building around the data contained in the object.
- Presenters (The Presenter Pattern)
- Steven Kabnik's Post on Object Oriented Programming and the Presenter Pattern in Rails
- The problem with Helpers
- In OO, you're allowed to add more Objects (or Classes)
- Building your models without referencing ActiveRecord
- Business objects that reference ActiveRecord data access objects
- Object Thinking
- The database is the last bastion of non-object oriented thinking
- Behavior centric vs data centric design
- The impedance mismatch between the database and the object oriented designs
- Object Oriented Design centers around maintainability and complexity
- Don't use generators
- Create classes and evolve them into models as you need the persistence
- Rather than asking for data, tell a class to do something for you
- Three small things to watch out for:
- Switch/case on class
- Arrays and hashes or arrays of hashes of arrays (Primitive Obsession)
- Subclassing Array or Hash
- Don't inherit from String
- Enumerable module
- Rake's FileList
- Skinny Controllers
- Is REST simply a way of pulling the impedance mismatch from the database all the way up to URL's?
- RailsBridge workshops (Josh)
- Ryan Tomayko's blog post on Unicorn (James)
- The Unix System Programming with Ruby list (James)
- awesome_print gem (David)
- vimcasts.org (David)
- hub (Avdi)
- AeroPress Coffee and Espresso Maker (Avdi)
- Institute of Awesome (Chuck)
- Bloons Tower Defense 4 (Chuck)
- World of Goo HD (Chuck)
- Writing Solid Code (Jim)
- Ukuleles (Jim)
- Jake Shimabukuro
JIM: What I really like is there is so many Ruby podcasts out there that are newsy, but this one dives into technical issues -- and I really enjoy that.
CHUCK: Hey everybody, and welcome back to the Ruby Rogues podcast. This is your host, Charles Max Wood. And this week on our panel, we have a special guest rogue. I met him at the Rocky Mountain Ruby Conference in Boulder, and he actually suggested this week’s topic. So, we are going to welcome Jim Weirich to the podcast.
JIM: Thanks for having me. Glad to be here.
CHUCK: Do you wanna introduce yourself really quickly, Jim, for those one or two people that don’t know who you are?
JIM: Oh sure, absolutely. Jim Weirich, I'm from Cincinnati, Ohio. I've been doing Ruby for over ten years now. I work for Edge Case, where I'm the Chief Scientist. And I've probably written software that you are using, such as Rake and various mocking frameworks, and some XML builder stuff too. So you are probably using some of my code somewhere.
CHUCK: Thanks, Jim. Yeah, we are still waiting for him to write something useful, so.
JIM: [Chuckles] I´ll get right on that.
CHUCK: All right, thanks. [Chuckles] Also on our panel, we have Avdi Grimm.
AVDI: Hey, Avdi here. Little known fact, I am actually on Emacs major mode. CHUCK: Okay. We also have Josh Susser.
JOSH: Good morning from San Francisco, where we are just two days away from the Golden Gate Ruby Conference, which makes me horribly excited. So if I sound incredibly upbeat on this podcast, that’s why.
CHUCK: [Chuckles] All right, thanks Josh. We also have David Brady.
DAVID: Hey, this is David Brady. Can you guys hear me?
CHUCK: We can all hear you.
DAVID: Okay good, I've been having mic troubles today. This is David Brady, and I run Shiny Systems and I'm an Emacs minor mode, that inherits from Avdi Grimm.
CHUCK: [Chuckles] All right, we also have James Edward Gray.
JAMES: Usually, I worry about being outnumbered on this podcast by people from Utah. Today, I'm worried about being outnumbered on this podcast by people who use Emacs.
JIM: I am a strong Emacs user as well, so I'm still tweaking it after 25 years.
DAVID: I got most of my .emacs file from Jim Weirich. I have to… I learned it from watching you, okay! I learned it from you!
JOSH: I don’t use Emacs, but I do remember when it was written in TCO.
CHUCK: All right, and I'm Charles Max Wood. I just watched RailsRookies.com. It's just at right now, a list of courses that I'm going to be teaching over the next few months. So if you are interested in learning or becoming better at Ruby on Rails, then go check that out. So this week’s topic, as suggested by Jim. We were talking, and Jim mentioned that a lot of Rails developers don’t do much object-oriented programming. And I thought it was interesting, considering that it's in Ruby, which is a heavily object-oriented language. So Jim, why don’t you go ahead and explain what you meant by that, and then we'll start discussing it.
DAVID: Do we need a definition first?
JAMES: Not again.
DAVID: All right, we'll waive that.
JIM: Well yeah, that’s a good question, “What is object-oriented programming?” And you might think, “Well, we do Ruby and that’s an object-oriented language, so of course, we are doing object-oriented programming.” But I don’t think that’s necessarily true. I know that when I was teaching C classes way back when, I had people who could write Fortran code no matter what language they were actually using. I think that’s still true today. And specially with something like Rails. Rails makes it really, really easy to massage databases. And so you got a lot of objects that represent stuff from database. And database stuff is all about data. And to me, object-oriented code is not so much about the data, but about what the code does and how it does it, and what does it do, the behaviors of objects. And I think we forget about this behaviors, and concentrate on the data just a little bit in Rails programs.
JOSH: I agree, Jim. I've seen a lot of Pascal written in Smalltalk myself. And I think maybe one of the flaws of Ruby, is that it's easy to just throw together an array or a hash to hold a few values, and not worry about what you are doing with that stuff.
JAMES: No, wait, wait, wait. Is that a flaw? Are we sure we are comfortable calling that a flaw?
JOSH: Well, anything that leads to the bad programs would be a flaw, in my opinion. [Chuckles]
CHUCK: Right, but you also have some powerful constructs like arrays and hashes, that allow you to do important things inside of your objects to get the job done, right?
JOSH: Okay, so I'm to saying that arrays and hashes are bad in of themselves, but it's kind of like a gun without a safety; it's too easy to shoot yourself in the foot. Ooh, I actually nailed that metaphor. [Laughter]
AVDI: So I have a question that I kind of wanna get out there early on. If Ruby or if Rails programmers aren’t programming in an object-oriented way, does it matter? And if so, why does it matter?
JIM: Let me address that a little bit since I brought the topic up. And I will say that programming in OO way, just to be programming in OO way is not the goal. It doesn’t make us good programmers, just to be doing it that way. There are a lot of programs -- and even Rails programs -- that don’t really need OO and just a purely data-driven approach is perfectly fine. I have no problem with that. But there are times when you wanna handle behaviors in your code, and because you are thinking so procedurally, you forget about the OO aspect of the language. I was just looking at some code yesterday, where someone was checking to see if it responds to a particular method before he calls it. In the same [inaudible] object, why don’t you just call the method, and put a definition up in the base class and just handle it polymorphically? He’s just wasn’t thinking in OO way, in that point in time.
JAMES: That point Jim just raised, the polymorphism, I think that’s the easiest way to tell the difference between Rails code that is object-oriented and Rails code that isn’t. Like when you see polymorphism done well, then you know you are looking at an object-oriented code. And when you don’t, you know that you are not.
CHUCK: So for our new listeners, why don’t you explain what polymorphism is?
JAMES: Okay, there's simple concepts of object-orientation, right? And attributes that represent object oriented code. And probably to that effect, this conversation the most would be encapsulation. And encapsulation is the idea that… basically what Jim was saying earlier, in that data inside of objects is kind of a private detail of the objects; and it's what those objects can do, that makes them interesting. Does that make sense?
JAMES: Then, polymorphism is the the way objects respond to messages. So a really good example of it that you see over and over again, is a lot of times you'll see like a case statement, where like if it's object xyz, do this; if it's object def, do this; object abc, do this. And then they handle it three different ways. Whereas the right polymorphic way to handle that is to get a method on those different objects, and then you pass in the object and you call that method. And one matter what it is, it will do the right thing for that context of that object. So, basically to use the object’s behaviors themselves, to differentiate what's actually happening to the object. And if I call such and such method on an xyz object, it should do this; and if I call the method on an abc object, it should do this because it knows what it is, and it knows how to behave as it should.
CHUCK: All right, so how does that help us with Rails? I guess we had one example. Do we have any other good examples of where people could use this to make their code better?
JOSH: So James touched on the case statement. And I’d say any piece of code you look at in a Ruby program that has a case statement, that switches on the class of an object is a bad smell. And that’s not the best object-oriented code you could be doing.
DAVID: One of the tricks that I use for polymorphism, specifically with case statements is when you write that case statement, look at the top of the condition, basically, the case foo, and ask yourself kind of the Law of Demeter, ask yourself, “Am I allowed to know this?” And ask yourself, “Am I looking at somebody else's implementation or am I looking at guts of something? And you can often find that yeah, if I wrap this up into an object, I can put the implementation of that case foo, I can put that into three different classes, and now all three classes can have the same behavior and I can say, “Just handle this.” And they can implement it differently, but the behavior is the same. And so that’s the trick that I use is I just ask myself, “Am I allowed to know this?” And I’d hardly ever write case statements anymore because I'm usually not.
AVDI: So let me play the devil’s advocate here for a second.
DAVID: I´ll point out that your client is the devil.
AVDI: [Chuckles] Let me point out like a common objection, I guess. So you have a case statement, which might have maybe three different cases for three different types of object. And for each type of business object, it writes out different HTML. Sort of the immediate polymorphic response to that, seems like it would be okay, you actually stick two HTML method on each of those business objects and then you just call to HTML. But now, those business objects are also responsible for displaying themselves, which seems like a pretty big violation of the single responsibility principle. So I'm curious how people respond to that.
CHUCK: Well, aren’t you just trading one problem for another?
AVDI: I'm curious how Jim addresses that. I mean, I know how I address it. And I certainly would not put a to HTML on one of my business objects, but I'm curious as to how other people address it.
DAVID: Rails does encourage you to make that trade off by the way -- and that’s bad. This is the whole point of our topic.
JAMES: Actually, I’d like to touch on what David just said right there briefly. David did just say, Rails encourages a lot of these. And I think that is true, and that we need to think about that, that Rails in its design for example, how it moves instance variables down from the controller to the views, that’s a violation of encapsulation, right? And yet, it's not a bad thing. I don’t really know that it is. Let’s imagine that that didn’t exist, and you wanted to get data down to the views, then you'd have to call some method and say something like, render whatever, and then pass some value down. Whereas the way Rails handles it is just through the simple assignment of variable. So I think that is interesting that Rails does encourage us to… in some places, helpers are another great example and stuff like that. But let’s go back to Avdi’s question. What about throwing to HTML or maybe to give a more Rails example, the Rails way would be the throw .toxml and .tojson on everything, right?
JIM: The problem is that you don’t know in the business logic exactly how things are going to be displayed, whether it's going to be XML or HTML to json. So, putting display responsibilities in that object is part of the problem. But we just moved that into different object, let the polymorphism work there; let the business object decide what information it wants to display, and go through some kind of presentation layer or whatever you need to get that done. I mean, it's a fairly easy thing to solve, but you'll have to think about it a little bit, and not just put a method, like oh, it will be convenient to put this method here and just drop it wherever you want to.
CHUCK: Right. So what you are saying is that you would have for example, a user object that manages the users and does the users stuff – it has the user job. And then you have the other one that’s like the user presenter, that presents the information for the user, in whatever means that you wanna put it in?
JIM: yeah. they key is you ask yourself when you design an object, what does this object do for you? And you use it based upon what it does, not necessarily what data it has. That’s the difference between a data-driven design, and object-oriented design, I think. Here's an example; I've mentioned XML builder library earlier, that is just a simple library that builds up XML, based upon the call that you make. You can abstract that a little bit, pretty much tie xml at this point but you could extract the builder object, or you just pump in the data, and then it would format the data in whatever format you wanted it to be in.
JAMES: So Jim, you were talking a lot about presenters and things like that. Did you see Steve Klabnik’s recent blog post about object-oriented programming in Rails, and then he had a follow up post about the Ruby presenters. Do you happen to read those posts?
JIM: I did read those posts. I kind of scanned them quickly, to be honest. But yeah, I did see them.
JAMES: Yeah, and when you think of this?
JIM: I thought it was really good, for the most part.
DAVID: So for the benefit of the listeners, I did not see those posts. Can someone summarize?
JAMES: Sure. So basically, Steve said that… for example in his first post, his primary point was Rails helpers are evil. And basically, his reasoning behind that was that they encourage you to do individual functions for presentation stuff, whereas using something like a presenter object like Jim has been discussing, is generally superior. Now Steve kind of said, “always superior,” I don’t think I agree with that. I think there's cases where a simple one off function is just fine. But for example, if you look at Rails helpers, to make Steve’s point a little bit, look at something like the number helper; because we have all these different ways to do numbers, like there’s currency or delimiters or whatever. You end up with all these one off functions, that aren’t really related to each other in every single view -- and it's kind of a mess. Whereas, it might be more useful to be able to treat something as a number, and then have all kinds of conversion methods on it. And then his second post basically goes into that in more detail, as far as what are presenter objects and how do they work. But what I thought was interesting. Both of Steve’s posts and what Jim has been saying is in object oriented programming, you are always allowed to introduce new objects. And I think people kind of feel a resistance to that. Well, in Rails, we think we are only supposed to make the models that inherent form ActiveRecord, and the controllers that Rails will make for us, those kind of things. That I think that some people forget that it's okay to make your own object that handles another part of the system, that isn’t necessarily directly tied to the database or inside the controller or something like that.
JIM: I think that’s absolutely true that people forget that you don’t have to make ActiveRecord objects. You can make any kind of plain old Ruby objects to do what you need the system to do. A little thought about what behaviors you want your object to have, goes a long way.
DAVID: I think by popular demand, we need to ask Josh Susser for a definition of plain old Ruby objects.
JOSH: [Chuckles] Well, they’re the unseasoned Ruby objects; if you like grill them in a little light oil and add some salt, they are great. [Chuckles] So, plain old Ruby objects, they are objects without all of the ActiveRecord stuff added in them. And what I really think about them is that they are the objects that are not directly backed by database records. But Ruby objects are amazingly functional. Well, maybe that’s the wrong adjective. But they have a lot of functionality in them, and you can do a lot with them. So I think that pulling out pieces of business logic into separate classes that have single responsibility, is a great way to improve Rails programs. I built a little gem to help do that. I call it informal. And this is an example of taking a plain old Ruby object, and enhancing it with a little bit of the ActiveRecord API, so that you can use it in your form in your view, and also in you controller and the object validation, and redisplay life cycle. And it's just a tiny bit of code; I pulled in some of the active model functionality. And it's slightly tricky to do and that’s the only reason I built informal, because active model wasn’t quite built to do this itself. And I extracted this basically from an application what I said, “Oh I got to do this.” Because it's so easy in Rails, with the way the view stuff works, to build these data structures -- things like arrays and hashes – that hold all the data from the view, that don’t do anything with it. So we had a login form that didn’t have back with an ActiveRecord object. And it was too difficult to work with, just as arrays and hashes of data, so we build a little class and suddenly, “Oh well, we should be just plugging this thing directly into the form.” And that whole part of the program is so much easier to deal with; you can just build a very simple restful controller; didn’t have to know the structure of things as much. And so that’s very classic object-oriented programming; it's not rocket science, it was very simple to do, and it's something that I think all Rails programmers should know how to do that kind of thing.
CHUCK: I think it's interesting too that in a lot of cases, we find that people, they kind of don’t completely understand things, and so they just assume it's hard and then you find out that something like that is actually really, really simple. And it's a really simple and elegant fix, for whatever issue you have; whether you are violating the single responsibility principle, or whether you are doing some of the other things we've talked about.
AVDI: Well to be fair, some of these stuff used to be harder than what it is now. It used to be kind of a pain to come up with just like a regular old Ruby class, which would play nicely with routing helpers and form helpers and all that stuff. And a lot of times you'd just hit a point where it’s like, “Oh, this is too much work. I'm just going to inherit from ActiveRecord.” And in ActiveRecord 3, they really have done a very nice job of splitting things out into active model, and making a lot easier and a lot better documented exactly what interfaces your object needs to support, in order to make the helpers happy. Now talking about the split between ActiveRecord-based objects, and plain old Ruby objects – which is a term I kind of hate, but I´ll leave that at the side for now. As there's kind of been a little bit of a renaissance in thinking about object-oriented principles in Rails lately, I've noticed that a lot of people are looking at an approach of actually having kind of thin models… not thin models, but having thin ActiveRecord objects which just have associations, and validations, and scope in them and no business logic, and they are basically treated as data access objects. And then you have business objects, which are just pure Ruby with no ActiveRecord base, and the business objects have an internal reference to their data access object. I'm curious about opinions on this approach.
JAMES: So I actually have opinions on that. I think it can be a good thing. I think the reason a move like that comes out to be positive is that, a lot of times, we have business logic applies to multiple pieces of our database. For example, this particular piece of business logic I'm managing, may affect users and their subscriptions. And in that case, splitting it out into a separate object seems to make more sense there, because where does that functionality belong? On user or on subscription or whatever. The fact that I may be manipulating more than one thing at a time, and working cross purposes like that where I just have to reference everything I'm going to work with, I think is more natural. But I think you can take it too far too, I mean, there is a user object and that is the user. And there are things that just apply to the user, like resetting their password or something like that. And I don’t see any problem attaching that functionality to the user object. In fact, I think it's good object-oriented design to do so, because it is the behaviors that operate on that data.
DAVID: There is a little bit of a danger here as well -- and maybe Avdi is being deceptive deliberately here -- but because Avdi, you've heard Object Thinking, haven’t you?
AVDI: I have not.
DAVID: You have not. Okay, so maybe this is unintentional. But in the great philosophical war between software composition, where you start at the bottom and build up; and software decomposition where you start at the top and build down, there's this thing called the impedance mismatch problem. And we've heard this stated a lot in other ways, but specifically with object-oriented programming, the database is the last bashing of the barbarians, when it comes to anti object-oriented thinking. And so ActiveRecord has completely surrendered to this, unfortunately. You do not interact with ActiveRecord in an object-oriented fashion, typically. You get something out of the database, and now you have passive data at rest and you can operate on it, you can query, you can change it, and then you can tell it to save itself back to the database. It's very three state machine, it's very passive data, it's data-based programming. And this can bleed up into the other rest of your program, because it's very easy to do this, right? It's, “Oh, the easiest way to run this object into something we can use on the form is to put a .tohtml on it, and now we have passive HTML data, that we can query and that we can pull and we can pull out.” And so, Avdi your question of, “Should we have a separate business logic layer?” And James’ answer of, “Maybe yes, maybe no. Sometimes yes, sometimes no,” there's churn here. And the reason there is churn, is because you got the database, which is kind of running at one speed and it's trying to mesh gears with object-oriented code in another… and the reason why it feels like there's an impedance mismatch here is because there's an impedance mismatch here. I'm curious to know if anybody has had this kind of problem. And I have a specific problem about presenters that happens with this, but I figure I´ll stop talking for a minute and let other people have a chance.
JIM: I have definitely seen the impedance mismatch. In fact, there was one job I was on, where I was working with another developer, and he had a very data-oriented way of approaching problems, and I have a very object-oriented way of approaching problems. We would butt heads all the time; couldn’t agree on how to approach problems at all. But it was very interesting, because one of the problems we were working on… I forgot the details, but it's some kind of an authentication library that we were using, and we had to authenticate users against roles, against you know particular privileges, against particular jobs that people needed to do. And I came up with object-oriented design very early on that specified how the system would behave. And because I captured the behavior of the system, that tend to be pretty stable throughout the life of this project. And we coded up the the objects and we were able to work with that. Now, the database side of the house had entirely different issues; they couldn’t decide whether they wanted to go with fully relational database, or maybe an LDAP system would be good. And I think we were making schema changes and fiddling with it down to the last week, before the thing went live. But the behavioral description of the system oop was steady and constant. All we had to do was change the mapping of how it was stored. I think that’s generally true, particularly with internet application, behavior tends to be the constant there. What you wanna do with the program tends to be same, so you program with that with an OO. However, very interesting thing, this will be where the OO db mismatch comes into is that when you start talking about larger applications spread across different user bases perhaps, the emphasis switch from the behavior aspect of that, to the data aspect of it. And data tends to be the thing that’s more constant. That’s why we have humongous enterprise-oriented databases, that lots of different groups are using and pulling different pieces out of it, because that tends to be… comes out of the big enterprise level stuff, whereas at the application level, it's the behaviors that remain constant.
JAMES: So kind of looking at the idea of how the separate business logical layer or from the data layer itself, it seems like that would make it easier to do things like switch from a relational database, to say a document database or things like that. Another point where it might be a win is if you have several application sharing the same business logic, their data wouldn’t necessarily have to be exactly the same; so lower level could be a little different, but the business logic on top of it if it was written correctly to work in those scenarios.
CHUCK: So what do you call that business layer? Are they models or super models?
JAMES: I like “super models.”
AVDI: They are models. They are just models.
JIM: They are objects.
AVDI: If they model a concept in your business domain, then I think it's correct to call them “models.”
DAVID: Yes, and that’s the trap that Ruby programmers fall into; they think that app/models is the directory for their ORM layer, and there must be a one to one mapping between classes there, and tables in the database -- and that is not true.
AVDI: Yeah. Very, very brief rant here, I'm going to get up on a soapbox for just a second. If you have an object which represents a concept in your business domain, it belongs in app models. It does not belong in lib.
JAMES: What? Not in lib?
CHUCK: It belongs in vendor plugins. [Laughter]
JOSH: Since Rails 3, it belongs in engines and a gem. [Laughter]
DAVID: I keep everything in my .bundle directory. I don’t know about you guys. [Laughter]
JOSH: So before we got too far field, I think there's a couple of other topics to look at, at the ways that Rails can encourage people to do poor object-oriented programming or discourage good object-oriented. [chuckles] So object-oriented programming, typically I think of it as encapsulation, polymorphism and inheritance, and we haven’t talked about inheritance very much here, but I think that that’s number one pitfall for people trying to do object-oriented programming; they try and, they think of inheritance as sole key to object-oriented programming, that if they are not inheriting from something, they are doing something wrong. And usually, it's the other way around. Being able to delegate stuff to another object to take care of something in your behalf, that's usually a better way to do that.
CHUCK: And I was going to point out anyway that inheritance is the only way I'm ever to get any money. [Laughter]
DAVID: Waiting for you parent objects to die. That's a whole topic. That’s great.
JOSH: Let’s do a show on that.
DAVID: I'm going to write a gem called Vulture. [Laughter]
JOSH: Okay, so ActiveRecord base; this is one of the things that experienced object-oriented programmers will complain about ActiveRecord base. And other rubyists will just look at that and go, “ What’s the big deal?” And part of it is what Avdi has been talking about here about the way to incorporate the persistence into your business model in Rails is to inherent from ActiveRecord based. And that's the standard way that everybody does that. And that adds all sorts of weirdness to things. For instance, you have to hack to the database when you are doing the tests. You can't just test your domain logic, your business domain logic, you're also testing all of your ActiveRecord queries. So if you are… the persistence was included in your models through an API that was meant to be through composition, then that would simplify that a lot, and people wouldn’t have to be reinventing the stuff all for themselves.
JIM: [inaudible] really have that though, isn’t that exactly what the database adaptor does? You compose with that and the save and create methods just delegate down to that.
JOSH: If there were a clean membrane between those two layers, I would agree; but there's too much of SQL that leaks into… it's a leaky abstraction. You get all of the SQL stuff that you have to deal with, with an ActiveRecord and that pop stuff even in the API all over the place. If you are doing joins or includes, which ActiveRecord queries, these things become very obvious.
DAVID: And we don’t have to go back in Rails very far to remember a time when there were bugs in the ActiveRecord… the MySQL adaptor or the PostgreSQL adaptor. Remember that? It was always lagging the MySQL adaptor. And in theory, we don’t have to test the database. We don’t have to test the database connection. And I think that’s great. And we can now be cavalier about it and say, “Let’s not test the database connection.” We can stub and mock that, but we don’t have to go back very many years; 2009, 2008 when you did not have to have too many joins before you really needed an acceptance test that exercise the full stack.
JAMES: So that’s an interesting point, but Josh talks about having a layer separate where we went through that. And the interesting part about that is wouldn’t that create more of the problem that you worry about Josh, where people are just throwing together arrays and hashes of data and running through those to do things?
JOSH: Perhaps, but that is an easier problem to mitigate, than the problem we have right now. That can be solved just with a little bit of good design. The problem of the persistence layer leaking into the business domain logic layer isn’t something that’s really easy to deal with in Rails right now. It's in fact very difficult.
JAMES: Interesting. So I’d like to go back to the question Avdi asked a long time ago, almost back in the beginning. He said, “Why do all this? Why does it matter? Can't we just build a procedural Rails app? Doesn’t that just work? Why do this? Why do we have to?”
CHUCK: Well apparently, it does work. I mean, people are doing it, right?
DAVID: It works up to about 20,000 lines of code.
JAMES: That’s right.
DAVID: Or based on a client that I'm looking at… not necessarily right now, but in the past two weeks, up to 70,000 lines of code. And in a Rails project, that’s too freakin big.
CHUCK: Right, but what I think we are talking about isn't necessarily does it necessarily does…
DAVID: What I'm saying is that it doesn’t scale. The complexity doesn’t scale beyond a certain point. Eventually, if you have passive data, you have to have universal adaptors. This is kind of like my comment about database adaptor; it has to be universal in order for everybody to interface it in every possible way. And if you write procedurally, you've got passive data. You must protect this data from any kind of weird side wards access. And that can work up to 5,000 lines of code, 10,00 lines of code, 20 or 30,000 if you are really bloody minded. But eventually, the technical debt just starts to go exponential on you. Where like Jim said, if you say, “Here’s the behavior at the very beginning,” And then you discover that the behavior didn’t change, it stayed very stable throughout the entire… now they refactored, they changed implementations, but the behavior stayed very stable the entire time.
CHUCK: Right. So what we are talking about here isn’t whether or not you can build a Rails app that works. What we are talking about is maintenance cost down the line.
DAVID: Right. Should you build a Rails app that works, no wait, that's not what I was trying to say.
JIM: [Chuckles] And I think that’s very important; capturing the behaviors and having that be constant, that's the benefit of going with the OO approach.
DAVID: Actually the better question is, and there is the discriminator. It's not “Can you build a Rails app that works in procedural?” Because the answer is absolutely yes. The question is, “Can you build a Rails app that can be maintained?”
JAMES: That's a great question, I think.
DAVID: And the answer is maybe.
CHUCK: The answer is, given enough resources.
AVDI: And the answer in my experience is, as long as you are willing to accept a very, very slow evolution of features, and a fair amount of, “Oops it's broken, we have to roll back.”
CHUCK: Well, I wanna raise my hands and ask a question. And this one really is, I'm really writing good at writing good procedural Rails code. So what approaches are there to make my life, and my code easier to deal with?
AVDI: I was just thinking about a very similar question. It's basically the same question, “What is the one thing you can do to approach your code, your Rails code, in a more object-oriented way?”
CHUCK: Or even several things.
AVDI: Well, I don’t think I have time to listen to several things, but I´ll say one -- don’t use generators. Generators, they sort of push you in the section of first of all, thinking about the data first, because you generate a resource with all of your data attributes listed out. And they also push you in the direction of thinking of things in terms of a 1 to 1 to 1 relationship table, model, helper, controller, view. Just start writing that object as just… a plain old Ruby object representing your domain, and add things in as you need them.
DAVID: Okay, so you said very succinctly and very well the answer that I was actually going to suggest. I recently did this on a small project, and it was absolutely brilliant. I learned this strict from Smalltalk. So in Smalltalk, when you start assigning your objects, you build everything together, but you need this image, right? Everything is in memory, in this virtual machine inside the image. And so, you can put off serializing to the database; you can leave it alone forever, because you can turn off Smalltalk, the image goes away, you bring it back up and all the objects that were on memory are still in memory. So why don’t just go ahead and translate that into your next Rails app? Just for fun, write you next Rails app. Start off by defining the objects and the interactions, and don’t inherit form ActiveRecord base. Don’t save them anywhere. Just leave them in memory. Let the server hang on to them. Okay you are going to need like a class level hash on each class and this is crazy and it's starting to get into the stupid side of crazy, but work out those behaviors and those interactions. And I was staggered by how many really obvious… like I was going to go right to the whiteboard and draw my entity relationship diagram for the database, that we sat down and we started figuring out, “Well this object really talks to this object. But you know, it turns out that this object really has more of this data.” And just by leaving the data in the server, okay when you reboot the server, you lose all your data because it's on memory, right? But this is just a scratch server; it's just a dev server. And if you just leave these things out there, work out the interactions between them, and then figure out how to serialize, you push the impedance mismatch problem all the way down to the very end, to the database layer. And then the database gives you grief, because it really is angry that you have dissed it like that. But it's extremely enlightening. I highly encourage anyone to try that on a project once. You will be astonished at how your object model changes.
CHUCK: That’s awesome.
JIM: That’s a good idea. I´d also like to make a suggestion; whenever you want to get data out of an object and do something with it, stop and think if you can do the reverse. Instead of asking for data, see if there's an object you can tell to go do something. The idiom is, “Tell, don’t ask.” If you start asking that question, that helps as well.
JAMES: I was going to basically say the same thing, that I learn the most when I started trying to push more than I pull. So when you call the method, to pass in all the parameters needed to figure it out and stuff like that. And that’s kind of what ActiveRecord gets a little backwards, in that it basically forces us to pull everything, because you pull that record from the table and then it has all of those attributes in them that you pull them out, to figure out what you are doing. And most of the time, it's much better than say pulling these attribute and changing it to something else, it would be much better to call a method that has the correct behavior, to affect the desired change.
JOSH: Three smalls things that people should watch out for, that are easy to avoid if you just think about it. And one is case statements -- especially case statements that switch on the class of the object. So if you see a lot of statement, especially case statements with object classes in them, that's an opportunity to move to a more polymorphic system and have better object-oriented design. Another thing that I've already mentioned is simple data structures, like arrays or hashes, or arrays of hashes of arrays, like we see in ActiveRecord a lot. That could often be modeled better as a class that…
DAVID: Primitive obsession, yeah. avoid it.
JOSH: Yeah. And then one thing that I´ll just say you should never, ever do is inherit from array or hash. Unless you are building another fundamental data structure class, like the hash within different access, just don’t do it. If you have a class that has a bunch of things in it, have an array instance variable and put the things in there. Don’t inherit from array.
DAVID: So compose from array and hash, but don’t inherit.
JOSH: Absolutely, thank you.
AVDI: The Forwardable library is your friend.
JAMES: Absolutely, yeah.
JAMES: I would even say, Josh gave a couple of circumstances where it's okay to inherit from array and hash. And actually, ignore those -- don’t even do it. Because Ruby takes some implementation shortcuts with those classes, and every now and then, it can come back and bite you, because it doesn’t follow the proper rules. An example I can think of right off the top of my head is that people inherit from string, and then when Ruby does like the regex replacement, sometimes it skips string initialization. So you may end up with a string, but your initialize method never got called. So yeah, just don’t do it. There's so many dragons down that road. You are going to get bit.
DAVID: Well, also in Ruby 1.9, haven’t they broken most of that stuff out now, so that if you really want a hash like object, you can just mix in enumerable, right? And a couple other modules. And then build enough data to support those, and then down the road you go, right?
JAMES: Yeah, basically what Avdi said is in the standard library, there's the delegate library and the Forwardable library, and you would be better in those cases to use those, so that there's a real object there.
DAVID: Right. If you mix in enumerable, your object will now present the API of an array or a hash, externally.
JIM: Rake actually does that. It actually provides an array like object called the file list. And the original version had that inherit from an array, and I found that it was much easier to more closely emulate the real behavior of array, if I didn’t inherit from it, but delegate it to it. Conversion issues came up and made that true.
CHUCK: That’s really interesting. So one other thing I wanna ask, and I was going to ask this earlier. So we kind of talked about… I think we talked about the presenter pattern a little bit with Steven Klabnik’s article. And we’ve talked a lot about models and ActiveRecord. Does this apply in cases with the controller at all? Or is this more focused toward handling things well for the view, and handling things well for the model?
DAVID: Good question.
JAMES: You know, controller is an interesting beast, because I think everybody is always trying to find a way to make it go away. And really, that seems like almost the ultimate best thing the controller can do is go away. Because ideally, all of our controllers should be almost identical. We are told that restful controller with the seven actions and they are just predefined. In the ideal scenario, that would be every controller, right?
DAVID: And it's so wrong.
JAMES: But in a way, what the controller does, the job of the controller is to get some data and then pretend for the view; to show that view, to trigger the process that will show that data to the user. And ideally, that would be handled in some uniform way, that wouldn’t require us to write code at all, but it's not really the way it works. So I think, as far as how I handle the controller, is I try to keep as little bit anything going in there as possible. I wanna just call a few queries to get some data back, and then hand off to presenter objects or whatever, that are actually going to make sure that the view is rendered as needed.
AVDI: It's probably worth noting that the MVC pattern was originally built up with desktop event driven GUIs in mind. And I think fatter controllers probably make more sense in that context than they do in the web. Just a thought.
DAVID: My devil’s advocate there is that I find a lot of people, especially when they are… when I say like a bible thumping rest advocate, a real rest evangelist, nine times out of ten, and I'm not trying to like incriminate them character-wise, but what I find is that somebody that’s really religious about rest, all they are really trying to do is pull the impedance mismatch off from the database, up through ActiveRecord, up into the API, so that now you have data at rest in the controller level. And so I tend to be the one guy on the team who advocates restless controllers, and you should hit a controller and it should make the system behave a certain way, rather than necessarily give you back a list of data. The controller should not just be an API into your SQL database to allow you to list objects and iterate on them. You should have some real behaviors there. Is that wrong?
JOSH: I think it's just a different focus. The point of rest is I think about… it's about applications interacting with each other, and the URLs become the API. And there's another level of impedance mismatch problem there. It depends on where your focus is. So I think rest is a really good solution to the problems of applications and servers interacting with each other. But it's not going to give you the best design necessarily for how your application operates internally. We have seen that it really reduces the amount of code that you write in your application, because a lot of the coding has now been pushed into the framework and you can reuse that. So that is one damage.
AVDI: And the common Rails idea of what is restful isn't terribly restful anyways. But that is a topic for another show.
DAVID: Yeah, we should do a whole show like on service oriented splitting things out like that because a lot of the SOA guys that I'm talking to are actually advocating staying away… you know, having rest as a basic fallback, but also having very complex controllers just to reduce the number of trips you have to make between services. And I need to stop talking about that because it needs to be for anther show.
CHUCK: All right, we are going to go ahead. It sounds like this is a good breaking point. And we’re definitely going to go over this time. We'll get into the picks. If you are new to the show, I say this every week. If you are new to the show and you don’t know what the picks are, basically we just talk about one or a few things that we've been using over the last week or the last little while, that we like or kind of tie into make our lives better. They can be code related, they can be non-code related. I mean, we have office toys and different toys. Anyway, it can be just whatever. We'll go ahead and we'll start out with Josh.
JOSH: Okay so my first pick is the Rails Bridge workshop. Have you guys heard of that?
JIM: Oh yeah.
JOSH: So Rails Bridge have been around for a couple of years, and it's a group of people who are supporting increased diversity in any Rails development community, by providing free Rails training workshops for women and their friends.
DAVID: Oh wow.
CHUCK: I have women friends.
JOSH: [Chuckles] Yeah, so they set it up, it's a yucky analogy but it's kind of like ladies night at a bar. You can get in free if you are a woman or if you are in a company of a woman.
DAVID: Rails is the new meet market. I love it.
JOSH: So I volunteered and taught at a couple of these workshops. They typically do a Friday night setup your laptop, so that you can do the… they are all ready to go for the all-day training in Saturday. They've been doing these in couple cities. There have been a bunch of them here in San Francisco, they do them in New York, they often do them before a Ruby conference in whatever city you happen to be in And they are great. It's an all-day training. You do test-driven learning of how to program. And a lot of the women who have been doing these workshops, I see them get hired to Pivotal Labs or at Blazing Cloud or various other Rails consultancies here in San Francisco. So it's a great way to expand the Rails development community. We all know how hard it is to hire Rails developers these days. And it's also a great way to enhance our diversity, which is a topic for another show, but there's definitely a lot we can be doing there. And I think it's great that they are doing this. And if you have a friend who’s not happy with her job as a PHP coder, bring her to one of these workshops and get her going on Rails.
CHUCK: Sounds terrific.
DAVID: James, what was your wife doing when she brought home that awesome regex problem and then she became the goddess of regexes?
JAMES: She was working for a food company at the time and they had to process just this massive amounts of ugly data, and she kept coming home frustrated with it and just asked me to show her something better, so I sat down and taught her regular expressions one evening and she used that to process through massive amounts of data.
DAVID: That's awesome.
CHUCK: I thought you said that what came to my mind was sorting food by regex.
JAMES: Kind of like that.
JOSH: [Chuckles] Okay, so that's enough for me this week. That’s my only pick.
CHUCK: By the way, you did mention and we've heard a few times, this is a topic for another time. If you wanna hear about this topics, go to rubyrogues.com, click on request a topic. And either vote them up if they are already in there, or type them in. And the diversity topic is an interesting one. Anyway, James go ahead.
JAMES: Okay, so I have the absolute best pick this week. Sorry, you guys all lose. A long time ago, there was a great blog post by Ryan Tomayko called I like Unicorn Because it's Unix. And that went around and it was very popular, just because they basically went through the unicorn server, which is my favorite Rails server because it uses all these Unix idioms to do what it does. And if you like that kind of thing, or if you think you would like to know about that kind of thing, the author of unicorn has started his own mailing list called Unix System Programming with Ruby. And so, there's this mailing list and he's going through, and basically teaching you the Unix model, what it is, how to you use it in your code, etcetera on this mailing list.
DAVID: Oh yeah!
JAMES: It's really great so far. The articles, they start at the very beginning. You do not have to have like a very deep knowledge in this topic. He actually gives the requirements you need to know. Ruby, a little bit of knowledge of bash is helpful but not required. You do not need to know C. And the four thing you need to know is like Ruby can have multiple variables can point to the same objects. So you can do like, a=some string, and then b=a, and both those variables point to the same object. And if you change one, you are going to change both. And that if you know those four things, you can get on this list and follow along. It's very basic. And there's a lot of great people on there. I've watched some people talking and stuff. I have some pretty high hopes for this list, that it might become kind of a replacement for the Ruby talk, so if you find this kind of stuff interesting or you've always wanted to learn about Unix and it's intimidating about your whatever, this list is the perfect place for you. And I recommend it.
CHUCK: All right, that’s sounds really interesting. Wow. I used to be a sys admin and I don’t completely understand that stuff. Sounds good. David, go head.
DAVID: Just two real quick. The first one is the awesome_print gem. We are always playing with different ways to present data better than IRB or at the console or in the Rails console. And I think I've use them all. I've used … and boson, which is great. I've used … which is really interesting, but I didn’t quite get into it. And I was pairing with Charles last week, and he showed me awesome print, which is awesome. It colorizes, and it really clarifies things out really nicely. And the other one, and this is going to sound a little bit traitorous, but I'm going to recommend vimcasts.org. I'm definitely a fan of Emacs, but I believe that whatever editor you use, you should know it; you should know more than copy paste insert. And vimcasts.org is the single best screencast, I think I've seen in the past two years, just in terms of production quality. I mean, they are very well put together, they are very cleanly edited. They’re like five minutes long each, there's like 35 episodes, you can sit down and watch them all in whatever 5 times 35 minutes is. Except that you can't do that do that, because your will explode after about the third run, because he really gets into just some great details on that. So if you are a vim user, you need to be watching The Vim Casts, I really, really, wish somebody would start Emacs Casts and put the same level of quality, and excellent production value out there. Those are my picks.
CHUCK: All right, thanks Dave. Avdi, go ahead.
AVDI: So if you’re listening to this, you’re portably using Git for version control. And chances are 99% of the interactions that you do with git are dealing with GitHub-hosted repositories. And lately, I've been using a git wrapper called Hub, which is avaible in GitHub, which is a wrapper that adds a lot of sugar around interactions with the GitHub repositories. So you don’t ever have to specify like the whole repo; you can just specify like for a project just username/project name. And lots of extra stuff like that, like really easily if you got a project locally, you can just very quickly, create a repo and GitHub to push it to and stuff like that. So that's been making my life a little bit easier. Non-programming pick, I am also a big coffee nerd, and I try to collect pretty much every coffee making implement under the sun. So I've got press pods and I've got vacuum pod, and various other contraptions. Recently, I acquired an aero press and I don’t think I've picked this before. I hope not. But I've been drinking aero press coffee lately, and I think it's some of the best coffee I've ever had. So the aero press, it's made by a Frisbee company, but makes great coffee.
JOSH: I got to say that the coffee nerds are right up there with the Emacs fanatics, in terms of how much they tweet and post about this stuff.
CHUCK: [Chuckles] Well thanks, Avdi. That was great. I'm going to go ahead and go next, and then we'll hear some more wisdom from Jim. My first pick is mailing list that’s being put together by Amy Hoy. It's called The Institute of Awesome. If you are a freelancer, this is something that you probably ought to go and check out. Every day, I get an email from my inbox and basically, it just kind of introduces some topics, and then links to PDF that has a ton of information in it about, “Here’s something you should think about doing with your freelance business.” And it's really just awesome. And I can see this is kind of a marketing tool for her other product which is Let’s Freckle. But anyway, I just have to say I've read a handful of them. I haven’t had time to get through all of them, but just the ones I've gone through, they talk about like building a process that you go through for user stories, building a process that you go through for interviewing clients and things like that, so that basically, you can spare your executive function for more important things, because this stuff is pretty much lined up. And it's just awesome. So that’s one that that I wanna point out. The other one that I've been just doing stuff with, I've been playing a game on my iPad. And I've actually had it for a while on my computer and this is World of Goo. This is something that several people recommended to me. It's a really fun game. Kind of a physics-based game, where you build towers and structures to get stuff. And you are basically trying to get your goo balls into this pipe and then if you get enough of them and then you can actually get an OCD rating on each level if you get enough goo balls in, so that’s a fun one. And the last one is also game that I've been playing online. This is the one that David introduced to me last year at Ruby Web Conference, and it's Bloons Tower Defense 4.
CHUCK: It's somewhat addictive and I kind of play it, like every few months I´ll get caught, I´ll play it when I'm taking a break from my coding. And it's a fun game, it's a tower defense game; you build a bunch of towers and you try and pop all the balloons before they get through. Anyway, that’s it. just remember that the monkeys are right handed, and you'll do all right. Anyway, Jim go ahead.
JIM: I've got a couple of picks here that I´ll share. First is a programming book, but it's an older programming book, about ten or so years ago, probably a little more, I just stumble across this book. And believe it or not, it's from Microsoft but before you…
JIM: But before you freak out, Microsoft Press and they put out a book called Writing Solid Code, which was one of the early books that really, really changed the way I was doing development. It really affected the way I was coding. And this is before the Agile movement, this is before Ruby -- this is before all of that. Basically, he's one of the managers for I think it was Excel, how he kind of inherited the project and how he kind of turned it around from kind of a buggy program to actually a really good product. And he talked about a lot of the techniques that they used there, to kind of turn around. And he talks about stuff like developers need to own the bugs that they write. They can't just pass it off to QA. He says there's no free features, so don’t allow needless flexibility. That’s the whole, “You Ain’t Going to Need It” model from Agile right there. So it's kind of the precursor to the whole Agile thing. I've been really enjoying rereading this. And I'm hoping to kind of do a talk with this in Ruby Conf in a couple weeks. So I will apply these old ideas and recycle them around for that. Other pick I have is, this is more of a general pick, but a couple of years ago, I picked up a ukulele fairly cheap. And if you are ever interested in learning a stringed instrument. You wanna pick up something that’s easy, that's fairly inexpensive, that’s fun to play, go to a store and find yourself a good ukulele that you can play. And I really recommend that. We've got about three or four ukulele players in the office now. We've declared Friday to be the day we can all bring them in. We jam a little bit at the end of the day.
CHUCK: [Chuckles] That’s awesome. That’s totally awesome. Is there a good place to play the ukulele?
JIM: You know what, there's a ukulele club here in Cincinnati, that is totally for beginners, we’d come and we’d go through some of the music. If you are a guitar player, ukuleles are easy to pick up because essentially, it's the top four strings of a guitar, so it's easy to move from guitar to ukulele. And since it's only four strings, even a non-guitar player can pick it up very easily.
DAVID: So Jim I have a quick question for you, and it's a confession. I am physically unable to taking ukulele seriously. Is there something I can do to fix that? Or is that the correct response towards ukulele, are they meant to be just lighthearted and fun?
JIM: Now that reminds me. I actually got a secondary pick related to this. Go and lookup on YouTube Jake Shimabukuro. Go Google some video of him. The first video I saw, he was playing my guitar gently weeps on ukulele. And all of a sudden, I realized that ukuleles are not toy instruments; they are real serious instruments that serious musicians would play. And yeah, Google for Jake and you will be blown away.
DAVID: Shimabukuro, I bet he also plays the Shamisen.
DAVID: Which is the Japanese banjo.
CHUCK: I just thought you sneezed.
CHUCK: All right, well I guess that’s it, we are going to go ahead and wrap this up.
JAMES: Before we do, one thing. I'm usually the book club guy, but this week, Josh is taking over. So Josh, you wanna give us an update?
JOSH: [Chuckles] Right. So David, you weren’t on that show last week, but because you always talk about Smalltalk Best Practice Patterns, we decided to use that as our book for our book club next month.
DAVID: That’s awesome.
JOSH: And because he's a fairly awesome guy, Kent Beck has agreed to come be on the show with us.
JOSH: [Chuckles] So I wanted to let the listeners know that, so that they can be reading the book. And we are also going to set up something on the site for people to submit questions, so that we can have a little more listener interaction, and get them involved in the book club more. So yeah, we'll put something up on the website for people to send in the questions.
DAVID: I will try very hard to keep my fan boyism in check.
JOSH: [Chuckles] We'll just have a very long preshow for you to get it all out of your system.
DAVID: Just work it out. Just run around the desk, Dave! Just run around the desk!
CHUCK: [Laughs] If you’re not familiar with Kent Beck, he is not just the Smalltalk guy; he was involved with Extreme Programming, if I remember correctly. And I think it was also involved in the original group that kind of drew up the Agile manifesto. So he's been around for a while, and he’s made a lot of contributions to computer science. And so, it's really, really exciting to have him come on and share his experience with us. All right, so now I'm going to wrap this up. If you want more information about the show, you can go to RubyRogues.com. If you have a topic that you want to suggest to us, then go to the website and click, request a topic. You can get us in iTunes. And if you have any other podcast aggregators that you want to find us in, then email me Chuck@teachmetocode.com and let me know and we'll see if we can get them added to those as well. If you wanna leave a review in iTunes, that really helps us out as well. Good reviews, bad reviews; preferably good reviews, but we'll take what we can get. And beyond that, we will catch you next week.
JAMES: Thanks, everybody! Thanks, Jim!
JIM: Oh, thanks for having me.