Ignored By Dinosaurs πŸ¦•

For some reason I got it into my head the other day to tinker around with MongoDB and Drupal, masochism I guess. Anyway, I finally had the opportunity to start tinkering with it last night.

The set up was fairly easy – if you're on a Mac with Homebrew just hit up the josegonzalez builds of PHP5.5 and then brew install php55-mongo or something like that. Just brew search php55-mongo before you go blindly copying that command in.

Making the connection between Drupal and Mongo was also pretty straightforward, just follow the instructions on this post.

After that I was ready to migrate some content, so I did the old drush | grep mongo to see a list that looked like this β€”

All commands in mongodb: (mongodb)
  mongodb-cli (mdbc) Open a mongodb command-line interface using Drupal's credentials.
  mongodb-conf Print mongodb connection details using print_r().
  mongodb-connect A string for connecting to the mongodb.
  mongodb-query (mdbq) Execute a query against the site mongodb.
Other commands: (adaptivetheme,archive,browse,cache,coder_review,topic,features_plumber,apachesolr_site,redirect,image,libraries,make,mongodb_migrate,nodequeue_generate,print_pdf,queue,rules_scheduler,runserver,search,shellalias,sitealias,ssh,acquia_search,acquia_spi,strongarm,test,usage,uuid,variable,views_bulk_operations,views_data_export,watchdog,xmlsitemap)
  mongodb-migrate Migrates fields. Run mongodb-field-update first.
  mongodb-migrate-prep Prepare for migrate. Resets existing migration. No data is lost.

So, cool! Only problem is I repeatedly got this back β€”-

The drush command 'mongodb-migrate-prep' could not be found. Run 'drush cache-clear drush' to clear the commandfile cache if you have installed new extensions.

Over and over, clearing drush cache over and over, until I finally figured to look in the drush files that came with the Mongo module. The trick is that Chx actually meant to say that command is called drush mongodb-migrate-prepare instead of just β€œprep”.

It's not that reassuring about the experience that lay ahead of me that an error this simple and fixable is lying there unpatched, apparently after 2 years worth of development, since I first tried the green β€œofficial” release before going -dev on it. I suppose I'll be submitting a patch for that.

I have another blog post about the migration tribulations, but I eventually got through it and it's kinda cool. Instead of a giant throbbing schema full of field_data_this_and_that, all you have is β€”-

> show collections
cache
cache_bootstrap
fields_current.file
fields_current.node
fields_current.taxonomy_term
fields_current.user
fields_revision.node
migrate.file
migrate.node
migrate.taxonomy_term
migrate.user
queue.feeds_importer_expire
queue.feeds_push_unsubscribe
queue.feeds_source_clear
queue.feeds_source_import
queue.file_entity_type_determine
queue.print_mail_send
queue.views_bulk_operations
session
system.indexes

Each node document in that collection has every field that it needs to have, right on the node! Win! Now to figure out what to do with it!!

#drupal

Nginx configs

So I recently had a couple of seemingly disparate tasks come across my desk. We recently launched a HMTL mobile app, an Angular front end to our Drupal sites. We decided to completely decouple the mobile app from the Drupal codebase after a fairly long exploratory period trying out different approaches.

When launch day finally came, we set up the mobile app at app.$$BRAND.com, with our main sites at www.$$BRAND.com. Acquia has this cool feature in their Varnish layer that will filter user-agent strings for ones that match a set of β€œmobile” user agents that they have defined in a central file. So flipping folks on iPhones over to the mobile site was a piece of cake. What I forgot was that the same logic wouldn't be triggered for the reverse β€” flipping desktop users to the desktop version of the site from app.$BRAND.com. (Our mobile app is hosted on our own, not with Acquia).

I already had a big list of regexes to test mobile user agent strings with (thanks Acquia!), so the trick was to recreate that in Nginx, the webserver for our mobile app.

Not wanting to do a bunch of evil if {} statements in the Nginx configs, I cast about for a more elegant solution, eventually stumbling upon map.

The Nginx map module

http://nginx.org/en/docs/http/ngxhttpmap_module.html.

