Analytics, Errors and Polymer
August 17, 2014
With Revisit.io being in beta, the priority right now is reviewing what features are being used and responding to feedback/bugs/errors. After looking into a bunch of analytics tools and error tracking apps, it became clear that an app that combines error tracking and analytics would be very useful.
Revisit Analytics and Errors is a custom prototype that combines:
- Analytics on a per user basis
- Error tracking
- Grouping data by day
- Filter analytics and errors by user and day basis — to show the path a user took to get to any given error
Architecture
Having a tiny data model and wanting to make the app super fast made Golang standout as the right choice for the backend. A bonus was being able to run it on a 512mb server. After writing a lot of Elixir and Orchestration Focused Design, it has become much easier to abstract out common issues with Golang and make it very enjoyable to program in.
There has been a lot of hype about Polymer as a new structure for frontend development recently. The platform has been maturing quite rapidly and it was worth trying in a small project before diving into something bigger.
The architecture for the app is shown below:

Building Sourcegraph, a large-scale code search engine in Go is a great post on how to structure Golang web applications. Following it meant that only one external package was required for this application: Gorilla / Mux. Best Practices for Production Environments is also a great break down of tips for writing Golang code.
Routing in the backend is quite simple:
router := mux.NewRouter()
router.HandleFunc("/events.json", ListEventsHandler).Methods("GET")
router.HandleFunc("/events.json", CreateEventHandler).Methods("POST")
router.HandleFunc("/create_event.json", CreateEventHandler).Methods("GET")
http.Handle("/", router)
http.ListenAndServe(":3001", nil)
The other big tip with Golang is don’t be fancy. For loops are used over and over in the code for filtering the main array. They are quick and simple. Abstracting them is very tempting but the Golang ethos is to keep programming simple, even if it involves a little bit of repetition. Below is an example:
func filterEventsByDate(events []Event, date string) []Event {
var eventsByDate []Event
for i := 0; i < len(events); i++ {
if events[i].CreatedAt.Format("2006-01-02") == date {
eventsByDate = append(eventsByDate, events[i])
}
}
return eventsByDate
}On to Polymer. Polymer lets you structure frontend components in one file. Each file encapsulates its own templating, stylesheets and javascript.
The structure of a Polymer component looks like:
<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="/bower_components/core-ajax/core-ajax.html">
<polymer-element name="time-group" noscript>
<template>
<style>
:host .analytics-events {
position: absolute;
width: 82.5%;
top: 4rem;
right: 0;
}
</style>
<core-ajax auto url="{{url}}" handleAs="json" on-core-response="{{eventsLoaded}}"></core-ajax>
<section class="analytics-events">
<template repeat="{{event in events}}">
<analytics-event on-select="{{changeSelection}}" event="{{event}}"></analytics-event>
</template>
</section>
</template>
<script>
Polymer('time-group', {
events: null,
created: function() {
this.events = [];
},
eventsLoaded: function(event, result) {
this.events = result.response || [];
this.eventCount = this.events.length;
}
});
</script>
</polymer-element>
The code above is a skeleton of the real code but you can see that all sections of the code are used to encapsulate the display and behaviour of the component.
Because Polymer uses Shadow DOM, it means that each component is fully encapsulated. This article http://www.html5rocks.com/en/tutorials/webcomponents/shadowdom-201/ has a great explanation of how Shadow DOM styling works and how you can punch through it when needed. The hard thing is that styles can’t leak through from the main stylesheet into the Shadow DOM, which means that all SASS helpers were useless without a small trick. Below is an example:
.events-menu a.logo {
color: pink;
}
If you explicitly reference the ::shadow pseudo-element then you can create styles that apply across Shadow DOM elements.
::shadow .events-menu a.logo {
color: pink;
}
Polymer leverages components to try and remove as much hand coding of javascript as possible. A good example of this is the core-ajax component. It provides a html tag like interface to making an ajax call. It can call a javascript function or just set a bound variable. If the data doesn’t need any transformation then having it populate a variable that is iterated over is a simple way to list data from a server in plain old tags.
Components can publish the variables that they allow invokers to pass in. And variables in the form of {{variable}} are passed by reference to the component so the template can access all of the nested data in the variable. A simple example is below:
var event = { name: 'Revisit Event' };<analytics-event event="{{event}}"></analytics-event><link rel="import" href="/bower_components/polymer/polymer.html">
<polymer-element name="analytics-event" attributes="event" noscript>
<template>
<style>
</style>
<div class="name">{{event.name}}</div>
</template>
<script>
Polymer('analytics-event', {});
</script>
</polymer-element>
Overall Polymer feels like an evolution of nesting controllers in Angular and laying out code in React. Polymer has all the fancy two way binding that Angular has. Most people are using Polymer with Angular for routing. This sounds like a great idea. I definitely think it is a great way to make apps and I’d consider using it over straight Angular.
My next post will be comparing Angular and Polymer with Angular on the same code base.