Ignored By Dinosaurs 🦕

We are currently running a Drupal multisite installation on Acquia's enterprise cloud. We have a bunch of different domains for the various sites, as well as the various environments in which they run. The development domains look like pddah.dev.abm, pddah.staging.abm etc, presumably to prevent them from being accessed from the outside world.

This setup requires a rather voluminous sites.php file in the root of the sites/ directory to map all the potential incoming hostnames to their correct websites.

A simpler way around this is to make use of how Drupal maps incoming hostnames to the correct sites/\* folder in the first place.


If there is nothing in the sites/ folder except for default, then that is what will get loaded no matter what the incoming domain. This is Drupal's default config, in fact. If you want to go multisite, you create sites/\* directories for each of your websites' domains and Drupal will figure it out for you. But, it's rules for how it routes are a little bit liberal.

For example, I'm running pddnet.com, but the website actually exists in www.pddnet.com. I only have a pddnet.com folder in sites/ though, so that means that any subdomain of pddnet.com will also route to that directory. If I create a local development domain local.pddnet.com, assuming my local network and apache configs are in order, Drupal will load the config out of the pddnet.com directory without having to do any more work or add anything to sites.php.

This means that you can create dev.pddnet.com, staging.pddnet.com, whateveryouwant.pddnet.com and provided the network plumbing is right between here and there, it'll just work.

Of course, this also requires you having the same settings file in all of these different environments, which means that either you have to have the same DB settings in every environment, or you need to figure out some other way to load in env specific config into that file.

Acquia has a methodology that I'm probably under NDA to not divulge here, but it was devised in an era before modern PHP was a thing. These days we have tools like phpdotenv, and it's that tool that I'm exploring currently for some work that we're doing here that'll span multiple environments.

When I work out how best to integrate it with Drupal, I'll let you know. So far so good though.

#drupal

I'm sure this is obvious, but I just spent too long trying to figure this one out. The mutator function wasn't even being called.

Make sure that the field you want to mutate is included in your model's $fillable array, else the ORM doesn't care that you want to do things to it.

#laravel

I've got this decoupled CMS brewing in my head, and wanted to jot down what I think would be a kind of cool method for generating URLs and URL redirects and making sure everything stays in sync without having to maintain a giant table of redirects (ala Drupal).

The basic scheme would look like this — site.com/{item_type}/{item_id}/{item_slug}. The type parameter could probably be optional, but it's there for now. An example URL could be ignoredbydinoaurs.com/posts/123/the-totally-awesome-seo-juice.

Of course, in whatever framework you're working in, those url segments are going to be broken down into tokens passed into whatever controller function is running the query. So your query would/could look like this in the controller


def show
	@post = Post.find(params[:id])
	if @post.slug != params[:slug]
	redirect_to "/#{@post.type}/#{post.id}/#{@post.slug}", :status => :moved_permanently
	end
end

This has the advantage of never going out of sync with a redirect table, and never opening up the possibility of having an alias and a redirect create a loop. This happens often in Drupal, so with this scheme, you're only looking up based on an item's ID, which should never change. If somehow a URL has made it out in the the wild that is not the preferred URL, nothing breaks, it just gracefully performs a redirect to the proper URL.

The only significant portion of the URL is the ID, everything else is decoration or SEO juice.


Somewhat off topic, but if you had a use cases where you were running multiple sites out of this CMS, and you had editors that frequently shared content, or wrote up each other's content for a sister site, then the primary key of of the article can stay consistent across different publications.

“How would different editors utilize each other's content in that case? Like, how would different pubs have the same article with a different summary on this instance from that instance?”

PostgreSQL schemas, that's how. I'll write that up, probably sometime in 2017.


2016, Django update

This wasn't exactly hard to do, but I was surprised to find that nobody really wrote up how to simply issue a redirect like this in a Django view (controller to the rest of the world). Assuming that get() or get_queryset() was the answer, but I was wrong. This was the help piece of info that I needed. Did I mention how much I love Django's docs?

This is the method that drives this very page.