So basically what this does is to test any arbitrary Nginx variable for some condition, and spit out a custom variable that you can user in your config. An example β€”-

map $http_user_agent $device_redirect {
  default "desktop";
  ~(?i)ip(hone|od) "mobile";
  ~(?i)android.\*(mobile|mini) "mobile";
  ~Mobile.+Firefox "mobile";
  ~^HTC "mobile";
  ~Fennec "mobile";
  ~IEMobile "mobile";
  ~BB10 "mobile";
  ~SymbianOS.\*AppleWebKit "mobile";
  ~Opera\sMobi "mobile";
}

This takes a look at the incoming user agent string (fun fact β€” grab any request header with $http_NAME_OF_HEADER) and compares it against a set of regexes. If one of them is a match, then the $device_redirect variable gets set to β€œmobile”, otherwise, it's set to the default of β€œdesktop”. This gets used later in the config β€”

if ($device_redirect = "desktop") {
  return 301 $scheme://$desktop_host$request_uri;
}

In other words, if the user agent doesn't match one of those regexes, redirect the user to the desktop site. Pretty neat!

As a side note, does anyone else think it's weird that the comparison syntax in that if statement only uses one '='? But yeah, that's the right way.

Later that day, on a different Angular app

So that mobile app and this one that I'm about to talk about kinda conform to the Drupal concept of β€œmultisite”. That is, a single codebase that serves a variety of different sites. I figured out a pretty simple hack for this one that maybe I'll share in another blogpost if I get around to it, but basically it involves setting a siteVar in the bootstrapping phase of the Angular app based off of the window.location.hostname. I have a Config Angular service that stores the mapping of hostname to siteVar. It's easy and it works.

The way we serve different stylesheets to different brands is by setting up a sites/$BRAND/ folder that houses site specific styles, etc. When Angular bootstraps, it uses the siteVar variable to fill in $BRAND, and the site specific stuff is loaded. It's easy and it works. Except in Firefox.

Firefox doesn't like this setup, and particularly on the favicon, it proved to be a real PITA.

The default Yeoman favicon would always show up in Firefox, nothing else, since we had that favicon path set dynamically by Angular after the app loaded. it just wasn't responding to any of the usual hacks, and it turns out FF has a long and storied history with the favicon.

Having just found the perfect hammer for the previous problem, I thought I'd see if it could solve this one.

Map all the things...

So for this one, I have an Nginx map companion to the one that I have in Angular.

map $http_host $sitevar {
  default "";
  ~rdmag "rd";
  ~alnmag "aln";
  ~bioscience "bt";
  ~cedmagazine "ced";
  # etc...
}

This maps the incoming host variable on the request to a $sitevar variable, used like this...

location /favicon.ico {
  try_files /sites/$sitevar/favicon.ico $uri;
}

So the browsers that respect the dynamic favicon path continue to work, and if FF never cooperates, Nginx catches that request and fixes the problem before anybody knows...

#devops #generaldevelopment #angular

Enlightenment is realizing that the straight job with the commute and the boss actually provides more of what you were seeking from the experience of being a professional grade musician than being in a professional grade band.

For the first time ever I'm part of a team with stated goals and long term plans laid out in service of attaining those goals. It's an amazing feeling that I wouldn't have had I not given the band thing the commitment that I did.

So thank you, Railroad Earth, for letting me have the experience that gives me the perspective that I have now.

And thank you, ABM teammates. Looking forward to it.

#life

I spent about an hour pulling my hair over this one. I'm deploying an Angular to dev for the first time, it works fine locally, but everything is busted when I grunt build and push it up to a server. I'm using ngMin and using the supposedly safe syntax for defining all my dependencies, but unfortunately any Google search that includes β€œgrunt build” and/or β€œminify angular” only turns up answer that pertain to that fairly well know problem.

So, I commented out Uglify in the build process and am still getting the error, only it's a lot easier to track down now, because my JS isn't minified. It blows up on the first one of my controllers that I wrote in Coffeescript, and is wrapped by Coffee's default function wrapper.

