FALVEY MEMORIAL LIBRARY

You are exploring: VU > Library > Blogs > Falvey Memorial Library Blog

Dig Deeper: VuFind Summit 2016

Did you know that an innovative search engine used by libraries in numerous countries for browsing catalogs was developed right here at Villanova University? It’s called VuFind, and its open source coding allows for continued innovation by institutions and individuals throughout the world.

Some of the most important contributors are coming to Falvey Memorial Library this week, on Oct. 10 and 11, for VuFind Summit 2016. Registration has closed, but you can still attend the conference virtually on the Remote Participation section of the VuFind Summit webpage. Speaking of remote participation, this year’s VuFind Summit will feature a video conference with another VuFind Summit occurring concurrently in Germany.

The VuFind Summit 2015 group.

The VuFind Summit 2015 group.

This year’s conference includes speakers such as Andrew Nagy, Leila Gonzales and Bob Haschart, among others. Nagy, one of the developers involved in starting the VuFind project here at Villanova, will be giving a talk on his new FOLIO project. FOLIO is another open source project that will integrate VuFind as it attempts to help libraries work together in new ways.

Gonzales has devised a method for using VuFind for geographical data. On her map interface, a user can draw a shape and pull full records from the designated space. Her talk features a brainstorming session for thinking up new features and applications for her software. Haschart will discuss his new SolrMarc software, which includes “extended syntax, faster indexing with multi-threading, easier customization of Java indexing code” (from Summit website above).

VuFind Summit could not be promoted, nor indeed occur, without speaking of Demian Katz. He is the VuFind Project Manager who has worked here at the Falvey Memorial Library since 2009. Demian brings the conference together each year and has even published scholarly articles on the topic of VuFind. Anyone who has spoken to him, or heard him lecture, can easily detect his passion for innovative technologies and how the user engages with them. His talk will focus on the innovations made since last year’s VuFind Summit, and he will participate heavily in mapping out the next year’s innovations.

Demian Katz lectures at VuFind Summit 2015

Demian Katz lectures at VuFind Summit 2015.

I know, on a personal level, that if you aren’t a coder, then this event might not seem pertinent to you. I encourage you, however, to check out the live stream or the YouTube videos that will be posted subsequently. Not many universities can list “developed an internationally renowned search engine” on their curriculum vitae. VuFind is part of what makes Villanova University a top 50 college in the country; VuFind is part of your daily research experience here at Villanova. It’s certainly worthwhile to give attention to those specialists who make VuFind a reality.


Website photo 2

Article by William Repetto, a graduate assistant on the Communications and Marketing Team at the Falvey Memorial Library. He is currently pursuing an MA in English at Villanova University.

 

 


Like

Sravanthi Adusumilli – New Library Technology Development Graduate Assistant

Tech grad asst 1 resize

Sravanthi (Sravs) Adusumilli , a graduate of Acharya Nagarjuna University, Guntar, India, joined the Library Technology Development team in August. She reports to Demian Kratz, team leader. She is currently working on redesigning “Finding Augustine.” “Finding Augustine” is “[a] rich and readily accessible biographical collection concerning Augustine of Hippo and his legacy;” it is sponsored by the Augustinian Institute at Villanova University.

Adusumilli has a bachelor’s degree in computer science engineering and is now enrolled in the Master of Science in Computer Engineering program with an anticipated graduation in May 2018. She plans to work as a data scientist.

Her hometown is Machilipatnam, India, a city on the southeast coast. Adusumilli’s hobbies are cooking and gardening.


Like

The Community Bibliography – A Falvey Memorial Library Project

Community Bibliography

The Community Bibliography is “[a] celebration of Villanova University community authors and scholars past, present and future.” It is “an open repository of the entire published output of the Villanova University community.” The goal is to digitally preserve “our proud scholarly heritage, from our community’s historical publications of the 19th century to the cutting edge research of today.” Community is defined as any individual (faculty, staff, student, alumnus, Augustinian, administrator) affiliated with Villanova University.

This Bibliography may be of interest to Villanova alumni returning for Reunion 2016 (Thursday, June 9 – Sunday, June 12). The Community Bibliography hosts citations for alumni authors from the Class of 1920 through the Class of 2015. Here is an opportunity to check out what your classmates have accomplished.

The Community Bibliography evolved from discussions among Library Director (at the time) Joe Lucia; Darren Poley, Theology/Outreach librarian; Michael Foight, Special Collections and Digital Library coordinator;  and Andrew Nagy, a former Falvey technical developer. Poley explains, “The idea was to use the citation management software Andrew developed for the Finding Augustine project to manage a comprehensive list of published artifacts by anyone affiliated with Villanova since the inception of the University. Michael and I agreed that his team would manage the image management associated with creating an institutional repository, while my Outreach team would oversee the development and maintain a bibliography that would be fully searchable on the Web and that [we] would not need to worry about copyright issues since it would only be supplying the citations.”

