One last bit of bookkeeping remains for my in my exploration of require.js with Backbone.js: file naming.
As I have worked with the code in my calendar application, it has become apparent that I need a better way of naming files. The entire list of scripts is currently:
javascripts ├── backbone.js ├── calendar │ ├── collections │ │ └── appointments.js │ ├── helpers │ │ ├── dayAfter.js │ │ ├── firstOfTheMonth.js │ │ ├── from_iso8601.js │ │ ├── template.js │ │ ├── to_iso8601.js │ │ └── weekAfter.js │ ├── models │ │ └── appointment.js │ ├── router.js │ └── views │ ├── Application.js │ ├── AppointmentAdd.js │ ├── AppointmentEdit.js │ ├── Appointment.html │ ├── Appointment.js │ ├── CalendarMonthBody.js │ ├── CalendarMonthDay.js │ ├── CalendarMonthHeader.js │ ├── CalendarMonth.js │ ├── CalendarMonthWeek.js │ ├── CalendarNavigation.js │ └── TitleView.js ├── calendar.js ├── css ├── jquery.min.js ├── jquery-ui.min.js ├── main.js ├── require.js ├── text.js └── underscore.jsAt a high level, that is not too bad. I have all of my collections in the
collections
directory. I have all of my views in the views
sub-directory. And all of my models, views, and collections are in my application directory: calendar
. So what's the problem? I have been struggling with editing these files. In particular, I wind up with multiple Emacs buffers named "Apppointment.js". The only way for me to edit the Appointment.js model is to open each buffer in turn until I find the right one (sometimes I even get confused with the
Appointments.js
collection). More often than not, by the time that I find the right one, I have forgotten why I wanted to open it in the first place. I do not believe that the current structure is a recipe for long term success.
But first, I think I will rename the top-level
public/javascripts
to simply public/scripts
. I do not recall where I read that recently, but the name javascripts
is long without conveying any useful information. So just scripts
should suffice.Mercifully, and thanks in no small part to require.js, making this change is trivial. I do a simple
git mv public/javascripts public/scripts
and then update a single line of HTML:<script data-main="scripts/main" src="scripts/require.js"></script>Now that both the
src
and data-main
attributes point to the new directory, everything else will be loaded relative to them. Admittedly, I will not be renaming my top-level directory often, but cool, nonetheless.Next up, I would like to namespace the filenames. That is, instead of
scripts/calendar/collections/Appointments.js
, I think scripts/Calendar/Collections.Appointments.js
will work better. I am not normally a fan of Hungarian notation, but maybe Hungarian notation in filenames is not such a bad thing.The capitalization in the file path and name (
scripts/Calendar/Collections.Appointments.js
) just makes sense. The Calendar is a top-level application object, so I am always going to capitalize the class name in code:require(['Calendar'], function(Calendar){ var calendar = new Calendar($('#calendar')); });Since I am doing it in code, I might as well do so on the filesystem. It makes it more obvious that this is an object.
Naming the file
Collections.Appointments.js
also follows the capitalize-a-class convention. Instead of grouping my collections in a directory, I will merely group them alphabetically when listing directories. Applying that same idea to the entirety of my Backbone application leaves me with:scripts ├── backbone.js ├── Calendar │ ├── Collections.Appointments.js │ ├── Helpers.dayAfter.js │ ├── Helpers.firstOfTheMonth.js │ ├── Helpers.from_iso8601.js │ ├── Helpers.template.js │ ├── Helpers.to_iso8601.js │ ├── Helpers.weekAfter.js │ ├── Models.Appointment.js │ ├── Router.js │ ├── Views.Application.js │ ├── Views.AppointmentAdd.js │ ├── Views.AppointmentEdit.js │ ├── Views.Appointment.js │ ├── Views.CalendarMonthBody.js │ ├── Views.CalendarMonthDay.js │ ├── Views.CalendarMonthHeader.js │ ├── Views.CalendarMonth.js │ ├── Views.CalendarMonthWeek.js │ ├── Views.CalendarNavigation.js │ └── Views.TitleView.js ├── Calendar.js ├── css ├── jquery.min.js ├── jquery-ui.min.js ├── main.js ├── require.js └── underscore.jsAgain, I do not believe that I have lost anything, but I have definitely made it easier to immediately know which buffer I am editing.
Unfortunately, require.js is no help here. For each of these changes, I have to update the requiring classes accordingly:
define(function(require) { var $ = require('jquery') , _ = require('underscore') , Backbone = require('backbone') , Router = require('Calendar/Router') , Appointments = require('Calendar/Collections.Appointments') , Application = require('Calendar/Views.Application') , to_iso8601 = require('Calendar/Helpers.to_iso8601'); ...Then again, require.js is not getting in my way either. If I made similar changes to a server-side scripted language, I would no doubt have to make the same number of changes.
With that, I think I am in much better shape for long-term maintainability of my Backbone application.
Day #234
Reading this series of posts and poking through your Git repo have been invaluable to me in getting RequireJS integrated into my Backbone app. Thank you for so thoroughly documenting your effort. You've spared me a lot of frustration.
ReplyDelete@schwartzie Glad it is proving helpful :)
ReplyDeleteI dunno what it is with require.js... It's an immensely powerful thing and simple enough in concept and execution. But, for some reason, it really took me a while to wrap my brain around it. Documenting it like this really helped me figure it out -- great to hear it helped you as well!
I'm not sure if this will help out. But I have aliased my module in require.config paths.
ReplyDeleteexample:
require.config({
"paths": {
"jquery": "libs/jquery-1.7.1"
,"underscore": "libs/underscore"
,"myModule": "someModule"
}
});
Modules with defined names (jquery, underscore) can not aliased, but your custom modules can be. Now you only need to reference the module path once.
Additional Resources
RequireJS – Configuration Options
http://requirejs.org/docs/api.html#config
Optionally call AMD define() to register module https://github.com/documentcloud/underscore/pull/338#issuecomment-3253751
AMD modules with named defines. So much pain for what gain? http://dvdotsenko.blogspot.com/2011/12/amd-modules-with-named-defines-so-much.html
In this post James Burk recommends not using name module. https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon