Chaining Rails Template (view) Handlers

I have been looking for a way to get rid of all the ERB markup in the file and simply use plain HTML, albeit with my custom set of HTML tags. I don’t want to learn a new markup language just to do something simple but on the same token don’t want <%= … do %> … <% end %> splattered all over the page either. For example, I want to use “<row>…</row>” to invoke/substitute <%= row do %>…<% end %> automatically when the page is processed.

Rails 3 makes it easy to chain preprocessors to assets that will perform manipulations on the file prior to storing it, as the guides talk about here. However, this method limits you to only the assests files – scripts, css, images, for the most part.

I’ve been searching for a way to chain template handlers to action views in the same manner. For example, if you have a file called index.html.erb.something the framework would first process the Something handler, followed by the ERB handler. So far I’ve not been able to find a way to handle it quite in this form, however I have found a way to inject your preprocessing without changing extensions at all.

To do this I created the following initializer, with a simple but functional example:


module CustomMarkupHandler
MAPPINGS = {
`Chaining` =&gt; `Awesome`
}
def self.call(template)
begin
details = {
:locals =&gt; template.locals,
:virtual_path =&gt; template.virtual_path,
:updated_at =&gt; template.updated_at
}
updated_source = template.source
MAPPINGS.each_pair {|find, replace|
updated_source = updated_source.gsub(find, replace)
}
new_template = ActionView::Template.new(updated_source, template.identifier, template.handler, details)
rescue
new_template = template
end
ActionView::Template::Handlers::ERB.call(new_template)
end
end
# Custom Markup Language registration
ActionView::Template.register_template_handler :erb, CustomMarkupHandler
end

Note that if you copy and paste this code you’ll need to replace the backwards quote with a normal one – my syntax highlighter doesn’t like single or double quotes.

Let’s walk through what this is doing:

  1. It defines a custom markup handler module
    • Typically you’d want to look up the registered handler for a given extension, however since we are overriding the handler for ERB, we’ve hard coded it here to use ActionView::Template::Handlers::ERB.
    • The code here simply does a gsub BEFORE allowing ERB to process the source. Create any *.erb page with the word “Chaining” in it and it will be replaced at render time with “Awesome”.
      • Want a guru to validate this part – I believe that by adding this before the ERB process it should not interfere with the way that the file is cached and served back up later, thus retaining roughly the same performance that we had prior to this injection.
    • The source attribute of the ActionView::Template is read only so we copied out the attributes we needed (details, identifier, handler) and simply use them, along with our updated source, to create a new template.
    • We then can send our modified source template through the ERB handler for traditional markup. Since we ran our modifications before ERB we can easily gsub in method calls that will be handled as if they were in the file natively.
    • Finally, just in case something unexpected happens, the whole thing is wrapped in begin/rescue that will simply process the unadulterated template, hopefully preventing a 500 error.
  2. It registers the handler as the default handler for *.erb files
    • This means that ALL files ending in *.erb will be routed through this new handler instead.

Rails Data Migrations Made Easy!

Introducing ActiveDataMigrations!

From the README file:

ActiveDataMigrations is a Ruby gem that allows developers to set up multiple migration locations, each of which can be run independent of one another. This library sits on top of ActiveRecord so all standard migration features remain available.

This is particularly useful in cases where you want to separate your data migrations from your schema migrations or where you have multiple steps in your migration process that must have other steps invoked throughout.

I’ve always felt that Rails migrations came up short in the area of data seeding and data migration. While you can use the standard schema migrations to manage your data it is a recipe for disaster.

For example, I’ve got my Tanglewood Turnings site that is now in production. I’ve got live customer and order data in the system so I can no longer simply apply a seed or global data update when I push updates out to production. Yet I still have a need to enter large amounts of new data.

In researching the problem on the interweb, the common answer in the Rails community seems to be to either add the data to your migrations files or to create a sql file and apply the changes directly. I personally make it a practice to avoid direct interaction with the database so I explored the migrations route. The problem was that my tests were failing due to the extra and unexpected data. So I added ” unless Rails.env == ‘test’ ” to the end of the data blocks. That stinks. As in bad code smell. Not a good option. The other alternative was to update my tests to accommodate the new data. That would have me updating test cases every time I updated production data. That also stinks.

So I decided to write a solution. I started out with this grandiose idea of somehow abducting the ActiveRecord::Migrator class, while still having it work within the Rails context, and twisting it to meet my own needs. Oh yeah, I also wanted to figure out how to do this without monkey patching. Figuring out how exactly to do this seemingly mundane task took me relatively deep into the Rails and ActiveRecord rabbit hole. While down there I realized that I was vastly over-complicating things and that I could achieve what I wanted with a couple lines of code and a new Rake task. The result was a Ruby Gem that is easy to use, easy to integrate, easy to maintain, and delegates 99.999% of its functionality right back to ActiveRecord, leaving it with the power and reducing my support overhead.

You can install it using:

gem install active_data_migrations

You can read the full write up, view the source, and get more information by visiting the git repository, here:

https://github.com/finn0013/active_data_migrations

Application Design for Mobile Devices

As I watch the mobile software market explode I can’t help but notice a pattern that keeps repeating itself. Like software architecture design patterns, software packages follow distinct patterns that have been repeating over the past 20-30 years.

While there is a lot of history before this point, let’s start at mainframes and terminals. See a pattern here?

  • Remote – Terminal / Mainframe
    During this phase of our history one could sit down at a dumb terminal that had a direct connection to the mainframe. This would provide remote access to the functionality on the server but wouldn’t provide much other functionality.
  • Local – Thick client apps
    Next came thick client apps. That is, applications that are physically installed on a computer. This typically moved a lot of the processing off the central system and onto a stand alone computer. A good example of this is word processing.
  • Remote/Local – Distributed thick client apps
    Once thick clients were widespread the software industry quickly realized they had a problem. They needed easy ways to update their thick client applications but also needed to be able to gather and aggregate data across all of the installations. The resulting evolution was a set of applications that could communicate with a central server as well as install updates as necessary, all without needing a technician physically present.
  • Remote – Web based apps
    The software industry quickly realized that the bulk of the functionality that they were installing on people’s computers could easily be handled via the internet, given the advance of software and networking technologies. Thus, the web based app was born. These apps provide 90% of what most users need without the overhead of distributing, installing, and supporting thick client software. Note, however that specific app needs still have to be addressed with this clients, even in this evolution, 3D rendering being a good example.
  • Local – Thick client mobile apps
    As the mobile world started to mature we found that the hardware in our mobile devices was becoming much more powerful and capable of handling apps. At this time the browser capabilities of these devices as well as the ability to have fast mobile networking was sufficiently infantile that once again we found ourselves utilizing thick client apps as the best way to deliver functionality.
  • Remote/Local – Distributed thick client mobile apps
    Once again, technology advances delivered us better mobile browsers and a newfound ability to handle much faster networking on mobile devices, giving birth to the distributed thick client apps for mobile devices. These apps, in many regards, follow the same basic principles as the distributed thick clients of the PC era. They are installed but can update themselves and communicate with a remote server or servers.
  • Remote – Mobile responsive apps
    This phase of our evolution is unfolding now and is really just starting to gain traction. Over the next year this will become the de facto way that the majority of companies get their data into a mobile device. The responsive layouts concept is a way of designing a web site/application in a such a way that it understands what kind of device (PC, phone, tablet, etc.) it is being displayed on and applies the correct styling for that type device. Bank of America has a pretty good example of this. If you visit their mobile site you are seeing the same basic content you would on their primary site, however it has been styled so that it works well and is easy to use on your mobile device.

Predicting what comes next in this evolution isn’t exact but also isn’t particularly difficult. We’ll likely see bridges to cross some of the major gaps that currently tie us to mobile devices, such as:

  • Browser support for mobile features
    Mobile browsers will likely continue to begin supporting, or at least provide an API for supporting, multiple features that are device specific, such as GPS or the accelerometer. Likewise, mobile operating systems will continue to evolve in such a way that allows the browser to more easily interact with the mobile system.
  • Generic push notification capabilities
    One of the major limitations today that cause companies to gravitate towards thick client mobile apps are push notifications. If I want to be able to notify a user of a particular event the only way to do so today is to utilize the push API for a particular device. My hope is that this functionality will become standardized in such a way that notifications can be sent to a central location (albeit, possibly different device dependent locations) that knows how to notify the device in a manner suitable to that device.For example, my web application may trigger an event that then notifies the Apple servers of the event and provides a unique key that was authorized by and identifies the device. The Apple servers would then know how to send the notification to that device based on the unique id. Users would be able to easily control who does and doesn’t have access to push to them in the same manner they do today, only the system would be distributed.

Until these changes take place there will always be a need for a thick client mobile app in certain situations. Just don’t write a custom thick client app for a mobile device unless you actually need the features that are offered by the mobile device you are writing for. If you just want an app for the sake of having an app, spend your money having a good designer build a mobile responsive layout for your site. However, if you actually need things like GPS, accelerometer, notifications, or other device specific features, you really don’t have much choice, in present day, but to write a thick client mobile app. In this situation you should still follow the web based paradigm as much as possible and keep as much content web based as you can.