IE9 blank page? Some tips.
I ran accross IE9 not rendering a page AT ALL yesterday. Completely valid (X)HTML, no CSS errors, nothing. Firefox - fine. IE9 set to render in IE7 mode - fine. IE8 mode - fine. IE 9 Compatibility View - fine. IE9 in standards mode? Nothing.
The developer tools showed the the contents of the <head>-element, but just an empty body-tag, yet “view sources” showed everything.
Turns out there are a few things that can cause this. Self-closing script tags like
<script type="text/javascript" src="somescript.js" />
are completely valid XHTML, but IE9 barfs on them. This was actually not my problem, but something I ran across when searching for a solution.
My problem, turns out, was rather funky. I was using conditional statements to include an extra stylesheet for Internet Explorer <= 8, like so:
<!--[if lt IE 9]>
<script language="javascript" type="text/javascript" src="js/excanvas.min.js"></script>
<![endif] -->
Note the little space in “<!—[endif] —>”. THAT was the culprit. Removed that and everything works properly again.
*sigh*
Fragment testing Agavi actions that handle file uploads
I recently had to fake file uploads with Agavi in order to run fragment tests on an action that handles file uploads. After a bit of a faff and some help from David, I managed to nail it.
Let’s assume we have a form looking something like this:
<form action="<?php $ro->gen('null');?>" method="post" enctype="multipart/form-data">
<input type="file" name="files[1]" />
</form>
Normally when you add request parameters in tests you’d do something like this:
public function testSomeWriteMethod() {
$this->setRequestMethod('write');
$this->setRequestData(
$this->createRequestDataHolder(
array(AgaviWebRequestDataHolder::SOURCE_PARAMETERS =>
array(
'foo' => 'bar',
'baz' => 'quux',
'asdf' => 15123
)
)
);
$this->runAction();
$this->assertViewNameEquals('Error');
}
In order to “attach” files to the request, we need to manually construct AgaviUploadedFile-instances and add them to the request data. AgaviUploadedFile can construct the data from a stream, file contents or resource.
$contents = file_get_contents('resources/8151.png');
$file = array('size' => strlen($contents),
'name' => '8151.png',
'contents' => $contents,
'is_uploaded_file' => true);
$uploadedFile = new AgaviUploadedFile(file);
Naturally you’d do that in the setUp() method of the test class and set the uploaded file as a class attribute, and if you need more than one file, you just add more class attributes.
The test method itself is rather simple:
public function testWriteSuccessWithFile() {
$this->setRequestMethod('write');
$this->setRequestData(
$this->createRequestDataHolder(
array(
AgaviWebRequestDataHolder::SOURCE_PARAMETERS =>
array(
'foo' => 'bar',
'baz' => 'quux',
),
AgaviWebRequestDataHolder::SOURCE_FILES =>
array(
'files' => array($this->uploadedFile)
)
)
)
);
$this->runAction();
$this->assertViewNameEquals('Success');
}
If you need to test handling of multiple file uploads, just change
array('files' => array($this->uploadedFile));
into
array('files' => array($this->uploadedFile1, $this->uploadedFile2, $this->uploadedFile3));
and you should be set to go!
Happy testing!
EDIT: Updated Jan 19, because David yelled at me :(
General principles for good URI design
Shamelessly nicked straight from a post by Bob Aman on Stackoverflow:
General principles for good URI design:
- Don’t use query parameters to alter state
- Don’t use mixed-case paths if you can help it; lowercase is best
- Don’t use implementation-specific extensions in your URIs (.php, .py, .pl, etc.)
- Don’t fall into RPC with your URIs
- Do limit your URI space as much as possible
- Do keep path segments short
- Do prefer either
/resourceor/resource/; create 301 redirects from the one you don’t use - Do use query parameters for sub-selection of a resource; i.e. pagination, search queries
- Do move stuff out of the URI that should be in an HTTP header or a body
(Note: I did not say “RESTful URI design”; URIs are essentially opaque in REST.)
General principles for HTTP method choice:
- Don’t ever use GET to alter state; this is a great way to have the Googlebot ruin your day
- Don’t use PUT unless you are updating an entire resource
- Don’t use PUT unless you can also legitimately do a GET on the same URI
- Don’t use POST to retrieve information that is long-lived or that might be reasonable to cache
- Don’t perform an operation that is not idempotent with PUT
- Do use GET for as much as possible
- Do use POST in preference to PUT when in doubt
- Do use POST whenever you have to do something that feels RPC-like
- Do use PUT for classes of resources that are larger or hierarchical
- Do use DELETE in preference to POST to remove resources
- Do use GET for things like calculations, unless your input is large, in which case use POST
General principles of web service design with HTTP:
- Don’t put metadata in the body of a response that should be in a header
- Don’t put metadata in a separate resource unless including it would create significant overhead
- Do use the appropriate status code
201 Createdafter creating a resource; resource must exist at the time the response is sent202 Acceptedafter performing an operation successfully or creating a resource asynchronously400 Bad Requestwhen someone does an operation on data that’s clearly bogus; for your application this could be a validation error; generally reserve 500 for uncaught exceptions403 Forbiddenwhen someone accesses your API in a way that might be malicious or if they aren’t authorized405 Method Not Allowedwhen someone uses POST when they should have used PUT, etc413 Request Entity Too Largewhen someone does something like ask for your entire database418 I'm a teapotwhen attempting to brew coffee with a teapot
- Do use caching headers whenever you can
ETagheaders are good when you can easily reduce a resource to a hash valueLast-Modifiedshould indicate to you that keeping around a timestamp of when resources are updated is a good ideaCache-ControlandExpiresshould be given sensible values
- Do everything you can to honor caching headers in a request (
If-None-Modified,If-Modified-Since) - Do use redirects when they make sense, but these should be rare for a web service
NetBeans Agavi plugin version 1.0.1.3 released
The new version fixes an issue with locating actions from views as well as an issue with Go To View displaying files that are under Subversion’s .svn-directory.
Note that if you don’t use camel case in your view class name (eg. you use FoosuccessView instead of FooSuccessView) the plugin has no way of deducing the action to use, as the code splits the view name on capital characters.
The plugin has been pushed to the update center (reload the catalog and it should pop up) and source code is in the usual place.
Comments and suggestions are welcome.
NetBeans Agavi plugin 1.0.1.2 released
I pushed a new version of the Agavi framework support plugin for NetBeans. This version should fix a rather annoying (and embarrassing) bug that would cause Go To View to display a wrong list of views if you used sub-actions.
Ticket reference is https://github.com/horros/NetBeans-Agavi-plugin/issues/1 and the new version should pop up if you go to Tools => Plugins => Updates and hit “Reload catalog”. Sources are, as usual, on github.
Released ReformatProject plugin for NetBeans
After a few hours of tinkering about, I am glad to announce the release of version 0.1 of the FormatProject plugin for NetBeans!
The plugin allows you to right-click on a folder in the Files-view and select “Reformat all files”, which will traverse the folder and all subfolders, open all files for which an appropriate editor is registered (so it should work with any language and filetype, tested with Java, PHP and XML) and reformat the file accoding to the formatting rules.
The current (very much alpha quality) version will, unfortunately, close any files you might have open in the editor if it reformats them and won’t, also unfortunately, report any sort of progress.
Please report any issues here or on https://github.com/horros/ReformatProject/issues
Source code (released under the BSD license) is available, as usual, at https://github.com/horros/ReformatProject
Oh, there’s a fancy UpdateCenter for the plugin. Add http://dev.necora.fi/markus/nb/reformat/updates.xml to your settings in the plugin manager and go to Available Plugins and hit “Update catalog” and the plugin should appear.
NetBeans Agavi plugin 1.0.1.1 released
Version 1.0.1.1 of the NetBeans Agavi plugin is released, this time only with minor changes;
- The “clear cache” action now works with custom source directories
- Fixed a NullPointerException that was thrown if the custom source directory didn’t exist
The most important change, however, is that the source code is restructured into a plugin suite so that NetBeans can automatically generate update center information for the plugin, and on that note it’s my great pleasure to announce that the plugin now has an update center!
Go to Options => Plugins => Settings, click Add, type “Agavi” into the name field and add “http://dev.necora.fi/markus/nb/catalog.xml” as the URL, click OK and switch to the “Available Plugins”-tab and hit “Reload Catalog”. You should see “PHP Agavi Framework Support” as an available plugin.
NOTE
Completely remove any old installations of the Agavi plugin before you attemt to install it via the update center, otherwise it will not work!
Sources are, as usual, available at https://github.com/horros/NetBeans-Agavi-plugin
A special thanks goes out to the person with the nickname “warui” on the #agavi channel on Freenode for helping me reorganise the github repository!
New version of the NetBeans Agavi plugin released
A new version of the NetBeans Agavi plugin has been released which fixes a problem with detecting the Agavi installation path on *nix machines. The NBM is available for download here, and as usual the source code is available here.
Thanks to Benjamin Boerngen-Schmidt for the fix!
UPDATE: Yet another version update. If you have a weird directory layout (ie. your Agavi project-wizard target was run in a subdirectory of your “main” directory) you can now set the directory via the project properties. Note that you need to select the directory under which your app-directory resides, so if your main project is in /home/whatever/MyApp and if the Agavi-generated tree is under /home/whatever/MyApp/src (has app, libs, pub and so on under it), you need to select the src directory. The NBM is up for grabs HERE.
(The first download link is now also changed to reflect the new version, the old version is still available via the download browser on github.)
Patch for Agavi 1.0.4 and 1.0.5-RC1 to add PHPUnit 3.5 support
I’ve patched Agavi to add support for PHPUnit 3.5.14 with a patch that should not break backwards compatibility at all. If you are running Agavi 1.0.4 or 1.0.5-RC1 and are using PHPUnit 3.4, I’d really appreciate if you could apply the patch and check that everything still works as it should in a “real” environment.
The patch is attached to ticket #1331 on Agavi’s trac, you can grab it here.
UPDATE: This is no longer necessary, as the fix is included in 1.0.5+