August 25, 2018
  A Tour of myPrayerJournal: The Front End

NOTES:

  • This is post 2 in a series; see the introduction for all of them, and the requirements for which this software was built.
  • Links that start with the text “mpj:” are links to the 1.0.0 tag (1.0 release) of myPrayerJournal, unless otherwise noted.

Vue is a front-end JavaScript framework that aims to have very little boilerplate and ceremony, while still presenting a componentized abstraction that can scale to enterprise-level if required1. Vue components can be coded using inline templates or multiple files (splitting code and template). Vue also provides Single File Components (SFCs, using the .vue extension), which allow you to put template, code, and style all in the same spot; these encapsulate the component, but allow all three parts to be expressed as if they were in separate files (rather than, for example, having an HTML snippet as a string in a JavaScript file). The Vetur plugin for Visual Studio Code provides syntax coloring support for each of the three sections of the file.

Layout

Using the default template, main.js is the entry point; it creates a Vue instance and attaches it to an element named app. This file also supports registering common components, so they do not have to be specifically imported and referenced in components that wish to use them. For myPrayerJournal, we registered our common components there (mpj:main.js). We also registered a few third-party Vue components to support a progress bar (activated during API activity) and toasts (pop-up notifications).

App.vue is also part of the default template, and is the component that main.js attaches to the app elements (mpj:App.vue). It serves as the main template for our application; if you have done much template work, you’ll likely recognize the familiar pattern of header/content/footer.

This is also our first look at an SFC, so let’s dig in there. The top part is the template; we used Pug (formerly Jade) for our templates. The next part is enclosed in script tags, and is the script for the page. For this component, we import one additional component (Navigation.vue) and the version from package.json, then export an object that conforms to Vue’s expected component structure. Finally, styles for the component are enclosed in style tags. If the scoped attribute is present on the style tag, Vue will generate data attributes for each element, and render the declared styles as only affecting elements with that attribute. myPrayerJournal doesn’t use scoped styles that much; Vue recommends classes instead, if practical, to reduce complexity in the compiled app.

Also of note in App.js is the code surrounding the use of the toast component. In the template, it’s declared as toast(ref='toast'). Although we registered it in main.js and can use it anywhere, if we put it in other components, they create their own instance of it. The ref attribute causes Vue to generate a reference to that element in the component’s $refs collection. This enables us to, from any component loaded by the router (which we’ll discuss a bit later), access the toast instance by using this.$parent.$refs.toast, which allows us to send toasts whenever we want, and have the one instance handle showing them and fading them out. (Without this, toasts would appear on top of each other, because the independent instances have no idea what the others are currently showing.)

Routing

Just as URLs are important in a regular application, they are important in a Vue app. The Vue router is a separate component, but can be included in the new project template via the Vue CLI. In App.vue, the router-view item renders the output from the router; we wire in the router in main.js. Configuring the router (mpj:router.js) is rather straightforward:

  • Import all of the components that should appear to be a page (i.e., not modals or common components)
  • Assign each route a path and name, and specify the component
  • For URLs that contain data (a segment starting with :), ensure props: true is part of the route configuration

The scrollBehavior function, as it appears in the source, makes the Vue app mimic how a traditional web application would handle scrolling. If the user presses the back button, or you programmatically go back 1 page in history, the page will return to the point where it was previously, not the top of the page.

To specify a link to a route, we use the router-link tag rather than a plain a tag. This tag takes a :to parameter, which is an object with a name property; if it requires parameters/properties, a params property is included. mpj:Navigation.vue is littered with the former; see the showEdit method in mpj:RequestCard.vue for the structure on the latter (and also an example of programmatic navigation vs. router-link).

Components

When software developers hear “components,” they generally think of reusable pieces of software that can be pulled together to make a system. While that isn’t wrong, it’s important to understand that “reusable” does not necessarily mean “reused.” For example, the privacy policy (mpj:PrivacyPolicy.vue) is a component, but reusing it throughout the application would be… well, let’s just say a “sub-optimal” user experience.

However, that does not mean that none of our components will be reused. RequestCard, which we referenced above, is used in a loop in the Journal component (mpj:Journal.vue); it is reused for every request in the journal. In fact, it is reused even for requests that should not be shown; behavior associated with the shouldDisplay property makes the component display nothing if a request is snoozed or is in a recurrence period. Instead of the journal being responsible for answering the question “Should I display this request?”, the request display answers the question “Should I render anything?”. This may seem different from typical server-side page generation logic, but it will make more sense once we discuss state management (next post).

