Ignored By Dinosaurs 🦕

I've been a Media Temple customer for a long while now, almost 4 years. The file system on my grid server is a timeline of my learning programming and systems administration. I mostly work on big-boy systems over the last 2 years or so, so my comfort level with working through SSH is pretty high now (much higher than working through GUI tools that want to hold your hand, actually). That's one thing that I always loved about MT – the fact that they give you a pretty decent command line experience for administering your server.

One thing that's kind of always gotten on my nerves, however, is the DB admin experience. Lately since it seems like their PHPMyAdmin installation has completely gone to shit. Seriously, it's unusable. I couldn't even restore/import a < 1 MB brand-new-site database the other day because it kept timing out. Of course, this would be a 3 second maneuver on the command line mysql -u user -p pass target_database < dump.sql, but they have the command line locked down on the grid server.

I've recently moved a rather involved client project back onto their production server which is hosted on said grid server. I'm still banging on the thing, so I'd like to delete some rows straight out of the DB without having to spend 30 minutes trying to get PHPMyAdmin to do what I need it to. So the other day I went poking around the hosting admin panel.

I'd seen this years ago, and fortunately it's still there, probably just for this use case. In the “global settings” under the “databases” section of your admin panel at the very bottom of the screen is an option to allow external IP addresses access to the DB server. It even has a handy option to “use current IP” to prefill the field for you. Do this.

Next, invoke mysql locally, but pass it the --host option, like this —

mysql -u user -p pass --host=external-db.s123456.gridserver.com.

You're welcome.

PS – I tried importing the aforementioned <1 MB db in this method to no avail. Don't know if their db servers were on the blink at the time, but it definitely wasn't bandwidth.

PPS – it has occurred to me since this morning that mysql doesn't run on localhost, that's why you can't login as you normally would. Thus, if you were logged into your webserver, you could pass the same --host argument and specify the internal-db host to carry on about your business.

Never said I was that good at this stuff.

#databases #devops #mysql

I love this blog. It's amazing how much I used to write. I was so young and so passionate! My ego was totally huge! I had no idea how much of a ride I was really in for! It's a good thing, too!!

So yeah. I got a job. I've been bouncing around a bunch of different technologies for the last few years, trying to figure out where to land, what to do, what to be. Emmitt-Nershi is kind of chilling for the time being, and I happened to find out this was going to be the case the day I got a call back from a recruiter to tell me that the interview that I went on a few days earlier wanted to talk to my references...


So let's back up a minute. I'd been out on my own (a rather precarious place to be), freelancing whatever web stuff I could get my hands on and playing some really fun bluegrass with ENB. We had our third back in April of last year, possibly the most handsome Grubb boy yet, although they're all pretty much in a league of their own as far as cuteness is concerned. You never realize the ways in which your life is going to change when this whole new-kid thing happens, but the biggest difference this time was that I had absolutely no time or energy to go and scare up new work. We had been at about 80% of bandwidth with 2 kids, and now we were permanently at about 140%. My earnest advice to young parents – two is enough. Maybe if we had family nearby or something, but damn.

So anyway, throughout the summer I'd been fielding emails and phone calls from recruiters, kinda curious what would stick. I got one in August or so asking if I might be interested in a full time gig. I was interested in not having to chase down new work and I was interested in a salary and benefits, so sure, what's the gig? It turned out to be a Drupal gig in the publishing business up here in Jersey, not NYC (where the vast majority of programming jobs up here are).

So to make a long story shorter so that I can finish it, it turns out they're working on a lot of really cool stuff at a very transitional time for the company. It's the publishing business, and perhaps you've heard a bit about the upheaval in that particular business sector lately? Anyway, this company has been around for decades and has a couple dozen different magazines, mostly really technical trade magazines, stuff for which I have no background. They have websites in support of all of those magazines though, which means about 24 websites – half of which had already been moved into a large D7 multisite install, and the other half of which were in the process of being migrated into the same base.

Of 120 people in the entire company, there are 3 Drupal developers, of which I'm one. There's a “web production” team, but what they actually do is build emails. There's a dedicated designer who has been doing the front end/theming stuff for all these sites that we're rolling out, but she's leaving next week for good. What that means is that I'll probably be put somewhat back in charge of the front end (where I started), but in the meantime I've been working with a dude named Mike who is exactly the guy I've been looking for for the last 4.5 years. He's really good, really patient, really free with his copious knowledge, and seems genuinely stoked to have me on board. And over the last 4 months or so, my skills have started really snowballing.

