Seven Habits of Highly Effective Gems
Brown, Mat
These days, publishing a Ruby Gem is incredibly easy—but publishing a good one isn’t. By following a few best practices when you release your code to the open source community, you can make your library stand out from the crowd. We’ll lay out some basic principles, touching on both code design and build tooling, that will have other programmers clamoring to use and contribute to your project, guaranteeing that you will become a code celebrity practically overnight.
 Music Alright, what's up everyone? How's it going? So, who among you has ever released a gem? Okay, good, I like it. Okay, who has released a popular-ass gem? Like tons of contributors, tons of users, tons of love. Okay, alright, so we want to get from like group one and not group one. Everyone into group two. Okay, so that's what we're going to do today. Before I get started, quick intro. My name is Matt. What's going on? If you want to tweet insults, that's a zero. I live in Brooklyn. Looks like this. I work for Genius. We're a platform for annotations. One time John Resig used our platform. It was really cool. And here's my talk. So, we're going to talk about seven specific things that are going to make your gem more appealing. It's going to make your users happy. So your gem should make your users happy, of course. This is a Ruby conference. So, we're going to talk about first how to get users up and running quickly. We'll talk about how to write really good documentation. We'll talk about how to change your gem without causing your applications pain. We'll talk about how to coexist with your user's applications environment in a nice way. We'll talk about how abstractions can lock people in and how we can prevent that. And we'll talk about how to make it easy on your contributors by giving them an easy to run test suite and giving them automatic feedback on their contributions. So, I'm going to start by talking about Quick Start. So, Sinatra, great gem. Really great micro, probably the best micro framework, I think. Sinatra, really good gem. And take a look at this. This is the top of the Sinatra Readme. This is the Quick Start. It's four lines long, which is amazing. There's four lines of Ruby. And then, you know, like the gem install, which doesn't even count, and one line to type in your shell, and you're good to go. And this is what we should all aspire to, right? We need a Quick Start that's this easy. And that's what, as Ruby programmers, we expect. And we don't expect that because we're not smart or because we're lazy. We expect it because we want to spend our time building beautiful, functional, wonderful products, not installing things and configuring them. So, having a really short, good Quick Start is actually a... It's a feature of your code, not of your Readme, right? You can't write a short Quick Start unless your gem allows it. So, how do we get to that point? Well, let's think about how we usually write a gem, right? So, probably what happens is I am working on my application. I have some code that I think, ah, I should open source this. This would be useful to some other people. So, I take the little bit of code out, and I create a new directory for my gem, and I probably stick something like this into my gem file so that my application points at the local version of the gem while I'm developing it. So, I might go on, you know, I might write some tests in my gem, I might flush out the interface a little bit, start writing documentation, and at some point I'll probably go back to my application and say, okay, well, does this all still work with this gem that it's now pointing at that I've been developing? But what I've never done at this point is actually feel what it's like to start using the gem from scratch, to start it in an application that doesn't use it already. So, something that's worked really well for me is to just basically make a, like an example app, it doesn't have to be a Rails app, whatever makes the most sense for the problem that you're solving, but make some sort of example app and really think, okay, if I wanted to do this, if I wanted to use this for some plausible use case, what does it feel like, what does it look like? I have always found at least one pain point that I can solve to make it easier to get started using this technique. So, getting that quick start down is all about just putting yourself in the position of a new user, which is something that doesn't necessarily come natural. Now, once you've got the user hooked, once things are installed, they want to learn more. They want to be able to easily find out everything that your gem has to offer. So, that's where documentation comes in. And we're back to Sinatra. Sinatra not only has a great quick start, but check this out. If we start scrolling down, we're still in the table of contents right now, okay? Still in the table of contents. This will take about a minute to get through the whole thing. Sinatra has an incredibly long readme, and that is a very, very good thing. So, there is nowhere that's more discoverable for documentation than the readme, right? It's there front and center in GitHub. It's there front and center when you're... We have generated code documentation. It's there on airplanes. It's there if you don't have the internet, because it comes with the gem and you install it. So, the readme is a really important place to give of what your gem is capable of doing. And in order to do that, I recommend making the readme a part of your development process, just like, say, testing as a part of your development process. So, you wouldn't submit a pull request, or you wouldn't even necessarily make a commit with new code that doesn't have some new tests. And you can take that a step further and say, I'm also going to submit a... Anytime I add something new to my application, I'm also going to add something new to the readme. And you can take that a step further and write the readme first. So, actually, think about, before you actually lay down any tests or code, think about how is... How am I going to explain this to my users? And you might end up writing a better interface once you think about things in that term, than those terms, than if you document after writing. So, the readme is really good. It's really important. Again, sort of every feature that your gem has, every major thing you can do with it, I think should appear in the readme. Very searchable. Command-F, great tool. But, of course, the readme can't be totally exhaustive. The readme can't take you through tons of examples of every method. It can't take you through all the possible options you can pass, and that's what inline code documentation is for. So, what we've got here is from addressable, another great gem. And addressable is using this... this params and return tag you can see here. And that is not built into standard RDoC. That is actually a documentation tool called Yard. So, that stands for Yay Ruby Documenter. And Yard adds a bunch of extra structure to Ruby code documentation that isn't in the built-in documentation for Ruby. And what's nice about this is that, you know, Ruby doesn't have a lot of structure as a language. You know, it's not statically typed. Everything's mutable at runtime. There's all this meta-programming. And that's great. You know, it's really good for us as programmers. But it does make it difficult to communicate the nuance of how our code works to users who are experiencing it for the first time. So, basically, this is sort of the Yard. Tags act as a counterpoint, which had a lot of structure that can really explicitly communicate, hey, this is the type we're expecting here. There's no type enforcement, but this is what we expect you to pass to us. This is what we're going to pass back to you. And then it generates really nice-looking documentation like this. So, Yard has dozens of tags. These are some of my favorites. A particular importance, I would say, the API tag. So, you know, Ruby visibility, we can do some stuff with it, but we can't, say, make a class private. So, the API tag allows us to say, hey, this is not part of the public API very simply. Deprecated is another good one. There's just one line, and you can mark a method. Don't use this if you're not already. Two of my other favorites are bang attribute and bang method. So, we like to embed a program, and sometimes we define a method in a sense by not actually defining the method. So, maybe we use def-delegator, or active-support delegation, or any number of other things. Well, that's not going to automatically show up, and our code documentation is being a method that somebody can call, but we can tell Yard, using this bang method, pretend there's a method here, and I'm going to document it for you, and then it just shows up as if it's a method in the documentation. So, the effect is that Yard documentation can actually be, in a sense, more authoritative than your code itself, believe it or not. So, the other advantage of Yard, kind of a freebie bonus type thing, is that rubidoc.info. Rubidoc.info is a site that hosts for free documentation for Ruby projects. It'll just pull the code off of GitHub or out of a gem, and it uses Yard, so if you do, you get this extra benefit, basically, for nothing. Okay, so, your users are now fully able to sort of get started with and understand your code, but we want to make sure also that your gem is not going to cause any unpleasant surprises. So, I should be able to do, if I'm a user of your gem, I should be able to do this without a great deal of fear, and that means that I may be updating to a new version of your gem. I need to know what sort of pain that could possibly cause, and ideally not feel any of that pain. So, the boot's basically a way to communicate that information very concisely and in a way that is widely understood is to use semantic versioning. So, semantic versioning divides versions into three numbers, like most versions look like this. Let's start from the back. So, if I bump that green number, if I bump it up from bugfix, or if I bump the bugfix number, it's called the patch level, that's communicating to my users that nothing outward facing is changing. So, this is a bugfix, it's a patch to the code that maybe refactors it or makes it faster, but there's no outward facing change. So, you really nothing to worry about here. Everybody upgrade it will, you're going to get some sort of invisible benefit, but no outward facing change. The middle number, the minor version, means I have added something, but I have not changed anything that was already there, and I have not removed anything that was already there. So, again, you are totally clear to upgrade to, say, 4.3 here, as long as you might not be able to downgrade, because that might mean the future goes away, but you can upgrade a minor version safely. Finally, the major version, the red one, is breaking change. So, anytime you rename a method, you get rid of a method, you change the types of the arguments that a method expects or the return value, just anything where your gem behaves differently for an existing public interface, you have to bump that major version. But, my claim is that, in fact, when we think about breaking changes, we're often maybe thinking a little bit too much about ourselves and not enough about our users. So, typically we want to keep things nice and clean, but we realize that, oh, you know, there's a better way to design this interface, and I want to make a beautiful interface for my users, so I'm going to change the way this method works, or maybe I'm just going to get rid of the old methods and write new ones. Well, that's nice for me as the developer of the gem, but my users are almost certainly feeling more pain having to go through their code and change from my admittedly worse old interface to my better new one than they would just not having to change anything. So, I'm not saying don't make better interfaces, I'm saying don't get rid of the bad ones. We can take some inspiration from JavaScript here, the language itself. So, ES6 is the newest version of the language that's actually beginning to exist in the world, and one of the things that they changed in ES6 was the scoping of variables. So, basically, we're all used to declaring variables with var. The scoping is kind of whack, it doesn't respect stuff like for loops or if statements, although that's the same in Ruby. But the point is they decided they don't like the way variables are scoped, and so they wanted to change it. Now, they could have just changed it, right? But that would have broken existing JavaScript code, and it would have meant that you needed to opt in and say, and explicitly say, I'm now in ES6 mode, and so you can scope variables in the ES6 way, and that's a common understanding we have. But instead, thanks to this blog post from a few years ago, and the idea of one JavaScript, JavaScript community basically decided we are going to always support every old version of JavaScript in every future, sorry, every old JavaScript code will be supported in every future version of JavaScript. And the way they solved the var problem is they just made a new keyword for declaring variables that have better scoping, which is let. So now, basically, you're not supposed to use var, you should never use var, it's deprecated, I mean, you can if you want, you'll always be able to, but JavaScript solved this problem by leaving kind of this, by dragging this trail of cruft along with it. And so it is very crufty to have two different ways of declaring a variable, only one of which you're actually supposed to use, but in the end, they're doing a real service to everyone who's written JavaScript code to date, because they're not breaking anyone's code. So we can take some inspiration from that. Sometimes you've got to make breaking changes, but probably not as often as you think. So now let's think about the environment that our gem exists in, right? We're not going to be the only library that our users are using. So it's important that we play nice in the larger environment. We have at least one dependency ourselves, which is Ruby, right? So we depend on Ruby, we probably depend on some Ruby version, or we're compatible with certain Ruby versions, and chances are we also probably have one or two gems that we depend on. Maybe we do, maybe we don't, but on average we do. So it's important to think about how tightly we're specifying what our dependencies are. So this is something that we could put in a gem spec, but that would be a bit horrifying. So essentially an exact version of active support. It means if you want to use a different version of active support, and you want to use my gem, you are out of luck. And it's very unlikely that this is necessary, so you would have to really, really, really, really not trust the developers of active support to use semantic versioning and not make random breaking changes to be justified in using such an exact version constraint. So we can get a little looser and we can say, okay, well, any 4.2 will do as long as it's at least 4.2.4. But again, semantic versioning, if we all agree on this, it tells us 4.3 is fine, right? 4.3 will only add features that won't remove or change them. So what I can do is I can say, okay, I'm now going to depend on at least 4.2, but any 4.x is okay. Now, I don't know that 5 is going to be okay. I don't know what 5 is going to have, and I know that 5 will break things because that's the definition of being 5. But I can at least, this is as far into the future as I can look with my dependencies and still be safe knowing that my gem won't be broken by a change in my dependencies. So that's fine, and now we're as future-proofed as we can get. But in fact, the truth is not everyone is using Active Support 4.2 right now. So you're still saying to your users, you have to be on the latest version of Rails for this to work. And it's unlikely that you really need to say that. So for instance, 4.0 is probably fine. That's good. And maybe you have to do a little bit of extra work to support 4.0, but the truth of the matter is a lot of shops, a lot of applications and projects aren't always able to keep up with the latest versions of things all the time. And so you're going to make your users' lives much easier if you can be as loose as possible about your version requirements. And I would even say, you know, if we're talking about Rails right now today, it's likely you can do this, because things haven't changed that much in Active Support between 3 and 4. So do what you can to support as far back as is reasonable, and then as far forward as you know, something won't break. And like I said before, we, you know, even in this constraint, we don't support Rails 5, we don't support Active Support 5. So how can we know when that is a problem? So I, you know, I could release this today, and that's fine, but then at some point in the future, Rails 5 is going to come out, and maybe I'm not totally, totally paying attention to that. And so I could suddenly have a gem that has dependencies that are locked in. And then, hopefully, some kind of soul or souls has created a service called Gemnasium, which will automatically audit your gem's dependencies, and it will send you an email alert if you are out of date now. So here's the, here's part of the report for the multi-json gem, great gem. And Gemnasium basically means you don't have to worry about it. You can be, you can respect the future, because in the future, you're going to know right away, oh, hey, I got to, you know, try this out with Rails 5 and see if anything's broken, and then bump the dependencies. But that see if anything's broken, that's kind of the hard part, right? I mean, it's one thing to say, I support lots of different versions of the same dependency, but does it actually work? So of course, we want to run our tests against all of the things we say we support. So all of the versions of Ruby we want to support, all of the versions of other core dependencies we want to support, stuff like Rails, Rack, Event Machine, whatever it happens to be. But particularly, we want to be careful about these very popular, very important gems that almost everyone's using. And so Travis is a great tool to do that. Travis is hosted continuous integration. It will, few lines of configuration, and it's going to run your gem's test suite against whatever Ruby versions you want, including JRuby, Rubinius, it's not limited to MRI. It will run it against a set of gem files, so you can have different gem files, different bundler gem files that specify different gem dependencies. And you can even have it just pick arbitrary environment variables that your test suite understands. So here you can see this devise ORM, it's a little hard to see, but you can see this devise ORM that is devised as using to run it against MongoE to end active record. So Travis is really nice. Travis is hosted. Travis integrates really well with GitHub, so when somebody makes a pull request, it will automatically put in that pull request whether or not the test suite passed on Travis if you haven't hooked up. Of course, you still might want to run your test locally. WWTD is a great gem for doing that, so that basically reads your Travis configuration and then does the same thing running the test on your computer. It'll use RBM for RBM or JRuby to do the different Rubies. So that's very nice. So now you have both the local copy for when you're on airplanes and the cloud-hosted copy. Now, I mentioned just a minute ago that Travis is really good at running against different gem files, but you probably don't want to maintain a bunch of gem files by hand, so I recommend another tool called appraisal, which allows you to just write one file that lists all of the variants of your gem dependencies you want to run again. So for instance, here, you've got four different versions of ActiveRecord that are specific in the appraisals, and this will actually generate four different full gem files that you can then give to Travis. So it just varies the part that you want to vary. That is... Put that all together, right? You get the appraisal gem files, put them up on Travis, and you suddenly have a very robust system for ensuring that you actually support the stuff you say you support and that when you change a feature or change some internals, you're not breaking it on any of the platforms you support. So that is dependencies and versions and not breaking things. There is a little code in this talk. Here we go. So let's think about social networks. Two social networks you might be familiar with are Twitter and Facebook. There are great gems for each of them. So there's a gem for talking to Twitter called Twitter, and there's a gem for talking to Facebook called Facebook. And I want to talk about those gems when we're thinking about designing public interfaces. So the public interface is the part that we basically, when we say we're not going to break stuff, that's the stuff we're not going to break. Let's look at Twitter first. So if I want to write a tweet, this is the method I'm going to call. So it's a method called update. I pass it a string. That's the body of the tweet. I can pass it some options if I want. And that's really nice. That is a fully nice little wrapped up abstraction for me. But if I want to poke my friend on Twitter using the new Twitter poke feature that begins to exist next week, that is actually not something that the Twitter gem supports because it doesn't exist yet. So in that case, I might want to go down to a lower level. And in this case, I'm looking at the code for update. And I see that, okay, there's this perform request with object. That's all right, perform post with object. And that looks like a nice little level interface, so I can dig a little deeper and get to this perfectly nice generic perform request method. And all this is doing is sending an authenticated request on behalf of a particular user to the Twitter API and kind of giving me a nice response. So it's a much lower level thing than that update method we were looking at a minute ago. So this would be perfect, right? I can make a poke request through the using perform request here, except this is not part of the Twitter gem's public API. This is a private method. So we kind of have this called trace that looks like this. We've got a nice public update method to do one specific thing that the author of the Twitter gem expected that I would want to do correctly. And then we have some private methods that are at decreasing levels of abstraction. So lower and lower, more generic, less wrapped up and packaged up nicely. But another way that one might think of this kind of gem when you're writing it is first I'm just going to write a gem that lets me make authenticated request to Twitter without any real high-level knowledge of what the Twitter API, what the resources are, what the endpoints are. And in that case, perform request would actually become one of the main interfaces to that layer of the gem. And then on top of that, I would essentially use my lower level library, which is fully tested, has a public interface that's stable, to write a nice high-level bunch of code. And really the effort that must have gone into this gem is incredible, because it does support basically everything you can do with the Twitter API is explicitly contemplated in this gem. I can go crazy like that. So this is sometimes referred to as a layered architecture. Another way to think of it is essentially I'm eating my own dog food, but I'm also just letting you have the dog food if you want, so dog or dog food, either way. That makes perfect sense. Another way I might choose to approach this problem though is to think, well, can I just get away with this? Can I just get away with perform request? Do my users really need this high-level kind of domain model, which again is really beautiful, but is that necessary? The Twitter API is pretty easy to understand. And that is the choice that Koala makes, it's the Facebook gem. So Koala has this put connections method, and put connections basically is the equivalent of that last method we were looking at. And this is the highest level of abstraction Koala has. So Koala does not know what resources are available in the Facebook API. And that lets it be first pretty lean, and second, when Facebook introduces some new features in its API, as long as it's not some totally new structure for the API, which it pretty much never is, Koala is instantly up to date. So I still prefer this one, really. I mean, this is the best if you got that kind of time. But you can also maybe do your users a lot of favor, a big favor, by just giving them a low-level API that they can work with that takes the boilerplate out, but doesn't necessarily provide the full abstraction. So the quick start is to your users what the test suite is to your contributors. If I want to contribute to your gem, the first thing I'm going to do is check it out and try to run the test suite. If I get frustrated, I'm not going to have a ton of patience for that. And it's pretty likely that I might just sort of keep my branch and never contribute it back because I can't prove to you that it works with the test suite, et cetera, et cetera. So it's really, really important for your contributors, if you want to have good contributions, to make sure that your test suite is really, really easy to run. So it's unlikely that you can just do this. It's unlikely that rake test alone will just work, but it's definitely possible that bundle and then rake test is sufficient. And this, I think, most gems will be able to provide this. So the key here is you already use Bundler to manage your application's runtime dependencies. Just use it to maintain your gem's test dependencies at runtime in the same way. So very simple way to do this. You have a gem file. You can just say use the gem spec, just this one line gem spec. Use the same gem spec that's in the directory we're in right now, and Bundler knows what to do. Then in your test code, in your test helper, a spec helper, you can just, that first line require Bundler setup. And you're good to go. And Bundler loads in the gem environment, it installs everything for your users, and so if you don't have any external dependencies, if you're just sort of like a Ruby self-contained library here, then now your users can very consistently and nicely run tests. But if you do have external dependencies, so say you need Postgres, then Bundler can't deal with that for you, right? Bundler can give you the Postgres library, but it can't give you an actual Postgres instance to test against. So my favorite method of getting this to my users, to my potential contributors, is using Vagrant. So Vagrant is a tool for managing virtual machines, like virtual box, specifically for development environments. So it's mostly used to sort of have a repeatable development environment for a team that's working on an application, but it's actually really good for having a repeatable test environment too, for a library. So if I wanted to, if my tests require Postgres, then I can ship a Vagrant file. It could be as simple as this. Basically, we're using Ubuntu. We are installing Postgres when we first spin up the machine, and then we're going to forward the Postgres port to the host computer so my tests we can actually interface with it. And that's it. And now instead of having to go through a bunch of instructions saying, okay, well, we expect Postgres to be on this port, so go ahead and just deal with it. Potential contributors can run Vagrant up, and they've got the environment that allows them to run the tests. So that's really nice. Now, of course, being able to run the tests is one part. Actually, writing the code is another part if I have contributors to my project. And few things are more heartbreaking to me than getting a pull request that, you know, it's a good new feature, say, it has a nicely factored code, really, just everything I want except maybe I see something like this. Now, I know this is hard to look at, but I would never put a multi-line block with curly braces. But I respect the decisions of those who do. And I would hate to be in a situation where I got a really good pull request where there's some style nitpick I have, and so instead of saying, hey, thanks, and accepting the pull request, I have to say, can you change that to a do end, and then I'll take your free work that you're giving me from my library? So can we avoid this? Yes. RuboCop is a tool for style checking and linting. You can set up rules like these. There are more. To your preferences, right? It's your library. It's your piece of software. So you get to decide what the style is. And the nice thing about RuboCop is that it's automatic. It's not coming from a human. It doesn't feel like a rejection. It just feels like a cold, impersonal robot being cold and impersonal. And it's very quick. So you can take the RuboCop, you just run RuboCop, basically, once you set up these rules, and you can put that in your Travis configuration so that when somebody submits a pull request, they'll see feedback right in the pull request saying, hey, the build didn't pass. Go look at the build report, and then that shows where the style doesn't match your style, or there are various linting problems. So then you get to have your cake, right? You don't have to have weird confrontations with contributors about things that are completely a matter of opinion, but you do get to have nice consistent style in your library. So that was number seven, and we are at the end. So real quick, just a recap. Write a gem that has a really short quick start. And the way to do that is try it yourself. Wait a really long read me. Use Yard for code documentation. Try not to ever, ever, ever do breaking changes if you can avoid it. Be sure to use semantic version to communicate what's going on to your users. Understand that your users are probably using old versions of things. At least some of them are, and support the past as much as you can, as well as supporting the future as much as you can. So keep track of new versions of things with gymnasium. Run your test suite against lots of different versions. Use Travis for that. Avoid abstractions that can lock people in to your assumptions about how they're going to use your code. So consider providing multiple layers of public interface. A nice high level one, but also an equally valid, equally public low level one. Make it easy to run the test suite by using tools like Bundler and Vagrant. And finally, use RuboCop to give instant and non-confrontational feedback on code style to your contributors. That's it. Got plenty of time for questions here. Right. Right. So the question was, the first part of the question was, you're wrong about RuboCop. And the second part of the question was, what about a contributing document? That is a great one. So in terms of the contributing document, basically you can make a file in your repo called contributing.md. All capital contributing. I'm not sure if that actually matters. And GitHub will put a banner at the top of the form where people submit pull requests and issues that link to that document. So it's kind of in people's face right when they begin to actually attempt to contribute, and you can write whatever you want, such as, follow my style. On the question of RuboCop, yeah, sure, opinions may differ. I guess I would be interested, maybe after we finish here to hear what the objections are. If you're uncomfortable with the idea that the build should break because of style issues, then RuboCop can also not be a part of the build, but still be there for people who are trying to match your style. Do I have a suggestion on which license we should use? MIT? Seems like the normal one. Yeah, a copy left doesn't seem to be enforceable, but I'm also not a lawyer. So yeah, I guess I don't really have an opinion, but I do MIT because that's what everyone else does, and yeah, usually is a good idea. Okay, for those on the other side of the room, avoid multi-license gems, pick a license, have that be the one true license. In my experience, what is the best way to publicize a gem? RubyFlow is good. I've had good luck with that. Depending on, you know, I mean, I'm not going to say anything that probably else that isn't pretty obvious. I mean, Hacker News, Reddit, Ruby, Subreddit, if you're into Reddit, Twitter. But RubyFlow is probably the only one that maybe is a little under the radar, but I've had pretty good responses from that. Ha-ha. Yeah, that's a good question. So the question is, for versioning, what is a good practice for the first release? I mean, when you see like 0.0.1, it's like, come on, you know, like actually out there in the real world. I would say like, okay, let me just back up a thousand slides here. So if you are at this point where you like haven't pushed it, then an 0.0.0.0.0.0.0.0 is cool. I would say after that, just go for it, you know, 1.0.0. Like when you're going on RubyFlow and Twitter and stuff, like you could always make a 2.0. It's not going to hurt anyone. So the question was, if I release a new version of MyGen that supports Rails 5, what kind of release is that from MyGen? So I haven't made any other changes, but I have, you know, let's say, for instance, I actually don't have to change MyGen to support Rails 5. Then I would say yes, that is a patch-level release. I guess it's hard to imagine a situation in which you can imagine sort of augmenting an API in some cases so that, okay, now I can take a new type as an argument for a particular method. And I guess that would technically fall into the minor version release. You're like adding functionality to the public interface. So yeah, I would basically sort of evaluate it on the merits if there's no change to the public-facing interface at all, then it's a patch-level. Otherwise, maybe it's a minor. Okay, so the question is, if another gem maker defines a particular namespace, like it uses a module to define a namespace, should you use that namespace or should you define your own? I assume we're talking about a case where your gem is kind of in addition to this like a plug-in of some sort to theirs. Right, so I think in most cases where that comes up, certainly the initial developer of a gem can establish a convention that this area of my gem's namespace is for you to put your stuff in. I think you have like a rescue here, has some good conventions around that. I would say if there's not a really well-defined convention by the sort of upstream library that you're building against, there's very little downside to just respecting the general rule that like the top-level namespace of everything in my code is the name of my gem in camel case. Ah, can I say a few words about the underscore versus the dash in the naming? So, like, mostly use underscore. Underscore is how we name our files, obviously. In the gem file, you know, when we put gem whatever, RubyGems is going to look for the underscore version, or it's going to look for the same thing that the gem's called, basically. So dashes are a bit suspect. I would say, actually, the scenario we were just talking about, though, where your gem is building on the namespace of another one, that's a situation in which I would probably go with the dash. So if I have, like, you know, rescue, like, T-party, then I would probably do, like, rescue-t underscore-party, and then, you know, like, the sort of root of my library would be rescue-slash-t-party in the file system. But I don't know, it's the Wild West out there with those dashes. You got to fend for yourself. What's up? How do I test my gem's compatibility with dependencies? Do you mean, like, Ruby dependencies or external ones? Oh, so that's what appraisal is for. So...oh, right past it. So appraisal allows us to just list all the different versions of different dependencies that we want to test against, and it generates an exhaustive set of gem files that we can then, using Travis, run our tests against. Yeah, so the question is, if you have a gem that depends on Rails, and it's sort of, like, baked it, it's really a tight part of... Rails is a tight part of using it. How do you test that? Do you generate a full Rails app? I personally have written a lot of gems that sort of hang out at the model layer, and I will say there's absolutely no need to do that if you're doing it at the model layer. Like, ActiveRecord is its own thing, and you can just test against ActiveRecord. And I don't have any great advice for if you're writing... Like, if you're writing a view-layer thing that has a bunch of helpers and you want to test those helpers, there's definitely no way to get around actually doing that in a real Rails runtime. I'm a little embarrassed I don't have any good advice for that because it sounds horrible and I would never want to do it. But maybe there is a good practice out there, and maybe the internet knows. Cool, so the question is, Ho, Bundle Gem, RubyGems, Tasks, Jeweler... So these are all tools for bootstrapping a gem. And my feeling is basically, definitely not Ho, definitely not Jeweler. Basically, like, the level zero thing is writing your own gem spec is really easy. It's just Ruby code. And there's definitely no need for anything that generates a gem spec. Like, you don't need code that looks almost exactly like the code it generates. I actually... I skipped this slide, but I do like Bundle Gems pretty good. Where did I put it? Anyway, Bundle Gem is a command that just generates a nice skeleton of your whole gem environment. Including all the stuff about... There it is. Including all the stuff about, you know, integrating Bundler into it. So, yeah, I think, you know, you don't want to literally have to sit there and make the lib directory every time, and Bundle Gem is nice for that. But I would avoid anything that is trying to add a level of indirection on top of, say, making your gem file. Like, your gem file can just be your gem file. All right, should we call it? Let's call it. Thank you, everyone. Thank you.
 Music  Alright, what's up everyone? How's it going?  So, who among you has ever released a gem?  Okay, good, I like it.  Okay, who has released a popular-ass gem?  Like tons of contributors, tons of users, tons of love.  Okay, alright, so we want to get from like group one and not group one.  Everyone into group two. Okay, so that's what we're going to do today.  Before I get started, quick intro. My name is Matt.  What's going on? If you want to tweet insults, that's a zero.  I live in Brooklyn. Looks like this.  I work for Genius. We're a platform for annotations.  One time John Resig used our platform. It was really cool.  And here's my talk. So, we're going to talk about seven specific things  that are going to make your gem more appealing.  It's going to make your users happy.  So your gem should make your users happy, of course.  This is a Ruby conference.  So, we're going to talk about first how to get users up and running quickly.  We'll talk about how to write really good documentation.  We'll talk about how to change your gem without causing your applications pain.  We'll talk about how to coexist with your user's applications environment  in a nice way.  We'll talk about how abstractions can lock people in  and how we can prevent that.  And we'll talk about how to make it easy on your contributors  by giving them an easy to run test suite  and giving them automatic feedback on their contributions.  So, I'm going to start by talking about Quick Start.  So, Sinatra, great gem.  Really great micro, probably the best micro framework, I think.  Sinatra, really good gem.  And take a look at this. This is the top of the Sinatra Readme.  This is the Quick Start.  It's four lines long, which is amazing.  There's four lines of Ruby.  And then, you know, like the gem install, which doesn't even count,  and one line to type in your shell, and you're good to go.  And this is what we should all aspire to, right?  We need a Quick Start that's this easy.  And that's what, as Ruby programmers, we expect.  And we don't expect that because we're not smart or because we're lazy.  We expect it because we want to spend our time  building beautiful, functional, wonderful products,  not installing things and configuring them.  So, having a really short, good Quick Start is actually a...  It's a feature of your code, not of your Readme, right?  You can't write a short Quick Start unless your gem allows it.  So, how do we get to that point?  Well, let's think about how we usually write a gem, right?  So, probably what happens is I am working on my application.  I have some code that I think,  ah, I should open source this.  This would be useful to some other people.  So, I take the little bit of code out,  and I create a new directory for my gem,  and I probably stick something like this into my gem file  so that my application points at the local version of the gem  while I'm developing it.  So, I might go on, you know, I might write some tests in my gem,  I might flush out the interface a little bit,  start writing documentation,  and at some point I'll probably go back to my application  and say, okay, well, does this all still work  with this gem that it's now pointing at that I've been developing?  But what I've never done at this point  is actually feel what it's like to start using the gem from scratch,  to start it in an application that doesn't use it already.  So, something that's worked really well for me  is to just basically make a, like an example app,  it doesn't have to be a Rails app,  whatever makes the most sense for the problem that you're solving,  but make some sort of example app  and really think, okay, if I wanted to do this,  if I wanted to use this for some plausible use case,  what does it feel like, what does it look like?  I have always found at least one pain point that I can solve  to make it easier to get started using this technique.  So, getting that quick start down is all about  just putting yourself in the position of a new user,  which is something that doesn't necessarily come natural.  Now, once you've got the user hooked,  once things are installed, they want to learn more.  They want to be able to easily find out everything  that your gem has to offer.  So, that's where documentation comes in.  And we're back to Sinatra.  Sinatra not only has a great quick start, but check this out.  If we start scrolling down,  we're still in the table of contents right now, okay?  Still in the table of contents.  This will take about a minute to get through the whole thing.  Sinatra has an incredibly long readme,  and that is a very, very good thing.  So, there is nowhere that's more discoverable  for documentation than the readme, right?  It's there front and center in GitHub.  It's there front and center when you're...  We have generated code documentation.  It's there on airplanes.  It's there if you don't have the internet,  because it comes with the gem and you install it.  So, the readme is a really important place to give  of what your gem is capable of doing.  And in order to do that,  I recommend making the readme a part of your development process,  just like, say, testing as a part of your development process.  So, you wouldn't submit a pull request,  or you wouldn't even necessarily make a commit  with new code that doesn't have some new tests.  And you can take that a step further and say,  I'm also going to submit a...  Anytime I add something new to my application,  I'm also going to add something new to the readme.  And you can take that a step further  and write the readme first.  So, actually, think about, before you actually lay down any tests or code,  think about how is...  How am I going to explain this to my users?  And you might end up writing a better interface  once you think about things in that term,  than those terms, than if you document after writing.  So, the readme is really good. It's really important.  Again, sort of every feature that your gem has,  every major thing you can do with it,  I think should appear in the readme.  Very searchable. Command-F, great tool.  But, of course, the readme can't be totally exhaustive.  The readme can't take you through  tons of examples of every method.  It can't take you through all the possible options you can pass,  and that's what inline code documentation is for.  So, what we've got here is from addressable,  another great gem.  And addressable is using this...  this params and return tag you can see here.  And that is not built into standard RDoC.  That is actually a documentation tool called Yard.  So, that stands for Yay Ruby Documenter.  And Yard adds a bunch of extra structure  to Ruby code documentation  that isn't in the built-in documentation for Ruby.  And what's nice about this is that, you know,  Ruby doesn't have a lot of structure as a language.  You know, it's not statically typed.  Everything's mutable at runtime.  There's all this meta-programming.  And that's great. You know, it's really good for us as programmers.  But it does make it difficult to communicate the nuance  of how our code works to users  who are experiencing it for the first time.  So, basically, this is sort of the Yard.  Tags act as a counterpoint, which had a lot of structure  that can really explicitly communicate,  hey, this is the type we're expecting here.  There's no type enforcement,  but this is what we expect you to pass to us.  This is what we're going to pass back to you.  And then it generates really nice-looking documentation like this.  So, Yard has dozens of tags.  These are some of my favorites.  A particular importance, I would say, the API tag.  So, you know, Ruby visibility, we can do some stuff with it,  but we can't, say, make a class private.  So, the API tag allows us to say, hey,  this is not part of the public API very simply.  Deprecated is another good one.  There's just one line, and you can mark a method.  Don't use this if you're not already.  Two of my other favorites are bang attribute and bang method.  So, we like to embed a program,  and sometimes we define a method in a sense  by not actually defining the method.  So, maybe we use def-delegator, or active-support delegation,  or any number of other things.  Well, that's not going to automatically show up,  and our code documentation is being a method that somebody can call,  but we can tell Yard, using this bang method,  pretend there's a method here, and I'm going to document it for you,  and then it just shows up as if it's a method in the documentation.  So, the effect is that Yard documentation can actually be,  in a sense, more authoritative than your code itself, believe it or not.  So, the other advantage of Yard, kind of a freebie bonus type thing,  is that rubidoc.info.  Rubidoc.info is a site that hosts for free documentation for Ruby projects.  It'll just pull the code off of GitHub or out of a gem,  and it uses Yard, so if you do, you get this extra benefit, basically, for nothing.  Okay, so, your users are now fully able to sort of get started with  and understand your code, but we want to make sure also  that your gem is not going to cause any unpleasant surprises.  So, I should be able to do, if I'm a user of your gem,  I should be able to do this without a great deal of fear,  and that means that I may be updating to a new version of your gem.  I need to know what sort of pain that could possibly cause,  and ideally not feel any of that pain.  So, the boot's basically a way to communicate that information very concisely  and in a way that is widely understood is to use semantic versioning.  So, semantic versioning divides versions into three numbers,  like most versions look like this.  Let's start from the back.  So, if I bump that green number, if I bump it up from bugfix,  or if I bump the bugfix number, it's called the patch level,  that's communicating to my users that nothing outward facing is changing.  So, this is a bugfix, it's a patch to the code that maybe refactors it  or makes it faster, but there's no outward facing change.  So, you really nothing to worry about here.  Everybody upgrade it will, you're going to get some sort of invisible benefit,  but no outward facing change.  The middle number, the minor version, means I have added something,  but I have not changed anything that was already there,  and I have not removed anything that was already there.  So, again, you are totally clear to upgrade to, say, 4.3 here,  as long as you might not be able to downgrade,  because that might mean the future goes away,  but you can upgrade a minor version safely.  Finally, the major version, the red one, is breaking change.  So, anytime you rename a method, you get rid of a method,  you change the types of the arguments that a method expects  or the return value, just anything where your gem behaves differently  for an existing public interface, you have to bump that major version.  But, my claim is that, in fact, when we think about breaking changes,  we're often maybe thinking a little bit too much about ourselves  and not enough about our users.  So, typically we want to keep things nice and clean,  but we realize that, oh, you know, there's a better way to design this interface,  and I want to make a beautiful interface for my users,  so I'm going to change the way this method works,  or maybe I'm just going to get rid of the old methods and write new ones.  Well, that's nice for me as the developer of the gem,  but my users are almost certainly feeling more pain  having to go through their code and change from my admittedly worse old interface  to my better new one than they would just not having to change anything.  So, I'm not saying don't make better interfaces,  I'm saying don't get rid of the bad ones.  We can take some inspiration from JavaScript here, the language itself.  So, ES6 is the newest version of the language that's actually beginning to exist in the world,  and one of the things that they changed in ES6 was the scoping of variables.  So, basically, we're all used to declaring variables with var.  The scoping is kind of whack, it doesn't respect stuff like for loops or if statements,  although that's the same in Ruby.  But the point is they decided they don't like the way variables are scoped,  and so they wanted to change it.  Now, they could have just changed it, right?  But that would have broken existing JavaScript code,  and it would have meant that you needed to opt in and say, and explicitly say,  I'm now in ES6 mode, and so you can scope variables in the ES6 way,  and that's a common understanding we have.  But instead, thanks to this blog post from a few years ago,  and the idea of one JavaScript,  JavaScript community basically decided we are going to always support  every old version of JavaScript in every future,  sorry, every old JavaScript code will be supported in every future version of JavaScript.  And the way they solved the var problem is they just made a new keyword  for declaring variables that have better scoping, which is let.  So now, basically, you're not supposed to use var, you should never use var,  it's deprecated, I mean, you can if you want, you'll always be able to,  but JavaScript solved this problem by leaving kind of this,  by dragging this trail of cruft along with it.  And so it is very crufty to have two different ways of declaring a variable,  only one of which you're actually supposed to use,  but in the end, they're doing a real service to everyone who's written JavaScript code to date,  because they're not breaking anyone's code.  So we can take some inspiration from that.  Sometimes you've got to make breaking changes, but probably not as often as you think.  So now let's think about the environment that our gem exists in, right?  We're not going to be the only library that our users are using.  So it's important that we play nice in the larger environment.  We have at least one dependency ourselves, which is Ruby, right?  So we depend on Ruby, we probably depend on some Ruby version,  or we're compatible with certain Ruby versions,  and chances are we also probably have one or two gems that we depend on.  Maybe we do, maybe we don't, but on average we do.  So it's important to think about how tightly we're specifying what our dependencies are.  So this is something that we could put in a gem spec, but that would be a bit horrifying.  So essentially an exact version of active support.  It means if you want to use a different version of active support,  and you want to use my gem, you are out of luck.  And it's very unlikely that this is necessary,  so you would have to really, really, really, really not trust the developers of active support  to use semantic versioning and not make random breaking changes  to be justified in using such an exact version constraint.  So we can get a little looser and we can say,  okay, well, any 4.2 will do as long as it's at least 4.2.4.  But again, semantic versioning, if we all agree on this,  it tells us 4.3 is fine, right?  4.3 will only add features that won't remove or change them.  So what I can do is I can say, okay, I'm now going to depend on at least 4.2,  but any 4.x is okay.  Now, I don't know that 5 is going to be okay.  I don't know what 5 is going to have,  and I know that 5 will break things because that's the definition of being 5.  But I can at least, this is as far into the future as I can look with my dependencies  and still be safe knowing that my gem won't be broken by a change in my dependencies.  So that's fine, and now we're as future-proofed as we can get.  But in fact, the truth is not everyone is using Active Support 4.2 right now.  So you're still saying to your users,  you have to be on the latest version of Rails for this to work.  And it's unlikely that you really need to say that.  So for instance, 4.0 is probably fine.  That's good.  And maybe you have to do a little bit of extra work to support 4.0,  but the truth of the matter is a lot of shops, a lot of applications and projects  aren't always able to keep up with the latest versions of things all the time.  And so you're going to make your users' lives much easier  if you can be as loose as possible about your version requirements.  And I would even say, you know, if we're talking about Rails right now today,  it's likely you can do this,  because things haven't changed that much in Active Support between 3 and 4.  So do what you can to support as far back as is reasonable,  and then as far forward as you know, something won't break.  And like I said before, we, you know, even in this constraint,  we don't support Rails 5, we don't support Active Support 5.  So how can we know when that is a problem?  So I, you know, I could release this today,  and that's fine, but then at some point in the future, Rails 5 is going to come out,  and maybe I'm not totally, totally paying attention to that.  And so I could suddenly have a gem that has dependencies that are locked in.  And then, hopefully, some kind of soul or souls has created a service called Gemnasium,  which will automatically audit your gem's dependencies,  and it will send you an email alert if you are out of date now.  So here's the, here's part of the report for the multi-json gem, great gem.  And Gemnasium basically means you don't have to worry about it.  You can be, you can respect the future, because in the future,  you're going to know right away, oh, hey, I got to, you know, try this out with Rails 5  and see if anything's broken, and then bump the dependencies.  But that see if anything's broken, that's kind of the hard part, right?  I mean, it's one thing to say, I support lots of different versions of the same dependency,  but does it actually work?  So of course, we want to run our tests against all of the things we say we support.  So all of the versions of Ruby we want to support,  all of the versions of other core dependencies we want to support,  stuff like Rails, Rack, Event Machine, whatever it happens to be.  But particularly, we want to be careful about these very popular,  very important gems that almost everyone's using.  And so Travis is a great tool to do that.  Travis is hosted continuous integration.  It will, few lines of configuration,  and it's going to run your gem's test suite against  whatever Ruby versions you want, including JRuby, Rubinius,  it's not limited to MRI.  It will run it against a set of gem files,  so you can have different gem files, different bundler gem files  that specify different gem dependencies.  And you can even have it just pick arbitrary environment variables  that your test suite understands.  So here you can see this devise ORM, it's a little hard to see,  but you can see this devise ORM that is devised as using  to run it against MongoE to end active record.  So Travis is really nice. Travis is hosted.  Travis integrates really well with GitHub,  so when somebody makes a pull request,  it will automatically put in that pull request  whether or not the test suite passed on Travis  if you haven't hooked up.  Of course, you still might want to run your test locally.  WWTD is a great gem for doing that,  so that basically reads your Travis configuration  and then does the same thing running the test on your computer.  It'll use RBM for RBM or JRuby to do the different Rubies.  So that's very nice.  So now you have both the local copy for when you're on airplanes  and the cloud-hosted copy.  Now, I mentioned just a minute ago that Travis is really good  at running against different gem files,  but you probably don't want to maintain a bunch of gem files by hand,  so I recommend another tool called appraisal,  which allows you to just write one file  that lists all of the variants of your gem dependencies you want to run again.  So for instance, here, you've got four different versions of ActiveRecord  that are specific in the appraisals,  and this will actually generate four different full gem files  that you can then give to Travis.  So it just varies the part that you want to vary.  That is...  Put that all together, right?  You get the appraisal gem files, put them up on Travis,  and you suddenly have a very robust system for ensuring  that you actually support the stuff you say you support  and that when you change a feature or change some internals,  you're not breaking it on any of the platforms you support.  So that is dependencies and versions and not breaking things.  There is a little code in this talk.  Here we go.  So let's think about social networks.  Two social networks you might be familiar with are Twitter and Facebook.  There are great gems for each of them.  So there's a gem for talking to Twitter called Twitter,  and there's a gem for talking to Facebook called Facebook.  And I want to talk about those gems when we're thinking about designing public interfaces.  So the public interface is the part that we basically, when we say we're not going to break stuff,  that's the stuff we're not going to break.  Let's look at Twitter first.  So if I want to write a tweet, this is the method I'm going to call.  So it's a method called update.  I pass it a string. That's the body of the tweet.  I can pass it some options if I want.  And that's really nice.  That is a fully nice little wrapped up abstraction for me.  But if I want to poke my friend on Twitter using the new Twitter poke feature  that begins to exist next week,  that is actually not something that the Twitter gem supports because it doesn't exist yet.  So in that case, I might want to go down to a lower level.  And in this case, I'm looking at the code for update.  And I see that, okay, there's this perform request with object.  That's all right, perform post with object.  And that looks like a nice little level interface,  so I can dig a little deeper and get to this perfectly nice generic perform request method.  And all this is doing is sending an authenticated request on behalf of a particular user  to the Twitter API and kind of giving me a nice response.  So it's a much lower level thing than that update method we were looking at a minute ago.  So this would be perfect, right?  I can make a poke request through the using perform request here,  except this is not part of the Twitter gem's public API.  This is a private method.  So we kind of have this called trace that looks like this.  We've got a nice public update method to do one specific thing  that the author of the Twitter gem expected that I would want to do correctly.  And then we have some private methods that are at decreasing levels of abstraction.  So lower and lower, more generic, less wrapped up and packaged up nicely.  But another way that one might think of this kind of gem when you're writing it  is first I'm just going to write a gem that lets me make authenticated request to Twitter  without any real high-level knowledge of what the Twitter API,  what the resources are, what the endpoints are.  And in that case, perform request would actually become one of the main interfaces  to that layer of the gem.  And then on top of that, I would essentially use my lower level library,  which is fully tested, has a public interface that's stable,  to write a nice high-level bunch of code.  And really the effort that must have gone into this gem is incredible,  because it does support basically everything you can do with the Twitter API  is explicitly contemplated in this gem.  I can go crazy like that.  So this is sometimes referred to as a layered architecture.  Another way to think of it is essentially I'm eating my own dog food,  but I'm also just letting you have the dog food if you want,  so dog or dog food, either way.  That makes perfect sense.  Another way I might choose to approach this problem though is to think,  well, can I just get away with this?  Can I just get away with perform request?  Do my users really need this high-level kind of domain model,  which again is really beautiful, but is that necessary?  The Twitter API is pretty easy to understand.  And that is the choice that Koala makes, it's the Facebook gem.  So Koala has this put connections method,  and put connections basically is the equivalent of that last method we were looking at.  And this is the highest level of abstraction Koala has.  So Koala does not know what resources are available in the Facebook API.  And that lets it be first pretty lean,  and second, when Facebook introduces some new features in its API,  as long as it's not some totally new structure for the API,  which it pretty much never is, Koala is instantly up to date.  So I still prefer this one, really.  I mean, this is the best if you got that kind of time.  But you can also maybe do your users a lot of favor, a big favor,  by just giving them a low-level API that they can work with that takes the boilerplate out,  but doesn't necessarily provide the full abstraction.  So the quick start is to your users what the test suite is to your contributors.  If I want to contribute to your gem,  the first thing I'm going to do is check it out and try to run the test suite.  If I get frustrated, I'm not going to have a ton of patience for that.  And it's pretty likely that I might just sort of keep my branch  and never contribute it back because I can't prove to you  that it works with the test suite, et cetera, et cetera.  So it's really, really important for your contributors,  if you want to have good contributions,  to make sure that your test suite is really, really easy to run.  So it's unlikely that you can just do this.  It's unlikely that rake test alone will just work,  but it's definitely possible that bundle and then rake test is sufficient.  And this, I think, most gems will be able to provide this.  So the key here is you already use Bundler to manage your application's runtime dependencies.  Just use it to maintain your gem's test dependencies at runtime in the same way.  So very simple way to do this.  You have a gem file.  You can just say use the gem spec, just this one line gem spec.  Use the same gem spec that's in the directory we're in right now,  and Bundler knows what to do.  Then in your test code, in your test helper, a spec helper,  you can just, that first line require Bundler setup.  And you're good to go.  And Bundler loads in the gem environment,  it installs everything for your users,  and so if you don't have any external dependencies,  if you're just sort of like a Ruby self-contained library here,  then now your users can very consistently and nicely run tests.  But if you do have external dependencies,  so say you need Postgres, then Bundler can't deal with that for you, right?  Bundler can give you the Postgres library,  but it can't give you an actual Postgres instance to test against.  So my favorite method of getting this to my users,  to my potential contributors, is using Vagrant.  So Vagrant is a tool for managing virtual machines,  like virtual box, specifically for development environments.  So it's mostly used to sort of have a repeatable development environment  for a team that's working on an application,  but it's actually really good for having a repeatable test environment too,  for a library.  So if I wanted to, if my tests require Postgres,  then I can ship a Vagrant file.  It could be as simple as this.  Basically, we're using Ubuntu.  We are installing Postgres when we first spin up the machine,  and then we're going to forward the Postgres port to the host computer  so my tests we can actually interface with it.  And that's it.  And now instead of having to go through a bunch of instructions saying,  okay, well, we expect Postgres to be on this port,  so go ahead and just deal with it.  Potential contributors can run Vagrant up,  and they've got the environment that allows them to run the tests.  So that's really nice.  Now, of course, being able to run the tests is one part.  Actually, writing the code is another part  if I have contributors to my project.  And few things are more heartbreaking to me  than getting a pull request that, you know,  it's a good new feature, say,  it has a nicely factored code, really,  just everything I want except maybe I see something like this.  Now, I know this is hard to look at,  but I would never put a multi-line block with curly braces.  But I respect the decisions of those who do.  And I would hate to be in a situation where I got a really good pull request  where there's some style nitpick I have,  and so instead of saying, hey, thanks,  and accepting the pull request, I have to say,  can you change that to a do end,  and then I'll take your free work that you're giving me from my library?  So can we avoid this?  Yes.  RuboCop is a tool for style checking and linting.  You can set up rules like these.  There are more.  To your preferences, right?  It's your library. It's your piece of software.  So you get to decide what the style is.  And the nice thing about RuboCop is that it's automatic.  It's not coming from a human.  It doesn't feel like a rejection.  It just feels like a cold, impersonal robot being cold and impersonal.  And it's very quick.  So you can take the RuboCop,  you just run RuboCop, basically, once you set up these rules,  and you can put that in your Travis configuration  so that when somebody submits a pull request,  they'll see feedback right in the pull request saying,  hey, the build didn't pass.  Go look at the build report,  and then that shows where the style doesn't match your style,  or there are various linting problems.  So then you get to have your cake, right?  You don't have to have weird confrontations with contributors  about things that are completely a matter of opinion,  but you do get to have nice consistent style in your library.  So that was number seven,  and we are at the end.  So real quick, just a recap.  Write a gem that has a really short quick start.  And the way to do that is try it yourself.  Wait a really long read me.  Use Yard for code documentation.  Try not to ever, ever, ever do breaking changes  if you can avoid it.  Be sure to use semantic version  to communicate what's going on to your users.  Understand that your users are probably using old versions of things.  At least some of them are,  and support the past as much as you can,  as well as supporting the future as much as you can.  So keep track of new versions of things with gymnasium.  Run your test suite against lots of different versions.  Use Travis for that.  Avoid abstractions that can lock people in  to your assumptions about how they're going to use your code.  So consider providing multiple layers of public interface.  A nice high level one,  but also an equally valid, equally public low level one.  Make it easy to run the test suite by using tools like Bundler and Vagrant.  And finally, use RuboCop to give instant  and non-confrontational feedback on code style to your contributors.  That's it. Got plenty of time for questions here.  Right. Right.  So the question was,  the first part of the question was,  you're wrong about RuboCop.  And the second part of the question was,  what about a contributing document?  That is a great one.  So in terms of the contributing document,  basically you can make a file in your repo called contributing.md.  All capital contributing. I'm not sure if that actually matters.  And GitHub will put a banner at the top of the form  where people submit pull requests and issues  that link to that document.  So it's kind of in people's face right when they begin to actually attempt to contribute,  and you can write whatever you want, such as, follow my style.  On the question of RuboCop,  yeah, sure, opinions may differ.  I guess I would be interested, maybe after we finish here to hear  what the objections are.  If you're uncomfortable with the idea that the build should break  because of style issues,  then RuboCop can also not be a part of the build,  but still be there for people who are trying to match your style.  Do I have a suggestion on which license we should use?  MIT? Seems like the normal one.  Yeah, a copy left doesn't seem to be enforceable,  but I'm also not a lawyer.  So yeah, I guess I don't really have an opinion,  but I do MIT because that's what everyone else does,  and yeah, usually is a good idea.  Okay, for those on the other side of the room,  avoid multi-license gems, pick a license,  have that be the one true license.  In my experience, what is the best way to publicize a gem?  RubyFlow is good.  I've had good luck with that.  Depending on, you know, I mean, I'm not going to say anything  that probably else that isn't pretty obvious.  I mean, Hacker News, Reddit, Ruby, Subreddit,  if you're into Reddit, Twitter.  But RubyFlow is probably the only one  that maybe is a little under the radar,  but I've had pretty good responses from that.  Ha-ha.  Yeah, that's a good question.  So the question is, for versioning,  what is a good practice for the first release?  I mean, when you see like 0.0.1, it's like, come on,  you know, like actually out there in the real world.  I would say like, okay, let me just back up  a thousand slides here.  So if you are at this point where you like haven't pushed it,  then an 0.0.0.0.0.0.0.0 is cool.  I would say after that, just go for it, you know, 1.0.0.  Like when you're going on RubyFlow and Twitter and stuff,  like you could always make a 2.0. It's not going to hurt anyone.  So the question was, if I release a new version of MyGen  that supports Rails 5, what kind of release is that  from MyGen?  So I haven't made any other changes, but I have, you know,  let's say, for instance, I actually don't have to change  MyGen to support Rails 5.  Then I would say yes, that is a patch-level release.  I guess it's hard to imagine a situation in which  you can imagine sort of augmenting an API in some cases  so that, okay, now I can take a new type  as an argument for a particular method.  And I guess that would technically fall into the minor version release.  You're like adding functionality to the public interface.  So yeah, I would basically sort of evaluate it on the merits  if there's no change to the public-facing interface at all,  then it's a patch-level. Otherwise, maybe it's a minor.  Okay, so the question is, if another gem maker  defines a particular namespace, like it uses a module to define a namespace,  should you use that namespace or should you define your own?  I assume we're talking about a case where your gem is kind of  in addition to this like a plug-in of some sort to theirs.  Right, so I think in most cases where that comes up,  certainly the initial developer of a gem can establish a convention  that this area of my gem's namespace is for you to put your stuff in.  I think you have like a rescue here, has some good conventions around that.  I would say if there's not a really well-defined convention  by the sort of upstream library that you're building against,  there's very little downside to just respecting the general rule  that like the top-level namespace of everything in my code  is the name of my gem in camel case.  Ah, can I say a few words about the underscore versus the dash in the naming?  So, like, mostly use underscore.  Underscore is how we name our files, obviously.  In the gem file, you know, when we put gem whatever,  RubyGems is going to look for the underscore version,  or it's going to look for the same thing that the gem's called, basically.  So dashes are a bit suspect.  I would say, actually, the scenario we were just talking about, though,  where your gem is building on the namespace of another one,  that's a situation in which I would probably go with the dash.  So if I have, like, you know, rescue, like, T-party,  then I would probably do, like, rescue-t underscore-party,  and then, you know, like, the sort of root of my library would be rescue-slash-t-party  in the file system.  But I don't know, it's the Wild West out there with those dashes.  You got to fend for yourself. What's up?  How do I test my gem's compatibility with dependencies?  Do you mean, like, Ruby dependencies or external ones?  Oh, so that's what appraisal is for.  So...oh, right past it.  So appraisal allows us to just list all the different versions  of different dependencies that we want to test against,  and it generates an exhaustive set of gem files  that we can then, using Travis, run our tests against.  Yeah, so the question is, if you have a gem that depends on Rails,  and it's sort of, like, baked it, it's really a tight part of...  Rails is a tight part of using it.  How do you test that? Do you generate a full Rails app?  I personally have written a lot of gems that sort of hang out  at the model layer, and I will say there's absolutely no need to do that  if you're doing it at the model layer.  Like, ActiveRecord is its own thing,  and you can just test against ActiveRecord.  And I don't have any great advice for if you're writing...  Like, if you're writing a view-layer thing that has a bunch of helpers  and you want to test those helpers,  there's definitely no way to get around actually doing that  in a real Rails runtime.  I'm a little embarrassed I don't have any good advice for that  because it sounds horrible and I would never want to do it.  But maybe there is a good practice out there,  and maybe the internet knows.  Cool, so the question is,  Ho, Bundle Gem, RubyGems, Tasks, Jeweler...  So these are all tools for bootstrapping a gem.  And my feeling is basically,  definitely not Ho, definitely not Jeweler.  Basically, like, the level zero thing  is writing your own gem spec is really easy.  It's just Ruby code.  And there's definitely no need for anything that generates a gem spec.  Like, you don't need code that looks almost exactly like the code it generates.  I actually... I skipped this slide,  but I do like Bundle Gems pretty good.  Where did I put it?  Anyway, Bundle Gem is a command  that just generates a nice skeleton of your whole gem environment.  Including all the stuff about...  There it is.  Including all the stuff about, you know, integrating Bundler into it.  So, yeah, I think, you know, you don't want to literally have to sit there  and make the lib directory every time, and Bundle Gem is nice for that.  But I would avoid anything that is trying to add a level of indirection  on top of, say, making your gem file.  Like, your gem file can just be your gem file.  All right, should we call it?  Let's call it.  Thank you, everyone.  Thank you.