Looking at some other reusable (and reused) components, the page title component (mpj:PageTitle.vue) changes the title on the HTML document, and optionally also displays a title at the top of the page. The “date from now” component (mpj:DateFromNow.vue) is the most frequently reused component. Every time it is called, it generates a relative date, with the actual date/time as a tool tip; it also sets a timeout to update this every 10 seconds. This keeps the relative time in sync, even if the router destination stays active for a long time.

Finally, it’s also worth mentioning that SFCs do not have to have all three sections defined. Thanks to conventions, and depending on your intended use, none of the sections are required. The “date from now” component only has a script section, while the privacy policy component only has a template section.

Component Interaction

Before we dive into the specifics of events, let’s look again at Journal and RequestCard. In the current structure, RequestCard will always have Journal as a parent, and Journal will always have App as its parent. This means that RequestCard could, technically, get its toast implementation via this.$parent.$parent.toast; however, this type of coupling is very fragile2. Requiring toast as a parameter to RequestCard means that, wherever RequestCard is implemented, if it’s given a toast parameter, it can display toasts for the actions that would occur on that request. Journal, as a direct descendant from App, can get its reference to the toast instance from its parent, then pass it along to child components; this only gives us one layer of dependency.

In Vue, generally speaking, parent components communicate with child components via props (which we see with passing the toast instance to RequestCard); child components communicate with parents via events. The names of events are not prescribed; the developer comes up with them, and they can be as terse or descriptive as desired. Events can optionally have additional data that goes with it. The Vue instance supports subscribing to event notifications, as well as emitting events. We can also create a separate Vue instance to use as an event bus if we like. myPrayerJournal uses both of these techniques in different places.

As an example of the first, let’s look at the interaction between ActiveRequests (mpj:ActiveRequests.vue) and RequestListItem (mpj:RequestListItem.vue). On lines 41 and 42 of ActiveRequests (the parent), it subscribes to the requestUnsnoozed and requestNowShown events. Both these events trigger the page to refresh its underlying data from the journal. RequestListItem, lines 67 and 79, both use this.$parent.$emit to fire off these events. This model allows the child to emit events at will, and if the parent does not subscribe, there are no errors. For example, AnswerdRequests (mpj:AnsweredRequests.vue) does not subscribe to either of these events. (RequestListItem will not show the buttons that cause those events to be emitted, but even if it did, emitting the event would not cause an error.)

An example of the second technique, a dedicated parent/child event bus, can be seen back in Journal and RequestCard. Adding notes and snoozing requests are modal windows3. Rather than specifying an instance of these per request, which could grow rather quickly, Journal only instantiates one instance of each modal (lines 19-22). It also creates the dedicated Vue instance (line 46), and passes it to the modal windows and each RequestCard instance (lines 15, 20, and 22). Via this event bus, any RequestCard instance can trigger the notes or snooze modals to be shown. Look through NotesEdit (mpj:NotesEdit.vue) to see how the child listens for the event, and also how it resets its state (the closeDialog() method) so it will be fresh for the next request.

 

That wraps up our tour of Vue routes and components; next time, we’ll take a look at Vuex, and how it helps us maintain state in the browser.


1 That’s my summary; I’m sure they’ve got much more eloquent ways to describe it.

2 …and kinda ugly, but maybe that’s just me.

3 Up until nearly the end of development, editing requests was a modal as well. Adding recurrence made it too busy, so it got to be its own page.

Categorized under , , , , , ,
Tagged , , , , , , , , , , , ,


August 24, 2018
  A Tour of myPrayerJournal: Introduction

Recently, we released version 1.0 of myPrayerJournal, a minimalistic prayer journaling application. This series aims to provide a tour of the code, with several stops along the way:

From a technical perspective, this application was going to be a learning experience. We knew we wanted to use a Single Page Application (SPA) framework with an API; we’d built APIs before, but had yet to build a SPA. For front-end frameworks, we started with Angular, went through Aurelia and Elm, then decided on Vue. For the back-end API, we started with Suave, then went live on Node.js with Koa; later, we moved it to Go, and after .NET Core 2.1 was released, landed on Giraffe. The “learning experience” part was a success; through all these attempts, we utilized 5 different languages and 3 different database access techniques.

To understand the requirements, a short explanation of the process will help. “Prayer journaling” is a discipline where a person will write down the things for which they are praying; this provides a defined list to help guide their prayer, and helps them not forget things. Then, as the situation changes, they can record updates, through to the resolution of the situation (also called the request being “answered”). This discipline not only helps to focus efforts, it also provides a record of requests and answers. Although people have successfully used a notebook, or something similar, for a long time, that approach does have some downsides:

  • For long term requests, you can run out of room for updates.
  • A physical journal can only be in one place at one time.
  • Answered requests coexist with unanswered requests, so you have to flip pages past them.
  • Books can end up under stacks of other things, falling victim to “out of sight, out of mind.”

