Tuesday, August 23, 2016

JavaScript on Rails 2016

Whilst I am primarily a JavaScript programmer, my framework of choice is Ruby on Rails. A lot of JS guys like Node or other frameworks which are JS all the way through, but there's a lot to like about Rails, even in 2016 and it's worth learning another language (Ruby) or two (SQL) in order to get the best environment on which to write your JavaScripts.

Before I say what I like about Rails though, I will take you through what I don't like just to balance things out.

What I don't like about Rails

Asset Pipeline still does not let you use ES6 Modules. 

Even at this late stage, one still cannot access ES6 Modules in Rails in a "Railsy" manner. You can either bypass the whole Asset Pipeline or else separate out the JS using Webpack or whatever, but in a standard Rails project, the fact that the asset is generated with unique hash in the file name before the extension pretty much nullifies its use for ES6 Modules which need a static location to store the files. You can probably try putting the library files directly in the public directory, but nowadays a lot of these need to be built first which means some kind of build system needs to be involved. DHH, please get on this!

Turbolinks

Turbolinks works great in theory, but in practice, as soon as you try to use some third party code which depends on document.ready, you are up a certain creek without a paddle. Not only that, but your JS will live on in memory between pages and if you are not careful about how you write your code, the browser's memory could end up filling up and slowing the user's experience to a crawl (but this is a problem most SPAs face).

Gem versions of JavaScript libraries

This is really a convenience thing that is only convenient if you don't mind running out of date code. Because the asset pipeline makes it harder to pull in 3rd party libraries and their dependencies, some nice people out there will create gem packages of these JS libraries which you can install into your Rails app and voila! They (mostly) just work! Until you want to access a new feature of the library and the gem maintainer has decided to go on an extended holiday. Then you end up trying to pull it in yourself and find out you need to change all the embedded stylesheet links because of the Asset Pipeline (see above)

CoffeeScript

I love coffee, I love scripts, but I hate CoffeeScript. Thankfully this seems to be on its way out as ES6 becomes more prominent (and ES6 did pull a few good things from CoffeeScript) but CoffeeScript is not JavaScript and it used to be the "defacto" Rails standard

What I like about Rails

Now that we are done with all the things I don't like about Rails (as a JavaScript programmer), I can talk about the things I do like.

Turbolinks

Wait, wasn't that in the dislike section? Yes it was, but there a number of things to like about Turbolinks as well.

Turbolinks allows you to (almost) get the speed benefits of an SPA (Single Page App) without the complexity. It also means you don't have to pivot techniques mid stream. Often when you start writing a Rails app you leave out the JavaScript sprinkles just to make sure your models and assumptions are interacting correctly and using Turbolinks, you can add in the JS goodness without having to rewrite everything as services.

It's very thin

Compared to a lot of other web frameworks, Rails pretty much gets out of the way. If you spend the time modelling your data properly, the Rails layer should be very very thin. They say fat models and skinny controllers, but you can even get away with relatively thin models too if you are going to push a lot of the logic up to JavaScript layer.

Ruby

Ruby as a language is a dream to work with and a lot of the functionality of underscore and lodash were inspired by Ruby's Enumerable methods (things like each and first and last and reject and I could go on...). Also the ActiveRecord framework ties in directly to how models in 3rd normal form relate to each other so that when you do have to work in the Rails layer it's quite simple.

React-rails Gem

The react-rails gem is more or less plug and play. The only problem is the lack of ES6 Module support. https://github.com/reactjs/react-rails

Jasmine Gem

