 What's up, everyone? So who among you has ever released a gem? OK, good. I like it. OK, who has released a popular-ass gem? Like, tons of contributors, tons of users, tons of love. OK, all right. So we want to get from group one and not group one, everyone into group two. OK, 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, right? So your gem should make 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 by giving them automatic feedback on their contributions. So I'm going to start by talking about QuickStart. So Sinatra, great gem, really great micro for 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 QuickStart. It's four lines long, which is amazing. There's four lines of Ruby. And then the gem install, which doesn't even count, and one line to type into your shell. And you're good to go. And this is what we should all aspire to, right? We need a QuickStart 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 QuickStart is actually, it's a feature of your code, not of your readme. You can't write a short QuickStart unless your gem allows it. 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. And then I go on. 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, OK, 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 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, OK, 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. Still in the table of contents. And then finally, this will take about a minute to get to the actual 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 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 when you install it. So the readme is a really important place to give to lay out a full map 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, any time 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 it before you actually lay down any tests or code. Think about, 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 param 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 Ruby doesn't have a lot of structure as a language. It's not statically typed. Everything's mutable at runtime. There's all this meta-programming. And that's great. 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, the Yard tags act as a counterpoint, which has 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 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. So 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 in our code documentation as 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. OK, 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 most basic 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, 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 at will, you're gonna get some sort of invisible benefit, but no outward facing change. The middle number, the minor version, means I've added something, but I've not changed anything that was already there and I've 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 gotta 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 wanna keep things nice and clean. We realize that, oh, there's a better way to design this interface and I wanna make a beautiful interface for my users so I'm gonna change the way this method works or maybe I'm just gonna 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 wack, 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 am 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, all 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 what 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 gotta 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 gonna 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 gonna depend on at least 4.2 but any 4.x is okay. Now I don't know that five is gonna be okay. I don't know what five is gonna have and I know that five will break things because that's the definition of being five. 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. 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 gonna 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 three and four. 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 gonna 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. Happily some kind 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 gonna know right away, oh hey I gotta 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 wanna support. All of the versions of other core dependencies we wanna support, stuff like Rails, RAC, Event Machine, whatever it happens to be. But particularly we wanna 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 gonna 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 devises using to run it against Mongo and 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 wanna 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 RVM 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 wanna 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 wanna run again. So for instance here you've got four different versions of Active Record 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 wanna 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 wanna talk about those gems. We're thinking about designing public interfaces. So the public interface is the part that we, basically when we say we're not gonna break stuff, that's the stuff we're not gonna break. Let's look at Twitter first. So if I wanna 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 wanna 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 wanna 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, sorry 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 kinda giving me a nice response. So this is 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 kinda have this call 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 wanna 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 gonna 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 since 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 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 wanna contribute to your gem, the first thing I'm gonna do is check it out and try to run the test suite. And if I can't, 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, 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 wanna 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 applications 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, use the 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, spec helper, you can just, that first line require bundler setup. And you're good to go. And bundler loads in the gem environment and 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. 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. They 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 gonna 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 a few things are more heartbreaking to me than getting a pull request that 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 for 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 and style doesn't match your style or there are various linting problems. So then you get to have you kick, 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 to 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. If you cannot avoid it, be sure you use semantic versioning 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 gonna use your code. So consider providing multiple layers of public interface. The 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? Ruby flow is good, I've had good luck with that. Depending on, I mean, I'm not gonna say anything that probably else that isn't probably pretty obvious. I mean hacker news, Reddit, Ruby subreddit, if you're into Reddit, Twitter. But Ruby flow is probably the only one that maybe is a little under the radar but I've had pretty good responses from that. 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 1,000 slides here. So if you are at this point where you like haven't pushed it, then an 0. is cool. I would say after that, just go for it, you know, 1.0. Like when you're going on Ruby flow and Twitter and stuff, like you could always make it 2.0. It's not gonna hurt anyone. Sure, that's a great question. So the question was if I release a new version of my gem that supports Rails 5, what kind of release is that from my gem? 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 my gem 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, I mean, 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 tracing 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 an 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'm thinking of 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 gonna look for the underscored version, like it, or it's gonna 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 I might, if I have like, you know, rescue like tparty, then I would probably do like rescue dash t underscore party, and then you know, like the sort of root of my library would be rescue slash t underscore party in the file system. But I don't know, it's the Wild West out there with those dashes. You gotta offend 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's for. So, oh, right past it. So appraisal allows us to just list all the different versions of different dependencies that we wanna 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's really a tight part of, or Rails is a tight part of using it, how do you test that? How 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 wanna test those helpers, there may be 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 wanna do it, but maybe there is a good practice out there and maybe the internet knows. Cool, so the question is, hoe, bundle gem, Ruby gems, tasks, jeweler, so these are all tools for bootstrapping a gem, and my feeling is basically, definitely not hoe, 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 integrating bundler into it, so yeah, I think you don't wanna literally have to sit there and make the lib directory every time and bundle gem's 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.