Ryan McGrath is an Entrepreneur, Engineer, Designer, Author, and Teacher. Buzzwords, eat your heart out.

Ramblings, code, and more. Find me around the world for free coffee.

Fri 29 June 2018

Rust, or: What's the Deal with GUIs?

Ah, a two year writing gap. Life can be distracting sometimes. Get ready for a rust-y blog entry!

Yeah that was pretty bad, even by my standards.

Two Years and GUIs

One of the big (and kind of annoying) discussions that's been bantered about in the tech world over those two years has been whether Electron, a web-browser-masquerading-as-native-app project, is a plague on society or not. Resource wise, it's probably got some argument there, but in terms of productivity it's hands down the king of the castle - effectively trading memory pressure and less focus on platform conventions in favor of just shipping something out the door. You can open and scan any thread on Hacker News or /r/programming and find people bemoaning this repeatedly.

I wouldn't keep those threads open, for what it's worth - there's generally little worth reading in them, beyond people on both sides completely misunderstanding one another. The tl;dr for pretty much every thread is: more traditional native developers tend not to understand web development, or they look down upon it as easier and less worthy of respect. Web developers tend to favor shipping faster to a wider audience, and being able to implement things across (just about) any platform. You'll see some native developers go on about Qt/GTK and co as ideal native approaches (they're not), or advocating tripling your development efforts and re-implementing a UI across n platforms (why would you bother to do this for most commerical projects? Do you enjoy wasting time and money?).

With that all said, I had reason to build some stuff in Rust recently, and wound up wanting to throw together a basic UI for it. Rust is a wonderful language, and I genuinely enjoy using it, but it's very clear that the community doesn't have a GUI focus. I figured I'd end up basically packaging together some web view (Electron or [insert your choice here]), but wanted to see how easily I could finagle things together at a native level. Since I haven't written anything in some time, I figured this would be a good fit for a super short post.

Going Down a GUI Rabbit Hole

I use a Mac pretty much exclusively, and am pretty familiar with Cocoa, so jumping to that from Rust seemed pretty reasonable. It's also ridiculously simple, thanks to Objective-C - the C interop in Rust makes this fairly transparent, provided you're willing to dabble in the unsafe side of things. What I came up with was a mix of rust-objc and rust-cocoa, from core-foundation-rs, along with some usage of Serde for a quickly hacked together CSS framework.

A basic app would look like this:

The declarative approach to the UI is inspired by React (and co). I threw the resulting package on Github as Shinekit, in case anyone out there finds it interesting and would want to hack on it themselves. Since it's basically hacking through Objective-C, I've got a theory that it could be wired up to Microsoft's port of Objective-C for Windows, which ultimately creates native UWP apps.

Of note, some things for GUI programming are a pain in the ass in Rust - e.g, parent and child relationships. Shinekit pushes that stuff over to Cocoa/Objective-C where possible, rather than tackling it head-on. In a sense, it's cheating - but it creates a slightly nicer API, which (in my experience) is important in UI development.

What's Next?

Well, if you're a native fan, you're gonna hate to hear that yes, I did wind up just throwing a web browser into the mix to ship. Goal wasn't to build a full GUI framework.

With that said... time permitting I'll probably hack on this some more, as the existing GUI options in Rust don't really fit my idea of what a GUI framework should look and function like. As Rust becomes more popular, a decent GUI approach (even for small toolkit apps and the like) would be great to have. If or when this becomes more mature, I'd throw it up on crates.io as something to be used more widely.

Tue 22 March 2016

A Deep Dive into PL/v8

Back in August, Compose.io announced the addition of JavaScript as an internal language for all new PostgreSQL deployments. This was thanks to the PL/v8 project, which straps Google's rocket of a JavaScript engine (V8) to PostgreSQL. This got me thinking - PL/v8 offers a rich and featureful way to write functions in PostgreSQL. Let's take a look at what it offers by building a mini JavaScript module system on top of it, complete with basic support for the CommonJS API.

Enabling PL/v8 on Your Deployment

First thing's first: you'll need to enable it by creating the extension. The quickest and easiest way to do this is likely using psql from a terminal (below), but if you prefer using another tool it shouldn't pose any problems.

PL/v8 also supports two extra JavaScript "dialects" - CoffeeScript, and LiveScript. CoffeeScript offers a more Ruby-esque syntax, and LiveScript is a more functional successor to CoffeeScript. We'll be using pure JavaScript in this article, but if you'd like to use either of these languages you'll need to create their extensions below as well.

JavaScript in PostgreSQL: The Basics

Creating a function using PL/v8 looks like any other PostgreSQL function, with the exception of a language specifier change. Take the (basic) example below: we're simply incrementing each int in an Array by 2, and returning it as pure JSON.

JavaScript isn't technically a functional programming language, but it has many elements of one. Those features shine here - mapping over results is incredibly expressive and easy to work with. While you can't get all crazy with any ES6 code yet (the version of V8 currently used is a bit older), pretty much all the good parts of ES5 are up for grabs. Couple that with the native JSON support PostgreSQL offers, and you can get many of the features (e.g, Schema-less storage) from Document-oriented databases like MongoDB or RethinkDB.

As far as working with SQL data in JavaScript goes, it's relatively straightforward. PL/v8 will convert most database types automatically for you, including polymorphic types (anyelement, anyarray, anyenum and anynonarray) and bytea (through JavaScript's Typed Arrays). The only real "gotchas" to be aware of are that any Array you return must be flattened (unless you're returning JSON, in which case go for it), and you can't create your own Typed Arrays for use in functions; you can, however, modify and return the ones passed to your function.

While not strictly a "gotcha", the ever-so-fun issue of context in JavaScript is present in PL/v8. Each SQL function is called with a different this value, and it can be confusing at first. SQL functions do share context though, as far as accessing globally declared variables and functions. As long as you pay attention to scoping issues, you can avoid context binding issues and write reusable code.

Hacking Together a Module System

V8 is synonymous with Node.js for many developers, and inevitably the question of importing modules comes up. There is no baked-in module system, but we can simulate one using some of the features of PL/v8. It's important to note that while this works, we're in a sandboxed environment - modules involving network calls or browser-related functionality won't work. We'll be simulating the CommonJS module.exports API though, so many modules should "just work" right off npm.

The first thing we'll need is a table to store our module source(s) in. We really only need two columns to start: the module name (module), and the source code (source). To sweeten the deal we'll add an autoload column (autoload) that we'll use to dictate whether a module should be transparently available at all times.

We'll need a function to handle wrapping the require() API, and ideally we'll want a cache for module loading so we're not pulling from a database table every time we require a module. The global plv8 object has a few things we'll make use of here - it brings important functionality like executing statements, subtransactions, logging and more to the table. We'll be eval()ing the source for each module, but we wrap it in a function to ensure nothing leaks into the global scope. Our autoloading of certain modules also takes place in this function, just to prime the module cache for later use.

Now in terms of using this, we have that dangling context problem to consider - how do we make sure that require() is available to each PL/v8 function that needs it? Well, it just so happens that PL/v8 supports setting a specific function to run before any other functions run. We can use this hook to bootstrap our environment - while ordinarily you could set it in your config files, you don't have access to those on Compose. We can, however, SET this value every time we open a connection. As long as you do this prior to any function call (including CREATE FUNCTION itself) you'll have access to require().

Let's try it out by throwing together a module that implements the Fisher-Yates shuffle algorithm - we'll name the module "shuffle", to keep things simple, and go ahead and set it to autoload.

Now we should be able to require() this! We can try it immediately - a simple table of people and a super readable random_person() function works well.

See how clean that becomes? A shuffle algorithm is only one application - modules like lodash are a prime candidate to check out for using here.

What Are You Waiting For?

Writing PostgreSQL functions in JavaScript can provide a few wins - if your stack is primarily JavaScript, there's less context-switching involved when dealing with your database. You can reap the benefits of Document-oriented, Schema-less databases while leaving yourself the option to get relational as necessary, and the native support for JSON marries perfectly with JavaScript. Modules can tie it all together and provide a method for organizing code that makes sense - you'll feel right at home.

Even more in-depth documentation on PL/v8 can be found over on the official docs. Try it out today!

Thu 04 June 2015

RedNoise, a Django-centric WhiteNoise addon

I've developed with Django for a number of years - out of all the frameworks I've ever used, it strikes the best balance between "let me get stuff done" and "don't try to provide me too much, get out of my way when I say so". I was ecstatic when WhiteNoise was released, as it solved a very annoying part of the development process with Django - static files. Rather than uploading things to S3 (ala the old Storages route) it's now just easier to toss CloudFront in front of your files and let uwsgi serve it up. Since WhiteNoise debuted, I've used it on a few different projects, and over time I found myself wanting a few things that it lacked - django-rednoise provides them.

Feel free to read on for details, or go find it on GitHub! I published it as a separate module as the goals differ from the established WhiteNoise goals, but I'd be absolutely open to it being merged or pulled from.

DEBUG Please

WhiteNoise has a Django-specific module that you can use, but it's essentially geared towards production use-cases. I tend to prefer having a development setup that mimics my production setup; patching Django urls to load static or media files in development just feels clunky to me.

RedNoise respects whether Django is in DEBUG mode or not. If DEBUG is set to True, RedNoise will mimic Django's default static-file loading pattern, so you don't need to reload the entire server just to debug some frontend issues.

Serve Media Files

WhiteNoise doesn't support serving user-uploaded media files, but I wound up having to throw together a CMS at one point and ran into this limitation. Content authors and editors wanted to be able to upload photos, but I didn't want to have to keep a separate S3 bucket for it all. RedNoise will serve media files, hooking into the associated settings.py parameters to make it "just work".

Should You Use It?

I've run it for a bit with no real issues; I would say that whatever you do, tossing a CDN in front of it all is pretty efficient. Provided you do that, the CDN should absorb most of the requests, leaving your server to do its thing.

If you use WhiteNoise, and wish you had the above features, give django-rednoise a shot. It's just a pip install away.

Wed 08 January 2014

Recording Live Audio Streams on iOS

Some people might consider this strange, but I'm still a fan of radio. It's not my 24/7 music source, but I find myself getting annoyed with algorithms trying to predict music that I'd like and ultimately failing. In addition to music, you've got news and sports radio - getting a large mix is fun.

These days tons of radio stations have moved their streams to be listen-able online, and recently I found myself wishing I had something that I could use to record these radio streams on a whim. My main device is an iPhone 5; sadly, I didn't find anything particularly pleasing or enjoyable to use in the App Store, so I set out to see about throwing together my own. What follows is a breakdown of a (relatively) easy way to download live audio streams using AVFoundation and Core Audio. It assumes you've got a working knowledge of Objective C, as well as a working knowledge in terms of building iOS apps in general.

Disclaimer

This code is here as an example and nothing more. In recording anything from a live stream that might possibly be copyrighted and/or legally protected you should ensure that you're allowed to do so. This post and the author holds no responsibility for what you do with the content hereafter.

Update 2015

In the comments spg has shown a case wherein .m3u8 files probably won't work with this method due to how they differ in playback. It would be cool to see if there's a way around this, but I don't have time at the moment to investigate sadly. Get in touch if you figure this out!

Update 2016

I've sinced removed comments from my site due to their neglect. Feel free to email me with questions about this though!

Audio Streaming

The basics of app structure/building aside, the first concern is the actual streaming of the audio. There are a myriad of factors here that are honestly just a massive chore to deal with - you've got network speed issues, you could have the network drop and pick back up, the list is endless. A cursory Google search tends to show that this is where people tripped themselves up a lot - the general consensus seems to be that relying on AVPlayer can't work for remote data, as there's seemingly no way to get at the AudioBuffer data - it only works with local files. This leads to everyone reinventing the wheel with the (arguably painful) Core Audio APIs.

Here's the thing: I had no desire to deal with any of that. They are certainly interesting problems, but right now they're just in the way. If possible I would much rather use AVPlayer and let Apple handle all the intricacies/edge cases in regards to playback.

Enter MTAudioProcessingTap

Now, MTAudioProcessingTap is no secret - it's what you'd use to, say, visualize audio data for local files played through AVPlayer. If you're not familiar with it, this is a pretty good writeup. The general gist is that you create a tap, set up an audio mix for a track, and set it on the AVPlayerItem of the audio player. The problem with remote data is that AVPlayer just works differently there - you don't have access to any AVAssetTracks with which to make an AVMutableAudioMix, presumably because streaming is just a different setup entirely behind the scenes.

However, if we look a bit further, after the streaming starts, you can access an AVAssetTrack on the player. Now it's a simple matter of KVObserving the status of the player, grabbing the track when it's available, and setting up our stream handler. Given that it's MTAudioProcessingTap you could do any number of things here, but personally I just needed to pass the raw audio data through.

Unlike other articles on this site, I'm opting to just include three gists at the bottom that act as a full (mini) library, along with example usage. There's a bit of Core Audio involved, but it's nothing too annoying - hopefully this helps anyone who's been wondering how to handle this. This code isn't meant to be drop-in-good-to-go; with this type of project it's kind of expected that you integrate it based on your own needs.

If you have questions, you can always feel free to get in touch, be it email, Twitter, or GitHub. Generally down to help!

Looking for More?

I've been writing for quite some time! You may want to check out the Essays section for a full list.