The jasmine gem (https://github.com/jasmine/jasmine-gem) is beginning to show it's age a little bit (still no ES6 support) but it's a very handy way to test your JavaScript components (react or otherwise). If you assume all the JS is available in memory as objects and then only instantiate instances as needed (which is indeed the way Rails works with Ruby objects) when you are golden.

Postscript

Even as a modern Rails developer, you will find yourself writing lots of JavaScript. Vice versa, there is plenty to like about Rails if you are a JavaScript developer. What you lack in monolingualism, you gain in terms of having each language perfectly suited to its task. This is akin to listening to Italian Opera and Norwegian Death Metal.

Tuesday, July 26, 2016

React: Viewless components

I have been using Facebook's React library for a while now (https://facebook.github.io/react/) and I like it for the most part.

The most controversial aspect is the fact that the View is mixed into the Logic using what looks like HTML in the "JavaScript" file using JSX.

While it is possible to write React components in plain JavaScript, you must include a render() method.

However, it is actually possible to do the following...

(in ES6 syntax)

render() {
  return (null);
}

Now, why would you want to do such a thing? I thought the whole point was to use React to tie together your view and logic layers (and shouldn't you be abstracting out your heavy logic to plain JS classes?).

Well, the main reason you might want to do it is because of React's component lifecycle.

In my case, I wanted to trigger some JS to run when the component was loaded, and I happen to be using Turbolinks and React-Rails in a Rails project.

This JS had no view component, but I could not rely on document.ready because of Turbolinks, and I didn't want to put an inline script tag in the page (because who wants to mix JS and HTML together?). I was however happy to use the react_component helper to add in a React component and then trigger it on componentDidMount.

It's also a handy way to add functionality onto some components which for whatever reason need to be rendered by the server (i.e. adding a JS slide show component onto a bunch of rendered divs without having to pass all the images into react_component).

Thursday, April 07, 2016

Testing React components with Jasmine in Rails with the react-rails gem

So in my latest project, we are using Ruby on Rails and the react-rails gem to implement our React components.

While react-rails is fine for the most part (and for simple components), there is a severe limitation in that you cannot use ES6 modules (i.e. using the import and export classes) because of the way asset-pipeline works.

Asset-pipeline precompiles all your JS into one mega JS file and therefore doesn't allow you to call individual JavaScript components in separate files (because they don't pushed to the server). This is a severe problem which I hope gets fixed soon, but in the meantime don't hold your breath.

There are workarounds using Browserify and/or Webpack, but they are messy (Google them for more info).

In any case, react-rails does allow simple React components in Rails apps and it mounts them all in the window/global namespace, so you can use a good 75% of React within a Rails application.

So now, we come to testing...

Facebook uses a testing framework called Jest (https://facebook.github.io/jest/docs/tutorial-react.html). It looks pretty good, but unfortunately does require the use of the import/export syntax...

But hey, React components are just JavaScript right? That means you can test them with any library (like Jasmine https://github.com/jasmine/jasmine-gem). Right?

Well, they are JavaScript, but they are also kind of closed off and it can be hard to actually unit test and mock them. I created a React component called DailyQuiz and for testing I tried to simply instantiate it normally.

var dailyQuiz = new DailyQuiz();

I was then able to see all the attributes on dailyQuiz in the console as if it were a regular JS object.

Until that is, I ran into setState in the code. Unfortunately, it gave me some guff about

Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the  component.

So, basically, this means we can't treat a React component as a plain JS object because there is a lot happening behind the scenes. In a way this makes sense because React does a lot of calculations before outputting to the DOM, so these restrictions are probably needed.

So rather than trying to fake the whole mounting of the component, I decided it would be better to actually mount it. I used jasmine-jquery (https://github.com/travisjeffery/jasmine-jquery-rails) to help me manipulate the DOM with jasmine.

I will show you the code snippet and then talk you through it.
  
  beforeEach(function() {
    deferred = new jQuery.Deferred();
    spyOn($, 'ajax').and.returnValue(deferred);
    setFixtures('[div id="react-fixture"]
test[/div]
');

    dailyQuiz = ReactDOM.render(
      React.createElement(DailyQuiz, {
        url: "/",
        saveUrl: "/save"
      }),
      document.getElementById('react-fixture')
    );

    deferred.resolve(data);
  });

* note for this example I had to replace the < and > with [ and ] for rendering purposes... Blogger is giving me some hassle...

So basically I am using $.ajax with the promise syntax and then spying on it so I can intercept the ajax call in my spec. I then use jasmine-jquery's setFixtures to create a place to mount the component, I mount the component with plain JS (not JSX) and the resolve the AJAX call.

From there, you can actually call dailyQuiz and it will reflect the state of the component.

The downside of this approach is that the data you use to populate your component has to actually work (you can't just stub any old data through it). There may be a way to spy on the subcomponents, but I haven't found a way yet.

The benefits are that you can test the actual state of the actual component. It's just not a headless component (it needs to be mounted). You can also use jasmine-jquery to test the DOM meets expectations.

Thursday, March 17, 2016

Rolify, Devise, Rspec and Capybara Webkit in Rails

So I was using Rolify, Devise, Rspec and Capybara Webkit in Rails and I ran into a problem with the Warden login_as helper when trying to do a feature spec with :js => true (if you didn't understand any of that, you are probably on the wrong blog).

Anyways, while the I was able to log in an admin, Rolify was not able to determine the roles. This is in spite of the fact that I added a role in Factory Girl.

FactoryGirl.define do
  factory :admin do
    email { Faker::Internet.email }
    password "password"
    password_confirmation "password"

    trait :administrator do
      after(:create) {|admin| admin.add_role(:administrator)}
    end
  end
end

This worked fine with js turned off, so why was it not working now?

So the answer had to do with the following line in rails_helper.rb

config.use_transactional_fixtures = true

So I believe what happened in Capybara webkit is that it runs in a different thread and while Warden's login_as worked fine in a regular spec (as the admin object is passed into memory), the role information (which needs a database lookup) was not passed through and hence when I was checking that the admin was an administrator (current_admin.has_role? :administrator) it failed.

So here's the fix

1) Install the database_cleaner gem

2) set transactional fixtures to false
config.use_transactional_fixtures = false

3) Set up database cleaner as per http://devblog.avdi.org/2012/08/31/configuring-database_cleaner-with-rails-rspec-capybara-and-selenium/

4) in your rails_helper.rb
require 'support/database_cleaner'

Your Capybara Webkit Rolify specs should now work with the Warden helpers.

PS Sorry this isn't that well written. This is primarily a reminder for myself (I am still in mid project), but I thought I would post it in case anyone else ran into the same issue.

Thursday, July 02, 2015

Global Warming is a Year 2000 problem

Global Warming is a Year 2000 problem.

What I mean is not that Global Warming has been solved (far from the case) nor that it is a problem that we could only have solved 15 years ago (though that might be closer to the truth).

What I mean is that if we solve Global Warming, then people will think it was a hoax (or something we should not have worried about).

Think back to the glory days of the 1990s. Media was full of stories about how all the world's computer systems were going to fail because programmers tried to save space on their systems by dropping the first 2 digits off the year field. Power stations and ATMs would stop working. Global financial crises would ensue (well, that did happen, but for other reasons...). Basically we were going back into the stone age unless we chucked large amounts of money at software developers to fix our computer systems and by and large that did happen.

After we partied like it was 1999, apparently nothing happened. I am sure a couple of systems failed, but nothing major. We were all still alive and we still had electricity and we could still get money out of ATMs.

Post 2000, everyone now thinks the whole thing was just a bunch of hooey.

But maybe we did avert major disaster by all our hard work. We will never really know.

I hope to an extent the same thing does happen with Global Warming. There are differences of course. We have not got a set date where the planet will be uninhabitable. A date when we can look back and laugh at ourselves for being so silly.

Instead, we will look back at ourselves in shame for being so oblivious (and remember what things used to be like).

There's probably not much we can do on an individual level. We will need to elect people who are aware of the problem and reach consensus (easier said than done).

Let's just hope that in 7984 years we have a Year 10,000 problem to deal with ("Why on earth did they think they could save space by only allocating 4 digits to a year?").