I still suck at this stuff, but I'm getting a lot better, and fast. I'm getting pretty comfortable with the Ops part of DevOps, and am even writing some decent Drupal code. I've been working on a contract project for the last 3 months and I can confidently say that it would probably be over my head if not for the stuff I'm being exposed to on this gig. If nothing else it's given me a lot more confidence that I actually know some stuff now. They in house bugtracker is called Redmine, which just happens to be a Rails thing. Nobody in the company knew Rails or Ruby server setup, so it's become mine. It's a huge PITA, but I'm learning a hell of a lot about this tiny little OSS community around Redmine, and how to run a production Ruby app that gets a fair amount of traffic. And the hell that being stuck in a Ruby deployment situation can be.

I was given a ginormous Dell laptop running Windows 7 as my work computer – the only thing that looked like it might be a hiccup in the awesomeness. Given that there are only 3 real developers in the entire company though, the corporate process and restrictions that I've been afraid of for the last 34 years were pretty much non-existent, at least as far as what I could do with my laptop were concerned. I installed a VM running Ubuntu 12.04 the second day and have been working in Ubuntu for 8 hours a day. This is in addition to working between 10 and 20 hours a week on the contract work that I still have, and unfortunately have to do to make the ends meet (supporting a family of 5 in NJ is expensive as it turns out). I don't think I could keep up a 55 hour week forever, but who knows? Practicing and practicing and practicing in the service of getting really, really good at something has pretty much been what I do since I've been a teenager, so in that regard I'm well equipped.

I finally feel like I'm at the station, though not on the train yet. This is the training ground I've been looking for for 4 years now. My mission for this year is to get really involved with the Drupal community. I didn't get very excited by D7, but D8 is looking like Drupal is finally going to broaden it's scope into a project that encompasses modern development techniques that are going to be useful beyond Drupal's borders – a rather big hangup of mine as far as putting eggs in the Drupal basket is concerned.

So anyway, that's about it for now. Need to play more music, but I will again some day.

#life

WARNING! D6 code ahead!!

As improved as D7 is, they don't seem to have caught on to this one, so maybe this will help those of you as well.


The internet is surprisingly scarce on information about this topic. Maybe most people don't need to alter the password retrieval process, but I've got a client with a variety of different roles on their site. These different roles have different profiles via the Content Profile module. On account of numerous other account/profile management related modules that I've added – LoginToboggan, Auto Assign role, Invite – the whole process as well as the standard Drupal callbacks have gotten a little muddled.

By muddled I mean broken. For instance, the standard user/%uid%/edit page is broken to hell. I don't know why really except that there's some competing permissions/access thing that I just can't, for the life of me, find. Unfortunately this is the page that the stock Drupal password reset process brings you to to reset your password after logging in.

“No sweat!”, you might say. “Just call hook_password_reset() and be on your merry way!”

“I'd love to!”, I'd say in reply. “But there is no hook_password_reset()!”

After much digging I finally found a clue to look in the user.module in a file called user.pages.inc. Herein lies the function that handles this very process – user_pass_reset(). Reading through this function is pretty cool. It's very readable, but also feels kind of bloated and hackish in a way that makes me feel a little better about how shitty a programmer I actually am.

Down around line 111 is where the magic happens. Now, if you're reading this, you've probably tried everything I tried. I tried setting #redirect on the form, which doesn't work because Drupal will ignore #redirect on the form if #action is set. I tried unsetting #action on the form, which breaks the whole thing because Drupal relies on that very unique URL being submitted to validate the password reset. All that logic can be viewed in user_pass_reset, so there's no monkeying with that. All you want to do is change that stinking URL inside that drupal_goto() and the end of that function! Has noone encountered this use case before?!?

So I did what any desperate soul would do. I hacked core. I didn't respect myself afterward, but at least the process worked like my client needed it to.

Step away, do some thinking. The comment at the top of the function says “menu callback for”...

Enter hook_menu_alter().

So obviously, the solution requires a custom module. In the absence of a hook_password_reset(), you have to move a little deeper down the chain. hook_menu_alter() is the backdoor into the club of elation that is having this project behind you. hook_menu_alter() lets you grab and alter any menu item in the whole process. Looking a little higher in the call chain on the User.module I take a peek inside user_menu().

