Welcome Guest, Not a member yet? Register   Sign In
Matchbox callers and _remap
#1

[eluser]tonanbarbarian[/eluser]
I am developing a CRUD library to use in building a number of apps
To facilitate development I decided to use Matchbox so that I could have each of the apps as a seperate module on the development site and they could all share the CRUD libraries in the base of the site, as well as having my apps "module aware" so that I know they can run both in and outside of matchbox

so quick run down of the layout

Code:
/application
  /libraries
    crud.php  - main crud controller library
    crud_index.php - crud index library
    crud_edit.php - crud edit and add library
    crud_delete.php - crud delete library
    MY_Controller.php - extends the controller to call the crud.php if needed
    Matchbox.php
  /modules
    /app1
      /controllers
        app1.php
    /app2
      /controllers
        app2.php

So the Matchbox and crud libraries are in the root libraries location.
Now the MY_Controller uses _remap and some checking code to determine if the controllers (e.g. app1.php) needs to perform a crud operation and if so it loads the appropriate crud library

So for example if we are adding a new record, then the MY_Controller is loaded and whichever controller is being called, e.g. app2.php
Because the MY_Controller has a _remap in it the remap is run and that looks at a property of the app2.php which is an array of the methods to crud. If the current method is in the list of crud method then it loads the crud library crud.php and calls the method. crud.php then has its own internal methods for each of the crud actions and it then loads the sub library e.g. crud_edit.php and that does most of the work. crud_edit.php will occasionally look in the model or controller for methods to help it do its work.

Now the issue with this is that Matchbox uses a debug_backtrace to determine what has called the current function and tries to work its way back through that information to determine which module it is in.

But if you look at what is happening when the crud is working it is never called directly from app2.php but rather from MY_Controller, so it never determines what the module is and therefore it cannot load any module specific model, libraries, views etc.

Now while I have a solution of sorts, by adding another _remap to the app2.php that calls the parent::_remap, it is not my preferred solution. I also have to add a LOT of callers, which I do in code rather than in the config, but I basically have to tell it to ignore ALL of my libraries or else it always finds MY_Controller or one of the CRUD libraries.

So I thought about what Matchbox is doing and determined that in most cases it is not necessary to look back in the backtrace to determine what module to use.

If you think about it in 99% of cases it will only be looking at the current module, or the root for the files. On top of that it would only be looking in other modules for files if another module has been explicitly specified.
i.e.
Code:
$this->load->module_view('module', 'file_name');
After that call to load 'module' the view 'file_name' may need to find resources in the same module, but it has to be specified somewhere in code first to load something from this module.

The only issue I could come up with with this approach is that if I load something from an external module and it contains a resource with the same name as a resource in my module then the system could be confused about which one to load (the calling module would take precedence)

continued in next post....
#2

[eluser]tonanbarbarian[/eluser]
So not wanting to break what in Matchbox but still wanting my code to run without having to add a _remap or lots of callers I came up with the following modification to the Matchbox library

It is a change to the find method
but first I add 3 more properties
Code:
/**
     * @sm2mod
     * Should the system track modules rather than callers
     *
     * @var        boolean
     */
    var $_track_modules = false;

    /**
     * @sm2mod
     * Modules that have been loaded and should be searched for a resource
     * Only used if we are tracking modules
     *
     * @var     array
     */
    var $loaded_modules = array();

    /**
     * @sm2mod
     * Directories to look at based on the modules and directory properties
     * Only used if we are tracking modules
     *
     * @var     array
     */
    var $test_directories = array();

