1425999450 1403860301 road compressor

Rails API as an Engine

Photo by Viktor Hanáček

Small intro

You’re about to see the beauty and ease of writing an engine application in Rails!

I first discovered engine apps when my team had the task to write an internal API that would export data to an external database.

Now, if you were the noobish me about 5 or 6 years ago, when I was still elbows deep in the beautiful thing that is non-frameworked PHP (blast it to hell) you’d probably just say, “screw it” and bundle it up with the rest of the code. Maybe you’d go as far as putting it all in its own controller, but nothing more.

This is obviously not the way to go about it, especially when rails gives us a pretty nice way to isolate the code that is not really a part of our main application.

Engines in Rails are miniapps that enhance the way a main app works; they’re plugins really. From what I’ve seen, you can actually use a generator but I haven’t used it yet, and I’m not going to use it now.

So, let’s get started. We just want to create a way to access data, right? Right. Let’s build a rails API as an engine.

Setup

To start off, I have some very basic rails app that has one model named Item. It doesn’t have any controllers, I created some data from the console.

Right now, I’m going to create a basic api with one controller that will simply return the items when asked.

We’re using mongoDB and Rails 4.0.2. I will only add one model so we have some data to show at the end here.

Here’s how the Item model looks:

And here’s me adding some sample data:

Aside from the steps above, I only added an index action in the application controller and set it as a root path in routes file.

And that’s all we need to start working on the engine app!

Now the fun begins...

Let’s start by creating a place to hold our engine apps. I created a simple directory called apps in the project root.

Now, we want to put our new engine app in the apps directory. We’re going to name it internal_api (I’ve never claimed to be good at naming this stuff). And while we’re at it - let’s stuff a lib dir in there too.

We should have this lovely tree:

After this we should let our rails app know that we want to load our engine thingy:

And here is one important step: extend the Rails engine. Create internal_api dir inside lib Note that we’ll be doing this quite a bit. Since we’re using a module, we need to nest the files properly. I kept forgetting this key step and was bombarded with name errors. Then, start up a new file apps/internal_api/lib/internal_api/engine.rb.

Now move on to config/application.rb and add these lines so we run it all:

Initialize the module - create apps/internal_api/lib/internal_api/internal_api.rb:

Here’s the module name I mentioned earlier.

We’re almost done with the engine setup! We only need to initialize some routes now. I’m going to put in a route to a controller we haven’t written yet, but will do so soon. Create a config directory in the apps/internal_api and put routes.rb in there:

Now we just mount them and we can move on to the real stuff we want to do. Go to config/routes.rb (not the apps one!) and mount your routes there:

Here’s what the rake routes output looks like:

See how the routes are nicely namespaced and isolated?

Tada! We can now put whatever we need inside of our engine app. So, let’s add a controller for our items and call it a day.

Here’s a basic application controller first. Remember to nest!

And then our items controller:

There shouldn’t be issues with using stuff (models, classes, services, workers, whatever you have in the app directory) from our main app in our engine app, but since we’re doing code isolation for a reason we should consider extracting shared classes into a lib directory instead of calling code straight from our main application (except for models).

Here’s the tree we should have, unless we lost a nest somewhere:

Let’s test it out, shall we? Run your server (mine runs at 0.0.0.0:3000).

It works!

Other uses

Engine apps are a really nice way to separate code. Of course, APIs like the one above aren’t the only way to use them.

Let’s say we have a huge app working with multiple platforms. One idea would be to write a separate engine app for every platform - the main application can then function only as an aggregator that displays data (HUB or BUS architecture, can get very creative here).

Or another scenario - for instance we have some processor that is only loosely connected to our main project. The code can very easily be extracted to an engine application.

There are tons of ideas and uses for engine apps. It’s worth tinkering with them to see how they can work for you.

Additional reading:

You can find the complete code here: github.com/MSzweda/aaewebinar

And read more about engines here:guides.rubyonrails.org/engines

On 27.06.2014 in Dev