class DetailView(generic.DetailView):
	model = Post
	
	def dispatch(self, request, *args, **kwargs):
	object = get_object_or_404(Post, pk=self.kwargs['pk'])
	if object.slug != self.kwargs['slug']:
	return redirect(object, permanent=True)
	# else, delegate up
	return super(DetailView, self).dispatch(request, *args, **kwargs)

#generaldevelopment #rails #django

So it took me days of trying to piece together the correct config options to make my local protractor test suite run in a bunch of different setups on Sauce Labs.

// An example configuration file.
exports.config = {

	sauceUser: 'jgrubb',
	sauceKey: 'fill-in-the-blank',
	sauceSeleniumAddress: 'localhost:4445/wd/hub',
	
	// Capabilities to be passed to the webdriver instance.
	// This option is called "capabilities" in the protractor docs
	// but whatever. this also works.
	multiCapabilities: [{
	// by default, these first two browsers will come up in 
	// Linux if you don't specify an OS
	'name': 'Chrome',
	'browserName': 'chrome'
	}, {
	'name': 'Firefox',
	'browserName': 'firefox'
	}, {
	'name': 'Win XP/IE8',
	'os': 'Windows XP',
	'browserName': 'internet explorer',
	'version': '8.0'
	}, {
	'name': 'Win7/IE8',
	'os': 'Windows 7',
	'browserName': 'internet explorer',
	'version': '8.0'
	}, {
	'name': 'Win7/IE9',
	'os': 'Windows 7',
	'browserName': 'internet explorer',
	'version': '9.0'
	}, {
	'name': 'Win8/IE10',
	'os': 'Windows 8',
	'browserName': 'internet explorer',
	'version': '10.0'
	}, {
	'name': 'Win8.1/IE11',
	'os': 'Windows 8.1',
	'browserName': 'internet explorer',
	'version': '11.0'
	}],
	
	// Spec patterns are relative to the current working directly when
	// protractor is called.
	specs: ['e2e/**/\*_spec.js'],
	
	// Options to be passed to Jasmine-node.
	jasmineNodeOpts: {
	showColors: true,
	defaultTimeoutInterval: 30000
	},

};

I burned tons of time just trying to figure out the correct names for the options, can't find where those are documented. This page – https://docs.saucelabs.com/reference/platforms-configurator/#/ turned out to be hugely helpful.

you probably figured this out already, but the Sauce Connect thing is awesome, and really easy to set up – https://docs.saucelabs.com/reference/sauce-connect/

#testing #angular

And I wonder – will you miss your old friends once you've proven what you're worth? And I wonder – when you're a big star, will you miss the Earth?

I heard some news this weekend that really shook me up – that one of my old musician acquiantances and his wife of several years had recently split up.

He's a young dude and his band is killing it, and I know first-hand how rough that is on a relationship, so it was not suprising at all. Yet somehow it was also simultaneously shocking.

It's brought up a lot of emotions and thoughts that I didn't have time to jot down, but I had this weird feeling all weekend. This feeling like I had to get off a plane that all my friends were on, and that plane later went down.


Another buddy of mine just moved to a new town, stocked with tons of music and musicians. He's trying to make a run at it, and is out and about meeting musicians and going to shows. It's causing a little bit of strain on his relationship, since his girl is out there for work, not to socialize.

I empathize with both parties. Once the music bug bites you, and god forbid you find some success with it, it's almost impossible to find anything to do with yourself that feels as meaningful as that. Pursuing music and playing music is one of the highest callings I've ever felt. I think it's kind of a miracle that I didn't go completely off the deep end when I walked away, and another miracle that I've managed to find a work situation and a home life that together have surpassed music in terms of how much, I dunno, living I get out of life now.

But on the other hand, I don't talk to God nearly as much as I did when I was playing music full time.


So yeah, I heard that news and my reaction was both really sad for them and really glad it wasn't me, and I'm guessing that's kinda what they call “survivor's guilt”. In my opinion, it's basically impossible to be the kind of man you want to be for your family at the same time that you're being the kind of musician that you want to be for yourself, and for that higher calling. That's why it feels sort of like a curse to me.

#music #life