Looking to address some of those, the initial requirements started as the first three bullets below. The remaining requirements emerged through using the application as it was being developed.

  • List unanswered requests, in a way that they can be marked as prayed or answered, and be updated
  • List answered requests, and allow full requests (and their history) to be viewed
  • Do the above in a way that will not be distracting
  • Allow notes to be recorded for a request; not every update on a situation requires a change in the verbiage of the request
  • Allow requests to be “snoozed” (removed from the journal, with a specified date when they will reappear), and list snoozed requests so that the snooze can be expired (returning the request to the journal immediately)
  • Allow requests to be prioritized (this became the request recurrence feature)

Armed with these requirements, we will pick up next time with a look at the Vue front end.

Categorized under , , , , , , , , , ,
Tagged , , , , , , , , , , , , , , ,


July 8, 2018
  F# Options with EF Core

UPDATE: The code described below is now available as a NuGet package.

The 2.1 release of Entity Framework Core brought the ability to do value conversions. This is implemented through an abstract class, ValueConverter, which you can implement to convert a data type. They also provided several built-in converters that you don’t have to write, such as storing enums as strings. To use a value converter, you provide a new instance of it and attach it to a property in your model’s OnModelCreating event.

F# provides an Option<'T> type as a way to represent a value that may or may not be present. There are many benefits to defining optional values as 'T option rather than checking for null; you can read all about it if you’d like.

As I was working on a project, I already used Option.ofObj to convert my possibly-null results from queries to options; at the field level, though, I was working with default values. Could I use this new feature to handle nullable columns as well? As it turns out, yes!

Here is the code for the value converter.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
module Conversion =

open Microsoft.FSharp.Linq.RuntimeHelpers
open System
open System.Linq.Expressions