If this sounds like you, go to your Gruntfile and add an option to the coffeescript config β€”-

  coffee: {
    options: {
      sourceMap: true,
      sourceRoot: ''
    },
  }

becomes this β€”-

  coffee: {
    options: {
      sourceMap: true,
      sourceRoot: '',
      bare: true
    },
  }

Just make sure you're defining your scripts with (one of) the approved syntax(s) for keeping stuff out of the global scope -

angular.module('fooApp').controller('fooController', function(){
 // stuff here
});

#javascript #angular

Hey Dennis, I became a first time WNCW member during the Gospel Truth a few weeks ago. It really is a different feeling you get listening to a radio station that you're helping to support, but that's not my point...

I moved up to New Jersey for the Railroad Earth gig a very long time ago now, but I'm an App State alumni, class of 2001. I've been trying to get back down to somewhere in the WNCW listening area ever since but despite my best efforts, me and my family have gone and grown roots up here. I don't know if I'll ever make it back down there, and that makes me sad.

But lately, I've been listening to Goin Across the Mt every weekend on some NPR app on my phone and hearing your voice makes me a little less sad, so thank you.

#life

The beginning

I'll make the β€œwhat is Angular” part as brief as possible.

Angular is a giant JavaScript framework from our friends at Google. It fits into a similar camp with Backbone, a framework that Drupalists will likely have heard of since it's included in D8 core. Angular aims to make it as easy as possible to build a single page CRUD app, and in my limited experience it succeeds.

I've never built anything with Backbone, but have the Peepcode series on it, and have been working heavily with Rails for a good while now. I'll avert a gush on Rails for the time being, but let's just say I really love the way that Rails doesn't really write any markup for you. It's much simpler to find your way through the request/response path, and in general I find developing with Rails to be a vastly more pleasant experience than developing with Drupal.

Alas, I've been a professional Drupal dev for about 4 years now.

The use case

I work at a publishing company. We publish about 26 different pubs, many of them still in print. Within the last year we finished a migration of all of our websites from various proprietary CMSs into a Drupal 7 multisite installation. The sites support desktop only at this point as we are a small company and resources are definitely constrained. (This also has it's upsides which we'll get to).

Last fall we rebuilt the company website from a static HTML site into Drupal 7. Since this was not a part of the multisite install, we were allowed to architect the site from scratch with my boss doing all the site building and myself doing all the theming. Mobile was a big priority this time, so I spent a good chunk of the development cycle implementing mobile-friendly behavior and presentation and generally getting a good feel for how to really do a responsive site. As an aside, for mobile/responsive site building and crafting responsive stylesheets, less is definitely more.

The end of this winter has brought a time table for offering a more accommodating mobile experience on our β€œbrand sites”.

The dream

So my boss and his boss want a mobile site like β€œThe financial Times has”. If you have an iOS device, go to app.ft.com, and if you're a Drupal developer, try and get your head around how you'd pull that off, but try and forget that this is a post/series about Angular first. Pretend that you were going to try and pull that off in a more or less responsive fashion.

I spent a couple of days surveying the landscape for JS libraries that help out with touch behavior, and trying to figure out how to prefetch content so that when the user swipes from page to page or section to section, there wouldn't be a giant delay while the network does its thing transferring 1,000 lbs. of panels-driven markup. This was Monday and Tuesday of last week.

A pause for reflection

My enthusiasm for the project already waning, I sat back and though about how we ought to be doing this thing. What they want is a mobile app, not a responsive website.

The way that you implement a mobile app is not by loading a page of HTML markup with every user action, it's by loading the app once and then communicating with the server via JSON (or XML or whatever if you wanna get pedantic). This kind of thing is supremely easy to do with Rails mainly due to Rails's deep embrace of Rest 6 years ago totally getting ahead of, perhaps even enabling, this whole javascript app revolution in which we find ourselves. Outputting a particular resource as JSON is as simple a line of extra code to allow the controller to respond to a request with a different format.

Step 1, Services