So years ago, in the early days of my HN acct, I bought a book called “Founders at work” that happened to be authored by PG's wife Jessica Livingstone. Great book with a bunch of interviews with various founder/hero types – Woz, Evan Williams, etc.

One of the interviews with with DHH – the Rails guy. I remember him talking about how he used to write PHP and it was just too hard, and that's how he found ruby and started building stuff with it.

So, I've been going through these Laravel screencasts lately. They're very helpful, especially if I were a total noob as far as most of thes concepts go, and the architecture of Laravel is so heavily based on Rails that I pretty much know what's coming next.

The thing about it though, is the amount of Laravel code that you have to write to do the same thing in Rails. And it's just uglier. And it just seems like more work.

So yeah, that's my 1 week assessment of Laravel.

#random

It's very simple. I work at a publishing company in northern New Jersey. I think we hire pretty smart, but the technical interview tends to be more of a conversation about technology than a series of quizzes on the whiteboard. We've been hiring for a few positions lately, and I've recently hit upon the perfect conversational tech interview question. It's separated a lot of wheat from a lot of chaff for me in the past few months.

“What text editor do you prefer?”


I've recently begun using PHPStorm after several years of bouncing between Vim for server side languages and TextMate for Javascript. I've had a license for PHPStorm for almost a year now, but never really got into it. In my younger days I guess I was kind of a hipster in that I thought I too cool for an IDE. Rather, I wanted to be too cool for an IDE. IDEs were something you used if you worked with some bloated language like Java, or some compiled thing like Objective-C. If you used a slim, elegant, interpreted language like Ruby, you only needed a text editor. TextMate was the perfect little text editor for me for a number of years.

When I started here, we were still on Windows machines. Our entire Drupal stack was running on Ubuntu, and all of my background was in Linux and Mac, so it didn't make any sense to me to learn the tooling to work on Windows. So I downloaded VMWare Player and spun up my first Ubuntu 12.04 desktop VM. I taught myself to set up an intermediate LAMP/LEMP stack on this VM, and decided to start using Vim as my editor. I'd read about it plenty and most of the TM community seemed to be scattering to either Vim or Sublime. So Vim it was.

I became pretty proficient with Vim, but when I started playing Angular about this time last year, I was really missing a tree view. One of the killer-est plugins for Vim is vim-rails, and the reason is actually because of the way that Rails is architected. It's very predictable if you go with the flow, and vim-rails let's you jump from controller to it's view to it's model to it's tests, over to any other model with a series of very simple and easy to learn shortcuts (the only kind I ever learn). I found myself really missing something like this with Angular, and just decided to go back to TextMate for Angular projects. TextMate has that fuzzy file search and a file tree explorer by default so it really made getting around an Angular project a little less tiresome. I know, I should have taken the opportunity to get to know Vim better, but I had (as you can imagine) a shitload of work to do.

So I spent several weeks back in TextMate. I'm vastly more productive in an editor with the Twilight theme, by the way. Those weeks go by and it's back to Drupal and Vim. My Vim chops have weakened by this point, and I've gotten pretty used to having the Twilight theme, which apparently didn't exist for Vim. So I spent a day hacking an approximation together. It's also annoying to not have a file tree. I could install NerdTree, and tried to several times, but just never could figure out if I was learning a tool to get a job done, or just as some kind of test.

Enter my first project in Java a month ago.


We have some infrastructure here. It runs a lot of Java. This is a critical and most interesting piece of infrastructure, and we only have one Java guy here and he's pretty booked. Building web sites and writing javascript is fun and all, but underneath that is the layer of data in which my company lives and breathes. This is the layer for which this piece of infrastructure does the plumbing, therefore it has become much more interesting to me as of late.

This requires, obviously, learning a little bit about Java – something I'd stringently avoided for several years now. Java was old, Java was derided, Java could simply not be written without an IDE. These are prejudices which I no longer hold. I take this as a point of pride that I've risen to a level where I no longer define myself by my choice of technology. I've chosen enough of them at this point to see the merits of each for what they're for, as well as what they shouldn't be for. This is called experience, I think.