A data entry pilot project began in January 2007 and that was a pivotal year for the Community Bibliography. In May the project officially came under the supervision of the Outreach team and, three months later, the project gained momentum with increased multi-faceted data gathering. Later that year Falvey personnel began talking to people outside of Falvey about inter-operability. In November a content review produced procedural and system refinements.

The Community Bibliography was unveiled to the University’s academic leaders at a March 1, 2008, gala dinner in Falvey. There, Poley said, “Our Community Bibliography specifically allows for all works, popular and scholarly, to be documented, but why bother? This information is already gathered both formally and informally. Professors keep track of works for Curriculum Vitae, offices and departments monitor faculty and staff publications. But how does one know altogether what Villanova as a community has published? The problem is that there is no one place where information on all of these works is available … Our Community Bibliography becomes the device for allowing ourselves and others to see in a measurable way what our community has produced.”

A February 2008 newsletter article, “The ‘institutional repository’ rethought:  Community Bibliography debuts,” not only explains the significance of the project, but also tells how it relates to the Faculty Fulltext project created by the Digital Library.

Stephen Spatz, assistant Outreach and Research Support librarian, does most of the day-by-day work on the Bibliography. He gathers and uploads citations of works by Villanova University community members; he searches mostly Falvey’s database collection, but also occasionally locates materials in faculty and departmental webpages and “even in a few cases, typewritten bibliographies, both published and unpublished.” He says, “There are currently about 12,000 citations in the database, most of which cover the most recent scholarly output of the VU community, but about 5% predate 1980 and, even in some cases, stretch back into the 19th century.” Spatz also maintains the Digital Library’s Faculty Fulltext database “which aims to parallel the citation-only content of the Community Bibliography with full-text versions of the most recent scholarly output of VU faculty.” Spatz also supervises students who do some of the data entry.

The two projects, Community Bibliography and Faculty Fulltext, developed from an academic movement to counter the commercialization of intellectual property, making information freely available as a means of sharing and promoting scholarship. Falvey’s early creation of these two projects puts it on the cutting edge of new ways of using technology to share scholarly information.

Community Bibliography submission form

For more information contact communitybibliography@villanova.edu

 

Darren Poley, Stephen Spatz and Michael Foight generously contributed information for this article.


Like

CFP: Libraries and Archives in the Anthropocene: A Colloquium at NYU

Call for Proposals

Libraries and Archives in the Anthropocene: A Colloquium
May 13-14, 2017
New York University

As stewards of a culture’s collective knowledge, libraries and archives are facing the realities of cataclysmic environmental change with a dawning awareness of its unique implications for their missions and activities. Some professionals in these fields are focusing new energies on the need for environmentally sustainable practices in their institutions. Some are prioritizing the role of libraries and archives in supporting climate change communication and influencing government policy and public awareness. Others foresee an inevitable unraveling of systems and ponder the role of libraries and archives in a world much different from the one we take for granted. Climate disruption, peak oil, toxic waste, deforestation, soil salinity and agricultural crisis, depletion of groundwater and other natural resources, loss of biodiversity, mass migration, sea level rise, and extreme weather events are all problems that indirectly threaten to overwhelm civilization’s knowledge infrastructures, and present information institutions with unprecedented challenges.

This colloquium will serve as a space to explore these challenges and establish directions for future efforts and investigations. We invite proposals from academics, librarians, archivists, activists, and others.

Some suggested topics and questions:

  • How can information institutions operate more sustainably?
  • How can information institutions better serve the needs of policy discussions and public awareness in the area of climate change and other threats to the environment?
  • How can information institutions support skillsets and technologies that are relevant following systemic unraveling?
  • What will information work look like without the infrastructures we take for granted?
  • How does information literacy instruction intersect with ecoliteracy?
  • How can information professionals support radical environmental activism?
  • What are the implications of climate change for disaster preparedness?
  • What role do information workers have in addressing issues of environmental justice?
  • What are the implications of climate change for preservation practices?
  • Should we question the wisdom of preserving access to the technological cultural legacy that has led to the crisis?
  • Is there a new responsibility to document, as a mode of bearing witness, the historical event of society’s confrontation with the systemic threat of climate change, peak oil, and other environmental problems?
  • Given the ideological foundations of libraries and archives in Enlightenment thought, and given that Enlightenment civilization may be leading to its own environmental endpoint, are these ideological foundations called into question? And with what consequences?

Formats:
Lightning talk (5 minutes)
Paper (20 minutes)

Proposals are due August 1, 2016.
Notifications of acceptance will be sent by September 16, 2016.
Submit your proposal here: http://goo.gl/forms/rz7uN1mBNM

