Skip to content

Commit

Permalink
Extended module functionality
Browse files Browse the repository at this point in the history
Modules now have the functionality of Marionette.extend. You may also
pass a custom class to be instantiated as the Module when adding a new
Module to the Application.
  • Loading branch information
jamesplease committed Jan 9, 2014
1 parent 871083d commit b7bb67c
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 9 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ node_modules/
npm-debug.log
_SpecRunner.html
components/
bower_components/

This comment has been minimized.

Copy link
@samccone

samccone Jan 9, 2014

split this off into another commit on this branch

106 changes: 104 additions & 2 deletions spec/javascripts/module.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -257,11 +257,11 @@ describe("application modules", function(){
var mod = MyApp.module("FooModule", function(Foo, MyApp, Backbone, Marionette, $, _, P1Arg, P2Arg){
r1 = P1Arg;
r2 = P2Arg;
}, p1, p2);
}, null, p1, p2);

MyApp.module("FooModule", function(Foo, MyApp, Backbone, Marionette, $, _, P3Arg){
r3 = P3Arg;
}, p3);
}, null, p3);

mod.start();
});
Expand All @@ -276,4 +276,106 @@ describe("application modules", function(){
});
});

describe("when providing a custom Module class to be instantiated", function(){
var MyApp, r1, r2, p1, p2;

beforeEach(function(){
p1 = 'construct1';
p2 = 'init1';

var myModule = Backbone.Marionette.Module.extend({
constructor: function() {
this.val1 = p1;
this.val2 = p1;
r1 = this.val1;
r2 = this.val2;
Backbone.Marionette.Module.prototype.constructor.apply( this, Array.prototype.slice.call(arguments) );
},
initialize: function() {
this.val2 = p2;
r1 = this.val1;
r2 = this.val2;
}
});

MyApp = new Backbone.Marionette.Application();

MyApp.start();

MyApp.module("FooModule", function(Foo, MyApp, Backbone, Marionette, $, _){
r1 = this.val1;
r2 = this.val2;
}, myModule);

});

it("should run the constructor function of the module", function(){
expect(r1).toBe(p1);
});
it("should run the initialize function after the constructor", function(){
expect(r2).toBe(p2);
});

});

describe("when instantiating an extended Module class", function(){
var MyApp, r1, r2, r3, p1, p2, p3, p4;

beforeEach(function(){
p1 = 'construct1';
p2 = 'construct2';
p3 = 'init1';
p4 = 'init2';

var myModule = Backbone.Marionette.Module.extend({
constructor: function() {
this.val2 = p1;
this.val3 = p1;
Backbone.Marionette.Module.prototype.constructor.apply( this, Array.prototype.slice.call(arguments) );
},
initialize: function() {
// This code shouldn't run; the extended initializer will
this.val3 = p4;

}
});
var myModuleTwo = myModule.extend({
constructor: function() {
this.val1 = p2;
this.val2 = p2;
this.val3 = p3;
r1 = this.val1;
r2 = this.val2;
r3 = this.val3;
myModule.prototype.constructor.apply( this, Array.prototype.slice.call(arguments) );
},
initialize: function() {
this.val3 = p3;
}
});

MyApp = new Backbone.Marionette.Application();

MyApp.start();

MyApp.module("FooModule", function(Foo, MyApp, Backbone, Marionette, $, _){
r1 = this.val1;
r2 = this.val2;
r3 = this.val3;
}, myModuleTwo);

});

it("should run the constructor function of the module", function(){
expect(r1).toBe(p2);
});
it("should run the extended constructor before the parent constructor", function(){
expect(r2).toBe(p1);
});
it("should run the initializer last", function(){
expect(r3).toBe(p3);
});

});

});
8 changes: 6 additions & 2 deletions src/marionette.application.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,18 @@ _.extend(Marionette.Application.prototype, Backbone.Events, {
},

// Create a module, attached to the application
module: function(moduleNames, moduleDefinition){
module: function(moduleNames, moduleDefinition, ModuleClass){

// Instantiate a new `ModuleClass`, if specified. Otherwise default to the default implementation
ModuleClass = ModuleClass || Marionette.Module;

// slice the args, and add this application object as the
// first argument of the array
var args = slice(arguments);
args.unshift(this);

// see the Marionette.Module object for more information
return Marionette.Module.create.apply(Marionette.Module, args);
return ModuleClass.create.apply(ModuleClass, args);
},

// Internal method to set up the region manager
Expand Down
18 changes: 13 additions & 5 deletions src/marionette.module.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,15 @@ Marionette.Module = function(moduleName, app){
this.startWithParent = true;

this.triggerMethod = Marionette.triggerMethod;

if (_.isFunction(this.initialize)){
this.initialize(this.options);

This comment has been minimized.

Copy link
@samccone

samccone Jan 9, 2014

this.initialize(this, this.options);

for calling in the correct context

}

};

Marionette.Module.extend = Marionette.extend;

// Extend the Module prototype with events / listenTo, so that the module
// can be used as an event aggregator or pub/sub.
_.extend(Marionette.Module.prototype, Backbone.Events, {
Expand Down Expand Up @@ -117,13 +124,13 @@ _.extend(Marionette.Module.prototype, Backbone.Events, {
_.extend(Marionette.Module, {

// Create a module, hanging off the app parameter as the parent object.
create: function(app, moduleNames, moduleDefinition){
create: function(app, moduleNames, moduleDefinition, ModuleClass){
var module = app;

// get the custom args passed in after the module definition and
// get rid of the module name and definition function
var customArgs = slice(arguments);
customArgs.splice(0, 3);
customArgs.splice(0, 4);

// split the module names and get the length
moduleNames = moduleNames.split(".");
Expand All @@ -136,21 +143,22 @@ _.extend(Marionette.Module, {
// Loop through all the parts of the module definition
_.each(moduleNames, function(moduleName, i){
var parentModule = module;
module = this._getModule(parentModule, moduleName, app);
module = this._getModule(parentModule, moduleName, app, ModuleClass);
this._addModuleDefinition(parentModule, module, moduleDefinitions[i], customArgs);
}, this);

// Return the last module in the definition chain
return module;
},

_getModule: function(parentModule, moduleName, app, def, args){
_getModule: function(parentModule, moduleName, app, ModuleClass){
ModuleClass = ModuleClass || Marionette.Module;
// Get an existing module of this name if we have one
var module = parentModule[moduleName];

if (!module){
// Create a new module if we don't have one
module = new Marionette.Module(moduleName, app);
module = new ModuleClass(moduleName, app);
parentModule[moduleName] = module;
// store the module on the parent
parentModule.submodules[moduleName] = module;
Expand Down

0 comments on commit b7bb67c

Please sign in to comment.