then we modify find
Code:
/**
     * Locates resources
     *
     * @param  string
     * @param  string
     * @param  string
     * @param  string
     * $param  integer
     * @return mixed
     * @access public
     */
    function find($resource, $module = '', $search = 1)
    {
        log_message('debug', '---Matchbox---');
        log_message('debug', 'Finding: ' . $resource.'::'.$module);

        // @sm2mod
        if ($this->_track_modules) {
            if (!in_array($this->_module, $this->loaded_modules)) {
                $this->loaded_modules[] = $this->_module;
                foreach ($this->directory_array() as $directory) {
                    $this->test_directories[] = APPPATH . $directory . '/' . $this->_module . '/';
                }
            }
            if ($module !== '') {
                $this->loaded_modules[] = $module;
                foreach ($this->directory_array() as $directory) {
                    $this->test_directories[] = APPPATH . $directory . '/' . $module . '/';
                }
            }

            $directories =  $this->test_directories;
            if ($search == 3) {
                $directories[] = '';
            } else {
                $directories[] = APPPATH;

                if ($search == 2) {
                    $directories[] = BASEPATH;
                }
            }
        } else {
            // original matchbox code
            $directories = array();
            if ($module !== '') {
                foreach ($this->directory_array() as $directory) {
                    $directories[] = APPPATH . $directory . '/' . $module . '/';
                }
            } else {
                $caller = $this->detect_caller();
                log_message('debug', 'No module so using: ' . $caller);

                foreach ($this->directory_array() as $directory) {
                    $directories[] = APPPATH . $directory . '/' . $caller . '/';
                }

                if ($search == 3) {
                    $directories[] = '';
                } else {
                    $directories[] = APPPATH;

                    if ($search == 2) {
                        $directories[] = BASEPATH;
                    }
                }
            }
        }

        foreach ($directories as $directory) {
            $filepath = $directory . $resource;

            log_message('debug', 'Looking in: ' . $filepath);

            if (file_exists($filepath)) {
                log_message('debug', 'Found');
                log_message('debug', '--------------');

                return $filepath;
            }
        }

        log_message('debug', 'Not found');
        log_message('debug', '--------------');

        return false;
    }
The end result of this being that Matchbox operates as normal, until you change $this->_track_modules to true.
Then it keeps track of all of the modules actually requested in the find calls, and only looks there for the resource.
It will look in the module that was called in the uri first and then any of the extra modules that were specifically called.

To me this is a more logical way of doing things, and as an added bonus it is twice as fast as the backtrace because there is not as much information to process on each find.

I am not sure if this modification will ever make it into the main Matchbox code, but I feel it has use in improving performance, and working with _remap and other libraries based processing easier.

And you can always turn it on specifically in each controller that needs it.
#3

[eluser]zdknudsen[/eluser]
Very interesting modification Smile

At one point Matchbox did in fact only look in the current module (no backtracing), however the reason I implemented it was that I wanted to render people able to import any standard CI application into a module and have it work. However, if you loaded a library from another module (e.g. authentication module) and that library loaded other resources you'd be in trouble.

This way of doing things will never completely replace how Matchbox is working now, and I guess the easiest is to let people download this if they want the functionality, but perhaps I will consider the possibility of choosing which way to do it in the configuration file. Some performance tests could be very interesting. Smile

Either way, I think it's increadibly cool someone moded my library Big Grin Thumbs up!
#4

[eluser]lancemonotone[/eluser]
Matchbox is an incredibly useful library. Thanks Zacharias. I implemented Tonan's modification and it does seem to be faster by about 0.1 sec in my small app. Is there any reason why I shouldn't track modules by default?

The reason I found this post is that using MB I was trying to load a view that was located in another module. Of course, Tonan's code didn't help with that; what I needed was:

Code:
$this->load->module_view();

That was mentioned in Tonan's post and briefly in your MB documentation (I think it should be more prominent). **EDIT: I just realized that load->module_view() is in the replacement Loader library that comes with Matchbox. Zacharias, you should definitely be louder about that method in your docs (which are otherwise much clearer than most). **

Also, Tonan, is your CRUD library going to be available? I'm looking for something lightweight and modular and maybe your CRUD will fit the bill.

Thanks!




Theme © iAndrew 2016 - Forum software by © MyBB