Planning committee:

 


Like

Automatically updating locally customized files with Git and diff3

The Problem

VuFind follows a fairly common software design pattern: it discourages users from making changes to core files, and instead encourages them to copy files out of the core and modify them in a local location. This has several advantages, including putting all of your changes in one place (very useful when a newcomer needs to learn how you have customized a project) and easing upgrades (you can update the core files without worrying about which ones you have changed).

There is one significant disadvantage, however: when the core files change, your copies get out of sync. Keeping your local copies synched up with the core files requires a lot of time-consuming, error-prone manual effort.

Or does it?

The Solution

One argument against modifying files in a local directory is that, if you use a version control tool like Git, the advantages of the “local customization directory” approach are diminished, since Git provides a different mechanism for reviewing all local changes to a code base and for handling software updates. If you modify files in place, then “git merge” will help you deal with updates to the core code.

Of course, the Git solution has its own drawbacks — and VuFind would lose some key functionality (the ability for a single instance to manage multiple configurations at different URLs) if we threw away our separation of local settings from core code.

Fortunately, you can have the best of both worlds. It’s just a matter of wrangling Git and a 3-way merge tool properly.

Three Way Merges

To understand the solution to the problem, you need to understand what a three-way merge is. Essentially, this is an algorithm that takes three files: an “old” file, and two “new” files that each have applied different changes to the “old” file. The algorithm attempts to reconcile the changes in both of the “new” files so that they can be combined into a single output. In cases where each “new” file has made a different change in the same place, the algorithm inserts “conflict markers” so that a human can manually reconcile the situation.

Whenever you merge a branch in Git, it is doing a three-way merge. The “old” file is the nearest common ancestor version between your branch and the branch being merged in. The “new” files are the versions of the same file at the tips of the two branches.

If we could just do a custom three-way merge, where the “old” file was the common ancestor between our local version of the file and the core version of the file, with the local/core versions as the “new” files, then we could automate much of the work of updating our local files.

Fortunately, we can.

Lining Up the Pieces

Solving this problem assumes a particular environment (which happens to be the environment we use at Villanova to manage our VuFind instances): a Git repository forked from the main VuFind public repository, with a custom theme and a local settings directory added.

Assume that we have this repository in a state where all of our local files are perfectly synched up with the core files, but that the upstream public repository has changed. Here’s what we need to do:

1.) Merge the upstream master code so that the core files are updated.

2.) For each of our locally customized files, perform a three-way merge. The old file is the core file prior to the merge; the new files are the core file after the merge and the local file.

3.) Manually resolve any conflicts caused by the merging, and commit the local changes.

Obviously step 2 is the hard part… but it’s not actually that hard. If you do the local updates immediately after the merge commit, you can easily retrieve pre-merge versions of files using the “git show HEAD~1:/path/to/file” command. That means you have ready access to all three pieces you need for three-way merging, and the rest is just a matter of automation.

The Script

The following Bash script is the one we use for updating our local instance of VuFind. The key piece is the merge_directory function definition, which accepts a local directory and the core equivalent as parameters. We use this to sync up various configuration files, Javascript code and templates. Note that for configurations, we merge local directories with core directories; for themes, we merge custom themes with their parents.

The actual logic is surprisingly simple. We use recursion to navigate through the local directory and look at all of the local files. For each file, we use string manipulation to figure out what the core version should be called. If the core version exists, we use the previously-mentioned Git magic to pull the old version into the /tmp directory. Then we use the diff3 three-way merge tool to do the heavy lifting, overwriting the local file with the new merged version. We echo out a few helpful messages along the way so users are aware of conflicts and skipped files.

#!/bin/bash