let toOption<'T> =
<@ Func<'T, 'T option>(fun (x : 'T) -> match box x with null -> None | _ -> Some x) @>
|> LeafExpressionConverter.QuotationToExpression
|> unbox<Expression<Func<'T, 'T option>>>

let fromOption<'T> =
<@ Func<'T option, 'T>(fun (x : 'T option) -> match x with Some y -> y | None -> Unchecked.defaultof<'T>) @>
|> LeafExpressionConverter.QuotationToExpression
|> unbox<Expression<Func<'T option, 'T>>>

type OptionConverter<'T> () =
inherit ValueConverter<'T option, 'T> (Conversion.fromOption, Conversion.toOption)

The Conversion module contains the functions that we’ll need to provide in the ValueConverter constructor. (With the way class inheritance is coded in F#, and the way ValueConverter wants its expressions in its constructor, this is a necessary step. I would have liked to have seen a no-argument constructor and overridable properties as an option, but I’m not complaining; this is a really great feature.) Within those functions, we make use of code quotations, then convert the quotation expressions to Linq expressions.

One other note; in the toOption function, if we used Option.ofObj instead of box x, the code would not support value types. This means that things like an int option field wouldn’t be supported.

Now that we have our option converter, let’s hook it into our model. In my project, each entity type has a static configureEF function, and I call those from OnModelCreating. Here’s an abridged version of one of my entity types:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[<CLIMutable>]
[<NoEquality>]
[<NoComparison>]
Member =
{ /// ...
/// E-mail format
format : string option
/// ...
}
with
/// ...
static member configureEF (mb : ModelBuilder) =
/// ... HasColumnName statements, etc.
mb.Model.FindEntityType(typeof<Member>).FindProperty("format").SetValueConverter(OptionConverter<string> ())
|> ignore

This line of code finds the type within the model, the property within the type, and provides the new instance of our option converter to it. In this entity, a None here indicates that the member uses the group’s default e-mail format; Some would indicate that they’ve specified which format they prefer.

That’s all there is to it! Define the converter once, and plug it in to all the optional fields; now we have nullable fields translated to options by EF Core. “Magic unicorn,” indeed!

(Credits: Many thanks to Jiří Činčura for the excellent value conversion blog post and Tomas Petricek for his Stack Overflow answer on converting quotation expressions to Linq expressions.)

Categorized under , , , ,
Tagged , ,


September 9, 2017
  Writing a Hexo Tag Plugin

In the process of migrating Daniel’s Weekly Devotions to Hexo, we ran into a problem that we felt sure a tag plugin could solve.

The Problem

Jekyll‘s Markdown parser follows the original one, where text within an HTML tag is not processed. This can be the desired behavior in many cases, as you could put what would otherwise be translated as Markdown between HTML tags, and the parser/renderer will leave it alone. One of the common features, used multiple times in most posts, are links to Scripture references and blocks of quoted text. We had an include to automate the links, but we needed a special class on the <blockquote> tag, which meant that all Scripture blockquotes could not use Markdown (or end up with “smartified” quotes and such; we had to use the HTML entities within these quotes.) We also included the verse numbers as superscripts within the quoted text; more tags.

It looked something like this… (the “ref” CSS class turns the text red)

1
2
3
4
5
6
7
8
<blockquote class="bible">
<p>
<sup>11</sup> &hellip;And Jesus said, <span class="ref">&ldquo;Neither do I condemn you;
go, and from now on sin no more.&rdquo;</span>
</p>
<cite>&mdash; <a href="https://www.biblegateway.com/passage/?search=John+8:11&amp;version=ESV"
title="Read John 8:11 (ESV) at Bible Gateway">John 8:11</a>b <em>(ESV)</em></cite>
</blockquote>

If you’ve ever edited Markdown, you’ll recognize how jarring all that HTML code is within the flow of the otherwise-regular text; and look at all those entities!

The Solution

We looked through at the Hexo Plugin List to find some examples, and began working towards writing a plugin to handle both the links (the part within the <cite> in the example above) as well as the entire blocks of quoted text. Some tags, like the {% codeblock %} tag, have a start tag and an end tag ({% endcodeblock %}); others, like the {% youtube %} tag, just pass arguments with the tag. (You can see all the default tags here.) Hexo passes two arguments to the tag plugin - the arguments within the (start) tag, plus the content (which is blank for tags that don’t have an end tag). The returned value from the plugin call is substituted in the document.

For generating a link, that is pretty easy; it could be an inline tag, and it’s just a matter of parsing the arguments and forming a link. For the quotes, we need to make sure that we include the content, and Hexo provides a way to run that content through the Markdown renderer. We are converging on a solution!

Hexo will pick up and execute any .js files in the scripts directory of the site as its generating it, so the first efforts were just local to that repo. The reference link looked something like this…

esv.js
1
2
3
4
5
6
7
8
9
hexo.extend.tag.register('esv', (args, content) => {
// option parsing with RegEx, similar to the way their tags do

let reference = arg.trim()
let urlReference = reference.split(' ').join('+')

return `<a href="https://www.biblegateway.com/passage/?search=${urlReference}&amp;version=${version}" `
+ `title="Read ${reference} (${version}) at Bible Gateway">${reference}</a>${extraText}${versionText}`
})

…which let the Markdown document go from…

1
2
<a href="https://www.biblegateway.com/passage/?search=John+8:11&amp;version=ESV"
title="Read John 8:11 (ESV) at Bible Gateway">John 8:11</a>b <em>(ESV)</em>

…to…

1
{% esv John 8:11 extra:b show-version %}

We refactored the link code to be version-agnostic and extracted it from the tag.register function so that we could reuse that for the blockquote citation. This made the local version of the blockquote look something like this:

bible.js
1
2
3
4
hexo.extend.tag.register('bible', (args, content) => {
let text = hexo.render.renderSync({ text: content, engine: 'markdown' })
return `<blockquote class="bible">${text}<cite>&mdash; ${generateRef(args)}</cite></blockquote>`
})

This means that the blockquote can support all of the arguments the inline reference did. We also switched out the marked Markdown processor for the markdown-it one, which lets us do superscripts by using the ^ character. Revisiting our example under “The Problem,” our Markdown source to generate the same blockquote is now:

1
2
3
4
{% bible John 8:11 extra:b show-version %}
^11^...And Jesus said, <span class="ref">"Neither do I condemn you; go, and from
now on sin no more."</span>
{% endbible %}

The Plugin

The plugin is available on npm, is fully tested, and its source is open. If you use Hexo, and wish to cite Scripture references in your posts with links where readers can see the text for themselves - enjoy!

Categorized under , , , ,
Tagged , , , ,


September 2, 2017
  Mapping Categories and Tags with Hexo

This blog moved today from Jekyll to Hexo (and it’s now open source as well). One of the final issues I had when wrapping up the conversion was how to handle categories and tags that do not necessarily have a slug that can be naturally derived from the name. Take the “C#” category, for example. The normal slug for that category would be c, which is an entirely different programming language; interestingly, “C++” will also normally get its slug as c.

Within Hexo’s default _config.yml file, there are two empty items named category_map and tag_map; their comments allude to a mapping, but I could not find what the proper syntax was for those items. We hopped onto the Hexo Gitter chat and asked the question, and someone pointed us to this issue. To define a mapping, create an item under either the category_map or tag_map top-level item. The maps for this site, as they currently are, look like this:

_config.yml
1
2
3
4
5
6
7
category_map:
C++: c-plus-plus
C#: c-sharp
.NET: dot-net
tag_map:
c#: c-sharp
.net: dot-net

As you can see by hovering over the links in the sidebar, “Programming > .NET > C#” ends up with a URL ending with /programming/dot-net/c-sharp/, which is exactly what we were looking for.

Categorized under , ,
Tagged , , , , ,


February 18, 2017
  Generating a Jekyll Site on Mercurial (Hg) Push

As we mentioned in our last post, we plan to share aspects of how we moved to Jekyll. This is the first of these posts.

Background

With a database-based solution, updating is easy; when the user updates content through the user interface, the new content is served when the page or post is requested. However, with a static site, an “update” is technically any change to the underlying files from which the site is generated. Typically, though, this is marked by source control commit and push to a master repository. GitHub Pages, the product for which Jekyll was developed, uses this as a flag to regenerate the site. We weren’t using GitHub*, though - we were using Mercurial (Hg) for our source code control, with the master repository on a different server than the one from which the site is served.

* There were a few reasons we did not wish to host our sites using GitHub, none of which are pertinent to how this works.

Options

With the need to regenerate the site after each site’s master repository receives a push, there were a few different options we considered.

  1. When a push occurs, regenerate the site on the Hg server, then use scp to delete the old files from and copy the new files to the web server.
  2. Set up a sandbox on the Hg server that updates and regnerates each time a push occurs, and run rsync on the web server to check for updates every so often.
  3. When a push occurs, notify the web server, and have it regenerate the site.

The first option has the potential to run afoul of SSH rate limits, plus has the potential to require much more data transfer than option 3. The second option had the advantage of running a process local to the Hg server, but would have required disk space utilization that we didn’t really need; and, as Jekyll regenerates all the pages in a site, rsync would have likely ended up transferring all the data for every update anyway, losing one of its benefits. The third option required Jekyll to be installed on the web server, and uses it for processing, potentially taking cycles that could be used to serve web pages.

Eventually, we decided to go with option 3.

Script All the Things

On the Hg server, in the master repository for each site, we put the following in .hg/hgrc (the following examples are for this site):

hgrc
1
2
[hooks]
incoming = /opt/jobs/notify.tech-blog.sh

…and then, notify.tech-blog.sh

notify.tech-blog.sh
1
2
#!/bin/bash
ssh user@web.server touch /opt/jobs/jekyll/.tech-blog

That is the only logic required on the Hg server. Now, over on the web server, we need logic to regenerate the site and make it live. Since we have multiple sites, we wrote a script that has a few variables, so it could be duplicated for other sites. The following is tech-blog.sh:

tech-blog.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
##
## DJS Consulting Tech Blog Jekyll Build Script
##
## This will check out, build, and replace a Jekyll-generated site. Just update
## the parts under the "Env" heading for the specific site.
##

## Env
REPO=jekyll.tech-blog
DEST=/path/to/public/techblog
TRIGGER=.tech-blog
## /Env

cd /opt/jobs/jekyll

if [ -e $TRIGGER ]
then
rm $TRIGGER

## Check out the site and build it
hg clone ssh://user@hg.server/HgPath/$REPO $REPO
cd $REPO
jekyll build

## Copy it to the proper directory
cd _site
rm -r $DEST/*
cp -r * $DEST

## Clean up
cd ../..
rm -r $REPO
fi

This script isn’t perfect; it needs to check the exit code from the Jekyll build process before whacking the current site (and notifying for a failed build would be a nice addition). However, with Jekyll being the same on both development and production, and a single committer, this is fine for our purposes.

Finally, each script needs to be run to check for the presence of the semaphore (or TRIGGER, as the script calls it). The following cron definition will check every 4 minutes for a change.

crontab
1
*/4 *   *   *   *    /opt/jobs/jekyll/tech-blog.sh > /dev/null

Conclusion

Overall, we’re pleased with the results. The inter-server communication is light, only requiring one initiated ssh connection from each server, so we won’t run afoul of rate limits. With the work being done on the destination server, the amount of time where there are no files in the directory (between the rm -r $DEST/* and the time the cp -r * $DEST finishes) is very short; it would have been much longer if the directory were being repopulated across the network, or more complex if we added a staging area on the web server. Each piece can be run separately, and if we’ve committed a post with a future date, we can run the same touch command to make that post appear.

Next time, we’ll discuss our experiences converting a non-WordPress site.

Categorized under , ,
Tagged , , , , , , , , ,