A mermaid.js Plugin for Vuepress

  • Reading time: 2 min
  • Published 1 month ago

Since the last time I wrote about integrating Mermaid with Vuepress the latter matured to version 1.0. With that came a much more refined plugin interface - or maybe that existed before and I just didn't notice it in the docs. In the meantime, I have been implementing that previous approach in a growing number of documentation sites and figured that having this as a plugin to a) reduce the hassle of getting it to work to basically zero and b) stop copying a bunch of code around would be a good idea.

So, without further ado I present vuepress-plugin-mermaidjs. When you do the search on npm, you will unfortunately find another package named very similar (vuepress-plugin-mermaid). One might ask now, "eFrane, why didn't you just contribute to that one instead, wouldn't that have been the healthier choice for the ecosystem?" Yes, it would have. Alas, the GitHub Repository for that plugin is empty and it doesn't have a readme and wasn't updated in over a year. All in all, that feels like an abandoned package to me. So that's why I decided to publish my take as a package. 

NPM Package: https://www.npmjs.com/package/vuepress-plugin-mermaidjs
GitHub Repo: https://github.com/eFrane/vuepress-plugin-mermaidjs

Symfony Console Additions v0.6.0

  • Reading time: 3 min
  • Published 1 month ago

I released efrane/console-additions v0.6.0 today. More than a year after the last bigger release this probably could have been a v1.0. However, I still feel like there's some fundamentals I do with Symfony's Console component in my everyday work which I would like to extract into a reusable form in this library.

So, what's changed?

For one, this development episode was focused on reorganising the code. There's still very few files in the repository but that doesn't mean that there wasn't room for optimisation. The result of this is, that the tests folder now resemble the folder structure of the src folder, thus making it easier to find the corresponding test file.

The bigger change is a partial rewrite of command batches. Before, the Batch class internally managed every possible action as a combination of strings and weird arrays. This type-conglomerate lead to a quite a few ugly places in the code which felt like they might break at a moment's notice. Since this version, Batch internally relies on Action classes which know how to handle themselves. This also means, that Batch just became that much more powerful since all Batch wants to know about them is how to represent them as a string and a way to start their execution:

interface Action
{
    public function execute(OutputInterface $output): int;
    public function __toString(): string;
}

Usable actions are now:

  • StringCommandAction - This handles executing console commands given a full command signature
  • InstanceCommandAction - No need to do string parsing of a signature? Pass a command instance to this one!
  • MessageAction - This is your basic echo action. Especially when PHP-izing former shell scripts, this can come in quite handy
  • ProcessAction - Pass a configured Process from the Symfony Process component to this one
  • ShellAction - Just want to execute a shell command and don't feel like configuring the process yourself? This one is there for your convenience!

All of the built-in actions have convenience methods on Batch, e.g. $batch->addProcess() or $batch->addMessage().

Along with these internal updates came the jump to PHP 7.0 or newer as a language dependency and Symfony Components 3.4 or newer are required to work with this library now. The latter also means that string parsing for commands in $batch->addShell() has been deprecated and will be removed in the next release. I highly encourage any user of this library to switch to the array-Syntax.

Adding mermaid.js support to Vuepress

  • Reading time: 3 min
  • Published 5 months ago

I recently set up Vuepress for the first time to create a documentation for a new library I am working on (more on that in a future post). Since that library will do a few things that might appear sort of magical it seems like a good idea to add a few explanatory graphs to the documentation.

Now, what tool combines markdown-friendliness with creating graphs? Yep, you guessed it: Mermaid.js.

There are numerous ways of making Vuepress do things it can't by default:

  • You can overwrite the whole theme
  • You can add Markdown-It plugins for additional markdown processing
  • You can add global components

The long story here would be to elaborate on why overwriting the theme is not an option and the plugin route didn't pan out. But let's not bore ourselves with that story. So yeah, I created a new global component in .vuepress/components/Mermaid.vue and worked away. However, it's unfortunately not exactly that simple. Since Vuepress is a SPA or rather even an PWA, rendering works a little bit different. Which in short means that it's very easy to get something to show up on first load of a component but there's a little work involved in having things work after navigation. The technical background to this can be referenced in the vue-router Documentation.

