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

Include files from git submodules when building a ruby gem

Today I ran into the following situation. I wanted to build a gem with bundler which has some vendor assets included as git submodules. My directory structure was something like that

.git/
app/*
lib/*
vendor/assets/javascripts/es5-shim (submodule)
vendor/assets/javascripts/pathjs (submodule)

When doing

rake build

the files from the submodules where not included in the gem, because the gemspec specifies the files as follows.

gem.files = `git ls-files`.split($\)

Unfortunately, git ls-files does not list files from submodules and that’s why, these files are not included in the gem.

I solved this by utilizing git submodules’s foreach statement in combination with some ruby string manipulation.

The resulting gemspec looks like this.


# -*- encoding: utf-8 -*-
require File.expand_path('../lib/example/version', __FILE__)
Gem::Specification.new do |gem|
gem.authors = ["John Doe"]
gem.email = ["john_doe@example.org"]
gem.description = %q{Write a gem description}
gem.summary = %q{Write a gem summary}
gem.homepage = ""
gem.files = `git ls-files`.split($\)
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
gem.name = "example"
gem.require_paths = ["lib"]
gem.version = Example::VERSION
# get an array of submodule dirs by executing 'pwd' inside each submodule
`git submodule –quiet foreach pwd`.split($\).each do |submodule_path|
# for each submodule, change working directory to that submodule
Dir.chdir(submodule_path) do
# issue git ls-files in submodule's directory
submodule_files = `git ls-files`.split($\)
# prepend the submodule path to create absolute file paths
submodule_files_fullpaths = submodule_files.map do |filename|
"#{submodule_path}/#{filename}"
end
# remove leading path parts to get paths relative to the gem's root dir
# (this assumes, that the gemspec resides in the gem's root dir)
submodule_files_paths = submodule_files_fullpaths.map do |filename|
filename.gsub "#{File.dirname(__FILE__)}/", ""
end
# add relative paths to gem.files
gem.files += submodule_files_paths
end
end
end

view raw

example.gemspec

hosted with ❤ by GitHub

Matt Connolly suggested a shorter version of the gemspec. Have a look at his comment.

Include files from git submodules when building a ruby gem