function merge_directory
{
    echo merge_directory $1 $2
    local localDir=$1
    local localDirLength=${#localDir}
    local coreDir=$2

    for current in $localDir/*
    do
        local coreEquivalent=$coreDir${current:$localDirLength}
        if [ -d "$current" ]
        then
          merge_directory "$current" "$coreEquivalent"
        else
          local oldFile="/tmp/tmp-merge-old-`basename "$coreEquivalent"`"
          local newFile="/tmp/tmp-merge-new-`basename "$coreEquivalent"`"
          if [ -f "$coreEquivalent" ]
          then
            git show HEAD~1:$coreEquivalent > $oldFile
            diff3 -m "$current" "$oldFile" "$coreEquivalent" > "$newFile"
            if [ $? == 1 ]
            then
              echo "CONFLICT: $current"
            fi
            cp $newFile $current
          else
            echo "Skipping $current; no equivalent in core code."
          fi
        fi
    done
}

merge_directory local/harvest harvest
merge_directory local/import import
merge_directory local/config/vufind config/vufind
merge_directory themes/vuboot3/templates themes/bootstrap3/templates
merge_directory themes/villanova_mobile/templates themes/jquerymobile/templates
merge_directory themes/vuboot3/js themes/bootstrap3/js
merge_directory themes/villanova_mobile/js themes/jquerymobile/js

Conclusion

I’ve been frustrated by this problem for years, and yet the solution is surprisingly simple — I’m glad it finally came to me. Please feel free to use this for your own purposes, and let me know if you have any questions or problems!


Like

A More Complex VuFind 2 Unit Test

Okay, much sooner than expected, I found a good candidate for some more complex unit testing: the VuFindCart object that we mocked in the previous post.

Preparing for Testing

As I mentioned in the previous post, sometimes a bit of work needs to be done before classes can be easily tested.

In the case of VuFindCart, the class reads and writes cookie values (since this is how VuFind tracks book bag contents).  This is problematic for testing — the class is dependent on web-specific elements in the environment.  The solution is easy enough, though: add some extra abstraction.

First, we add the ability to override $_COOKIE through an extra constructor parameter: diff.

Next, we create a wrapper function around setcookie(): diff.

Now we can test everything easily even if we don’t have real cookies.  This abstraction may also come in handy in the future if Zend Framework offers its own cookie handling utilities, since we have reduced the number of times that VuFind directly utilizes low-level PHP functionality.

Writing the Test

As of this writing, we don’t have 100% test coverage for VuFindCart, but the work-in-progress test demonstrates a few useful techniques.  You might want to open the full code in another tab so you can look at it as you read the notes below.

First of all, as discussed in the previous post, VuFindCart depends on VuFindRecordLoader.  I use a mock object to satisfy that dependency.  The mock object is created as a property of the test class — this way, if tests need to make assertions about how VuFindCart interacts with VuFindRecordLoader, these can easily be set up.  (I haven’t done this yet, but it will be necessary to get 100% coverage).

I have created a getCart() convenience method which returns a VuFindCart mock object.  This routine uses the second parameter of getMock(), which can be used to specify which methods are to be mocked out.  In this case, we are only mocking out the setCookie() wrapper function, since we don’t want our test to actually call PHP’s setcookie() function.  Everything else will be executing real code from the VuFindCart class.

Most of the actual test cases are simple low-hanging fruit — setting values in the constructor and making sure that the corresponding get methods return them correctly, etc.  It should be fairly self explanatory.

The testFullCart() method might be of some interest — this demonstrates using a series of assertions to confirm that an expected sequence of events happens in the appropriate order (in this case, we are making sure that the cart registers as being full at the right point in time).

The testCookieWrite() method further demonstrates the power of mock objects.  We want to test that adding an item to the cart writes the correct values to the cookie.  Since we have mocked out the setCookie() method, we can set up expectations that it will be called at particular times with particular values — this allows us to test that a call to $cart->addItem() results in the expected calls to $cart->setCookie(), without actually writing any real cookies.  The PHPUnit syntax can seem a bit weird at first, but it’s not really too bad.  In this example, $this->at(0) and $this->at(1) are being used to set up rules relating to the first and second calls to $cart->setCookie; the ->with() portion sets up expectations for particular parameters at each call.

Conclusions

I hope this further demonstrates how testing can be achieved.  As before, please feel free to ask questions!


Like

A Simple VuFind 2 Unit Test

I have said on several occasions that test-writing might eventually become a good way for new developers to contribute to the VuFind project. By increasing our test coverage (and thus future code stability), new tests are a valuable contribution to the software… and the act of studying all the code paths in order to write a test is a good way to learn how things work.

Of course, the reality of the situation is not so straightforward. There is still a lot of code in VuFind that is inherently hard to test, usually due to complex dependencies. At this point in time, the process of writing tests is often also the process of refactoring code to reduce coupling, which in turn makes it more testable — and this often requires a deeper understanding of the system than a newcomer is likely to have. (See my previous blog post for an example of the sort of refactoring I mean).

So testing isn’t always easy…  but still, it can sometimes be straightforward.  Here’s a relatively simple example to demonstrate some of the principles.

Getting Set Up

This post assumes you have a clean copy of VuFind 2 checked out somewhere separate from your production instance, and that you have the necessary tools (PHPUnit, Phing, etc.) installed.  More background on the setup can be found in the VuFind wiki.

What We Are Testing

For this example, we are testing VuFind’s Cart view helper (VuFindViewHelperRootCart).  This is a very trivial piece of glue code whose purpose is to make VuFind’s cart object (which keeps track of user selections in the optional “book bag” feature) available for use in view scripts.  You don’t really gain much by writing a test for this class — it is unlikely to change much and it contains no complex logic — but since it is so simple, it’s a good candidate for this tutorial.  We can get to 100% coverage very quickly.

You can view the full code of the view helper in our Git repository.

There are only two methods: the constructor (which takes a VuFindCart object as a mandatory parameter) and __invoke (a PHP magic method which returns the Cart object when the helper is invoked as if it were a function).

Getting Started

By convention, unit tests (i.e. all tests that exercise VuFind functionality without relying on an active test instance) reside in module/VuFind/tests/unit-tests/src.  Within this directory, test classes are arranged and namespaced to correspond with the code that they test.

This means that we’re going to create a module/VuFind/tests/unit-tests/src/View/Helper/Root/CartTest.php, living in the VuFindTestViewHelperRoot namespace.

All PHPUnit test cases must be subclasses of the PHPUnit_Framework_TestCase class.  VuFind includes some base test classes with additional convenience methods (see the VuFindTest namespace), but this particular test is simple enough that we can simply extend the default base class:

class CartTest extends PHPUnit_Framework_TestCase

Writing the Test

One of the keys to writing an effective test is to avoid doing any work that is not related to the task at hand.  You don’t want your test to fail because of a problem in a different area of the code — this will make bugs harder to locate when something breaks.

We are trying to test something very simple here — essentially we want to be sure that when we put a cart object into the view helper’s constructor, we get the same object out when we invoke the helper.

This is where the “don’t do unrelated work” rule comes in.  We could construct a real VuFindCart object for testing purposes, but then if there was a bug in the VuFindCart constructor, that might cause our test to fail, even though that has nothing at all to do with our view helper.  Fortunately, we don’t have to construct a real VuFindCart object, thanks to PHPUnit’s mock object feature.

With mock objects, we can create objects that serve as placeholders for real classes in our code.  They accept the same method calls and pass the same instanceof tests as real objects, but they don’t actually do anything — unless we configure them to expect particular incoming data or simulate specific responses under specific circumstances.  These are a very valuable testing tool!

In our particular case, we don’t need to do anything fancy with mocks — we just need to call PHPUnit’s built-in $this->getMock() method to construct a fake Cart object.

There’s just one small issue.  If you just call:

$this->getMock(‘VuFindCart’);

You will get an error.  VuFindCart’s constructor expects a VuFindRecordLoader object, and we have to satisfy this dependency even when building a mock.  Fortunately, the third parameter of getMock() accepts constructor parameters for the new mock object, and nothing stops us from creating a mock VuFindRecordLoader to satisfy the dependency.  Thus, we end up with:

$cart = $this->getMock(
‘VuFindCart’, null, array($this->getMock(‘VuFindRecordLoader’))
);

Now that we have our fake cart, the rest is simple…  Just construct a view helper:

$helper = new VuFindViewHelperRootCart($cart);

…and then test that invoke works by making an assertion that invoking the helper will return the same object that we passed to the constructor:

$this->assertEquals($cart, $helper());

Assertions are the most important part of any test — these are what determine whether each test passes or fails.  Never write a test without any assertions!  PHPUnit includes a wide range of assertion methods, allowing you to express many different conditions.

Running the Test

Now that the code is written (full test class available here), it’s just a matter of using your VuFind test instance to run it, as described in the wiki.

After confirming that a new test passes on my local system, I push it to the Git master and then check the code coverage report in Jenkins after everything rebuilds.  In this case, I’m now seeing 100% coverage for the cart helper.

What Next?

I hope this has served as a helpful introduction to some fundamentals, but I realize that most real-life testing is significantly more complicated.  I may try to write an article describing a more difficult test in the future if time permits.  In the meantime, if you want to try your hand at test-writing, feel free to send me questions — I’ll be happy to recommend some areas that might be worth looking at, and I can help with any refactoring that may be necessary.


Like

VuFind, Zend Framework 2, and Flash Messages

One minor weakness of VuFind 1.x is that it doesn’t always provide good feedback for user actions.  One of the advantages to switching to a standard framework is that we can leverage an existing “flash message” mechanism to display messages to users in a consistent way.  Zend Framework 2 includes a simple FlashMessenger helper, but it took a bit of learning to use it as effectively as possible.  I hope that a discussion of my solution to this problem will helpfully demonstrate some aspects of the framework and VuFind 2.x’s architecture.

The Problem

In Zend Framework, flash messages are stored in an object by the controller.  For example:

$this->flashMessenger()->setNamespace('info')
    ->addMessage('Email sent successfully.');

or

$this->flashMessenger()->setNamespace('error')
    ->addMessage('Operation failed.');

 

The view is then responsible for pulling information out of the object and formatting it for display.

I wanted VuFind to display messages in a consistent way, so it seemed to make the most sense to use a View Helper to render flash messages.  This helper could take care of pulling messages from the namespaces in the object, applying translation, and building <div> tags with appropriate classes. This way, all I would have to do is add this code to a template:

<?=$this->flashMessages()?>

 

and my flash messages would display.

There’s just one hard part: getting the object containing the messages from the controller to the view with a minimum of fuss.

Solution #1: Brute Force

My first solution was not hard to come up with, but I was never happy with it.

Controllers in Zend Framework 2 pass values to their corresponding views by returning a value (often a ZendViewModelViewModel object).  If we want to ensure that all of these objects contain a particular value, we can establish a convention where VuFind’s controllers never construct a ViewModel directly; instead, they always call a $this->createViewModel() method defined in an abstract base class.  This helper method is responsible for constructing a ViewModel and then attaching the flash messenger object to it.  Once the ViewModel has a flash messenger attached, the View Helper responsible for rendering the messages can simply pull the messenger from a known named property in the model.

Advantage: It works.

Disadvantage: It’s ugly.  It relies on a particular arbitrary value being in a particular arbitrary place.  It takes away some of the flexibility of the framework by forcing the user to always return ViewModels, and to construct them in a particular way.  It makes the user jump through a hoop (calling createViewModel to create a view model) and punishes them in an unintuitive way if they don’t comply (flash messages stop appearing).

Solution #2: Working with the Framework

Fortunately, there is a better way.

One of the nice features of ZF2 is its service manager system (described in more detail in this blog post).  Service managers are responsible for constructing and storing instances of important objects used by the application.  Ugly application-specific details can be pushed into service manager configuration so that the actual components of the application can be simpler and more reusable.

The flash messenger controller helper and our custom view helper are both constructed by service managers.  This means that, through proper service manager configuration, we can ensure that the flash messages view helper always has access to the flash messenger controller helper, with no need for the intervention of a nasty createViewModel() method.

This is achieved with the help of a factory in our view helper service manager configuration:

'flashmessages' => function ($sm) {
    $messenger = $sm->getServiceLocator()
        ->get('ControllerPluginManager')
        ->get('FlashMessenger');
    return new VuFindViewHelperRootFlashmessages(
        $messenger
    );
},

 

Here’s how it works: service manager factories are passed an instance of the service manager that is doing the object construction ($sm in the example above — an instance of the service manager devoted to view helpers). $sm->getServiceLocator() retrieves the service manager that constructed $sm — the top-level service manager that manages the whole VuFind application. From here, we can call ->get(‘ControllerPluginManager’) to obtain the service manager for controller plugins. We can then call ->get(‘FlashMessenger’) on the controller plugin service manager to get hold of the flash messenger. This object ($messenger) is then passed to the constructor of the new VuFindViewHelperRootFlashmessages helper. The object now has access to the flash messenger as soon as it is constructed. None of the other code needs to know about the details — it just works like magic.

Yes, this is a bit ugly. But the important thing is that the ugliness is now in the configuration of the application — part of the glue that holds everything together. It’s okay for this part of the code to be a bit convoluted and application-specific, because its whole purpose is to make the various parts of the application fit together. The beautiful thing is that those component parts don’t have to know anything about the details of how the glue works — as a whole, the application has become less coupled and more flexible. Compare this to solution #1, where the ugliness was more deeply embedded in the application’s controllers and in the behavior of the view helper itself.

The Code

If you want to see how VuFind transitioned from Solution #1 to Solution #2, take a look at these diffs.

The Bottom Line

There’s definitely a learning curve involved with ZF2, but I continue to feel that it is worth the effort.  I hope this example has shed a little light on some of the issues… but if anything is unclear (and I’m sure something is), feel free to ask questions.  I’m always happy to help, especially if it means that more people are potentially able to help with the process of improving the code.


Like

Moving VuFind to Zend Framework 2: Part 5 — The Dreaded forward() Bug

IMPORTANT UPDATE: The problem discussed in this post only applies to Zend Framework 2.0 RC4 and earlier. Starting with RC5, a fix was introduced which eliminates the double-rendering issue described below. With the fix in place, you can do the logical thing of returning the output of $this->forward()->dispatch() rather than having to return false. Thanks to Nicholas Calugar, Matthew Weier O’Phinney, and all the others on zf-contributors and #zftalk.2 who talked to me about this problem and helped me refine my pull request to fix it.


It’s a law of software development that if you can’t explain some weird behavior and you choose to ignore it, it will inevitably come back to bite you. I know this very well, but knowing it doesn’t prevent it from happening.

The Problem

When I first implemented VuFind’s Flash Messenger view helper (designed to display status messages stored in the session), I ran into a strange problem. The behavior of the helper was simple: check the session for messages, display them, then clear the session. The problem was that flash messages never displayed. Eventually I just commented out the “clear the session” line and everything started working. I didn’t have time to figure out what was really going on, so I just put a note in the code to check it later, and moved on to other things.

The Real Problem

I kept running into bizarre flash messenger behavior, so I decided I needed to solve this problem for real rather than just putting on a band-aid. After digging deep into ZF2 and putting logging lines all over the place, I figured out what was causing the problem: many of my view templates were getting rendered twice. Even though the templates displayed on screen just once, the code was executing twice. This was the reason for my flash messenger problem: the first render displayed the messages and cleared them. The second render replaced the first, and by this point, the messages were gone.

The Root Cause

After a lot more digging and some extremely valuable help from mpinkston on the #zftalk.2 IRC channel, I discovered the root cause. Sometimes you want to forward from one controller action to another. In ZF1, you could use “return $this->_forward(…)” to achieve the effect. In ZF2, there is a controller plug-in called forward() that does the same basic thing. I was using it like this:


return $this->forward()->dispatch('controllerName', array('action' => 'actionName'));

Seems harmless enough, but what actually happens here is that the forward() plug-in dispatches an action, which causes ZF2 to attach a view model to the top-level layout object. The forward() plug-in then returns that view model. When the calling controller returns it, ZF2 attaches a second copy to the layout object. And then both copies get rendered!

What I should have been doing was this:


$this->forward()->dispatch('controllerName', array('action' => 'actionName'));
return false;

A simple, honest mistake with evil, subtle consequences.

The Solution

I’ve already found a solution to my immediate problem, as described above. I won’t make this mistake again, and VuFind’s flash messenger now works correctly. I was concerned that the solution would only work when forwarding to actions that return a ViewModel, but it seems to work for all cases, even when a Response is returned instead (i.e. when redirecting to another page).

I remain concerned that this is a big potential pitfall for users of ZF2 in general. It’s an easy mistake to make, and the consequences are extremely non-obvious, especially since in many cases, the only result of the mistake is a slight performance hit caused by duplicate work being done.

Hopefully this will be addressed in the future either by notes in the documentation or by the introduction of a convenience method in the forward() plugin that returns false instead of the actual output of the dispatched action. If either of these things had existed, I probably wouldn’t have spent the better part of a work day chasing this problem. But I don’t mean to criticize the ZF2 team — I like the framework, and these are the perils of working with software that is still under development. I just point this out as something to watch out for that should be addressed, one way or another, before the final 2.0 official release.


Like

Moving VuFind to Zend Framework 2: Part 4 — Command Line Tools

UPDATE – July 26, 2012: The release of Zend Framework 2 RC 1 makes much of this obsolete since it includes better native console support. I’ll try to post again when I have time to rework everything to use the new functionality, but for now I’ve just patched in a workaround. See comments at the bottom of the article if you are interested.


VuFind is primarily a web application, but it also includes a number of command-line tools for performing various harvest, import and maintenance tasks.  It would be nice if these command-line tools could leverage the infrastructure of the web application so we don’t need to write redundant code for setting up autoloaders, configuring resources, etc.  However, we don’t want to accidentally expose command-line behaviors through the web interface.  Fortunately, Zend Framework 2’s module system makes this fairly easy to achieve.

The Goal

In order to achieve some level of granularity, it would be nice if, when you run any given command-line utility, VuFind routes your request to a special command-line controller whose name corresponds with the directory containing the tool, executing an action corresponding with the tool’s filename.  So, for example, running import/import-xsl.php would call importController::importXslAction().

Naming the Module

Zend Framework 2 modules correspond with PHP namespaces.  For example, the main VuFind module is located in module/VuFind/Module.php, and it defines a VuFind namespace.  All supplementary files living within the VuFind namespace are found under module/VuFind/src/VuFind, and Zend Framework knows how to access them based on the module’s configuration.

When creating the CLI module, we have two options:

1.) We can create a new namespace, such as VuFindCLI, and locate the module in module/VuFindCLI/Module.php with a structure totally parallel to the main VuFind module.  This results in the cleanest directory structure, but the namespacing isn’t very logical, since this is really a subset of VuFind functionality.

2.) We can create a sub-namespace, such as VuFindCLI, and locate the module in module/VuFind/CLI/Module.php.  Because this namespace is a subset of the main VuFind namespace, supplemental files will live in module/VuFind/src/VuFind/CLI rather than module/VuFind/CLI/src/VuFind/CLI — a potential source of some confusion.

I opted for approach #2 — I prefer having logical namespaces at the expense of a little directory irregularity.

Loading the Module

Having decided what to call the module, loading it is simply a matter of modifying the main application configuration (config/application.config.php) to load the CLI module when it detects that it is running in CLI mode:

$config = array(
    'modules' => array(
        'VuFind',
    ),
    /* ... trimmed for clarity ... */
);
if (PHP_SAPI == 'cli') {
    $config['modules'][] = 'VuFind\CLI';
}
return $config;

Creating the Module

The CLI module itself doesn’t need to contain very much content. We need a configuration to tell it how to load CLI-specific controllers (module/VuFind/CLI/config/module.config.php), and a module definition to set up custom routing (module/VuFind/CLI/Module.php).

The routing is a little bit complicated, so let’s look more closely at it. Here is the relevant code from Module.php:

    public function onBootstrap(MvcEvent $e)
    {
        $callback = function ($e) {
            // Get command line arguments and present working directory from
            // server superglobal:
            $server = $e->getApplication()->getRequest()->getServer();
            $args = $server->get('argv');
            $filename = $args[0];
            $pwd = $server->get('PWD', CLI_DIR);

            // Convert base filename (minus .php extension) and containing directory
            // name into action and controller, respectively:
            $baseFilename = basename($filename);
            $baseFilename = substr($baseFilename, 0, strlen($baseFilename) - 4);
            $baseDirname = basename(dirname(realpath($pwd . '/' . $filename)));
            $routeMatch = new RouteMatch(
                array('controller' => $baseDirname, 'action' => $baseFilename), 1
            );

            // Override standard routing:
            $routeMatch->setMatchedRouteName('default');
            $e->setRouteMatch($routeMatch);
        };
        $events = $e->getApplication()->getEventManager();
        $events->attach('route', $callback);
    }

The onBootstrap() method is called automatically on every module. Within this method, we are using the Zend Framework 2 event manager to associate a callback function with the route event. The callback function is defined as a closure.

Within the closure, we need to do two things:

1.) Figure out the directory and filename that were used to access VuFind (the idea here is that every CLI utility will simply be a wrapper that loads the core of Zend Framework with an include statement).