What I ended up with is rendering the mermaid graphs to SVG and then rendering the SVG in the component's render() Function:

import mermaid from 'mermaid/src/mermaidAPI'

let graphIdCounter = 0

export default {
  name: 'Mermaid',
  data() {
    return {
      svg: undefined,
      graph: this.$slots.default[0].text
    }
  },
  render(h) {
    return h('div', { domProps: { innerHTML: this.svg }})
  },
  mounted() {
    mermaid.initialize({ startOnLoad: true })

    let renderDiv = document.createElement('div')
    document.body.appendChild(renderDiv)

    mermaid.render(
      'mermaid' + ++graphIdCounter,
      this.graph,
      (svg) => {
        this.svg = svg
        document.body.removeChild(renderDiv)
      },
      renderDiv
    )
  }
}

Alas, this was not quite the end of it. I would have hoped that I'd be off to the races with yarn add mermaid. That's not even close. Here's the full list of dependencies you'll have to add:

  • mermaid
  • sass-loader
  • node-sass

After this, adding mermaid graphs anywhere in the documentation markdown is as easy as

<mermaid>
graph TD
mermaid-->isAwesome
isAwesome-->mermaid
</mermaid>

Which then renders to:

Update (2019-10-16): I published an npm package that provides the <mermaid>-Component.

Get the number of days in a month with moment.js

  • Reading time: 1 min
  • Published 3 years ago
var date = moment();
var currentMonth = date.month();

var daysInMonth = 31;         // Every month has 31 days
if (currentMonth % 2 == 1) {  // except every other month
    if (currentMonth == 1) {  // and then there is februrary
        if (date.isLeapYear()) { 
            daysInMonth = 29; // which has 29 if it's a leap year
        } else {
            daysInMonth = 28; // and 28 if it isn't.
        }
    } else {
        daysInMonth = 30;
    }
}

Using Git Hooks for code style fixes

  • Reading time: 1 min
  • Published 4 years ago

Pretty much every modern language has an established Coding Style Guide. For PHP, this is PSR-2. As with other style guides of this sort, there is a tool for automatically applying the appropriate fixes to one's code

I only recently finally switched to using PSR-2 and to make sure I wouldn't accidentally miss running the style fixer, I added this little snipped to my .git/hooks/pre-commit-Hook:

modified_php=$(git ls-files --modified | grep "\.php$")

for f in $modified
do
    php-cs-fixer fix --level psr2 $f
done

Redirecting in Laravel Form Requests

  • Reading time: 1 min
  • Published 4 years ago

While Form Request Validation in Laravel 5 usually does exactly what it's supposed to do, I recently had to append a location hash to the redirect URL. Turns out that's as simple as overwriting the FormRequest's getRedirectUrl()-method:

protected function getRedirectUrl()
{
  $url = $this->redirector->getUrlGenerator();
  return $url->previous().'#hash';
}

Dependency Injection with inherited Controllers in Laravel 5

  • Reading time: 3 min
  • Published 4 years ago

Usually, when setting up my controllers in a Laravel 5 application, I end up creating one or several base controllers for different entry points (e.g. APIController, FrontendController, ...) While I deem this a good practice, it can lead to problems with Laravel's otherwise fantastic Dependency Injection. To illustrate, take a look at the following:

// FrontendController.php

class FrontendController extends Controller
{
  public function __construct(\Illuminate\Contracts\Events\Dispatcher $dispatcher)
  {
    $dispatcher->listen('composing: frontend.base', function ()
      {
        // handle root view compose
      }
    );
  }
}

// BlogController.php

class BlogController extends FrontendController
{
  protected $repo = null;

  public function __construct(TextRepository $repo)
  {
    $this->repo = $repo;
  }
}

As some IDEs and testing tools would say: There are multiple problems!

  1. The BlogController's constructor does not call the parent.
  2. The constructors do neither match nor can all required parameters be handed down.
  3. Actually there is no number three but lists with two points always seem awkward.