Seven_Habits_of_Highly_Effective_Gems_0_3263.png Seven_Habits_of_Highly_Effective_Gems_3264_3599.png Seven_Habits_of_Highly_Effective_Gems_3600_4007.png Seven_Habits_of_Highly_Effective_Gems_4008_4319.png Seven_Habits_of_Highly_Effective_Gems_4320_4463.png Seven_Habits_of_Highly_Effective_Gems_4464_4523.png Seven_Habits_of_Highly_Effective_Gems_4524_5471.png Seven_Habits_of_Highly_Effective_Gems_5472_6959.png Seven_Habits_of_Highly_Effective_Gems_6960_7163.png Seven_Habits_of_Highly_Effective_Gems_7164_7259.png Seven_Habits_of_Highly_Effective_Gems_7260_8267.png Seven_Habits_of_Highly_Effective_Gems_8268_8531.png Seven_Habits_of_Highly_Effective_Gems_8532_8795.png Seven_Habits_of_Highly_Effective_Gems_8796_25931.png Seven_Habits_of_Highly_Effective_Gems_25932_37595.png Seven_Habits_of_Highly_Effective_Gems_37596_46403.png Seven_Habits_of_Highly_Effective_Gems_46404_46451.png Seven_Habits_of_Highly_Effective_Gems_46452_46607.png Seven_Habits_of_Highly_Effective_Gems_46608_47303.png Seven_Habits_of_Highly_Effective_Gems_47304_47711.png Seven_Habits_of_Highly_Effective_Gems_47712_48263.png Seven_Habits_of_Highly_Effective_Gems_48264_48323.png Seven_Habits_of_Highly_Effective_Gems_48324_48503.png Seven_Habits_of_Highly_Effective_Gems_48504_49403.png Seven_Habits_of_Highly_Effective_Gems_49404_50027.png Seven_Habits_of_Highly_Effective_Gems_50028_51947.png Seven_Habits_of_Highly_Effective_Gems_51948_51995.png Seven_Habits_of_Highly_Effective_Gems_51996_52151.png Seven_Habits_of_Highly_Effective_Gems_52152_52403.png Seven_Habits_of_Highly_Effective_Gems_52404_53051.png Seven_Habits_of_Highly_Effective_Gems_53052_53411.png Seven_Habits_of_Highly_Effective_Gems_53412_53699.png Seven_Habits_of_Highly_Effective_Gems_53700_53747.png Seven_Habits_of_Highly_Effective_Gems_53748_53795.png Seven_Habits_of_Highly_Effective_Gems_53796_55835.png Seven_Habits_of_Highly_Effective_Gems_55836_56471.png Seven_Habits_of_Highly_Effective_Gems_56472_56543.png Seven_Habits_of_Highly_Effective_Gems_56544_57251.png Seven_Habits_of_Highly_Effective_Gems_57252_57299.png Seven_Habits_of_Highly_Effective_Gems_57300_57659.png Seven_Habits_of_Highly_Effective_Gems_57660_57743.png Seven_Habits_of_Highly_Effective_Gems_57744_58271.png Seven_Habits_of_Highly_Effective_Gems_58272_58319.png Seven_Habits_of_Highly_Effective_Gems_58320_58774.png
Music Alright, what's up everyone? How's it going? So, who among you has ever released a gem? Okay, good, I like it. Okay, who has released a popular-ass gem? Like tons of contributors, tons of users, tons of love. Okay, alright, so we want to get from like group one and not group one. Everyone into group two. Okay, so that's what we're going to do today. Before I get started, quick intro. My name is Matt. What's going on? If you want to tweet insults, that's a zero. I live in Brooklyn. Looks like this. I work for Genius. We're a platform for annotations. One time John Resig used our platform. It was really cool. And here's my talk. So, we're going to talk about seven specific things that are going to make your gem more appealing. It's going to make your users happy. So your gem should make your users happy, of course. This is a Ruby conference. So, we're going to talk about first how to get users up and running quickly. We'll talk about how to write really good documentation. We'll talk about how to change your gem without causing your applications pain. We'll talk about how to coexist with your user's applications environment in a nice way. We'll talk about how abstractions can lock people in and how we can prevent that. And we'll talk about how to make it easy on your contributors by giving them an easy to run test suite and giving them automatic feedback on their contributions. So, I'm going to start by talking about Quick Start. So, Sinatra, great gem. Really great micro, probably the best micro framework, I think. Sinatra, really good gem. And take a look at this. This is the top of the Sinatra Readme. This is the Quick Start. It's four lines long, which is amazing. There's four lines of Ruby. And then, you know, like the gem install, which doesn't even count, and one line to type in your shell, and you're good to go. And this is what we should all aspire to, right? We need a Quick Start that's this easy. And that's what, as Ruby programmers, we expect. And we don't expect that because we're not smart or because we're lazy. We expect it because we want to spend our time building beautiful, functional, wonderful products, not installing things and configuring them. So, having a really short, good Quick Start is actually a... It's a feature of your code, not of your Readme, right? You can't write a short Quick Start unless your gem allows it. So, how do we get to that point? Well, let's think about how we usually write a gem, right? So, probably what happens is I am working on my application. I have some code that I think, ah, I should open source this. This would be useful to some other people. So, I take the little bit of code out, and I create a new directory for my gem, and I probably stick something like this into my gem file so that my application points at the local version of the gem while I'm developing it. So, I might go on, you know, I might write some tests in my gem, I might flush out the interface a little bit, start writing documentation, and at some point I'll probably go back to my application and say, okay, well, does this all still work with this gem that it's now pointing at that I've been developing? But what I've never done at this point is actually feel what it's like to start using the gem from scratch, to start it in an application that doesn't use it already. So, something that's worked really well for me is to just basically make a, like an example app, it doesn't have to be a Rails app, whatever makes the most sense for the problem that you're solving, but make some sort of example app and really think, okay, if I wanted to do this, if I wanted to use this for some plausible use case, what does it feel like, what does it look like? I have always found at least one pain point that I can solve to make it easier to get started using this technique. So, getting that quick start down is all about just putting yourself in the position of a new user, which is something that doesn't necessarily come natural. Now, once you've got the user hooked, once things are installed, they want to learn more. They want to be able to easily find out everything that your gem has to offer. So, that's where documentation comes in. And we're back to Sinatra. Sinatra not only has a great quick start, but check this out. If we start scrolling down, we're still in the table of contents right now, okay? Still in the table of contents. This will take about a minute to get through the whole thing. Sinatra has an incredibly long readme, and that is a very, very good thing. So, there is nowhere that's more discoverable for documentation than the readme, right? It's there front and center in GitHub. It's there front and center when you're... We have generated code documentation. It's there on airplanes. It's there if you don't have the internet, because it comes with the gem and you install it. So, the readme is a really important place to give of what your gem is capable of doing. And in order to do that, I recommend making the readme a part of your development process, just like, say, testing as a part of your development process. So, you wouldn't submit a pull request, or you wouldn't even necessarily make a commit with new code that doesn't have some new tests. And you can take that a step further and say, I'm also going to submit a... Anytime I add something new to my application, I'm also going to add something new to the readme. And you can take that a step further and write the readme first. So, actually, think about, before you actually lay down any tests or code, think about how is... How am I going to explain this to my users? And you might end up writing a better interface once you think about things in that term, than those terms, than if you document after writing. So, the readme is really good. It's really important. Again, sort of every feature that your gem has, every major thing you can do with it, I think should appear in the readme. Very searchable. Command-F, great tool. But, of course, the readme can't be totally exhaustive. The readme can't take you through tons of examples of every method. It can't take you through all the possible options you can pass, and that's what inline code documentation is for. So, what we've got here is from addressable, another great gem. And addressable is using this... this params and return tag you can see here. And that is not built into standard RDoC. That is actually a documentation tool called Yard. So, that stands for Yay Ruby Documenter. And Yard adds a bunch of extra structure to Ruby code documentation that isn't in the built-in documentation for Ruby. And what's nice about this is that, you know, Ruby doesn't have a lot of structure as a language. You know, it's not statically typed. Everything's mutable at runtime. There's all this meta-programming. And that's great. You know, it's really good for us as programmers. But it does make it difficult to communicate the nuance of how our code works to users who are experiencing it for the first time. So, basically, this is sort of the Yard. Tags act as a counterpoint, which had a lot of structure that can really explicitly communicate, hey, this is the type we're expecting here. There's no type enforcement, but this is what we expect you to pass to us. This is what we're going to pass back to you. And then it generates really nice-looking documentation like this. So, Yard has dozens of tags. These are some of my favorites. A particular importance, I would say, the API tag. So, you know, Ruby visibility, we can do some stuff with it, but we can't, say, make a class private. So, the API tag allows us to say, hey, this is not part of the public API very simply. Deprecated is another good one. There's just one line, and you can mark a method. Don't use this if you're not already. Two of my other favorites are bang attribute and bang method. So, we like to embed a program, and sometimes we define a method in a sense by not actually defining the method. So, maybe we use def-delegator, or active-support delegation, or any number of other things. Well, that's not going to automatically show up, and our code documentation is being a method that somebody can call, but we can tell Yard, using this bang method, pretend there's a method here, and I'm going to document it for you, and then it just shows up as if it's a method in the documentation. So, the effect is that Yard documentation can actually be, in a sense, more authoritative than your code itself, believe it or not. So, the other advantage of Yard, kind of a freebie bonus type thing, is that rubidoc.info. Rubidoc.info is a site that hosts for free documentation for Ruby projects. It'll just pull the code off of GitHub or out of a gem, and it uses Yard, so if you do, you get this extra benefit, basically, for nothing. Okay, so, your users are now fully able to sort of get started with and understand your code, but we want to make sure also that your gem is not going to cause any unpleasant surprises. So, I should be able to do, if I'm a user of your gem, I should be able to do this without a great deal of fear, and that means that I may be updating to a new version of your gem. I need to know what sort of pain that could possibly cause, and ideally not feel any of that pain. So, the boot's basically a way to communicate that information very concisely and in a way that is widely understood is to use semantic versioning. So, semantic versioning divides versions into three numbers, like most versions look like this. Let's start from the back. So, if I bump that green number, if I bump it up from bugfix, or if I bump the bugfix number, it's called the patch level, that's communicating to my users that nothing outward facing is changing. So, this is a bugfix, it's a patch to the code that maybe refactors it or makes it faster, but there's no outward facing change. So, you really nothing to worry about here. Everybody upgrade it will, you're going to get some sort of invisible benefit, but no outward facing change. The middle number, the minor version, means I have added something, but I have not changed anything that was already there, and I have not removed anything that was already there. So, again, you are totally clear to upgrade to, say, 4.3 here, as long as you might not be able to downgrade, because that might mean the future goes away, but you can upgrade a minor version safely. Finally, the major version, the red one, is breaking change. So, anytime you rename a method, you get rid of a method, you change the types of the arguments that a method expects or the return value, just anything where your gem behaves differently for an existing public interface, you have to bump that major version. But, my claim is that, in fact, when we think about breaking changes, we're often maybe thinking a little bit too much about ourselves and not enough about our users. So, typically we want to keep things nice and clean, but we realize that, oh, you know, there's a better way to design this interface, and I want to make a beautiful interface for my users, so I'm going to change the way this method works, or maybe I'm just going to get rid of the old methods and write new ones. Well, that's nice for me as the developer of the gem, but my users are almost certainly feeling more pain having to go through their code and change from my admittedly worse old interface to my better new one than they would just not having to change anything. So, I'm not saying don't make better interfaces, I'm saying don't get rid of the bad ones. We can take some inspiration from JavaScript here, the language itself. So, ES6 is the newest version of the language that's actually beginning to exist in the world, and one of the things that they changed in ES6 was the scoping of variables. So, basically, we're all used to declaring variables with var. The scoping is kind of whack, it doesn't respect stuff like for loops or if statements, although that's the same in Ruby. But the point is they decided they don't like the way variables are scoped, and so they wanted to change it. Now, they could have just changed it, right? But that would have broken existing JavaScript code, and it would have meant that you needed to opt in and say, and explicitly say, I'm now in ES6 mode, and so you can scope variables in the ES6 way, and that's a common understanding we have. But instead, thanks to this blog post from a few years ago, and the idea of one JavaScript, JavaScript community basically decided we are going to always support every old version of JavaScript in every future, sorry, every old JavaScript code will be supported in every future version of JavaScript. And the way they solved the var problem is they just made a new keyword for declaring variables that have better scoping, which is let. So now, basically, you're not supposed to use var, you should never use var, it's deprecated, I mean, you can if you want, you'll always be able to, but JavaScript solved this problem by leaving kind of this, by dragging this trail of cruft along with it. And so it is very crufty to have two different ways of declaring a variable, only one of which you're actually supposed to use, but in the end, they're doing a real service to everyone who's written JavaScript code to date, because they're not breaking anyone's code. So we can take some inspiration from that. Sometimes you've got to make breaking changes, but probably not as often as you think. So now let's think about the environment that our gem exists in, right? We're not going to be the only library that our users are using. So it's important that we play nice in the larger environment. We have at least one dependency ourselves, which is Ruby, right? So we depend on Ruby, we probably depend on some Ruby version, or we're compatible with certain Ruby versions, and chances are we also probably have one or two gems that we depend on. Maybe we do, maybe we don't, but on average we do. So it's important to think about how tightly we're specifying what our dependencies are. So this is something that we could put in a gem spec, but that would be a bit horrifying. So essentially an exact version of active support. It means if you want to use a different version of active support, and you want to use my gem, you are out of luck. And it's very unlikely that this is necessary, so you would have to really, really, really, really not trust the developers of active support to use semantic versioning and not make random breaking changes to be justified in using such an exact version constraint. So we can get a little looser and we can say, okay, well, any 4.2 will do as long as it's at least 4.2.4. But again, semantic versioning, if we all agree on this, it tells us 4.3 is fine, right? 4.3 will only add features that won't remove or change them. So what I can do is I can say, okay, I'm now going to depend on at least 4.2, but any 4.x is okay. Now, I don't know that 5 is going to be okay. I don't know what 5 is going to have, and I know that 5 will break things because that's the definition of being 5. But I can at least, this is as far into the future as I can look with my dependencies and still be safe knowing that my gem won't be broken by a change in my dependencies. So that's fine, and now we're as future-proofed as we can get. But in fact, the truth is not everyone is using Active Support 4.2 right now. So you're still saying to your users, you have to be on the latest version of Rails for this to work. And it's unlikely that you really need to say that. So for instance, 4.0 is probably fine. That's good. And maybe you have to do a little bit of extra work to support 4.0, but the truth of the matter is a lot of shops, a lot of applications and projects aren't always able to keep up with the latest versions of things all the time. And so you're going to make your users' lives much easier if you can be as loose as possible about your version requirements. And I would even say, you know, if we're talking about Rails right now today, it's likely you can do this, because things haven't changed that much in Active Support between 3 and 4. So do what you can to support as far back as is reasonable, and then as far forward as you know, something won't break. And like I said before, we, you know, even in this constraint, we don't support Rails 5, we don't support Active Support 5. So how can we know when that is a problem? So I, you know, I could release this today, and that's fine, but then at some point in the future, Rails 5 is going to come out, and maybe I'm not totally, totally paying attention to that. And so I could suddenly have a gem that has dependencies that are locked in. And then, hopefully, some kind of soul or souls has created a service called Gemnasium, which will automatically audit your gem's dependencies, and it will send you an email alert if you are out of date now. So here's the, here's part of the report for the multi-json gem, great gem. And Gemnasium basically means you don't have to worry about it. You can be, you can respect the future, because in the future, you're going to know right away, oh, hey, I got to, you know, try this out with Rails 5 and see if anything's broken, and then bump the dependencies. But that see if anything's broken, that's kind of the hard part, right? I mean, it's one thing to say, I support lots of different versions of the same dependency, but does it actually work? So of course, we want to run our tests against all of the things we say we support. So all of the versions of Ruby we want to support, all of the versions of other core dependencies we want to support, stuff like Rails, Rack, Event Machine, whatever it happens to be. But particularly, we want to be careful about these very popular, very important gems that almost everyone's using. And so Travis is a great tool to do that. Travis is hosted continuous integration. It will, few lines of configuration, and it's going to run your gem's test suite against whatever Ruby versions you want, including JRuby, Rubinius, it's not limited to MRI. It will run it against a set of gem files, so you can have different gem files, different bundler gem files that specify different gem dependencies. And you can even have it just pick arbitrary environment variables that your test suite understands. So here you can see this devise ORM, it's a little hard to see, but you can see this devise ORM that is devised as using to run it against MongoE to end active record. So Travis is really nice. Travis is hosted. Travis integrates really well with GitHub, so when somebody makes a pull request, it will automatically put in that pull request whether or not the test suite passed on Travis if you haven't hooked up. Of course, you still might want to run your test locally. WWTD is a great gem for doing that, so that basically reads your Travis configuration and then does the same thing running the test on your computer. It'll use RBM for RBM or JRuby to do the different Rubies. So that's very nice. So now you have both the local copy for when you're on airplanes and the cloud-hosted copy. Now, I mentioned just a minute ago that Travis is really good at running against different gem files, but you probably don't want to maintain a bunch of gem files by hand, so I recommend another tool called appraisal, which allows you to just write one file that lists all of the variants of your gem dependencies you want to run again. So for instance, here, you've got four different versions of ActiveRecord that are specific in the appraisals, and this will actually generate four different full gem files that you can then give to Travis. So it just varies the part that you want to vary. That is... Put that all together, right? You get the appraisal gem files, put them up on Travis, and you suddenly have a very robust system for ensuring that you actually support the stuff you say you support and that when you change a feature or change some internals, you're not breaking it on any of the platforms you support. So that is dependencies and versions and not breaking things. There is a little code in this talk. Here we go. So let's think about social networks. Two social networks you might be familiar with are Twitter and Facebook. There are great gems for each of them. So there's a gem for talking to Twitter called Twitter, and there's a gem for talking to Facebook called Facebook. And I want to talk about those gems when we're thinking about designing public interfaces. So the public interface is the part that we basically, when we say we're not going to break stuff, that's the stuff we're not going to break. Let's look at Twitter first. So if I want to write a tweet, this is the method I'm going to call. So it's a method called update. I pass it a string. That's the body of the tweet. I can pass it some options if I want. And that's really nice. That is a fully nice little wrapped up abstraction for me. But if I want to poke my friend on Twitter using the new Twitter poke feature that begins to exist next week, that is actually not something that the Twitter gem supports because it doesn't exist yet. So in that case, I might want to go down to a lower level. And in this case, I'm looking at the code for update. And I see that, okay, there's this perform request with object. That's all right, perform post with object. And that looks like a nice little level interface, so I can dig a little deeper and get to this perfectly nice generic perform request method. And all this is doing is sending an authenticated request on behalf of a particular user to the Twitter API and kind of giving me a nice response. So it's a much lower level thing than that update method we were looking at a minute ago. So this would be perfect, right? I can make a poke request through the using perform request here, except this is not part of the Twitter gem's public API. This is a private method. So we kind of have this called trace that looks like this. We've got a nice public update method to do one specific thing that the author of the Twitter gem expected that I would want to do correctly. And then we have some private methods that are at decreasing levels of abstraction. So lower and lower, more generic, less wrapped up and packaged up nicely. But another way that one might think of this kind of gem when you're writing it is first I'm just going to write a gem that lets me make authenticated request to Twitter without any real high-level knowledge of what the Twitter API, what the resources are, what the endpoints are. And in that case, perform request would actually become one of the main interfaces to that layer of the gem. And then on top of that, I would essentially use my lower level library, which is fully tested, has a public interface that's stable, to write a nice high-level bunch of code. And really the effort that must have gone into this gem is incredible, because it does support basically everything you can do with the Twitter API is explicitly contemplated in this gem. I can go crazy like that. So this is sometimes referred to as a layered architecture. Another way to think of it is essentially I'm eating my own dog food, but I'm also just letting you have the dog food if you want, so dog or dog food, either way. That makes perfect sense. Another way I might choose to approach this problem though is to think, well, can I just get away with this? Can I just get away with perform request? Do my users really need this high-level kind of domain model, which again is really beautiful, but is that necessary? The Twitter API is pretty easy to understand. And that is the choice that Koala makes, it's the Facebook gem. So Koala has this put connections method, and put connections basically is the equivalent of that last method we were looking at. And this is the highest level of abstraction Koala has. So Koala does not know what resources are available in the Facebook API. And that lets it be first pretty lean, and second, when Facebook introduces some new features in its API, as long as it's not some totally new structure for the API, which it pretty much never is, Koala is instantly up to date. So I still prefer this one, really. I mean, this is the best if you got that kind of time. But you can also maybe do your users a lot of favor, a big favor, by just giving them a low-level API that they can work with that takes the boilerplate out, but doesn't necessarily provide the full abstraction. So the quick start is to your users what the test suite is to your contributors. If I want to contribute to your gem, the first thing I'm going to do is check it out and try to run the test suite. If I get frustrated, I'm not going to have a ton of patience for that. And it's pretty likely that I might just sort of keep my branch and never contribute it back because I can't prove to you that it works with the test suite, et cetera, et cetera. So it's really, really important for your contributors, if you want to have good contributions, to make sure that your test suite is really, really easy to run. So it's unlikely that you can just do this. It's unlikely that rake test alone will just work, but it's definitely possible that bundle and then rake test is sufficient. And this, I think, most gems will be able to provide this. So the key here is you already use Bundler to manage your application's runtime dependencies. Just use it to maintain your gem's test dependencies at runtime in the same way. So very simple way to do this. You have a gem file. You can just say use the gem spec, just this one line gem spec. Use the same gem spec that's in the directory we're in right now, and Bundler knows what to do. Then in your test code, in your test helper, a spec helper, you can just, that first line require Bundler setup. And you're good to go. And Bundler loads in the gem environment, it installs everything for your users, and so if you don't have any external dependencies, if you're just sort of like a Ruby self-contained library here, then now your users can very consistently and nicely run tests. But if you do have external dependencies, so say you need Postgres, then Bundler can't deal with that for you, right? Bundler can give you the Postgres library, but it can't give you an actual Postgres instance to test against. So my favorite method of getting this to my users, to my potential contributors, is using Vagrant. So Vagrant is a tool for managing virtual machines, like virtual box, specifically for development environments. So it's mostly used to sort of have a repeatable development environment for a team that's working on an application, but it's actually really good for having a repeatable test environment too, for a library. So if I wanted to, if my tests require Postgres, then I can ship a Vagrant file. It could be as simple as this. Basically, we're using Ubuntu. We are installing Postgres when we first spin up the machine, and then we're going to forward the Postgres port to the host computer so my tests we can actually interface with it. And that's it. And now instead of having to go through a bunch of instructions saying, okay, well, we expect Postgres to be on this port, so go ahead and just deal with it. Potential contributors can run Vagrant up, and they've got the environment that allows them to run the tests. So that's really nice. Now, of course, being able to run the tests is one part. Actually, writing the code is another part if I have contributors to my project. And few things are more heartbreaking to me than getting a pull request that, you know, it's a good new feature, say, it has a nicely factored code, really, just everything I want except maybe I see something like this. Now, I know this is hard to look at, but I would never put a multi-line block with curly braces. But I respect the decisions of those who do. And I would hate to be in a situation where I got a really good pull request where there's some style nitpick I have, and so instead of saying, hey, thanks, and accepting the pull request, I have to say, can you change that to a do end, and then I'll take your free work that you're giving me from my library? So can we avoid this? Yes. RuboCop is a tool for style checking and linting. You can set up rules like these. There are more. To your preferences, right? It's your library. It's your piece of software. So you get to decide what the style is. And the nice thing about RuboCop is that it's automatic. It's not coming from a human. It doesn't feel like a rejection. It just feels like a cold, impersonal robot being cold and impersonal. And it's very quick. So you can take the RuboCop, you just run RuboCop, basically, once you set up these rules, and you can put that in your Travis configuration so that when somebody submits a pull request, they'll see feedback right in the pull request saying, hey, the build didn't pass. Go look at the build report, and then that shows where the style doesn't match your style, or there are various linting problems. So then you get to have your cake, right? You don't have to have weird confrontations with contributors about things that are completely a matter of opinion, but you do get to have nice consistent style in your library. So that was number seven, and we are at the end. So real quick, just a recap. Write a gem that has a really short quick start. And the way to do that is try it yourself. Wait a really long read me. Use Yard for code documentation. Try not to ever, ever, ever do breaking changes if you can avoid it. Be sure to use semantic version to communicate what's going on to your users. Understand that your users are probably using old versions of things. At least some of them are, and support the past as much as you can, as well as supporting the future as much as you can. So keep track of new versions of things with gymnasium. Run your test suite against lots of different versions. Use Travis for that. Avoid abstractions that can lock people in to your assumptions about how they're going to use your code. So consider providing multiple layers of public interface. A nice high level one, but also an equally valid, equally public low level one. Make it easy to run the test suite by using tools like Bundler and Vagrant. And finally, use RuboCop to give instant and non-confrontational feedback on code style to your contributors. That's it. Got plenty of time for questions here. Right. Right. So the question was, the first part of the question was, you're wrong about RuboCop. And the second part of the question was, what about a contributing document? That is a great one. So in terms of the contributing document, basically you can make a file in your repo called contributing.md. All capital contributing. I'm not sure if that actually matters. And GitHub will put a banner at the top of the form where people submit pull requests and issues that link to that document. So it's kind of in people's face right when they begin to actually attempt to contribute, and you can write whatever you want, such as, follow my style. On the question of RuboCop, yeah, sure, opinions may differ. I guess I would be interested, maybe after we finish here to hear what the objections are. If you're uncomfortable with the idea that the build should break because of style issues, then RuboCop can also not be a part of the build, but still be there for people who are trying to match your style. Do I have a suggestion on which license we should use? MIT? Seems like the normal one. Yeah, a copy left doesn't seem to be enforceable, but I'm also not a lawyer. So yeah, I guess I don't really have an opinion, but I do MIT because that's what everyone else does, and yeah, usually is a good idea. Okay, for those on the other side of the room, avoid multi-license gems, pick a license, have that be the one true license. In my experience, what is the best way to publicize a gem? RubyFlow is good. I've had good luck with that. Depending on, you know, I mean, I'm not going to say anything that probably else that isn't pretty obvious. I mean, Hacker News, Reddit, Ruby, Subreddit, if you're into Reddit, Twitter. But RubyFlow is probably the only one that maybe is a little under the radar, but I've had pretty good responses from that. Ha-ha. Yeah, that's a good question. So the question is, for versioning, what is a good practice for the first release? I mean, when you see like 0.0.1, it's like, come on, you know, like actually out there in the real world. I would say like, okay, let me just back up a thousand slides here. So if you are at this point where you like haven't pushed it, then an 0.0.0.0.0.0.0.0 is cool. I would say after that, just go for it, you know, 1.0.0. Like when you're going on RubyFlow and Twitter and stuff, like you could always make a 2.0. It's not going to hurt anyone. So the question was, if I release a new version of MyGen that supports Rails 5, what kind of release is that from MyGen? So I haven't made any other changes, but I have, you know, let's say, for instance, I actually don't have to change MyGen to support Rails 5. Then I would say yes, that is a patch-level release. I guess it's hard to imagine a situation in which you can imagine sort of augmenting an API in some cases so that, okay, now I can take a new type as an argument for a particular method. And I guess that would technically fall into the minor version release. You're like adding functionality to the public interface. So yeah, I would basically sort of evaluate it on the merits if there's no change to the public-facing interface at all, then it's a patch-level. Otherwise, maybe it's a minor. Okay, so the question is, if another gem maker defines a particular namespace, like it uses a module to define a namespace, should you use that namespace or should you define your own? I assume we're talking about a case where your gem is kind of in addition to this like a plug-in of some sort to theirs. Right, so I think in most cases where that comes up, certainly the initial developer of a gem can establish a convention that this area of my gem's namespace is for you to put your stuff in. I think you have like a rescue here, has some good conventions around that. I would say if there's not a really well-defined convention by the sort of upstream library that you're building against, there's very little downside to just respecting the general rule that like the top-level namespace of everything in my code is the name of my gem in camel case. Ah, can I say a few words about the underscore versus the dash in the naming? So, like, mostly use underscore. Underscore is how we name our files, obviously. In the gem file, you know, when we put gem whatever, RubyGems is going to look for the underscore version, or it's going to look for the same thing that the gem's called, basically. So dashes are a bit suspect. I would say, actually, the scenario we were just talking about, though, where your gem is building on the namespace of another one, that's a situation in which I would probably go with the dash. So if I have, like, you know, rescue, like, T-party, then I would probably do, like, rescue-t underscore-party, and then, you know, like, the sort of root of my library would be rescue-slash-t-party in the file system. But I don't know, it's the Wild West out there with those dashes. You got to fend for yourself. What's up? How do I test my gem's compatibility with dependencies? Do you mean, like, Ruby dependencies or external ones? Oh, so that's what appraisal is for. So...oh, right past it. So appraisal allows us to just list all the different versions of different dependencies that we want to test against, and it generates an exhaustive set of gem files that we can then, using Travis, run our tests against. Yeah, so the question is, if you have a gem that depends on Rails, and it's sort of, like, baked it, it's really a tight part of... Rails is a tight part of using it. How do you test that? Do you generate a full Rails app? I personally have written a lot of gems that sort of hang out at the model layer, and I will say there's absolutely no need to do that if you're doing it at the model layer. Like, ActiveRecord is its own thing, and you can just test against ActiveRecord. And I don't have any great advice for if you're writing... Like, if you're writing a view-layer thing that has a bunch of helpers and you want to test those helpers, there's definitely no way to get around actually doing that in a real Rails runtime. I'm a little embarrassed I don't have any good advice for that because it sounds horrible and I would never want to do it. But maybe there is a good practice out there, and maybe the internet knows. Cool, so the question is, Ho, Bundle Gem, RubyGems, Tasks, Jeweler... So these are all tools for bootstrapping a gem. And my feeling is basically, definitely not Ho, definitely not Jeweler. Basically, like, the level zero thing is writing your own gem spec is really easy. It's just Ruby code. And there's definitely no need for anything that generates a gem spec. Like, you don't need code that looks almost exactly like the code it generates. I actually... I skipped this slide, but I do like Bundle Gems pretty good. Where did I put it? Anyway, Bundle Gem is a command that just generates a nice skeleton of your whole gem environment. Including all the stuff about... There it is. Including all the stuff about, you know, integrating Bundler into it. So, yeah, I think, you know, you don't want to literally have to sit there and make the lib directory every time, and Bundle Gem is nice for that. But I would avoid anything that is trying to add a level of indirection on top of, say, making your gem file. Like, your gem file can just be your gem file. All right, should we call it? Let's call it. Thank you, everyone. Thank you.
some code that I think, ah, I should open source from a few years ago, and the idea of one JavaScript, JavaScript community basically support, and you want to use my gem, you are out contributors. If I want to contribute to your sort of like a Ruby self-contained library here, have contributors to my project. And few things basically you can make a file in your repo called is a good idea. Okay, for those on the other side release? I mean, when you see like 0.0.1, it's