So anyway, my experience with Java and Eclipse is rather interesting. “Jump to declaration”. Wow. A tree view not just of the files within my project, but of the methods within a file! Wow. It's at this point that I have to jump back into Drupal to do some plumbing between this and that. I fire up PHPStorm, thinking hey, this IDE thing is kinda cool.

Lo and behold, many of the shortcuts in Eclipse are the exact same in PHPStorm. The menu and layout are very similar. I'm being productive in a tool which had previously only been sort of useful under come circumstances. I haven't even gotten to the debugger yet, on account of plumbing it into Vagrant and back being a pain in the ass. Just being able to reformat files and jump to function definitions is pretty nifty.

Then I have to jump back into another Angular project. Hey, I think. PHPStorm is basically also WebStorm, and therefore should be aware of Angular. It is! It indexes my entire project and within 30 minutes I'm being way more productive in PHPStorm on an Angular project than I would've been in TextMate. It's effing amazing! So I'm pretty much sold on PHPStorm, and then I notice that it doesn't support the little Sinatra/Sidekiq endpoint that goes along with this Angular project. I start thinking about IntelliJ a little bit, as I'm really starting to see the gains from being able to use one tool across many different technologies.


My point here is that I can go on for a ridiculously long time about my text editor and why. There's a story, and it also takes a winding path through what technologies I've used, why I used them, why I moved on or stayed with them.

I don't care what editor you use. Ok, I kinda do actually, but I care much more that you have some opinions on the matter. If you don't, I'm not sure you really write code and that's kind of a problem for the types of technical positions that we are filling. These positions require a curious mind, one that likes to check out new things, and one that's ok with using old things if that's the right tool for the job.

If you're interviewing at ABM and you come across this blog post in doing some research about the team and the company, feel free to mention that. Taking the initiative to do some research on your potential teammates speaks much more to the kind of curious mind that you posses than a long winded discussion about your text editor ;).

#general-development

Prelude

Fastly is a CDN (content deliver network). A CDN makes your site faster by acting as a caching layer between the world wide web and your webserver. It does this by having a globally distributed network of servers and by using some DNS trickery to make sure that when someone puts in the address of your website they're actually requesting that page from the nearest server in that network.

$ host www.ecnmag.com
www.ecnmag.com is an alias for global.prod.fastly.net.
global.prod.fastly.net is an alias for global-ssl.fastly.net.
global-ssl.fastly.net is an alias for fallback.global-ssl.fastly.net.
fallback.global-ssl.fastly.net has address 23.235.39.184
fallback.global-ssl.fastly.net has address 199.27.76.185

If they don't have a copy of the requested page, they'll get it from your webserver and save it for the next time. Next time, they served the “cached version” which is way faster for your users and lightens the load on your webserver (since the request never even makes it to your webserver). Excellent writeup here.

There are many different CDN vendors out there – Akamai being the oldest and most expensive that you may have heard of. A new entrant into the market is a company called Fastly. Fastly has decided on using Varnish as the core of their system. They have some heavyweight Varnish talent on the team and have added a few extremely cool features to “vanilla” Varnish that I'll get to in a moment.

Fastly's being built on top of Varnish is cool, mainly because every CDN out there has some sort of configuration language and to throw your hat in with any of them is also to throw your hat in with their particular configuration language. Varnish has a well known config format called VCL (Varnish configuration language) which, on top of having plenty of documentation and users out there already, is also portable to other installations of Varnish so that learning it is time well spent. This is the killer Fastly feature that first drew me in.


(you can skip this – backstory, not technical)

Prior to using the CDN as our front line to the rest of the internet, we'd been on a traditional “n-tier” web setup. This meant that any request to one of our sites from anywhere in the world would have to travel to a single point – our load balancer in Ashburn, Virginia in this case – and then travel all the way back to wherever. In addition to this obvious global networking performance suck, we use a managed hosting vendor, so they actually own and control our load balancer. Any changes that we'd want to have made to our VCL – the front line of defense against the WWW – would have to go through a support-ticket-and-review process. This was a bottleneck in the event of DDos situations, or any change to our caching setup for any reason.