I'd never played with Services, so I didn't know how easy it was to output a node as JSON. On Wednesday of last week, some time after lunch, I decided to find out. Turned out we already had Services installed for another use case that we just recently implemented (offloading our Feeds module aggregation to another open source project called Mule, but that's a whole other series of posts), so all I had to do was bumble through a tutorial on how to set up a Node endpoint.

In less that 5 minutes I had exactly what I needed set up in terms of dumping a node to JSON. I've been reading Lullabot's recent posts about their use of Angular, so the rest of this series will follow along as I learn how to use this giant beast of a JS framework to build the mobile app my boss' boss wants without a minimum of Drupal hackery.

The next post will pick up Thursday morning (as in, 6 days ago) where I first downloaded Angular into the project.

#angular #javascript #drupal


  • Episode 01 – Sharam_s Mix.m4a
  • Episode 02 – Sultan_s Mix.m4a
  • Episode 03 – Dean Coleman_s Mix.m4a
  • Episode 04 – Cedric Gervais_ Mix.m4a
  • Episode 05 – Prompt_s Mix.m4a
  • Episode 06 – David Tort_s Mix.m4a
  • Episode 07 – Miss Nine_s Mix.m4a
  • Episode 08 – Ned Shepard_s Mix.m4a
  • Episode 09 – D-Unity_s Mix.m4a
  • Episode 10 – Pig & Dan_s Mix.m4a
  • Episode 11 – DJ Simi_s Mix.m4a
  • Episode 12 – 16 Bit Lolitas_ Mix.m4a
  • Episode 13 – Kamisshake_s Mix.mp3
  • Episode 14 – Cocodrills_ Mix.m4a
  • Episode 15 – The Flash Brothers_ Mix.m4a
  • Episode 16 – Andrew Bayer_s Mix.mp3
  • Episode 17 – Namito_s Mix.mp3
  • Episode 18 – Dimitri Andreas_ Mix.mp3
  • Episode 19 – Prompt_s Mix.mp3
  • Episode 20 – Boris M.D._s Mix.mp3
  • Episode 21 – Spektre_s Mix.mp3
  • Episode 22 – Alex Kenji_s Mix.mp3
  • Episode 23 – Schadenfreude_s Mix.mp3
  • Episode 25 – Timo Garcia_s Mix.mp3
  • Episode 26 – Anton Pieete_s Mix.mp3
  • Episode 27 – D-Formation_s Mix.mp3
  • Episode 29 – Pig & Dan_s Mix.m4a
  • Episode 30 – Solee_s Mix.mp3
  • Episode 35 – D-Formation_s Mix.mp3
  • Episode 36 – Matt Lange_s Mix.mp3
  • Episode 38 – Coll Selini_s Mix.mp3
  • Episode 39 – Solee_s Mix.mp3
  • Episode 40 – Scalambrin & Sgarro_s Mix.mp3
  • Episode 42 – Pig & Dan_s Mix.mp3
  • Episode 44 – Ahmet Sendil_s Mix.mp3
  • Episode 45 – Federico Epis_s Mix.mp3
  • Episode 46 – Sinisa Tamamovic_s Mix.mp3
  • Episode 47 – Koen Groeneveld_s Mix.mp3
  • Episode 48 – Pig & Dan_s Mix.mp3
  • Episode 49 – Quivver_s Mix.mp3
  • Episode 50 – Tom Middleton_s Mix.m4a
  • Episode 51 – Babicz_s Mix.mp3
  • Episode 52 – Yoshitoshi Best of 2011.m4a
  • Episode 53 – DBN_s Mix.mp3
  • Episode 55 – Tom Novy_s Mix.mp3
  • Episode 56 – Nicole Moudaber_s Mix.mp3
  • Episode 57 – Ahmet Sendil Mix.mp3
  • Episode 58 – Heren Mix.mp3
  • Episode 59 – Sinisa Tamamovic Mix.mp3
  • Episode 60 – Rene Kuppens Mix.mp3
  • Episode 61 – Scalambrin & Sgarro_s Mix.mp3
  • Episode 62 – Pirupa_s Mix.mp3
  • Episode 63 – Darwin & Backwall Mix.mp3
  • Episode 64 – Nicole Moudaber Mix.mp3
  • Episode 65 – Stefano Noferini Mix.mp3
  • Episode 66 – HEREN [Dirt Jugglerz] Guest Mix.mp3
  • Episode 67 – Sharam Live at Sensation (Denmark).mp3
  • Episode 68 – D-Unity Guest Mix.mp3
  • Episode 69 – Sharam Live at Kool Beach BPM Festival.mp3
  • Episode 70 – Sharam Live at BPM Festival (Part 0).mp3
  • Episode 71 – Philipp Straub Guest Mix.mp3
  • Episode 72 – El Mundo Guest Mix.mp3
  • Episode 73 – Sisko Electrofanatik Guest Mix.mp3
  • Episode 74 – Sharam Live at Space Ibiza Opening Fiesta 2013.mp3
  • Episode 75 – Sharam 4th of July Edition.mp3
  • Episode 76 – Mar-T Guest Mix.mp3

#music

This is currently in regards to the Atom editor that I dutifully filled out an β€œinvite” request for. It could be about anything, though. I get this same feeling every time.

It takes me right back to high school gym class and waiting to get picked for a team. And waiting. And waiting. And God this is embarrassing, will somebody please fucking pick me already?


This is a piece of software that you install on your computer. Not a Saas thing that'll buckle under the weight of too many users. I thought Mailbox actually did a pretty cool thing by providing that countdown that gave you all the visibility you really needed into the process, and removed that feeling from the waiting.

And waiting.

And waiting.

#random #memories

That unwanted thing that Google put in your top menu bar? That bothers you, too? Can't find where to turn it off? That's because they wanted to hide it from you.

  1. chrome://flags – put that in your address bar.
  2. Search for 'notifi', as in 'notifications' (which is what it's called right now).
  3. DISABLE

#ui

I've finally been getting around to using Redis for caching on this blog and other Rails projects I've got laying around, and I've finally gotten around to Homebrewing PHP 5.5 to use for my local set up at work. Jose's default PHP 5.5 brew doesn't install the new Opcache, so look around and make sure you install that version because it screams.

I guess you could fairly say that caching has been on my mind a lot lately as I also finally got around to installing and using Memcached here in development with our company's Drupal sites. That's also a separate Jose homebrew install, so look around for that, too. Anyway, after fiddling around with all the Drupal configs that I had commented out previously that dealt with Memcached, I got it up and running. Make that hauling ass. It's a lot of fun.

So of course, the next step is to install some outdated script that gives me some visibility into the cache statistics. Here's where I get to the point.


Out of the box, the Homebrew installation of Memcached gives you 64M, which is also what you get if you install it from Aptitude on Ubuntu. I've finally done enough Ubuntu to get that most config files are stashed somewhere in /etc (/etc/memcached.conf), but where do these files live on the Mac?

This is the only downside to Homebrew, that most of the brewers (and God bless y'all, not complaining) pick some arbitrarily weird places to stash stuff, and crazy commands to access the executables. mysql.server start? What distro does that come from? It just doesn't make a lot of sense, but I guess that's what keeps your mind sharp.

Anyway, I wanted to feed Memcached some more memory, and I couldn't find the config file for the life of me. Maybe you're here for the same reason. I dug pretty deep into Google before I found the answer. It's in ~/Library/LaunchAgents/homebrew.mxcl.memcached.plist.

Out of the box it looks like this β€”

xml version="1.0" encoding="UTF-8"?

Label
homebrew.mxcl.memcached
KeepAlive

ProgramArguments

/usr/local/opt/memcached/bin/memcached
-l
localhost

RunAtLoad

WorkingDirectory
/usr/local


So instead of pointing at a config file, you just pass all the config in the startup command. To bump it up, you need to pass in -m 128, or however much you want to feed it, like this.

xml version="1.0" encoding="UTF-8"?



Label
homebrew.mxcl.memcached
KeepAlive

ProgramArguments

/usr/local/opt/memcached/bin/memcached
-l
localhost
-m
256

RunAtLoad

WorkingDirectory
/usr/local


#devops #generaldevelopment