Doing like a module with Rails asset-pipeline and CoffeeScript

Some days ago, I played around with requirejs. Although I liked to group my client side code into AMD modules, using the (only) appropriate gem ‘requirejs-rails’ doesn’t felt that good. Main reasons for that

  • last commit some months ago
  • no Rails 4 support so far
  • asset precompilation failed with my concrete project

To be fair, that asset:precompile issues with requirejs-rails were caused by some third-party Gems I used. But anyway, they compiled under stock Rails and they failed when using requirejs-rails. So it seems that sprockets integration isn’t that flawless with requirejs-rails.

So I thought about how to get some module-like-feeling, with just the tools, Rails delivers out of the box. Let’s approach.

Assumptions

Let’s assume the following class hierarchy

  Component
  SeatPlan < Component
  SeatPlan.InputDataSanitizer
  (SeatPlan.Ui)

SeatPlan should be a subclass of Component. Also I wanted InputDataSanitzer to be a class on its own, but located “below” SeatPlan, because it only sanitizes SeatPlan input data. Think of some kind of namespacing. Same for Ui. The only difference between these two is, that SeatPlan should store a reference to DataInputSanitizer, where as for Ui it should only store a concrete instance.

AMD/requirejs

With AMD, I would write something like this


# assets/javascript/Component.js.coffee
define ->
class
methodEverybodyShouldHave: ->
#
# assets/javascript/SeatPlan/InputDataSanitizer.js.coffee
define ->
class
sanitize: (data) ->
#
# assets/javascript/SeatPlan/Ui.js.coffee
define ->
class
constructor: (el) ->
#
# assets/javascript/SeatPlan.js.coffee
define ['Component', 'SeatPlan/InputDataSanitizer', 'SeatPlan/Ui'], (Component, InputDataSanitizer, Ui) ->
class extends Component
constructor: (el)
@InputDataSanitizer = InputDataSanitizer
@ui = new Ui(el)

Imitate AMD with sprockets

Without something like AMD, you have to put these classes somewhere in global scope in order to access them from within your client-side code. Let’s put them below window.app

  window
  + app
    + Component
    + SeatPlan
      + InputDataSanitizer
      + Ui

Combining Rails asset-pipeline directives and some CoffeeScript we can imitate something like a module system. Let’s look at the code.
https://gist.github.com/msievers/6120667
Thanks to CoffeeScripts ‘do’, which allows us to explicitly create a closure, it feels almost like doing AMD. Just without the need for any additional Gem/module loader.

Doing like a module with Rails asset-pipeline and CoffeeScript

Ungroup objects grouped programatically with fabric.js

I wanted to group some objects, manipulate that group (center, rotate) and ungroup the objects in order to handle events on each object, rather that on the whole group. I spent some time to get this working, so I think, it’s worth sharing.

tl;dr

// grab the items from the group you want to "ungroup"
items = group._objects;
// translate the group-relative coordinates to canvas relative ones
group._restoreObjectsState();
// remove the original group and add all items back to the canvas
canvas.remove(group);
for(var i = 0; i < items.length; i++) {
  canvas.add(items[i]);
}
// if you have disabled render on addition
canvas.renderAll();

I’ve created a short demo gist, which can be execute withing fabric’s kitchensink demo.


// clear canvas
canvas.clear();
// add red rectangl
canvas.add(new fabric.Rect({
width: 50, height: 50, left: 50, top: 50, fill: 'rgb(255,0,0)'
}));
canvas.add(new fabric.Rect({
width: 50, height: 50, left: 110, top: 50, fill: 'rgb(255,0,0)'
}));
var group = new fabric.Group([
canvas.item(0).clone(),
canvas.item(1).clone()
]);
canvas.clear().renderAll();
canvas.add(group);
// move group, rotate group
group.centerH();
group.centerV();
group.rotate(70);
// ungrouping is here
var items = group._objects;
group._restoreObjectsState();
canvas.remove(group);
for(var i = 0; i < items.length; i++) {
canvas.add(items[i]);
}
canvas.renderAll();

Ungroup objects grouped programatically with fabric.js