owen

I've rewritten this year's Brewfest website using an old design I liked on top of Phoenix.  Phoenix is an MVC framework, like Rails, but for Elixir.  The rewrite has been interesting.

The design requirements include displaying a single-page website that describes the Brewfest, while capturing information from brewers about themselves and the beer they plan to bring to the fest to share.  I wanted the "login" to be extremely simple so that brewers didn't need to go through a whole enrollment process.  The information they enter should also go through an editorial process before anyof it is published to the site.  Some nice-to-have features include displaying ticket sales information/graphs, and the ability to edit the single-page site content from an administrative area on the site (protected by a preset http-authenticated login).

The initial build out of the site was a little challenging, since I was fighting with the Phoenix template system.  It's not so much that it's hard to work with, just that the layout vs content templates were unfamiliar, and the use of View modules (which are mostly empty in this design) was confusing at first.  I did eventually get the templating worked out to the point that the single-page site worked the way I wanted.  The more complex forms pages for the brewers to enter data were only slightly challenging, but after sorting out some confusion about whether I was editing the form for the brewer or for the admin (the files are named the same, but in different directories), it was reasonably smooth.

One definite problem with Phoenix is that the generators for model forms do not create any type of field for a relation.  The Beer model, for example, related to the Brewer model, but the form for it did not include any type of field for the admin to connect one to the other.  This was easily fixed with some hand-rolled code, but it was a conspicuous absence.

The brewer-facing forms were an interesting challenge, since I wanted to provide the brewers with a URL that included a unique code as part of the path rather than forcing them to use typical login authentication.  This meant making some changes to the routing to support this feature.  It was not dificult, but was definitely different from the typical.

I added several custom Plug modules to implement various facets of the application.  Both the authentication and adding the appropriate navigation to the page were handled via Plug.

Because the system used coded URLs for the brewers to log in, I devised a communication mechanism that would send email to the brewers and add on their unique URL to the end of the message so that they could look at any email we sent them and find their login URL.  To do this, I added a package that connected the application to PostMark, which reliably sent out the emails for me, rather than using sendmail or some kind of local MTA.  This went together very easily.  It was more difficult putting the WYSIWYG editor on the page that lets you edit the email.

Overall, the worst part of the project was getting the javascript/css build system to work cleanly.  Phoenix uses Brunch, which, if you're not doing anything other than what Phoenix comes with, seems to work fine.  But I had a lot of difficulty understanding how Brunch was doing things, and its insistence on AMD and packaging everything up to be used from calls to require() were a huge stumbling block for me.  It turns out that you can replace the entire javascript build system with whatever you want, and so I swapped out Brunch for Gulp, which fits my personal taste better.  I feel like Gulp provides a lot more direct control over how it works, rather than Brunch's implied conventions.  I think Gulp is rather a lot like Phoenix itself in this respect, and I'm surprised that Brunch is the default.  In any case, I spent hours trying to get the javascript to do what I wanted with Brunch, and after I switched to Gulp, it took probably 30 minutes at most to get everything working the way I wanted.  If I start a new Phoenix project for anything, switching the build system out for Gulp will probably be one of the first things I do.

I managed to connect the admin pages of the site to Brown Paper Tickets' API system so that I can get ticket purchase information and display it on the site.  This is an advantage because it's difficult to set up logins for BPT that access this information, and it's better to see it directly on the site, anyway.  One thing more I did here was institute a cache so that the values are only updated from the API every two hours at most.  This keeps them fresh enough without BPT complaining about how frequently the site polls their API.  One thing I would like to do yet is pull daily sales information from last year and compare it to this year's sales to get an idea of how well ticket sales are tracking.  Putting this information into a graph could show whether we're on pace with last year's sales.

I have yet to do the templating for the beer information on the public site.  I plan to have the beer template use a carousel for display.  This will keep the design aesthetic sane and allow all of the beer text to be used for SEO purposes.  I need to institute the approval system also.  I plan to implement this in the database by adding an "approved_description" field to the beer table that will appear on the site.  A button in the admin will copy the description to the approved field.  Any row with an approved value will be displayed. This seems like the simplest mechanism.

I also want to add a feature that will let me add, edit, and remove sections from the site directly.  This will make the system more flexible for the future.  I think I will do this by offering a form that lets the admin select a template, and then providing a simple WYSIWYG editor for filling in the template content.  In this way, I could create templates that display brewers, beer, and sponsors to be included in the single-page layout, and use the admin to organize the order of these things.  I also want to add a sponsor editor for this purpose, and a drag-and-drop UI for reordering the created sections of the page.

For next year, I would like to add a "year" column to each table so that the rows could be filtered by year, and past years' data could be displayed potentially on a separate URL.  Brewers have asked for this information in the past.  I should also add some metadata for who won what award, so that prior winners are recorded in the site data.

The most amazing statistic about the site is that the compiled application, which runs solely behind HAProxy (no Apache, no Nginx), typically returns its slowest page result in at most 7ms.  Most page loads are returned in microseconds, even with database lookups.  This is orders of magnitude faster than prior sites using plain HTML or WordPress.  Not only that, but unlike the WordPress site used previously, this application is virtually unhackable, since the code that runs it is compiled.  While the application is still potentially vulnerable to user input issues and the server could still have unknown open doors, the application has no power to replace key pieces of itself, like some insecure WordPress plugins do.

On top of that, this application and the Postgres database it runs on top of are hosted on a dirt-cheap $2.50/month VPS at Vultr.  This small server is not only meeting the needs of the site, but seems like significantly more power than the site really needs.  I credit this largely to the small footprint of the compiled application and its complete lack of dependencies (no PHP, no Apache, no Varnish, etc.).  The site is costing me less, is doing more, is more secure, and does exactly what I want with no extra cruft.  I am very pleased with the outcome so far.

There is still a lot of work to do getting the site into shape for this year, but I'm enthusiastic about what Phoenix has been able to provide and how well everything is coming together.