Taking control of our caching front line was a neccessary step. This became the second killer Fastly feature once we started piloting a few of our sites on Fastly.


The killer-est killer feature of all has only just become clear to me. Fastly makes use of a feature called “Surrogate Keys” to improve the typical time-based expiration strategy that we'd been using for years now. They have a wonderful pair of blog posts on the topic here and here.

The way that Varnish works is basically a big, fast key-value store. How keys are generated and subsequently looked up, as well as how their values are stored are all subject to alteration by VCL, so you have a wonderful amount of control over the default methodology. By default it's essentially URLs as keys, and server responses as values, and this will get you pretty far down the line, but where you bump into the limits is as soon as you start pondering that each response has but one key that references it. Conversely, each key references only one object. By default...

Real life example – I work for a publishing company. Our websites are not super complicated IA-wise. We have pieces of content and listing pages of that content, organized mostly by some sort of topic. A piece of content can have any number of topics attached to it, and that piece of content (heretofore referred to as a “node”) should show up on the listing pages for any one of those terms.

Out of the box, Fastly/Drupal works really well for individual nodes. Drupal has a module for Fastly that communicates with their API to purge content when it's updated, so if an editor changes something on the node they won't have to wait at all for their changes to be reflected to unauthenticated users. The same is not true for listing pages. Since these pages are collections of content and have no deeper awareness of the individual members of the collection, they function on a typical time-based expiration strategy.

My strategy for the months since we launched this across all of our sites has been to set TTLs (time to live, basically the length of time something will be cached) as high as I can until an editor complains that content isn't showing up where they want it to. I recently had an editor start riding me about this, so lowered the TTLs to values so low that I knew we weren't getting much benefit of even having caching in the first place. I'd known about this Surrogate Key feature and decided to start having a deeper look.


The ideal caching scenario would have not only the node purged when editors updated it, but to have listing pages purge when a piece of content is published that should show up on that listing. This is where Surrogate Keys come into play. The Surrogate-Key HTTP header is a “space delimited collection of cache keys that pertain to an object in the cache”. If a purge request is sent to Fastly's API to purge “test-key”, anything with “test-key” in the Surrogate-Key header should fall out of cache and be regenerated.

In essence, what this means is that you can associate an arbitrary key with more than one object in the cache. You could tag anything on the route “api/mobile” with a surrogate-key “mobile” and when you want to purge your mobile endpoints, purge them all with one call rather than having to loop through every endpoint individually. On those topic listing pages you could use the topic or topic ID as a surrogate-key, and then any time a piece of content with that topic is added or updated, you can send a purge to that topic ID and have that listing page dropped. And only that listing page dropped.

// the basic algorithm, NOT functional Drupal code

if ($listing_page->type == "topic") {
	$keys = [];
	
	// Topics can have children, so fetch them.
	// pretend this returns a perfect array of topic IDs
	$topics = get_term_children($listing_page->topic);
	// Push the parent topic into the array as well.
	$topics[] = $listing_page->topic;
	
	foreach($topics as $topic) {
	$keys[] = $topic;
	}
	
	$key = implode(" ", $keys);
	add_http_header('Surrogate-Key', $key);
}

This results in a topic listing page getting a header like this -

# the parent topic ID as well as any child topic IDs
Surrogate-Key: 3979 3980 3779

Then, upon the update or creation of a node you do something like this -

// this would be something like hook_node_insert if you're a Drupalist
function example_node_save_subscriber($node) {
 $fastly = new Fastly(FASTLY_SERVICE_ID, API_KEY);
 foreach($node->topics as $topic_id) {
 $fastly->purgeKey($topic_id);
 }
}

This fires off a Fastly API call for each topic on that node that would cause anything with that surrogate key, aka topic ID, to be purged. This would be any topic listing page with this topic ID on it. Obviously if there are 500 topics on any piece of content you'll probably want to move this to a background job so you don't kill something, but you get the idea.


