During my recent trip to our Vancouver office, our frontend developers think if we have more Rails developers building backend APIs, the frontend development works could go swifter and faster.
I conducted a team learning session on this topic. I talked about the font end development boundary and how far we can push the frontend work without any help from the server end. The final take away is that with some preparation and simple technics, it is possible to build an entire React frontend app without any server end APIs. Once the server end APIs are in place, we would only need to touch a couple of places at max, to “turn on” the frontend React app to be production ready.
Let’s talk about the boundary between frontend and backend first. React with Flux gives us an easy to understand uni-directional data flow. We know our React app should be fully functional as long as we have “data” in our store. All this means is that our store should be the boundary between our React app and the server APIs. As long as our store is the only place that cares about retrieving and persisting data, once the store emits changes, other parts of our React app should just work! React with flux makes defining the boundary super easy. I still remember in the old days with hand-rolled JS frontend apps, this defining boundary business takes a lot more consideration and discipline. Not so much these days!!!
Now knowing the boundary. What are the technics we could use to make our React app behave as if it is connected to the backend APIs? The answer is easy: FAKE IT.
Take a look at data retrieval for example. In a React store, we typically retrieve data like this
The only interesting part is the Ajax.get with the callback lines. We have a couple of things need to fake here. First, we need to be able to get some fake data, which we can pass to this.setData(). Second, we need to make sure we fake the async nature of the Ajax.get call. We are looking at something like below
The Ajax.get call is replaced by setTimeout, which is async. In order to pass fake data to this.setData, we created a FixtureDataFactory class with a getDummyData method. The getDummyData method is super simple. It just returns some fixture JSON data.
The above technics are very simple, but very effective and has some awesome side effects.
The use of setTimeout to simulate async behaviour means once we switch to the real Ajax.get, all React components’ lifecycle calls will not be upset. It’ll be an async to async switch. Nothing should care, nothing should change.
The introduction of FixtureDataFactory means 2 things. First, FixtureDataFactory will be used to provide fixture data for our tests. Second, the JSON data returned by getDummyData is an easy to read contract between the frontend app and the backend APIs. Backend API developers should be able to take the fixture JSON data and write the API accordingly. Easy job!
This post will be a re-cap of my first hand experience with adopting ReactJS in an existing Rails application, which is in production and used by real users.
We have all seen enough ‘template’, ‘boilerplate’ or ‘starter’ apps showing us how to move our Rails front end development to React. They all work great, if you start from scatch or if your Rails app is still small (meaning your can afford to rewrite a lot of existing front end code).
Unfortunately, it’s not my case. My Rails application’s first commit was done on 9th of March 2012. The app has been developed as a typical Rails app until Aug 2015 (when I introduced React to the dev stack). That’s about 3.5 years of your typical “Rails way” development. This means a lot of HAML, SCSS, Coffeescript files. I simply cannot afford to rewrite everything from scatch.
I obviously started with react-rails and react-router-rails. The issue with my-asset-rails gems is always about the upgradability. At the time writing this post, HEAD of react-router-rails is still pinned to react-router 0.13.3. react-router on npm is already at 2.0.1. Since I am not using server side render, using my-asset-rails gems to just use their UJS hooks doesn’t seem to be a smart idea.
As someone who’s big on being pragmatic, I removed react-rails and react-router-rails and simply registered the following JS object in the head of the rails layout file
In my application, I decided to render at most one main react component for any given Rails route. There’s no magic in the above code. It simply plays within the rules I set on my own app. It lays out an object with a few values to be filled by the Rails view file (discussed later) and a couple of mount functions (One for plain React components; the other for react-router wrapped components). The reason why the above JS code needs to be included in the layout header is that my app’s main application.js is loaded async at the end of the HTML body using the following JS code.
The application.js manifest file is the beast. It requires for all those good old coffeescripts, as well as 2 special pieces.
One being the webpack transpiled js bundle file, dist_react_components.js, for all of my React components
The other being a special mount_react_component.js file.
The mount_react_component.js file is super simple. When it comes to life (loaded on to DOM asynchronously), it invokes one of the LazyReactComponent’s mount functions depends on the LazyReactComponent.type value set by the Rails view file (I’ll go into later).
The dist_react_components.js file exposes all mountable React backed UI components in a JS object literal. It’s something like below. Note that there’s no export, since this file would be used outside the module system. It’s included by Rails’ application.js.
After I write the ActivityReport React component, I now only have 1 thing left to do. If I have a Rails route like below
We all know how to do the normal Rails controller, action, view stuff. The only thing interesting here is the view file, activity_reports/index.html.erb
JS snippet is actually being written in the erb view file. It simply sets up the required values on the LazyReactComponent, make LazyReactComponent’s mount methods ready to be invoked by mount_react_component.js discussed above.
In future posts, I’ll discuss more about my learnings around react-router and flux.
I need to implement an image cropping feature on user uploaded avatars. The ever gold RailsCasts episode shows us the way.
All sweet stuff, until MiniMagick tells me it’s mogrify shell command cannot find the image resource. Backtrace points to image.crop(x, y, w, h) line. After some messing around and examining the MiniMagick source. Here’s the fix.
Problem solved by changing the image.crop method argument from x, y, w, h to an interpolated string that complies to Imagemagick’s geometry format spec.
EDIT: while writing this post, I found the exact solution has been mentioned on RailsCasts episode’s comments section. Damn, I want my time back :`(
While I was implementing the home dashboard widgets with Gridster, I needed to ajax load the contents of each widget base on the widget key. The Gridster widget keys are string based, however some keys are dynamically generated.
Let’s look at an example.
The simple case first. A widget key can be my_calendar. I need ruby to translate it to a symbol such as :my_calendar. I’ll then be able to utilise the translated value :my_calendar to fetch the widget content. To achieve this, it’s dead simple … A contrive way to do it can be somethign like this
Now let’s go to the deep end. Many widgets have dynamically generated keys like my_type_123 and my_type_456. I need to extract out :my_type, as well as the integers (sort of like type IDs) such as 123 and 456.
So … let’s see if we can implement a Hash-alike data structure that allows us to fetch values by passing in strings that can be recognised by a Regex.
With the above in place, I can use the DYNAMIC_WIDGET_KEY_SPLIT_MAP constant as if it’s just a regular Hash object. The translated return values can be used to call up named URLs defined in my routes.rb and have the ability to pass in those additional params.
I don’t know if anybody would have a similar use case to use this implementation. However it solved my problem quite nicely.
We recently had a requirement of implementing a new Dashboard inside Envisio. The new dashboard needs to be customisable, meaning the end users can add, remove, resize and drag-n-drop widgets on the dashboard. The configuration of the user dashboard needs to be memorisable.
I went through a few options and settled on Gridster. It fits our requirements very well after some tweaking. The only complaint is that Gridster isn’t reponsive, which is less of a concern for our app.
To set it up, first load it using Bowerfile, which is enabled by the bower-rails gem.
Load the JS and CSS into the corresponding manifest files.
How to setup the dashboard as well as the widget HTML and how to initialise Gridster is very subjective. To highlight how I got serialisation, resizing working, I’ll disect the JS and explain a little.
In order to remember user configuration, we needed to pay attention to the serialize_params, resize.stop and draggable.stop options.
An issue I ran into was when resizing, the scrollable content loaded inside each grid does not resize. When I want to reduce the widget size, The content height doesn’t seem to be adjusted and it will stick out. To overcome this, I pulled some dodgy hacks by tapping into the resize.start callback to put a tint over the widget that’s being resize to hide the ugliness.
Your mileage may vary, but the aboves are a few obstacles I encountered (amongst many others …). There’s also this nice comparison article talking about various alternative plugins to Gridster.
I had a need to prepare a Ubuntu VM image, which can house the Envisio app for development. Setting up a Rails dev environment on Ubuntu is a no brainer. Being able to find an alternative solution to Pow.cx on Ubuntu is a different story.
After setting up a working Rails dev environment, which allows me to run rails s. I had to do a few things.
First thing is to be hack the host file so that I can use *.envisio.dev instead of localhost. I quickly installed the ghost gem and registered a few host entries
After this I can point my browser to client_one.envisio.dev:3000. Next up, we need to get rid of the port 3000 part.
I’m messing around with the new Ionic Framework. Ionic builds on top of Angular and builds using Cordova. Integrating a QR code scanner cannot be any simpler with the Cordova BarcodeScanner plugin in place.
But things are never as simple as they look … I run into funny issues on Android. Basically the problem is if a user cancels the barcode scanning process on Android using the back button, the application will simple quit itself, if I don’t do an alert() or something similar. It seems like the Android back button click was registered twice, first time in the QR scanner, and 2nd time in the main app. I had to write the following hacks to make sure it doesn’t quit my app.
First, qr_scan_service.js. Very straight forward service class wrapping the cordova plugin.
Next, putting some voodoos in the calling controller code. See comments.
Git’s cheap branching and merging ability coupled with Git Flow makes software feature development process a lot easier. However product development is only 1 piece of the puzzle when it comes down to feature release. It’s almost certain that when we release a feature, there will be involvements from support/sales/marketing team. Feature code ready simply doesn’t mean release ready. Some may say, why not just keep the non-released feature code in the Git feature branch… I personally don’t like keeping feature branches long-lived and prefer quick merge-backs. This leads to the question, how can we decouple product development code release and product feature release.
Since knowing that the feature toggle requirement is all on UI side for now, I quickly rolled up my sleeves and hacked together a solution.
First create a features model and back it up with a DB table (yes, it’s the Rails way …). The only significant method is the class method feature_map, which returns me a hash of active features.
Next up, create a plain Ruby class with a class method get_map, which does a cache fetch on a given hash key.
Then, I created a couple of helper methods as such
Last, in my (HAML) view files, I can just wrap the feature related view code inside the envisio_show or envisio_alternative helper methods like this
That’s it. The view files are still declaritive without if/else checks.
Of course, there’s an admin only control panel, where administrators can signin to and toggle on/off features without involvement from development team.
I couldn’t believe it’s been 6 months since my last programming related blog post. I’ve been busy taking photos lately intead (see my photo blog here).
Back to some Ruby. Today I finally made the decision to spend some time in the current sprint to migrate our background processing gem from Resque to Sidekiq. Not that Resque is doing anything particularly wrong, it’s just the Resque setup code was done by me about 3 years ago (not long after I just started working full time on Ruby), and the setup has been copied from project to project, but never improved. After reading so many good things about Sidekiq, I thought I’ll give Sidekiq a try.
First thing first, Gemfile.
and bundle install it.
Next a global search on Resque.enqueue and replace with Sidekiq::Client.enqueue.
Then go to the old Resque worker classes to find @queue = :abc and replace with include Sidekiq::Worker. Of course, as the awesome Sidekiq doc points out, turn def self.perform into def perform
For mailer classes, in my old Resque setup, I use the resque_mailer gem and have the following async_mailer.rb, which gives me a good super class for any async mailer classes that can extend from.
I simply took out the include Resque::Mailer line from AsyncMailer. Another global search on .deliver to find all application codes that send out emails. For all the matches, simply change the email deliver call to use the Sidekid’s Delayed extension syntax for ActionMailer.
More hacks required to make the Devise mailer happy.
In devise.rb initializer, add config.mailer = 'Devise::Async::Proxy'. Yes, I’m using a very old version of Devise …
Create a new devise_async.rb under initializers, and add single line Devise::Async.backend = :sidekiq
Last, in routes.rb, replace the old Resque web admin interface with the Sidekiq equivalent, mount Sidekiq::Web, at: '/sidekiq'
That’s pretty much it. Of course, I had to do more work to make Heroku, Unicorn and Sidekiq all happy and live in harmony. That’ll be another topic for another post.
A bit update first. I’ve moved to Vancouver for 1.5 months now. Despite all the good things (e.g. work, people), my banking experience has been super bad! I used to think the banks in Australia were bad, I now clearly see what’s worse! I opened my bank account with BMO on the first day I landed here, and till now, there’re still unresolved issues. It made me so upset, I had to setup a random script on Heroku to tweet how bad @BMO is everyday … So if you follow me on twitter and see my rants, it’s that!
Anyway, some interesting stuff now! Call me late adopter, after all these buzz about AngularJS, I finally gave it a shot. This great blog post certainly helped me to get started. But I had to figure some stuff out myself in order to get the AngularJS app to talk to the Rails API app. It’s Friday afternoon, I’m not really in the mood of writing too much here … My play app source code can be found here on github, and I’ll keep playing and pushing the changes there.