2.) Using this contextual information, force the router to load the appropriate controller by injecting a routeMatch object that matches the ‘default’ route defined by the core VuFind module.

As it turns out, step 1 was a little harder than anticipated. Figuring out the filename that was accessed is easy; PHP’s $_SERVER superglobal (accessible in Zend Framework through the getServer() call) contains an ‘argv’ element representing command-line parameters, and the first element of this array will always contain the base filename. The hard part is figuring out the containing directory. The __DIR__ magic constant is of no use to us, because it refers to the context of the currently-executing file, not the top-level script run by the user. Similarly, the getcwd() function is of no help, because part of the standard ZF2 initialization sets the current working directory to a fixed location.

Some versions of PHP come to the rescue with a $_SERVER element called ‘PWD’ which contains the directory from which the user executed PHP. The problem is that this is not present in every operating system (it is missing in Windows, for example). For lack of a more elegant solution, I eventually settled on defining a constant called CLI_DIR in my command-line scripts so that code deeper in the framework can figure out the context. Hence the code:

$pwd = $server->get('PWD', CLI_DIR);

This attempts to use the $_SERVER[‘PWD’] variable, but if it is not set, it fails over to the CLI_DIR constant. That way, the ugly workaround is only triggered when absolutely necessary.

Putting it All Together

