17 Mar

Deploy your Rails and AngularJS app

Last update on 2015-03-17

In our previous post, we successfully configured our server-side and client-side applications in a decoupled way in a development environment, but what about deploying on a different environment (i.e. staging, production)?, how would we join our applications together and deploy them as one?.

Well, there are at least two ways to deploy our application and it basically depends on the following:

  1. When we have total control of the filesystem of the target server.
  2. When we don't (as it occurs with Heroku, because of its Ephemeral Filesystem)

In any of the cases above we have two other sub-options:

a. Copy the generated client-side files (i.e. under frontend/dist folder) to the Rails application public folder (or the corresponding assets folder for js, css, etc.) before pushing (the generated files are checked in version control for this to work).

b. Use lineman on the remote server to build the client application from its source files and copy the generated files to the Rails application to its final location, as in option a.

If we choose option a, then we can push the bundled-ready-to-operate application to our remote server, provided we have generated all needed files in our development machine and committed the changes.

On the other hand, if we choose option b we don't have to commit the generated files, thus reducing the size of the our version-controlled repository, but in contrast, running the generation on the server could take more time and be more complex to setup (since you need nodejs and lineman and all the other things to be able to generate the files).

Depending on what you need and if you do or do not want to commit generated files in version control, you can choose between these two, so we will leave that to you, but we will present both alternatives.

Note: As mentioned earlier these apply to the example app from our previous post.

Generate the client-side application before pushing (option a)

Let's assume you have all changes ready, and you wish to push your app to Heroku, but could be other platform or your own server at this point. The steps you would need to perform are the following:

Generate the frontend app files.

$ lineman build

This will generate all the app into the frontend/dist folder. Then, for example, manually (or via a single "cp" line script) copy this folder's contents into the Rails public folder (which we recommend), or painstakingly copy the corresponding files into each of the assets folders for the asset pipeline to work, which we do not recommend since all the minify, uglify and fingerprinting are already performed by lineman.

$ cp -R frontend/dist/* public/

lastly, commit these changes to the repo.

$ git commit -am ‘add client application version x.y.z’

In any case we could write a script to do this and later deploy. Note that we will be overriding the generated files each time we deploy, but that's pretty much it, you app would be deployed without any "special" steps.

Generate the client-side application after pushing (option b)

As we mentioned before, this process is truly more complex compared to the previous one, but the good news is that there is already the rails-lineman gem to helps us deal with this. However we did some tweaks of our own to the gem to improve the possible scenarios for deployment that it could manage. With this option you do not have to commit any generated files, only the source ones.

Overview of the rails-lineman gem

Generally speaking, this gem adds the CSS and JS files built by Lineman and adds them to asset pipeline like any other asset in a Rails application. While this is good for some situations where the views are built by Rails and the CSS and JS by Lineman separately, we would like to just be able to copy our generated files without any Rails intervention or dependency on its views.

Improving the rails-lineman gem

For the purpose of exploring the deployment solution we modified the rails-lineman gem adding it a new deployment method, which is just to copy our AngularJS client application to the rails public folder and not use rails assets precompile feature at all, however you can switch to the asset pipeline feature if you want. For now, we have our own fork of the gem here. The modified gem dynamically generates one of the two following rake tasks, depending on the deployment method you choose:

  • assets:precompile , which adds the client application assets to the Rails asset pipeline flow (if you choose the :asset_pipeline method)

  • deploy:frontend which runs Lineman and copy/pastes all files into dist folder to the Rails public folder.

To choose one of the two deployment methods you have to setup your Rails application configuration config/application.rb the deployment_method option, as follows:

config.rails_lineman.deployment_method = :asset_pipeline

or

config.rails_lineman.deployment_method = :copy_files_to_public_folder

Deployment method processes

When you choose the :asset_pipeline then the rake task assets:precompile will be overridden by the rails-lineman gem, so the deployment process will be like:

  1. You invoke the command you use to deploy the application to the server (e.g. by using Capistrano or by pushing to Heroku or any platform of your choice) this process and setup is up to you.

  2. The assets:precompile rake task will be run, which will:

    • Install NodeJS and Lineman with npm if not already available (just for Heroku, the gem checks for this particular case).
    • Build the client application assets (e.g. css and js files ) using Lineman. The generated output goes to the frontend/dist folder.
    • Add the client asset paths from frontend/dist to the Rails asset_path so that they are precompiled together.
  3. The Rails app will be started and ready to go!.

On the other hand, when you choose the :copy_files_to_public_folder option the process will be:

  1. You invoke the command you use to deploy the application to the server (e.g. by using Capistrano or by pushing to Heroku or any platform of your choice) this process and setup is up to you.

  2. The Rails app will be started, and afterwards this will happen:

    • Install NodeJS and Lineman with npm if not already available (just for Heroku, the gem checks for this particular case).
    • Build the client application assets (e.g. css and js files ) using Lineman. The generated output goes to the frontend/dist folder.
    • Copy all these generated files to the rails public folder).

Be sure to gitignore the frontend/node_modules, frontend/generated and the frontend/dist folders and be careful to not gitignore all the frontend folder. This way you only have the source files in your version control.

Deploying the example app

Ok, now that we have explained a little bit about that, let's continue deploying our example app. We will use the :copy_files_to_public_folder deployment method and deploy to Heroku, where we do not have complete control over our filesystem, so that you can test this without acquiring a dedicated machine.

Heroku

install our fork of rails-lineman(version 0.3.1).

To do so, add to your Gemfile:

gem 'rails-lineman', github: 'degzcs/rails-lineman'

setup/update the config/application.rb, as per the README, like this:

# Integrate with lineman
config.rails_lineman.lineman_project_location = "frontend"
config.rails_lineman.deployment_method = :copy_files_to_public_folder

Commit all changes and push to Heroku!

$ git push heroku master

You can see this app running on heroku here and grab the code from here.

Deployed Rails AngularJS App

Conclusion

There are many ways in which you can organize, separate and deploy your project, this is proposed approach way of dealing with this specific technology pairing (AngularJS/Rails). This post plugs in with our previous post just in case you wonder why we are eperimenting with these deployment methods.

Other awesome related projects worth checking out are Meteor, Gyoman, Ember, Sails, MEAN, and a bunch of others.

We hope this information helps you to start with these awesome technologies. See you around!

comments powered by Disqus