Chronicles of a build - the theme framework 05

Back to work means back to problems and possibilities and the theme I’m working on will need, for its subscription via front-end process, to use AJAX; the simple use of JavaScript to hide and show the right text part would not make it as I need to save the state of the subscriptions, as in the “what the user was actually compiling”, and different subscription phases will lead to different views to be shown. Think of, as an example, showing a subscribing user some options based on some initial choices.

Who’s calling the shots?

Ajax in WordPress is not such a chore and it all comes down to:

  1. Registering the Ajax action via a call to add_action function
  2. Enqueueing the JS script that will actually fire the action

Having adopted a proto-MVC model, VC actually, it only makes sense that it’s the Controller responsibility to take care of both the steps above. Since each Controller object knows nothing of the other controllers the glue among them will be the Theme class.
This means that controllers could:

  • Register an AJAX action
  • Tell Theme to enqueue a JS file
  • Tell Theme to enqueue a CSS file (to style their own view)

To deal with the fact that having n controllers enqueueing n JS files and n CSS file is very bad optimization I will delegate the Theme class the task to actually enqueue one single JS file and one single CSS file.

Views, controllers, scripts, styles: it’s becoming crowded in here

Since I’ve gone with folder organization to structure the framework, and made some assumptions about it, I will stay on that path:

  • Controllers are in the /controllers folder
  • Views are in the /views folder
  • Scripts will be in the /scripts folder
  • Styles will be in the /styles folder

and the folder structure will be updated to the general structure

/root
|   Main.php
|   /controllers
|   |   FirstController.php
|   |   SecondController.php
|   /views
|   |   FirstView.php
|   |   SecondView.php
|   /scripts
|   |   FirstScript.js
|   |   SecondScript.js
|   /styles
|   |   FirstStyle.css
|   |   SecondStyle.css

Where a Controller extending class might as well not have any script or style to enqueue.

Outsourcing the name resolution

Since the framework puts so much weight on names, those of files and namespaces, it’s not feasible any more to rely on the single classes to take the responsibility to resolve them, furthermore the classes implementing the function are two already, Controller and Main, and this is a violation of the DRY principle . It makes sense to build a separate class to do the job and will design it with tests first like

  • the class should return a View name given a Controller name
  • the class should return a Controller name given a View name
  • the class should return a View name given just an element name (like “First”) in the example above
  • the class should return a Controller name given just an element name

and so on; using codeception I set-up a unit test class, NamebotTest, and get down to write some tests to watch them fail.

/**
 * @dataProvider nameProvider
 */
public function testWillReturnProperNames($elementName, $controllerName, $viewName, $scriptName, $styleName)
{
    // get proper names from the element name
    $this->assertEquals($controllerName, NameBot::getControllerName($elementName));
    $this->assertEquals($viewName, NameBot::getViewName($elementName));
    $this->assertEquals($scriptName, NameBot::getScriptName($elementName));
    $this->assertEquals($styleName, NameBot::getStyleName($elementName));
    // get proper names from the controller name
    $this->assertEquals($elementName, NameBot::getElementName($controllerName));
    $this->assertEquals($viewName, NameBot::getViewName($controllerName));
    $this->assertEquals($scriptName, NameBot::getScriptName($controllerName));
    $this->assertEquals($styleName, NameBot::getStyleName($controllerName));
    // get proper names form the view name
    $this->assertEquals($elementName, NameBot::getElementName($viewName));
    $this->assertEquals($controllerName, NameBot::getControllerName($viewName));
    $this->assertEquals($scriptName, NameBot::getScriptName($viewName));
    $this->assertEquals($styleName, NameBot::getStyleName($viewName));
    // get proper names from the script name
    $this->assertEquals($elementName, NameBot::getElementName($scriptName));
    $this->assertEquals($viewName, NameBot::getViewName($scriptName));
    $this->assertEquals($controllerName, NameBot::getControllerName($scriptName));
    $this->assertEquals($styleName, NameBot::getStyleName($scriptName));
    // get proper names from the style name
    $this->assertEquals($elementName, NameBot::getElementName($styleName));
    $this->assertEquals($viewName, NameBot::getViewName($styleName));
    $this->assertEquals($scriptName, NameBot::getScriptName($styleName));
    $this->assertEquals($controllerName, NameBot::getControllerName($styleName));
}

the method providing the test data simply returns an array of arrays in the form

            array(
            'SomeNameWith3Numb3rs1inIt',
            'SomeNameWith3Numb3rs1inItController',
            'SomeNameWith3Numb3rs1inItView',
            'SomeNameWith3Numb3rs1inItScript',
            'SomeNameWith3Numb3rs1inItStyle'
        )

and will use those to test for possible combinations. After some time and some red to green light transition the NameBot class passes all the tests. The class code is here on Github.
I will move to namespacing and filename resolution then.