As my first proof of concept, I decided to implement the import/import-xsl.php script. The code inside the script is very simple:

define('CLI_DIR', __DIR__);     // save directory name of current script
require_once __DIR__ . '/../public/index.php';

This just sets the CLI_DIR constant described above and loads the framework.

Now we just need to define a controller to respond to the request. I created a base controller with shared methods that are likely to be used by other CLI-oriented controllers (module/VuFind/src/VuFind/CLI/Controller/AbstractBase.php) and then extended that with the actual ImportController functionality (module/VuFind/src/VuFind/CLI/Controller/ImportController.php).

At this point, all the pieces are in place. When you run import-xsl.php, it loads Zend Framework. Zend Framework detects CLI mode and loads the CLI module. The CLI module overrides the router and directs the user to ImportController::importXslAction(). The controller is able to make use of all the same classes and resources as a web application, and no setup code has been duplicated anywhere.

The Rough Edges

There is one piece of the puzzle that I am not entirely happy about right now. Zend Framework 2 controllers work by building up a ViewModel or Response object and then returning that for further processing. This model does not work well in the CLI environment for two reasons:

1.) CLI tools often need to produce real-time output. Unlike a web request which gets built all at once, a CLI tool will often show incremental details as it works (“loading file 1, loading file 2, etc.”).

2.) CLI tools need to return an exit status to the operating system in order to indicate success or failure, which is critical for incorporating PHP tools into shell scripts and batch files.

Neither of these use cases are currently met through native framework features (at least as far as I can tell). For now, I am simply using “echo” and “exit” inside the controllers to achieve the desired effects, which is functional but less than ideal.

There is hope, though: the Zend Framework community is currently thinking about CLI integration, as evidenced by this Request For Comment. I’ll try to keep an eye on developments in this area, and once the framework has the capabilities we need, the existing code can be more tightly integrated into it.


Like

Next Page »

 


Last Modified: July 19, 2012