$items['user/reset/%/%/%'] = array(
	'title' => 'Reset password', 
	'page callback' => 'drupal_get_form', 
	'page arguments' => array('user_pass_reset', 2, 3, 4), 
	'access callback' => TRUE, 
	'type' => MENU_CALLBACK, 
	'file' => 'user.pages.inc',
);

So let's copy that user_pass_reset() into our module (the sweet_module in this case) and rename it sweet_module_user_pass_reset(). Then let's define my_menu_alter() to be something like this -

function sweet_module_menu_alter(&$items) {
	$items['user/reset/%/%/%'] = array(
	'title' => 'Reset password',
	'page callback' => 'drupal_get_form',
	'page arguments' => array('sweet_module_user_pass_reset', 2, 3, 4),
	'access callback' => TRUE,
	'type' => MENU_CALLBACK,
}

Note the new function name for the callback, and the removal of the 'file' bit at the end. Flush your menu cache and you should now be hooking into that damn function finally.

What you do now is totally up to you, but you can hack away at the entire function until it behaves properly. In my case it was sending this type of user this way and that type of user that way. And nobody to the stock drupal user edit path. And you didn't hack core, and you can keep upgrading your site like you always did with the flick of a drush command without ever having to worry.

You're welcome.

#drupal

So here I go again.

I've written at length on this blog over the years about Drupal. It's a great tool for getting things done right now. It's seen a huge uptake over the last couple years among government agencies and educational institutions, so being versed in Drupal development is a good career bet at the moment, meaning over the next couple years.

In the longer term though, I see it as pretty shaky and here's why.

Drupal's raison d'etre is that non-technical users (meaning – people who aren't programmers) can build fairly complex sites without writing a lick of code. It has seen massive growth, mainly because the fact that websites are actually computer applications with which users interact is a fact that is really just dawning on the majority of people who use the internet and/or need a website for their business. Most people don't have CS degrees, but every business needs a website. Thus, they either turn to someone who knows how to build websites, or they hook up with GoDaddy and do a one-click install of Wordpress.

However...

I think the world at large is rapidly getting used to the idea of writing code, and the proportion of people out there who are at least somewhat comfortable enough with that idea to hack together their idea is growing. And their idea might not be a good candidate for a Drupal site. The other thing about Drupal is that whatever your idea is, you can probably get pretty close without writing code, but chances are high that it's not going to be exactly what you want without writing some code (or hiring someone to write some code). And at that point, you either settle for something that isn't what you want or you teach yourself to write some code. And after you get comfortable with writing code, you start to wonder why you are dealing with all of this Drupal overhead that you didn't ask for in the first place. And some time after that you might find a language like Ruby, that makes infinite more sense to me than PHP, and looks a hell of a lot prettier to boot. And soon after that you might find it hard to get excited about Drupal work, because overriding other people's code is way less fun than writing your own to begin with, and that's the vast majority of working with Drupal code, at least form the backend.

#drupal

v0 – Blogger.

I think I got 3 or 4 posts in before realizing I wanted to try actually throwing up a website of my own.

v1 – Wordpress

One click installer at GoDaddy, my webhost for the first three months.

v2 – Drupal

I wanted to get more involved in the development side of it and was really loving the opportunities in the Drupal job market. Migrating a Wordpress blog was so predictable that there was a Drupal module to do so, and it brought all of my old posts, categories, everything. The setup of the categories was a little weird – it dumped everything into a Drupal Taxonomy called “Wordpress Categories”. I guess whomever wrote the module was tired of writing the module by the time they got to that point.

v3 – Back to Wordpress

Not really sure why. I have a vague recollection of blogging in Drupal just not feeling as fun, so I didn't do it as much. I'd moved into MarsEdit because it felt more “hacker-y”, but it didn't provide as fertile-seeming an environment as good ole Wordpress.

v4 – Back to Drupal

I was serious about Drupal now. I was starting to “get” Views and needed to put a portfolio together since I'd quit RRE by that point.

v5 – Jekyll

I was starting to get braver, even though this is the dead simplest tool out there. Some other soul put together a Ruby script that'd pull all my posts out of the Drupal DB and turn them into Markdown, the format that Jekyll required. Still, blogging just didn't seem quite as fun. I guess I didn't realize how much time I had in that tour bus after all.

v6 – Back to Drupal

I was pretty much a full time Drupalist by this point and put together a pretty decent portfolio site with it in a day or so. I was working on my Sass library and put together a “responsive” grid in a few hours. Good times. Lasted me a while but then the itch came again.

v7 – Rails

Finally came back to Rails about two months ago and rewrote this entire blog in about 6 hours from scratch. Not the trickiest of endeavors, but it took me 4 years to do that 6 hour rewrite. Wrote a Ruby script with Sequel to pull the posts out of the Drupal DB and put them into my Rails DB, URL aliases and all. The migration script took way longer than the actual build. Drupal likes to “normalize the fuck out of your data”, which means it scatters things all over the place in an attempt to store them in only one place. Did you know that the name of the column in the URL alias table is “src”, whereas everywhere else in the Drupal DB it's “source”. Shit like that, Drupal... I was able to put together with 4 MySQL tables what took Drupal something like 75 tables to do. Of course, the vast majority of those DB tables were never touched, which is a whole other thing about Drupal.

v7.1 – current – Rails and Mongo

I've been playing with Mongo for the last couple weeks and figured “fuck it” and did it yesterday. Took me 20 minutes to migrate the posts out of MySQL and into Mongo and replace ActiveRecord with MongoMapper. 20 minutes. It took me the rest of the night to get sessions and login working again since I dumped my entire user row into Mongo, so I had keys for :_id and :id in the same document. Once I realized that and played nicely with Mongo's idea of id (same for posts), everything worked out nicely.

Of course, for all that fun with cutting edge technology, there's going to be some sysadmin pain. And there was. I followed the wrong instruction to get Mongo going on this Debian server (followed the Ubuntu instructions. Whoops!) and spent the better part of this morning trying to untangle the mess. Never did, but typed mongo and it magically came up somehow. then spent the rest of the day migrating data and trying to figure why the hell ActiveRecord is still looking for a DB even though I ripped out every vestige of AR. Anyway, it's working now, and this will be the first post committed to a non-relational (aka NoSQL) database in my programming career.

Happy times. The amazing thing is that I've kept all of these posts with me through 8 or 9 different databases so far. Yay IBD.

#random

I see lots of these posts on Craigslist -

Meeting Planning firm is seeking a web designer on a freelance basis to work with provided art work in Photoshop/Illustrator and host onto closed invitation system.

Requirements:

Web designer must have design sense. Must code HTML/CSS — Java a plus.

I think they'd probably get taken more seriously if they replaced all instances of “designer” with “developer”.

So, it's pretty simple. A designer designs. A developer develops. “Web designer must have design sense.” Well, yeah. A designer must design.

So there are two parts to building anything on the web. See if you can guess what they are. I like to describe the difference to clients or my parents like this.


Think of your website project as building a house. Before you start building anything you probably want to meet with an architect. That architect will meet with you to find out what kind of house you want, will use their prior experience to help guide you with different ideas and options, and will ultimately deliver detailed plans of what your house will look like so that you can go hire someone who will actually build it.

The person who will actually build it is the general contractor. They have experience in building things from plans and can help guide you with difference options based on their experiences once in the building phase of the project.

Designer – architect. Developer – general contractor.

Do you already have artwork that you're happy with and need someone to help you build the darn thing? You need a web developer.

Are you just starting the planning phase and need to flesh out the ideas in your head? You probably want to meet with a web designer.

Note

This is a very loose metaphor. Many designers have some skills coding, and many developers have at least some clue about design. I am one that has some clue. I get layout, flow, what makes for good and bad user interface. I am a hell of a finish carpenter. I can make buttons like you wouldn't believe. What I struggle with is the “blank canvas”. I can't brand your idea. I can't come up with what I'd call a “design”. for that I call my buddy Bob. Bob is an artist. He does shit in Photoshop you wouldn't believe, but couldn't/wouldn't write a line of HTML if his life depended on it. We work really well together, and less well apart.

I sincerely hope this helps at least one person write a better Craigslist ad. Thank you.

It's simple really, but you have to know a few things about how a browser renders a page to get it. jQuery is a wonderful thing because it removes the need to know a LOT about a LOT to do with the DOM and still get amazingly cool things done – slideshows, form validation, etc. However, if you go and learn a bit more about how JavaScript works in a browser you can go beyond copying and pasting code snippets and really start getting creative. Or surgical. Or whatever you want.

So when code comes through the wire into your browser, your browser parses it from the top down. That's the fancy, mechanical way of saying it follows the instructions from the top of the page to the bottom. Any time it encounters a piece of JavaScript, be it either directly embedded in the page source or linked to another file (from your site or somewhere else), it will take a detour into that JavaScript and execute any instructions in that script before it moves on with parsing the rest of the page. If you put a humongous blob of JavaScript at the top of the page, your site will seem slower because everybody's browser is stopping to follow/execute the JS before it moves on down into the content of your page.

That means what? Well say you have a date field on a form in your page and you want to use the jQueryUI datepicker on it. All you then need to do is write $('#event-date').datepicker(); and you have a datepicker. But there's a catch. If you put that code at the top of your page (in the head), or in a file that's linked to in the head of your page (using script src), then the browser will execute that instruction as soon as it encounters it. The datepicker won't show up. You'll pull your hair out. The reason is that the browser goes and sets a datepicker() on an element that it doesn't know yet exists because it hasn't gotten down into the page to actually render that yet. It'd be like giving the steps for a recipe before giving the ingredient list. Only your computer is too stupid to look down the page and read the ingredients list. It'll just say "flour? There's no flour on my table. Moving on." and no datepicker.

There are two solutions.

One is to delay the execution of the datepicker() instruction until the page has been rendered/parsed/whatever you want to call it. You do this by putting all of your custom code inside a call to jQuery(document).ready() like this -

jQuery(document).ready(function($) {
 $('#event_date').datepicker();
}

// equivalent (and better) ->

jQuery(function($) {
 $('#event_date').datepicker();
}

That basically “hides” the instruction inside of a function that doesn't get called until the page is ready. When the page is “ready” (that happens once the browser has loaded the whole page and knows what's there) the function fires and your browser gets the instruction to add datepicker functionality to the field that it knows exists now. You can put as many things inside that function as you want, all your code if you like. And you should learn more about JS because it's a wonderfully cool language.

The other solution is to put all of your JS code at the bottom of the page. This was in vogue over the last couple years as a good way to increase the perceived performance of your website anyway, but I bet lots of people were still wrapping their jQuery code in document.ready() even though they didn't need to. I was anyway. Since your JS isn't encountered by the browser until after the rest of the page you don't need to wrap it in that call if you put it at the bottom.

#javascript

I've just recently started to discover what Dropbox is really good at. I've had one for at least a year and almost never used it. The only thing I'd ever really used it for was client assets like PSDs and the like. I just discovered the Dropbox secret weapon – the symlink.

A symlink (short for symbolic link), if you don't know, is basically like a pointer to a folder/directory. It's a really nifty way to help you organize your filesystem. Say you use iTunes and for some reason you like to dig around in your iTunes music folder a lot. Rather than going into the Finder and drilling down into the folder from there, you could just create a symlink from your Desktop into that folder. Then, without actually moving your iTunes folder and possibly screwing things up, you've just created a shortcut to get into that folder.

So say I've got a client. Let's also say I have two different computers that I regularly work from. I've started using some code to test the project with that I don't want to check into their repo because they don't even really need to know that I'm doing automated testing with a tool of my choosing. So I add the directory that contains the tests to my .gitignore file. But (!), then I switch computers and need access to those test files. A git pull doesn't do anything because those files were never checked in. This struck me as the perfect use case for my dormant Dropbox folder.

I moved all the test files into a folder in my Dropbox. Then I created symlinks on both computers from that folder into the project directory. That way, they stay in sync across my two computers without needing to be checked in to the client's repo. Thank you Dropbox!

#devops #workflow #git

Hi, this post is wildly out of date. I tried to follow it to set up some test within the last year and none of this stuff actually worked. The concepts are likely still valid, but don't expect to be able to copy much.

jg – Oct 2015


I went down to Drupaldelphia (the name should be self-explanatory) a couple weeks ago mainly for a session called “Testing your site with CasperJS”. CasperJS is what's known as a “headless” Webkit testing framework. That means it's essentially a browser and can click around your site, fill out forms, test validation, etc. It's pretty much exactly what I've been looking for for one of my clients for a while now. I knew they were out there, but there's nothing like having something shown to you for an hour to really help you get your head around it.

So I came home and immediately went to work trying to figure out how to use it.

I have an assignment right now from a client to reorganize their website per instructions from their SEO vendor. It's about 10 or 12 little, tiny changes to about 50 pages. Most of the changes involve redoing their URLs, splitting content up, updating meta tags, page titles, and the like. The pattern for the updates is completely repetitive, but altogether we're talking about 500 or so changes to a Drupal site, which means it's going to be me sitting there typing all of this stuff into a web page edit form. I will screw up. I wont know it until somebody sends an email yelling at me. Enter Casper.

Casper is a Javascript tool. You can also use Coffeescript.

The tricky thing about Casper is that it basically has two scopes. One is the testing environment in which you write most of your code, and the other is the actual page environment, where the actual markup you might want to test is. Casper has a host of functions that all center around one called Casper.evaluate(). Code executed here in executed in the browser window context/scope.

I had a hell of a time trying to figure out how to test the meta tag for these pages. I knew I wanted to set up an array for the 25 different states =

A few utility functions to help out -

The base url = url = "http://example.com"

Create an instance of Casper. casper = require('casper').create()

I'll plop the code first and explain after.

The first line casper.start() obviously starts the test. each is a method which will look familiar if you work with any modern OO languages. It iterates over an array. The array in this case is states. self is the Casper instance, and state is the name of the current member of the array we're testing.

Most of this is pretty standard Casper 101. The more interesting stuff is about halfway down.

@evaluate drops us into the actual page context. There we can operate on what's in the DOM, as well as things that appear as a result of actions in the DOM. The contents of that function make a selection from one select box. The value of that select box determines what gets returned from a jQuery Ajax call. The return of that Ajax call populates another select box. Ajax 101, but we're testing, automatically, 25 pages here! You have to use legit javascript unless you can figure out how to get jQuery into this context. I haven't figured it out yet because actually learning core JS is something I need to do more of.

After that all happens we drop back into the DOM again to make sure that there are more than one option in the second select box.

Finally – Meta tags

Sorry. So I'm kinda new to JS and function scope gave me a time on this one. The main problem I was having was trying to get a piece of text that could only be found in the DOM scope back into Casper.each() scope so I could run some sort of assert() on it. Turns out it was really easy after several days of passive Googling.

So this drops back into the DOM and pulls out the contents of the Description meta tag and assigns it to the var descrip. Then all you have to do is a simple assertEquals()

Presto.

#testing #javascript

The short version -

https://github.com/JGrubb/laptop


The longer version -

Setting up your Macintosh for Rails development is actually sort of like Rail development itself – you have to at least kind of know about a lot of different things before you can really get anywhere. I'd say I'm new to Rails development even though I've been poking at it since 2.1. 2.1 is the first version I remember after I first bought a Mac and started trying to teach myself to “program”. It's taken me until 3.2 to come back to Rails with enough of a rounded web development skill set to really be able to fly around and build the things that have been locked up in my head for the last 4 years. I've written about this before, as have many others, but Rails isn't exactly a framework for beginners. That is, it requires you to have at least some idea of what you're trying to do before it will let you sit down and do it. Many of us probably grew up programming in Basic, but some of us might have gotten sidetracked into other vocations besides software development. the world has come quite a ways since then. So this post, or series maybe, will be an attempt to teach you not only what to install, but why.


Step 0 – the compiler

If you are on a Mac, you are lucky to already have a rather large lot of tools that software developers use already installed. But they haven't given you everything. Some of the ones that will come in handy down the line as you get further in to this process will have to be installed by you. This will toughen you up and help you get more acquainted with a side of your computer that perhaps you didn't even know about. The world (and by that I mean the guy who made up Homebrew) has made life a lot easier for the Mac-using developer, but in order to access all of those goodies, you'll need to install a compiler. A compiler is basically a piece of software that builds other software.

One of the first pieces of software that you're going to build will be the Ruby language itself, but first things first.

XCode

If you're on Lion or the latest Snow Leopard, you can open up the App Store and search for “XCode”. XCode is the official Apple development environment. With it you can build iPhone apps, applications for the Macintosh, and really pretty much anything that isn't specifically meant to be run on Windows. It's an enormous download, about 4GB recently.

After you download it, open it up, open the Preferences, and find the extra downloads part. What you're looking for is the “Command Line Tools” bundle. It's in one of the tabs toward the right. Download and install. Should be fairly simple. Just to be safe, restart your computer. Once it's back on open the Terminal.app (get used to the terminal, you'll be there a lot) and type which gcc. If it doesn't spit anything back at you, you haven't installed the compiler. Figure it out and come back for the next step – the package manager.

#rails #devops