Rails with no JS framework
Tags: softwareengineering, ruby, javascript
Published: 2020-03-02 16:15:59

---
title: Rails with no JS framework
published: true
date: 2014-02-28 21:57:00 UTC
tags: Software Engineering,Ruby,JS
canonical_url: https://www.christianblavier.com/rails-with-no-js-framework/
---

### Because all apps don’t need Ember or Angular
 ![Rails with no JS framework](https://www.christianblavier.com/content/images/2019/12/library.jpeg)

I’m writing code for over a decade now, consider myself as a geek, and as a geek I’m always very curious and excited about any new fancy technology released every now and then.

These days client-side MVC frameworks are very trendy, and to be honest what I read and learned regarding _Ember_ or _Angular_ (among many others) looked really shiny and well-thought. These are typically frameworks that I would love to love.

But right now, I don’t need them. **Do you?**

* * *

### The playground

In addition to my freelancing activity, I’m running [Folyo](http://www.folyo.me/) (which by the way, can help you to find great designer talents). It’s as _Ruby on Rails_ website with a UI that could be defined as slick, reactive and responsive (especially thanks to [Sacha](http://sachagreif.com/), my co-founder), but **by no means complex**.

As a job board, Folyo has quite a common _page driven architecture_ where any action performed on a page will often lead you to another page or refresh current page content. Folyo’s UI **does not involve rich interactions** like _live data charts_ or _dynamic data binding._ No, just web pages and it’s perfectly fine!

#### So what about client-side frameworks?

Why not, since it seems like the way to go these days?

Because such frameworks are complex tools designed to solve complex interaction issues. You must be aware that they also have serious drawbacks regarding productivity, testing, SEO, etc. (read [this](https://sourcegraph.com/blog/switching-from-angularjs-to-server-side-html)) and also have a steep learning curve.

Building a website with client-side MVC frameworks will require you to build your Rails server-side application as an API application only, which means depriving you of some of Rails beauty and increasing your development effort (API + UI) significantly.

Still, using a framework also brings some virtues. One of the first being to help you with organizing your code in a more manageable way. While Rails is really directive regarding the way you need to organize server-side code, it dictates only one rule for Javascript:

_Put whatever you want in application.js._

**Eh. Really?**

### Time to clean it up

I will now explain the way we decided to organize Javascript code on [Folyo](http://www.folyo.me/), in a much more manageable way than _everything in application.js_. I believe it’s quite pragmatic and I’m sure it will fit with a lot of other applications.

Required libraries:

- _jQuery_, obviously.
- _Coffeescript_, not mandatory but its syntax for class definition is very convenient.
- _Turbolinks._ It will really improve user experience by making navigation from page to page AJAX only.
- _HeadJS,_ used through _headjs-rails_ gem, will speed up your app by loading your JS asynchronously.

#### Code hierarchy

To organize the code, I will simply obey to following rules:

![Rails with no JS framework](https://cdn-images-1.medium.com/max/800/1*UpRTNIf-ll1ovWBUtolpJA.png)<figcaption>Folyo JS code hierarchy</figcaption>

- Any page requiring JS code will have it’s own class (a JS view).
- If you need to share piece of code between views, put it in a widget class.
- _application.js_ is just doing the glue between your classes, jQuery, HeadJS and Turbolinks.
- That’s all!

#### Views

ApplicationView is the default view, inherited by any other view, and instantiated by default when no specific view is provided.

<!--kg-card-begin: markdown-->
```
window.Views ||= {}
class Views.ApplicationView

 render: ->
   Widgets.FancyBox.enable()
   Widgets.MarkdownEditor.enable()
   
 cleanup: ->
   Widgets.FancyBox.cleanup()
   Widgets.MarkdownEditor.cleanup()

```
<!--kg-card-end: markdown-->

Since we want _FancyBox_ and our _Markdown_ editor to work on a lot of pages, we put it in the _ApplicationView_ as a default behavior.

Then a typical view would look like that:

<!--kg-card-begin: markdown-->
```
window.Views.Newsletters ||= {}
class Views.Newsletters.EditView extends Views.ApplicationView

 render: ->
   super()
   $('a.preview').click (e) ->
     e.preventDefault()
     url = $(e.target).attr('href')
     window.open(url, '_blank', 'width=800,height=800')
     
 cleanup: ->
   super()

```
<!--kg-card-end: markdown-->

Simple as that! But why the cleanup stuff?

Because with _Turbolinks,_ the Javascript environment is not reset between each page. For instance, if you define a timer on a page, it will keep ticking on next pages. So, just remember to stop your timer in the cleanup method (or remove any document-wide event listener)

#### **Widgets**

Sorry, no rocket science here.

<!--kg-card-begin: markdown-->
```
window.Widgets ||= {}

class Widgets.FancyBox
  @enable: -> $(".fancybox").fancybox()
  @cleanup: -> $(".fancybox").off()

```
<!--kg-card-end: markdown-->
#### The glue

_application.js_ is now only the entry point which will listen to _Turbolinks_ events to render proper views and clean them up.#= require everything you need

<!--kg-card-begin: markdown-->
```
#= require everything you need

pageLoad = ->
  className = $('body').attr('data-class-name')
  window.applicationView = try
    eval("new #{className}()")
  catch error
    new Views.ApplicationView()
  window.applicationView.render()
  
head ->
  $ ->
    pageLoad()
    $(document).on 'page:load', pageLoad
    
    $(document).on 'page:before-change', ->
      window.applicationView.cleanup()
      true
      
    $(document).on 'page:restore', ->
      window.applicationView.cleanup()
      pageLoad()
      true

```
<!--kg-card-end: markdown-->

You also need to define a specific data attribute on each page, to indicate which JS view needs to be rendered.

In your _application\_controller.rb_, define _js\_class\_name_ method:

<!--kg-card-begin: markdown-->
```
def js_class_name
   action = case action_name
     when 'create' then 'New'
     when 'update' then 'Edit'
    else action_name
   end.camelize
   "Views.#{self.class.name.gsub('::', '.').gsub(/Controller$/, '')}.#{action}View"
 end

```
<!--kg-card-end: markdown-->

And then use it in your layout (as well as _HeadJS_ initialization)

<!--kg-card-begin: markdown-->
```
%html
  %head
    = javascript_include_tag 'head.min'
    = headjs_include_tag 'vendor', 'application'
  %body{'data-class-name' => js_class_name}

```
<!--kg-card-end: markdown-->
### Final words

I’m seing most and most Rails projects bootstrapped using either _Angular_, _Ember_ or _Backbone_ as their _de facto_ choice.

Whilst these frameworks are very powerful, I hope that after reading this post you will consider that _not using a JS framework_ is also a valid choice.