This is sort of like chasing the holy grail of caching. In theory this means that you are turning the caching TTLs up to maximum and only expiring something when it actually needs to be expired based on user action and intent, not based on some arbitrary time that I decide on based on my lust for having everything as fast as possible. The marvelous side effect of this is that (again in theory) everything should load even faster since there's almost no superfluous generation of pages at all.

I just released the code on Friday morning, and the editor who was previously riding me about this topic had only positive feedback for me, meaning – so far, so good.


FYI – the holy grail actually looks more like this -

#braindump #varnish #drupal #devops

And here we are, but first – a story...


Yesterday was my 4 year old's 5th birthday. Michelle and I go back and forth about who's going to take video and who's going to take stills for singing “Happy Birthday”, and decide I'll take the video.

We get up to the part where she's lighting the candle and my phone stops recording – “Your phone is full, please manage storage under blah blah blah...”. This is an iPhone 6+, bought it two months ago. Obviously I bought the 16GB model, but this was never a problem with my 5c so why would it be a problem with the 6+?

It has been a problem with my 6+. I just deleted all my music and podcasts off of the phone less than a week ago, and as you can tell from the screenshot, whatever is on here does not add up to 11.something GB of stuff. Not to mention, why is there only 11.something GB of available storage in the first place. The OS is taking up over a quarter of the disk space??

So I'm googling last night, trying to figure out what's taking up the space on my phone. Obviously something is cached it would seem to me, but I have no control over what this is or how to free up that space. Some random post advises me to backup and restore the phone, which seems really janky to me, but the poster says this will wipe the cached stuff and only leave “your stuff”. I decide to try it, even though I'm respecting myself a little less at this point (I'm a developer for pete's sake, not some non-technical moron who has to search the internet for how to free up storage on his phone. Or am i??).

I'm told by iTunes that I don't have enough free space on the phone to restore from the backup I've just made. I'm wasting my life being frustrated at a phone at this point, rather than spending time with my wife on our son's birthday.

I have a moment and I remember why I jumped to Apple gear in the first place...


After years of loyalty as a Windows user, after years of hating Justin Long's smug pre-hipster persona as the cool kid in the commercial opposite John Hodges, I bought a new HP laptop with Windows Vista on it. I felt betrayed. It was such a poor, clunky experience that I immediately regretted buying the laptop. Two weeks later I bought an iPhone, as chronicalled in part 1 of this series. It was, I can honestly say, life changing. It just worked. It didn't nag me, it didn't crash, it didn't hide useful features behind 3 submenus, it just worked. It catalyzed the entire career path I've been on for the 7 years since I bought it.

You can guess where this is going. My iPhone 6+ is no longer a device that “just works”. It does the exact opposite and costs me video of my 5 year old's birthday. I guess Marco Arment wrote about this a month ago, but I'm officially done paying this much money to be frustrated by technology.


I'll go ahead and say it – if Jobs were still alive, he would've fired the motherfucker who even suggested shipping their top-of-the-line phone with only 16GB of storage, not only because it makes for an obviously crappy user experience but because he had an apparently much clearer long view – that happy customers keep coming back and unhappy customers flee at the first opportunity.

My first opportunity is in 10 months when my T Mobile jump plan comes back around. I'll probably go retro, since I have less than a home screen's worth of apps installed on this thing anyway. The only ones I actually really use are Email, Twitter, and Reddit, and arguably all of those on a phone are just ways to kill time when I could be enjoying the life around me.

Life moves on.

#life #random #iphone

GUIs change, but the command line is eternal. Memorize these 5 commands and a long and happy life awaits.


$cd
change directory. This is how you move 
around the file system.

$ls
List, or tell me what's in this directory. This has a 
huge list of useful modifying flags, such as -l (long, 
tell me the size, ownership, and permissions on each 
thing in here too), or -a (all, as in, show me hidden 
dotfiles as well)

$mv
Move, this is how you move something from here 
to there. This is also how you rename something 
even if it's in the right place.

$cp
Copy. Add -r to make it recursive, else it 
won't copy directories because it won't 
descend into them.

$pwd
present working directory. this 
is how you get it to tell you where you are 
in the file system.

#bareminimum #devops