Look at the mess I made!

Not calling the parent constructor which probably does important things? Bad idea. Could be fixed with changing the constructor of BlogController to:

public function __construct(TextRepository $repo)
{
  $this->repo = $repo;
  parent::__construct();
}

But then again that's not quite right too, is it? It should be more along the lines of this:

public function __construct(\Illuminate\Contracts\Events\Dispatcher $dispatcher, TextRepository $repo)
{
  $this->repo = $repo;
  parent::__construct($dispatcher);
}

Which is ugly as hell. Let alone remembering that for all of the other descendants of the FrontendController.

Let's make that pretty

Right around the time I realized that I didn't want to write that second version even once, I thought "hey, Eloquent models have this boot() method. That could be a way to go."

Said and done. I refactored my code to this:

// FrontendController.php

...
  public function __construct(\Illuminate\Contracts\Events\Dispatcher $dispatcher)
  {
    // the dispatcher stuff

    if (method_exists($this, 'boot')) $this->boot();
  }

// BlogController.php

public class BlogController extends FrontendController
{
  public function boot(TextRepository $repo)
  {
    $this->repo = $repo;
  }
}

Which, of course, did not work. And - as too often is the case - the error was all too obvious to me once I had committed the crime. So, after a while, I figured that if Laravel can resolve class names to inject from type hints, why shouldn't I. Thus followed some research on method reflection and I ended up with this:

// FrontendController.php

...
  public function __construct(\Illuminate\Contracts\Events\Dispatcher $dispatcher)
  {
    // the dispatcher stuff

    if (method_exists($this, 'boot'))
    {
      // resolve the boot dependencies
      $reflect = new \ReflectionMethod($this, 'boot');
      $reflectedParameters = $reflect->getParameters();

      $bootArguments = [];

      foreach ($reflectedParameters as $reflectedParameter)
      {
        preg_match("/.*<required> (.+) \${$reflectedParameter->getName()}/", $reflectedParameter, $typeHint);

        if (count($typeHint) == 2)
        {
          $className = $typeHint[1];
          $resolved = app()->make($className);

          array_push($bootArguments, $resolved);
        } 
      }

      call_user_func_array([&$this, 'boot'], $bootArguments);
    }
  }

And this, finally, works just as expected. One thing to note though: Bad things will happen if you have anything but type-hinted to-be-resolved classes as attributes to the boot() method.

Deep Linking in Eloquent Models

  • Reading time: 1 min
  • Published 4 years ago

Sometimes, when designing data models, it turns out that access to a property of a related model might be handy from the current model. For the case of one-to-many relations, Laravel’s Eloquent already comes with the hasManyThrough()-method. But what if it’s a one-to-one relation? And what if the attribute one wants to have access to is up a few levels in the model hierarchy? Well, turns out that can be pretty easily done, too.

Assuming, three models A, B, C all related, e.g. B has a_id, C has b_id and C would benefit from direct access to A, that can be achieved through Eloquent with the following code:

class C extends Model
{
    ...

    public function b()
    {
        return $this->belongsTo('B');
    }

    public function a()
    {
        return $this->belongsTo('A');
    }

    public function getAIDAttribute()
    {
        return $this->b->a_id;
    }

    ...
}

Of course, one might also use this to fetch a specific attribute instead of creating a complete relation. Keep in mind though, that this may cause serious performance issues since the ORM has to fetch all referenced entities before it can return anything.

Lightweight links for Prism.js

  • Reading time: 1 min
  • Published 4 years ago

Although Prism.js provides a very good autolinker plugin, it did not quite fit my needs for a recent project. Here's what I came up with as a stupidly simple replacement that's not as sophisticated (i.e. does not do links in Markdown and such stuff) but works perfectly well with to-be-highlighted JSON.

Prism.hooks.add('wrap', function(env) {
    if (env.type == 'string', env.content.match(/http/))
    {
        env.content = "<a href=\\"" 
                    + env.content.replace('"', '') 
                    + "\\">" 
                    + env.content 
                    + "</a>";
    }
});

Just add this to your site's scripts and everything will be better.