diff --git a/gatsby-config.js b/gatsby-config.js index 3acbd31b..e5c1931e 100644 --- a/gatsby-config.js +++ b/gatsby-config.js @@ -32,7 +32,7 @@ const plugins = () => [ }, // Posts - ...['2018', '2019', '2020'].map((year) => ({ + ...['2013', '2015', '2017', '2018', '2019', '2020'].map((year) => ({ resolve: 'gatsby-source-filesystem', options: { name: 'posts', path: `${__dirname}/posts/${year}` }, })), diff --git a/posts/2013/easy-favicons.md b/posts/2013/easy-favicons.md new file mode 100644 index 00000000..4304d483 --- /dev/null +++ b/posts/2013/easy-favicons.md @@ -0,0 +1,38 @@ +--- +date: '2013-01-07' +title: Easily make favicon.ico +layout: simple +tags: [Development] +description: Never look for a favicon generator again. +--- + +### + + + +To create `favicon.ico`, you don't need anything other than ImageMagick. + +```bash +brew install imagemagick +sudo apt-get install imagemagick +``` + +### Creating icon files + + + +Use it to convert `.png`'s into `.ico` (use 32px and 16px sizes for retina compatibility). + +``` +convert favicon-32.png favicon-16.png favicon.ico +``` + +### Resizing icons + + + +You can even use it to generate a 16px version from a 32px: + +``` +convert favicon-32.png -resize 16x16 favicon16.png +``` diff --git a/posts/2013/get-started-with-ansible.md.disabled b/posts/2013/get-started-with-ansible.md.disabled new file mode 100644 index 00000000..53e66404 --- /dev/null +++ b/posts/2013/get-started-with-ansible.md.disabled @@ -0,0 +1,104 @@ +--- +date: '2013-11-27' +title: Get started with Ansible in 2 minutes +tags: [Development, Ansible] +description: Provisioning servers is easy using Ansible. Here's a guide to set it up from scratch. +--- + +[Ansible](http://ansible.com) is a fantastic tool for provisioning servers. I personally prefer it over Chef, Puppet and Salt. Here's how to get an Ansible project started. + +## Install Ansible + +Ansible is officially available via `pip`. + +```sh +brew install ansible # OSX +[sudo] pip install ansible # elsewhere +``` + + + +## Start your project + +Make the directory. Put this under version control, preferrably. + +```sh +~$ mkdir setup +~$ cd setup +``` + + + +## Create an inventory file + +This is a list of hosts you want to manage, grouped into groups. (Hint: try +using 127.0.0.1 to deploy to your local machine) + +```dosini +# ~/setup/hosts + +[sites] +127.0.0.1 +192.168.0.1 +192.168.0.2 +192.168.0.3 +``` + +## Create your first Playbook + +A playbook is just a YAML file. + +```yaml +# ~/setup/playbook.yml + +- hosts: 127.0.0.1 + user: root + tasks: + - name: install nginx + apt: pkg=nginx state=present + + - name: start nginx every bootup + service: name=nginx state=started enabled=yes + + - name: do something in the shell + shell: echo hello > /tmp/abc.txt + + - name: install bundler + gem: name=bundler state=latest +``` + +## Run it + +Use the `ansible-playbook` command. + +```sh +~/setup$ ls +hosts +playbook.yml +``` + + + +```sh +~/setup$ ansible-playbook -i hosts playbook.yml +PLAY [all] ******************************************************************** + +GATHERING FACTS *************************************************************** +ok: [127.0.0.1] + +TASK: [install nginx] ********************************************************* +ok: [127.0.0.1] + +TASK: [start nginx every bootup] ********************************************** +ok: [127.0.0.1] +... +``` + + + +## Further reading + +Ansible's source is available via GitHub: [ansible/ansible](https://github.com/ansible/ansible). + +- [Getting Started With Ansible](http://lowendbox.com/blog/getting-started-with-ansible/) (lowendbox.com) +- [Ansible Documentation](http://docs.ansible.com/modules.html) (ansible.com) diff --git a/posts/2015/alfred-color-schemes.md.disabled b/posts/2015/alfred-color-schemes.md.disabled new file mode 100644 index 00000000..9c9fcb1f --- /dev/null +++ b/posts/2015/alfred-color-schemes.md.disabled @@ -0,0 +1,17 @@ +--- +date: '2015-06-06' +title: Alfred color schemes +description: Here's my nice color scheme for Alfred. +tags: [MacOS] +attachments: + - ./alfred-color-schemes/alfred-dark.png +--- + +
+ +
+ +Today I learned you can change colors in [Alfred]. [Here's mine.][color] + +[alfred]: http://alfredapp.com +[color]: alfred://theme/searchForegroundColor=rgba(255,255,255,1.00)&resultSubtextFontSize=1&searchSelectionForegroundColor=rgba(0,0,0,1.00)&separatorColor=rgba(0,0,0,0.00)&resultSelectedBackgroundColor=rgba(22,9,7,0.25)&shortcutColor=rgba(76,76,76,1.00)&scrollbarColor=rgba(38,38,38,1.00)&imageStyle=8&resultSubtextFont=Helvetica%20Neue%20Light&background=rgba(30,31,49,0.84)&shortcutFontSize=2&searchFontSize=3&resultSubtextColor=rgba(127,127,127,1.00)&searchBackgroundColor=rgba(0,0,0,0.00)&name=OSX%20Yosemite%20Dark%20%2B&resultTextFontSize=2&resultSelectedSubtextColor=rgba(186,186,186,1.00)&shortcutSelectedColor=rgba(127,127,127,1.00)&widthSize=2&border=rgba(4,19,37,0.00)&resultTextFont=Helvetica%20Neue%20Light&resultTextColor=rgba(255,255,255,1.00)&cornerRoundness=3&searchFont=Helvetica%20Neue%20Light&searchPaddingSize=0&credits=&searchSelectionBackgroundColor=rgba(178,215,255,1.00)&resultSelectedTextColor=rgba(255,255,255,1.00)&resultPaddingSize=2&shortcutFont=Helvetica%20Neue%20Light diff --git a/posts/2015/alfred-color-schemes/alfred-dark.png b/posts/2015/alfred-color-schemes/alfred-dark.png new file mode 100644 index 00000000..ea2bfe52 Binary files /dev/null and b/posts/2015/alfred-color-schemes/alfred-dark.png differ diff --git a/posts/2015/babel-ie-class-inheritance.md.disabled b/posts/2015/babel-ie-class-inheritance.md.disabled new file mode 100644 index 00000000..4fa1d64c --- /dev/null +++ b/posts/2015/babel-ie-class-inheritance.md.disabled @@ -0,0 +1,28 @@ +--- +date: '2015-05-26' +title: Babel class inheritance in Internet Explorer +tags: [JavaScript] +description: IE doesn't support class inheritance in Babel. Here's a way to fix that. +outdated: | + This post was written for Babel version 5. Also, this post is made for supporting IE8, a version which has very negligible use in 2019. +--- + +```js +class Circle extends Shape { + getArea() { + return this.radius * Math.PI * 2 + } +} +``` + +### Class inheritance caveats + +When using class inheritance with Babel.js, keep in mind that IE10 and below are [not supported](http://babeljs.io/docs/advanced/caveats) by default. Babel's class inheritance relies on \_\_\_proto\_\_\_ which is not available on legacy IE versions. + +Using _super_ is also not supported on IE8 and below, as it compiles down to using [Object.getPrototypeOf](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getPrototypeOf). + +To get around these caveats, use the [protoToAssign](http://babeljs.io/docs/advanced/transformers/spec/proto-to-assign/) transformer to make inheritance work, along with loose mode on classes to enable support for `super`. + +```bash +babel --optional spec.protoToAssign --loose es6.classes script.js +``` diff --git a/posts/2015/css-ligatures.md.disabled b/posts/2015/css-ligatures.md.disabled new file mode 100644 index 00000000..70196bef --- /dev/null +++ b/posts/2015/css-ligatures.md.disabled @@ -0,0 +1,60 @@ +--- +date: '2015-03-05' +title: Using ligatures in CSS +tags: [CSS] +description: Implement fancy typographic ligatures with just CSS... no images required +layout: simple +attachments: + - './css-ligatures/ligatures.png' +--- + +Headings can benefit from nice ligatures. Use the [font-feature-settings](https://developer.mozilla.org/en-US/docs/Web/CSS/font-feature-settings) CSS property to enable these OpenType features. Here's a snippet to start you with: + +```css +.headings { + /* don't display digraphs in languages that don't support it */ + font-language-override: normal; + + /* use font-defined kerning info */ + font-kerning: auto; + + /* opentype options: kerning, ligatures, horiz ligatures, + * discretionary ligatures, contextual swash */ + font-feature-settings: 'kern', 'liga', 'dlig', 'hlig', 'cswh'; + + /* allow browser to auto-infer missing glyphs */ + font-synthesis: weight style; +} +``` + +### Example + + + +Here is what they would look like with EB Garamond. Be sure to also check [List of OpenType features](http://en.wikipedia.org/wiki/List_of_typographic_features) for more features you can use. + +
+ +
+ +## Use brick.im + +Fonts from Typekit and Google Web Fonts may be stripped of all these extra OpenType information. Use fonts from [brick.im](http://brick.im/) instead. + +## Swashes + +First letters can look good with swashes (eg, an `R` with an extra long stem). Just be sure to turn this on on a as-needed basis since it may overlap with the rest of the text. + +```css +.headings:first-letter { + font-feature-settings: 'kern', 'swsh'; +} +``` + +## Disabling ligatures + +You can turn off ligatures using `font-feature-settings`. + +```css +font-feature-settings: 'liga' 0; +``` diff --git a/posts/2015/css-ligatures/ligatures.png b/posts/2015/css-ligatures/ligatures.png new file mode 100644 index 00000000..0d46e1ea Binary files /dev/null and b/posts/2015/css-ligatures/ligatures.png differ diff --git a/posts/2015/easy-color-computations.md.disabled b/posts/2015/easy-color-computations.md.disabled new file mode 100644 index 00000000..13640706 --- /dev/null +++ b/posts/2015/easy-color-computations.md.disabled @@ -0,0 +1,50 @@ +--- +date: '2015-03-03' +layout: simple +title: Easy color computations using Sass & Stylus CLI +tags: [CSS, Sass] +description: Need to lighten or darken colors easily from the command line? Here's how you can do it with Sass or Stylus. +--- + +**Use [stylus]'s CLI for easy color computations.** Stylus CLI is faster than Sass and has shorter syntax for color computations. + +```bash +$ npm i -g stylus +$ stylus -i +``` + +Here's _lighten_ and _hue shift_: + +``` +> #aaa + 10% +=> #b2b2b2 + +> #ff0 - 10deg +=> #ffd500 +``` + +### One-liner version + + + +Not quite as elegant, but it can be useful. + +```bash +echo "*{a: #ff0 - 10deg}" | stylus -p +* { a: #ffd500; } +``` + +### Using Sass + + + +If you need Sass for some reason, it also takes `-i`. + +```sh +$ gem install sass +$ sass -i +>> darken(red, 10%) +#cc0000 +``` + +[stylus]: http://learnboost.github.io/stylus diff --git a/posts/2015/es6-class-pitfalls.md.disabled b/posts/2015/es6-class-pitfalls.md.disabled new file mode 100644 index 00000000..963e988e --- /dev/null +++ b/posts/2015/es6-class-pitfalls.md.disabled @@ -0,0 +1,41 @@ +--- +date: '2015-02-25' +title: ES6 class pitfalls +tags: [JavaScript] +description: Keep aware of this one caveat when making classes in ES6. +--- + +```js +class Shape { + get area() { + return this.width * this.height + } +} +``` + + + +### Decorated functions + + + +ES6 makes it easy to define classes, but you can't have decorated functions. For that, you'll still need to drop to using `prototype`. + +```js +Shape.prototype.iterate = memoize(function () { + ... +}) +``` + +### Non-method attributes + + + +Same with non-method attributes. + +```js +Shape.prototype.template = require('fs').readFileSync( + './template.html', + 'utf-8' +) +``` diff --git a/posts/2015/extend-sucks.md.disabled b/posts/2015/extend-sucks.md.disabled new file mode 100644 index 00000000..a165bf2d --- /dev/null +++ b/posts/2015/extend-sucks.md.disabled @@ -0,0 +1,46 @@ +--- +date: '2015-02-16' +title: '@extend sucks' +tags: [CSS, Sass] +description: Let me tell you why you should never use @extend again. +--- + +### What's with @extend? + + + +Using `@extend` in CSS preprocessors really suck... so use mixins instead. Consider this example. + +```scss +%antialias { + text-rendering: optimizeLegibility !important; + -webkit-font-smoothing: antialiased !important; + -moz-osx-font-smoothing: grayscale; +} +``` + +### Using @extend + + + +Seems innocent enough: + +```scss +h3 { + @extend %antialias; +} +``` + +### With media queries + + + +Until you try it with media queries, then it won't work. Consider using mixins instead. + +```scss +@media (max-width: 300px) { + h3 { + @extend %antialias; + } +} +``` diff --git a/posts/2015/factory_girl_with_custom_factories.md.disabled b/posts/2015/factory_girl_with_custom_factories.md.disabled new file mode 100644 index 00000000..317656e6 --- /dev/null +++ b/posts/2015/factory_girl_with_custom_factories.md.disabled @@ -0,0 +1,75 @@ +--- +date: '2015-11-28' +title: Using factory_bot with custom factories +tags: [Ruby] +description: Do you use an actual factory pattern in Rails? Set up factory_bot to use this factory. +--- + +[Factory Bot](https://github.com/thoughtbot/factory_bot) is great when creating 'presets' of ActiveRecord models. This is all you really need for most simple cases. + +```rb +factory :user do + name { 'John' } + email { 'john@example.com' } +end +``` + +```rb +create :user +# same as User.create(name: 'John', email: 'john@example.com') +``` + +### Using custom factories + +At some point however, your models may get too complicated and you may need an actual [factory](https://en.wikipedia.org/wiki/Factory_method_pattern)—a class that constructs an object and performs actions along with it. + +```rb +class UserCreator + attr_reader :user + + def initialize(attrs) + @user = User.create(attrs) + @user.profile = Profile.create(@user) + @user.posts = create_sample_post + end +end +``` + +```rb +creator = UserCreator.create(name: 'John') +creator.user +``` + +### Setting it up (the hard way) + +Factory Bot will then consume a class in this manner: + +``` +user = User.new +user.name = 'John' +user.save! +``` + +You can set up a `factory_bot` factory to use this by passing a `class` option. You'll have to make your factory implement these methods. This is silly and painful. + +```rb +factory :real_user, class: 'UserCreator' do + ... +end +``` + +```rb +create(:real_user).user +``` + +## Even easier + +Why not use the [attributes_for](http://www.rubydoc.info/gems/factory_bot/file/GETTING_STARTED.md#Using_factories) helper instead? + +```rb +UserCreator.create attributes_for(:user) +``` + +### Also see + +Also see the [Factory Bot cheatsheet](http://devhints.oo/factory_bot.html), along with other cheatsheets from my [cheatsheets](http://ricostacruz.com/cheatsheets) archive. diff --git a/posts/2015/get-started-with-mocha.md.disabled b/posts/2015/get-started-with-mocha.md.disabled new file mode 100644 index 00000000..87855eca --- /dev/null +++ b/posts/2015/get-started-with-mocha.md.disabled @@ -0,0 +1,150 @@ +--- +date: '2015-02-13' +title: Get started with Mocha testing +tags: [JavaScript] +description: In 60 seconds, let's learn how to write tests for any Node.js package. +--- + +Testing Node.js packages is easy. Here's the condensed 1-minute guide to setting up tests on a Node.js project. + +## Quickstart guide + +### Start with a package + + + +Make a `package.json` file if you don't have one yet, then install your weapons of choice. + +```bash +npm init +# just keep pressing enter. +# this will create the file `package.json` +``` + +```bash +npm install --save-dev mocha chai +``` + +### Write tests + + + +Make your first test file `test/my_test.js`: + +```js +/* test/my_test.js */ +var expect = require('chai').expect + +describe('my test suite', function() { + it('fails majestically', function() { + expect(3).to.eql(2) + }) +}) +``` + +### Update scripts + + + +Update your `package.json` to use mocha. + +```diff + "scripts": { +- "test": "echo \"Error: no test specified\" && exit 1" ++ "test": "mocha" + }, +``` + +## Run tests + +### + + + +Type `npm test` to run your tests. It should fail. Now go write tests that will pass! + +``` + my test suite + 1) fails majestically + + + 0 passing (17ms) + 1 failing + + 1) my test suite fails majestically: + + AssertionError: expected 3 to deeply equal 2 + + expected - actual + + +2 + -3 + + test/test.js:5:18: Context. +``` + +## Learn a bit more + +Here's a quick Mocha cheatsheet. Also see [mochajs.org](http://mochajs.org). + +```js +describe('test suite', function() { + beforeEach(function() { + /*...*/ + }) + afterEach(function() { + /*...*/ + }) + + before(function() { + /*...*/ + }) + after(function() { + /*...*/ + }) + + it('a basic test', function() { + /*...*/ + }) + + it('a test with a promise', function() { + return somePromiseObject + }) + + it('an asynchronous test', function(next) { + if (success) { + next() + } else { + next(error) + } + }) + + xit('use "xit" for pending tests', function() { + /*...*/ + }) +}) +``` + +## Expectations with Chai + +Here's a quick Chai cheatsheet. See [chaijs.com](http://chaijs.com/api/bdd/) for other `expect()`ations. + +```js +expect(3).to.eql(2) + +expect(obj).to.be.a('string') +expect(obj).to.be.null +expect(obj).to.be.true +expect(obj).to.be.false +expect(obj).to.be.undefined + +expect(list).to.include('item') +expect(list).to.have.length(3) +expect(list).to.have.length.gt(0) +``` + +## Further reading + +Use Sinon ([sinonjs.org][sinon]) for mocks. + +[mocha]: http://mochajs.org/ +[sinon]: http://sinonjs.org/ diff --git a/posts/2015/github-two-factor-authentication.md.disabled b/posts/2015/github-two-factor-authentication.md.disabled new file mode 100644 index 00000000..141b3926 --- /dev/null +++ b/posts/2015/github-two-factor-authentication.md.disabled @@ -0,0 +1,114 @@ +--- +date: '2015-03-27' +title: GitHub two-factor authenication guide +description: Secure your GitHub account with these easy steps. +tags: [Git] +--- + +Two-factor authentication in GitHub is pretty simple to set up, though the exact steps are layed out in maybe 3 different articles. I've consolidated them all together to this one simple guide. + +## Setting up 2FA + +### Enable 2FA + + + +Enable [2 factor authentication](https://github.com/settings/security) on the GitHub website (Settings → Security → Two Factor Authentication). + +
+ +[2 factor authentication settings](https://github.com/settings/security) _(github.com)_ + +
+ +### Prepare your phone + + + +Install [Google Authenticator](https://en.wikipedia.org/wiki/Google_Authenticator), or an equivalent of such. Works for iOS and Android. In Authenticator, tap _Add_, then _Scan Barcode_. Take a picture of the QR code on your computer. ([More info on apps](https://help.github.com/articles/configuring-two-factor-authentication-via-a-totp-mobile-app/)) + +
+ +[Google Authenticator](https://en.wikipedia.org/wiki/Google_Authenticator) _(en.wikipedia.org)_ + +
+ +## For HTTPS + +If you're using SSH keys to access your Git repos, you can skip this section. If you're using `https://` URL's in your Git repos, then read on! + +### Enable git credential caching + + + +On your computer, [enable caching your GitHub HTTPS credentials](https://help.github.com/articles/caching-your-github-password-in-git/). This allows you to store your 2FA token and not get asked for it everytime. + +```bash +# MacOS: +git config --global credential.helper osxkeychain +``` + +```bash +# Linux: +git config --global credential.helper cache +``` + +This only applies if you use HTTP authentication for your Git repositories. If you use SSH keys, this shouldn't be necessary. + +### Generate an API key + + + +[Generate a GitHub API key](https://github.com/settings/tokens) under "Personal Access Tokens". You'll use this for the Git command line. Leave the scopes unchanged. + +
+ +[Generate a GitHub API key](https://github.com/settings/tokens) _(github.com)_ + +
+ +### Use HTTPS on your repos + + + +If your git repos still use SSH (`git@github.com:user/repo.git`), change them to use HTTPS (`https://github.com/user/repo.git`). ([More info on remote URLs](https://help.github.com/articles/which-remote-url-should-i-use/#cloning-with-https-recommended)) + +```bash +cd project +vim .git/config +``` + +## Try it out + +### Git push + +Push a repo. You'll be asked for a password. Use the token for the password. You won't have to do this again if enabled credential caching. + +```bash +$ git push +``` + +``` +Username for 'https://github.com': rstacruz +Password for 'https://rstacruz@github.com': +``` + + + +### Save your recovery codes + +Get your [recovery codes](https://github.com/settings/auth/recovery-codes) (Settings → Security → Two Factor Authentication → Recovery Codes) and put them somewhere safe. This will allow you to log onto your account when, say, your phone isn't charged. ([More info on recovery codes](https://help.github.com/articles/downloading-your-two-factor-authentication-recovery-codes/)) + +## That's it! + +If everything worked, your `git push` should work just fine. + +### References + +- [Two factor authentication articles](https://help.github.com/categories/two-factor-authentication-2fa/) (github.com) +- [TOTP mobile apps](https://help.github.com/articles/configuring-two-factor-authentication-via-a-totp-mobile-app/) (github.com) +- [About two factor authentication](https://help.github.com/articles/about-two-factor-authentication/) (github.com) +- [Providing your 2FA Authentication Code](https://help.github.com/articles/providing-your-2fa-authentication-code/) (github.com) +- [Caching your GitHub password](https://help.github.com/articles/caching-your-github-password-in-git/) (github.com) +- [Downloading your two factor recovery codes](https://help.github.com/articles/downloading-your-two-factor-authentication-recovery-codes/) (github.com) +- [HTTPS remote URLs](https://help.github.com/articles/which-remote-url-should-i-use/) (github.com) diff --git a/posts/2015/ie-polyfills.md.disabled b/posts/2015/ie-polyfills.md.disabled new file mode 100644 index 00000000..f4d41fa9 --- /dev/null +++ b/posts/2015/ie-polyfills.md.disabled @@ -0,0 +1,57 @@ +--- +date: '2015-04-17' +title: Legacy IE polyfills +tags: [CSS] +layout: simple +description: Speed along your legacy IE development with this all-purpose 8-line snippet. +--- + +
+ +**Update (Jan 2019):** This document was written for Internet Explorer 8. IE9 and below occupies a global share of less than 0.17% today. Please rconsider not supporting IE versions below 11. + +
+ +Use this convenient snippet in almost any project. It will fill in some basic support for modern CSS features for legacy IE versions. + +```js + +``` + +## What's going on? + +The world has largely moved on from IE6 to IE8, but this is the least you can do to make your sites at least don't appear broken on older browsers. This snippet packs these 6 libraries which can be dropped into any project: + +- **[html5shiv]** — for new HTML5 tags like `
` (IE6-8) +- **[respond]** — for `@media` queries for responsive design (IE6-8) +- **[selectivizr]** — for all modern CSS selectors (IE6-8) +- **[json2]** — for JSON parsing (IE6-7) +- **[nwmatcher]** — the selector engine for selectivizr +- **[es5-shim]** — add ES5+ functions (IE6-8) + +It's wrapped in an [IE conditional comment][concom], so only the poor souls with outdated IE's will bear the extra load. + +### Selectivizr + +While Selectivizr has been abandoned at 1.0.2, there are new changes that haven't been officially released yet (see [#67](https://github.com/keithclark/selectivizr/issues/67)) that fixes interoperability with Respond.js. [This article](http://selectivizr.com/tests/respond) has the technical details, but is outdated in a few ways: Respond.js already [has support](https://github.com/scottjehl/Respond/pull/43) for selectivizr in upstream, and the selectivizr changes described are already sitting unreleased in the [master branch](https://github.com/keithclark/selectivizr). + +This snippet uses [rawgit.com](http://rawgit.com/) to fetch the unreleased 1.0.3 from the fork [gisu/selectivizr](https://github.com/gisu/selectivizr) to fix interoperability issues with respond.js. + +### Legacy IE cheatsheet + +I've put together a [Legacy IE cheatsheet](http://ricostacruz.com/cheatsheets/ie.html) to help you figure out what features you can use for older versions of IE. + +[html5shiv]: https://code.google.com/p/html5shiv/ +[json2]: https://github.com/douglascrockford/JSON-js +[respond]: https://github.com/scottjehl/Respond +[selectivizr]: http://selectivizr.com/ +[concom]: http://www.quirksmode.org/css/condcom.html +[nwmatcher]: http://javascript.nwbox.com/NWMatcher/ +[es5-shim]: https://github.com/es-shims/es5-shim diff --git a/posts/2015/images/unite-ctags.png b/posts/2015/images/unite-ctags.png new file mode 100644 index 00000000..d83f5b81 Binary files /dev/null and b/posts/2015/images/unite-ctags.png differ diff --git a/posts/2015/inline-svg-in-css.md.disabled b/posts/2015/inline-svg-in-css.md.disabled new file mode 100644 index 00000000..f91788b7 --- /dev/null +++ b/posts/2015/inline-svg-in-css.md.disabled @@ -0,0 +1,18 @@ +--- +date: '2015-03-02' +title: Inline SVG in CSS +tags: [CSS] +description: Create complex shapes easily by combining SVG and CSS. +layout: simple +--- + +You can create complex shapes easily by combining SVG and CSS. **[Demo here](http://cssdeck.com/labs/ip24y9lj)** of a white triangle notch implemented using inline CSS, perfect for making parallelograms. The caveats: + +```css +.box { + background: url("data:image/svg+xml;utf8,") + left top / 100% auto no-repeat; +} +``` + +IE9+ supported, legacy IE's arent. Also, hex color codes are not supported. Use `rgb(0, 100, 200)` instead. diff --git a/posts/2015/iojs-on-homebrew.md.disabled b/posts/2015/iojs-on-homebrew.md.disabled new file mode 100644 index 00000000..d39c0690 --- /dev/null +++ b/posts/2015/iojs-on-homebrew.md.disabled @@ -0,0 +1,48 @@ +--- +date: '2015-05-19' +title: Installing iojs on Homebrew +tags: [MacOS] +image: images/iojs-linking.png +description: Here's how to get iojs on OSX using Homebrew. +layout: simple +attachments: + - ./iojs-on-homebrew/iojs-linking.png +--- + +
+ +**Update (Jan 2019):** io.js has already been merged into Node.js since v4, and is now considered deprecated. Consider using nodejs instead. + +
+ +
+ +
+ +### Homebrew formula + + + +An updated [homebrew-iojs] formula is maintained by [@aredridel] and is available with just one command. + +```bash +brew install aredridel/iojs/iojs +``` + +### Default formula + + + +As of iojs 2.0.2, Homebrew ships with its own formula for iojs. This isn't preferrable, in my opinion, since global npm packages aren't being ran properly. + +```bash +brew install iojs +brew unlink node +brew link iojs --force +``` + +If you'd like to use this anyway, simply doing `brew install iojs` is not enough. To get iojs working on [Homebrew] You will need to unlink `node` first then use `link --force` to install iojs. + +[homebrew-iojs]: https://github.com/aredridel/homebrew-iojs/blob/master/Formula/iojs.rb +[@aredridel]: https://github.com/aredridel +[homebrew]: http://brew.sh/ diff --git a/posts/2015/iojs-on-homebrew/iojs-linking.png b/posts/2015/iojs-on-homebrew/iojs-linking.png new file mode 100644 index 00000000..6c54f5c3 Binary files /dev/null and b/posts/2015/iojs-on-homebrew/iojs-linking.png differ diff --git a/posts/2015/javascript-orms-suck.md.disabled b/posts/2015/javascript-orms-suck.md.disabled new file mode 100644 index 00000000..b69bdfc6 --- /dev/null +++ b/posts/2015/javascript-orms-suck.md.disabled @@ -0,0 +1,43 @@ +--- +date: '2015-02-15' +title: JavaScript ORM's suck +tags: [JavaScript] +description: Database ORM's and JavaScript aren't a good mix. Here's why. +layout: simple +--- + +
+ +**Update (Dec 2019):** Node.js now supports [top-level await](https://v8.dev/features/top-level-await) which would solve the pain point outlined in this article. This article was written in 2015 with Node.js v0.12 in mind. + +
+ +Consider this scenario: there's a strange bug in production's data that you need to inspect. In Rails, you can tell `rails console`: "show me all project IDs that are active and made by users that never logged in" like so: + +```ruby +pry(main)> User.where(last_signed_in: null) + .map { |u| u.projects.active } + .flatten + .pluck :id +``` + +This may not be the most efficient way to do it, but some situations are urgent and will call for one-offs like these. + +In Node.js, you'll probably do something like this: + +```js +> User.where({ last_signed_in: null }) + .fetch().then(function (users) { + return Promise.all(users.map(function(u){ + return u.projects.active.fetch(); + })) + }) + .then(function(projects) { + return projects.map(function(p) { + return p.id; + }); + }) + .then(console.log) +``` + +Ah, granted that promises and ES6 (fat arrows) would make this better, but still... diff --git a/posts/2015/javascript-shortcuts.md.disabled b/posts/2015/javascript-shortcuts.md.disabled new file mode 100644 index 00000000..480eaff7 --- /dev/null +++ b/posts/2015/javascript-shortcuts.md.disabled @@ -0,0 +1,29 @@ +--- +date: '2015-03-20' +title: JavaScript shortcuts using bitwise operators +description: Learn some shorter ways to write some common JavaScript expressions. +tags: [JavaScript] +layout: simple +--- + +Some common operations can be made shorter using bitwise operators. Here are some of my favorites. + +### Rounding off a number + +`Math.floor(3.5)` can be written as `3.5|0`. The [Bitwise OR](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Bitwise_OR) operator will make an integer out of the number, and this construct is in fact used in [asm.js](http://asmjs.org/spec/latest/) to denote an integer. + +```js +list[list.length * Math.random() | 0] +// ...same as: +list[Math.floor(list.length * Math.random()] +``` + +### Checking for -1 + +The [Bitwise NOT](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Bitwise_NOT) operator will turn `-1` into zero. This makes substring checks with `indexOf()` a little shorter. + +```js +if (~string.indexOf("John")) { ... } +// ...same as: +if (string.indexOf("John") !== -1) { ... } +``` diff --git a/posts/2015/local-google-fonts.md.disabled b/posts/2015/local-google-fonts.md.disabled new file mode 100644 index 00000000..14adbde0 --- /dev/null +++ b/posts/2015/local-google-fonts.md.disabled @@ -0,0 +1,27 @@ +--- +date: '2015-05-18' +title: The best way to get Google Fonts offline +tags: [Misc] +layout: simple +attachments: + - local-google-fonts/skyfonts.png +description: Chances are you've downloaded a few Google fonts to your computer. Here's a better way to do it. +--- + +
+ +
+ +If you're doing any work creating UI mockups, chances are you've downloaded a few [Google fonts] to your computer. Doing this manually is tedious, and you wouldn't be up to date when the fonts get updated. + +[SkyFonts] is a free font syncing service that puts webfonts into your computer. It works with paid services like [fonts.com], but also offers unlimited support for Google Fonts. + +This by far is the most convenient way to get Google Fonts onto your computer. Adding new fonts is mostly a one-click affair, fonts updates are automatically pushed to your computer, and you can even conveniently install the top 50 Google Fonts. + +Monotype Skyfonts is available via [Caskroom] as `monotype-skyfonts`. + +[caskroom]: http://caskroom.io/ +[caskroom-fonts]: https://github.com/caskroom/homebrew-fonts +[google fonts]: http://www.google.com/fonts +[skyfonts]: https://skyfonts.com/ +[fonts.com]: http://www.fonts.com/ diff --git a/posts/2015/local-google-fonts/skyfonts.png b/posts/2015/local-google-fonts/skyfonts.png new file mode 100644 index 00000000..cd94d0ee Binary files /dev/null and b/posts/2015/local-google-fonts/skyfonts.png differ diff --git a/posts/2015/markdown-in-jekyll.md.disabled b/posts/2015/markdown-in-jekyll.md.disabled new file mode 100644 index 00000000..e47ff7fe --- /dev/null +++ b/posts/2015/markdown-in-jekyll.md.disabled @@ -0,0 +1,47 @@ +--- +date: '2015-05-25' +title: Markdown in Jekyll +description: Insert Markdown into any Jekyll document with this simple tag. +layout: simple +tags: [Development, Jekyll] +--- + +To add Markdown to any HTML file in Jekyll, capture it into a variable and print that variable using _markdownify_. That is: + +``` +{% capture x %} +## This is markdown + +so and so +{% endcapture %}{{ x | markdownify }} +``` + +### Using Kramdown + +You can also use [kramdown] as your Markdown processor: it supports GitHub-like code fencing and many useful Markdown extensions. + +```yaml +# _config.yml +markdown: kramdown +kramdown: + input: GFM +``` + +### Markdown in Kramdown + +If you do, there's a great alternative. Name your HTML files as `.md`, which is fine, because Markdown will ignore HTML blocks by default. You can then opt into Markdown processing via `markdown="1"`. More info on this on Kramdown's [documentation]. Hat tip to [@marksteve]. + + +```html +
+

I'm HTML

+
+ I am *Markdown text*. Be sure not to indent me, + else I'll be interpreted as a code block. +
+
+``` + +[kramdown]: http://kramdown.gettalong.org/ +[@marksteve]: http://marksteve.com +[documentation]: http://kramdown.gettalong.org/syntax.html#html-blocks diff --git a/posts/2015/mocha-clean.md.disabled b/posts/2015/mocha-clean.md.disabled new file mode 100644 index 00000000..db73702b --- /dev/null +++ b/posts/2015/mocha-clean.md.disabled @@ -0,0 +1,50 @@ +--- +date: '2015-03-06' +title: Cleaner Mocha stack traces +tags: [JavaScript] +description: Suppress stack trace frames from 3rd-party modules in Mocha.js. +image: https://raw.githubusercontent.com/rstacruz/mocha-clean/gh-pages/comparison.png +--- + +[Mocha] is a great way to test JavaScript, but its stack traces are riddled +with useless frames from Mocha internals and 3rd-party modules. Let's try and +clean it up. + +
+ +![Comparison screenshot](./mocha-clean/comparison.png) + +
+ +## Introducing mocha-clean + +`mocha-clean` is a plugin for Mocha. It strips away mocha internals, +node_modules, absolute paths (based on cwd), and other unneccessary cruft +from stack traces. + +### Usage + + + +Simply invoke Mocha with `-r mocha-clean`. The easiest way to do this is to add it to your _test/mocha.opts_ file. + +``` +--require mocha-clean +``` + +### Installation + + + +It's available via npm, and works with Mocha 1.x in Node.js and in the browser. The source is available in GitHub: [rstacruz/mocha-clean][src]. + +```bash +npm install --save-dev mocha-clean +``` + +## Merging into Mocha? + +A pull request is currently open in Mocha to integrate this functionality into Mocha itself. Check out [#1564](https://github.com/mochajs/mocha/pull/1564) and offer your support and comments. + +[src]: https://github.com/rstacruz/mocha-clean +[mocha]: http://visionmedia.github.io/mocha diff --git a/posts/2015/mocha-clean/comparison.png b/posts/2015/mocha-clean/comparison.png new file mode 100644 index 00000000..79c3c131 Binary files /dev/null and b/posts/2015/mocha-clean/comparison.png differ diff --git a/posts/2015/mocha-istanbul-coverage.md.disabled b/posts/2015/mocha-istanbul-coverage.md.disabled new file mode 100644 index 00000000..3b95135e --- /dev/null +++ b/posts/2015/mocha-istanbul-coverage.md.disabled @@ -0,0 +1,88 @@ +--- +date: '2015-07-06' +title: Getting Mocha coverage reports using Istanbul +description: Here's how to get detailed coverage reports from Mocha via Istanbul. +image: images/mocha-istanbul.png +tags: [JavaScript] +attachments: + - ./mocha-istanbul-coverage/screenshot.png +--- + +
+ +
+ +### + + + +This is all you really need to run coverage reports on [Mocha] tests via [Istanbul]. + +```sh +istanbul cover _mocha +``` + +### Opening reports + + + +Your reports will be available under `coverage/`. By default, you'll get JSON files and an HTML report. + +```sh +open coverage/lcov-report/*.html +``` + +## Improving your setup + +### Locking istanbul + + + +Preferably, though, you'll want to add `istanbul` to your project so you can pin down the version you need and have it available on your CI. + +[mocha]: http://mochajs.org/ +[istanbul]: https://www.npmjs.com/package/istanbul + +```nohighlight +npm install --save-dev istanbul +./node_modules/.bin/istanbul cover _mocha +``` + +### Adding to gitignore + + + +There's no need to commit the coverage reports. + +```nohighlight +echo "/coverage" >> .gitignore +``` + +### Making an npm task + + + +To make things a bit easier, add a script to your `package.json` to run this. After that, just invoke `npm run coverage`. + +```js +/* package.json */ +{ + ... + "scripts": { + "coverage": "istanbul cover _mocha -- -R spec" + } +} +``` + +### Travis integration + + + +If you're using [Travis] to automate your tests, you can also set it up to show coverage reports on your builds. [Looks like this](https://travis-ci.org/rstacruz/ractive-ractive). + +```yml +# .travis.yml +script: npm run coverage +``` + +[travis]: https://travis-ci.org/ diff --git a/posts/2015/mocha-istanbul-coverage/screenshot.png b/posts/2015/mocha-istanbul-coverage/screenshot.png new file mode 100644 index 00000000..7650dff4 Binary files /dev/null and b/posts/2015/mocha-istanbul-coverage/screenshot.png differ diff --git a/posts/2015/navigate-code-with-ctags.md.disabled b/posts/2015/navigate-code-with-ctags.md.disabled new file mode 100644 index 00000000..1cb9a4a0 --- /dev/null +++ b/posts/2015/navigate-code-with-ctags.md.disabled @@ -0,0 +1,153 @@ +--- +date: '2015-04-22' +title: Navigate code like a pro with Ctags +description: Move through code fast. Set up Ctags. Here's how. +layout: simple +tags: [Vim] +attachments: + - ./images/unite-ctags.png +outdated: Ctags is not always the best solution to this today. There are Language Server implementations for some modern languages that wpuld offer these features and more. Also, as of 2019, there are other ctags alternatives to consider today, such as Universal Ctags. +--- + +
+ +
+ +[Ctags] lets you navigate code fast, and is perhaps the single most useful productivity boosting tool in any developer's arsenal. If you're not using Ctags yet, let's get you started. + +### What's it for? + + + +Ctags indexes a project's _tags_, or names of its classes and methods. Coupled with some integration with your editor (shown: Vim's [unite-tag]), it will give you two interesting features to help you traverse code base: + +
+ +1. Jump to where any class, module, method, or function is defined given its name. In vim, that's `:tag`. + +2. Place your cursor on a word, and jump to where it's defined with one keystroke. In vim, that's `^]`. + +
+ +## Installing Ctags + +### Exuberant Ctags + + + +In OS X, use Homebrew to install [Exuberant Ctags][ctags]. This is a more useful version of ctags than the one that ships with Xcode. + +```bash +brew install ctags +``` + +### Ctags options + + + +Let's make ctags ignore some common directories. Save this file as `~/.ctags`. + +```bash +--recurse=yes +--exclude=.git +--exclude=vendor/* +--exclude=node_modules/* +--exclude=db/* +--exclude=log/* +``` + +### Generate ctags for a project + + + +Go to your project's path, and run ctags to generate a `tags` file in your project. This is the index of all tags in your project that your editor will use. + +```bash +ctags . +``` + +### Ignore all ctags files + + + +It's safe to make all projects ignore all files called `tags`. I recommend setting up a global git ignore list. + +```bash +echo "tags" >> ~/.global_ignore +git config --global core.excludesfile $HOME/.global_ignore +``` + +## Vim setup + +### Auto-update ctags files + + + +Use [vim-autotag] to automatically update `tags` files. This will only work on projects that have already had `ctags -R` performed before. + +```bash +Plug 'craigemery/vim-autotag' +``` + +### Jumping to tags + + + +Use `:tag` to go to the definition of a certain tag. Usually, you will want to use this to jump to a certain Class or Method. Yes, this supports tab completion! + +```vim +:tag ClassName +``` + +### From the command line + + + +Use `vim -t` to open vim to a certain tag. + +```bash +vim -t +``` + +## Navigating through multiple definition + +If you used `:tag` on a tag that's got multiple definitions, use these commands to sift through them all. + +| Shortcut | Definition | +| -------- | ------------------------------------------ | +| `:tn` | Move to next definition (`:tnext`) | +| `:tp` | Move to previous definition (`:tprevious`) | +| `:ts` | List all definitions (`:tselect`) | + +## Key shortcuts + +You can also place your cursor on some text and press `^]` to jump to that tag. + +| Shortcut | Definition | +| -------- | ------------------------- | +| `^]` | Jump to definition | +| `^t` | Jump back from definition | +| `^W` `}` | Preview definition | +| `g]` | See all definitions | + +## Unite integration + +### + + + +If you're using [unite.vim], you can use [unite-tag] to browse tags. You can also check out my plugin, [vim-fastunite], which offers a pre-packaged distribution of Unite.vim. + +```bash +:Unite -start-insert tag +``` + +## Futher reading + +- [Browsing programs with tags](http://vim.wikia.com/wiki/Browsing_programs_with_tags) (vim.wikia.com) + +[vim-fastunite]: https://github.com/rstacruz/vim-fastunite +[vim-autotag]: https://github.com/craigemery/vim-autotag +[unite.vim]: https://github.com/Shougo/unite.vim +[unite-tag]: https://github.com/tsukkee/unite-tag +[ctags]: http://ctags.sourceforge.net diff --git a/posts/2015/node-js-exiting.md.disabled b/posts/2015/node-js-exiting.md.disabled new file mode 100644 index 00000000..be5cb27d --- /dev/null +++ b/posts/2015/node-js-exiting.md.disabled @@ -0,0 +1,16 @@ +--- +date: '2015-02-17' +title: Node.js exit hooks +tags: [JavaScript] +description: Catch a Node program from exiting with these events. +layout: simple +--- + +Catch a Node program from exiting with these events. Usually useful for test frameworks or daemon runners or whatever. + +```js +process.on('uncaughtException', err => { ... }) +process.on('SIGHUP', ...) +process.on('exit', ...) +process.on('beforeExit', ...) +``` diff --git a/posts/2015/organizing-js.md.disabled b/posts/2015/organizing-js.md.disabled new file mode 100644 index 00000000..50ff1b8b --- /dev/null +++ b/posts/2015/organizing-js.md.disabled @@ -0,0 +1,60 @@ +--- +date: '2015-02-26' +title: Organizing JavaScript for simple sites +tags: [JavaScript] +description: How do you sort JavaScript files in a Rails project? Here's one way. +--- + +Some sites don't need big JavaScript architectures, only bits of small behaviors that will affect certain elements. I prefer to put them into a directory called `behaviors`. + +- Each file is a self-contained piece of code that only affects a _single_ element type. +- Name each one of the files according to the `class` (or `role`) it affects. +- They should also be independent of each other, and therefore, order-agnostic. + +## Example project + +```js +... +└── javascripts/ + └── behaviors/ + ├── colorpicker.js + ├── modal.js + ├── select2.js + ├── tooltip.js + └── wow.js +``` + +```js +// select2.js -- affects `.select2` +$(function() { + $('.select2').select2() +}) +``` + +```js +// wow.js -- affects `.wow` +$(function() { + new WOW().init() +}) +``` + +```js +// tooltips.js -- affects `.tooltip` +$(document).on('hover', '.tooltip', function () { + ... +}); +``` + +## Using behaviors + +### + + + +Just include them all into one file. In Rails, this should be as easy as: + +```js +//= require_tree ./behaviors +``` + +This JavaScript will be safe to include in all pages. diff --git a/posts/2015/osx-keyboard-shortcuts.md.disabled b/posts/2015/osx-keyboard-shortcuts.md.disabled new file mode 100644 index 00000000..b1a07f23 --- /dev/null +++ b/posts/2015/osx-keyboard-shortcuts.md.disabled @@ -0,0 +1,39 @@ +--- +date: '2015-04-20' +title: Navigate OS X with the keyboard +description: Speed through the Mac OS X GUI with these shortcuts. +tags: [macOS] +layout: simple +attachments: + - ./osx-keyboard-shortcuts/keyboard.png +--- + +
+Keyboard shortcuts dialog +
+ +After switching to OS X, one thing I've always missed in Windows and Linux is the ability to spawn menus using `Alt` `F`. Today I realized that OS X has two ways to accomplish this. + +Use `Ctrl` + `F2` to navigate menus with arrow keys, or use `⌘` `⇧` `/` will get you the help menu. + +## Useful shortcuts + +### Spotlight for menus + +The `⌘` `⌥` `/` shortcut is essentially a Spotlight for menu items—it opens the help menu where you can type in the menu item you want. Not all applications support it, though. + +### Full keyboard access + +Under the _Keyboard_ preference pane, there's an option for _Full Keyboard Access_. I suggest enabling this, as this lets you navigate through file save windows with just the keyboard. + +## More shortcuts + +| Key | Description | +| ----------- | --------------------------- | +| `^` `F1` | Turn keyboard access on/off | +| `^` `F2` | Menu bar | +| `^` `F3` | Dock | +| `^` `F5` | Toolbar | +| `^` `F6` | Floating window | +| `^` `F8` | Status menu | +| `⌘` `⇧` `/` | Help | diff --git a/posts/2015/osx-keyboard-shortcuts/keyboard.png b/posts/2015/osx-keyboard-shortcuts/keyboard.png new file mode 100644 index 00000000..e3ae2073 Binary files /dev/null and b/posts/2015/osx-keyboard-shortcuts/keyboard.png differ diff --git a/posts/2015/parallel-npm-commands.md.disabled b/posts/2015/parallel-npm-commands.md.disabled new file mode 100644 index 00000000..b451e7db --- /dev/null +++ b/posts/2015/parallel-npm-commands.md.disabled @@ -0,0 +1,28 @@ +--- +date: '2015-11-23' +title: Running NPM tasks in parallel +tags: [JavaScript] +description: You can run multiple NPM script tasks in parallel. This is great for tasks like running build watchers. +--- + +You can run multiple NPM script tasks in parallel. This is great for tasks like running build watchers. Just use the bash construct `&` to put each command in the background, then finally add `wait` so they can be terminated with `^C`. The final syntax would be: + +```bash +command1 & command2 & wait +``` + + + +In practice, this looks like so: + +```js +{ + "scripts": { + "watch": "npm run watch:js & npm run watch:css & wait", + "watch:js": "watchify -v -t babelify -s Editor index.js -o dist/index.js", + "watch:css": "stylus -w css/style.styl -o dist/index.css", + } +} +``` + + diff --git a/posts/2015/pausing-capybara-selenium.md.disabled b/posts/2015/pausing-capybara-selenium.md.disabled new file mode 100644 index 00000000..efe3ae0f --- /dev/null +++ b/posts/2015/pausing-capybara-selenium.md.disabled @@ -0,0 +1,67 @@ +--- +date: '2015-11-17' +title: Pausing Capybara tests +tags: [Ruby] +layout: simple +description: Use the web inspector in Capybara/Selenium tests. +--- + +Want to use the Web Inspector in Capybara/Selenium tests? The first thing you'll probably try is to use [pry-byebug](https://rubygems.org/gems/pry-byebug) to pause your tests. You'll probably find that this doesn't work as intended. + +
+
A bad example
+ +```rb +scenario 'visiting the home page' do + visit root_path + binding.pry # ✗ +end +``` + +
+ +Using `binding.pry` will halt everything, making your browser not load anything because Rails can't respond to the request. + +## A better alternative + +A better alternative is to use `$stdin.gets`. This is what [Poltergeist](https://rubygems.org/gems/poltergeist) uses to pause execution. That method is not available in Capybara/Selenium though, so you'll need to add it in yourself: + +```rb +$stderr.write 'Press enter to continue' +$stdin.gets +``` + +## Using with other libraries + +### Using with RSpec + +If you're using Capybara with Rspec, you can turn this into a helper. You can then just use `pause` in your tests. + +
+
spec/support/pause_helpers.rb
+ +```rb +module PauseHelpers + def pause + $stderr.write 'Press enter to continue' + $stdin.gets + end +end +``` + +
+ +
+
spec/rails_helper.rb
+ +```rb +RSpec.configure do + config.include PauseHelpers, type: :feature +end +``` + +
+ +### Using Poltergeist + +When using Poltergeist (for PhantomJS support), just use its [Remote Debugging](https://github.com/teampoltergeist/poltergeist#remote-debugging-experimental) feature with the `inspector: true` flag. diff --git a/posts/2015/rails-and-cloudfront.md.disabled b/posts/2015/rails-and-cloudfront.md.disabled new file mode 100644 index 00000000..49648b9c --- /dev/null +++ b/posts/2015/rails-and-cloudfront.md.disabled @@ -0,0 +1,171 @@ +--- +date: '2015-12-17' +title: Using Cloudfront as a Rails CDN +tags: [Ruby, Devops] +description: This guide will walk you through using Amazon CloudFront as an asset CDN for your Rails app. +--- + +Setting up a CDN for your application assets is easy nowadays. This guide will walk you through using Amazon CloudFront as an asset CDN for your Rails application. + +There are other guides out there today; [Heroku's CloudFront guide](https://devcenter.heroku.com/articles/using-amazon-cloudfront-cdn) is pretty good, but I think misses a few key points about CORS and denying requests outside `/assets`. This guide should fill those in. + +## Set up CloudFront + +### + + + +Sign up in [aws.amazon.com](http://aws.amazon.com) and create a new CloudFront distribution. This will get you a subdomain (like `d1h2t3n5.cloudfront.net`) that will act as a caching proxy to your actual site. You may opt to use your own domain names if you like. + +
+ +[Amazon Web Services (AWS)](http://aws.amazon.com) _(aws.amazon.com)_ + +
+ +### Custom configuration + + + +Make sure that `OPTIONS` is also being passed through. This will allow CORS requests through (see next section). Also, enable "compress automatically" to let CloudFront handle gzip compression for you. + +
+ +**Origin Settings** + +- Origin domain name: `www.yoursite.com` + +
+
+ +**Default Cache Behavior Settings** + +- Allowed HTTP Methods: `GET, HEAD, OPTIONS` +- Cached HTTP Methods: Turn on `OPTIONS` +- Compress Objects Automatically: `on` + +
+ +## Set up asset host + +### + + + +This will make `image_tag`, `asset_url` and other asset-related helpers point your assets to your CloudFront distribution. Do this only for `production.rb`. + +```rb +# config/environments/production.rb +``` + +```rb +config.action_controller.asset_host = '.cloudfront.net' +``` + +## Serve static assets + +### + + + +Enable the serving of static assets. You will want to do this if you're using Heroku or any 12-factor-style deployment. +If you use a reverse proxy like Nginx or Haproxy, skip this section and configure your reverse proxy to handle CORS instead. + +```rb +# config/environments/production.rb +# Rails 5+ +config.public_file_server.enabled = true +config.public_file_server.headers = { + 'Cache-Control' => 'public, max-age=31536000' +} +``` + +```rb +# Rails 4.x and below +config.serve_static_assets = true +config.static_cache_control = 'public, max-age=31536000' +``` + +## Enable CORS in assets + +If you use `serve_static_assets`, you will need to enable cross-origin requests for assets. This will prevent issues like Firefox not loading custom icons and fonts. + +### Install the `rack-cors` gem + + + +Use the [rack-cors] gem to enable cross-origin requests. At time of writing, it is at version 0.4.0. + +[rack-cors]: https://rubygems.org/gems/rack-cors + +```rb +# Gemfile +gem 'rack-cors' +``` + +### Configure rack-cors + + + +This will make assets accessible from any website. You want to enable this because you'd want `yoursite.com` to be able to load assets out of `.cloudfront.net`. + +```rb +# config/initializers/cors.rb +Rails.application.config.middleware.insert_before 0, Rack::Cors do + allow do + origins '*' + + resource '/assets/*', + headers: :any, + methods: [:get] + end +end + +# In older versions of Rails, you'll need to use +# 'Rack::Cors' (as a string with quotes) +``` + +## Deny everything but /assets + +### + + + +Set up your app to disallow Cloudfront from fetching anything but `/assets`. This uses User Agent detection; see CloudFront's docs on [User-Agent headers](http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/RequestAndResponseBehaviorCustomOrigin.html#request-custom-user-agent-header) for information. + +```rb +# app/services/cloudfront_denier.rb + +# Middleware to deny CloudFront requests to non-assets +# http://ricostacruz.com/til/rails-and-cloudfront +class CloudfrontDenier + def initialize(app, options = {}) + @app = app + @target = options[:target] || '/' + end + + def call(env) + if cloudfront?(env) && !asset?(env) + [302, { 'Location' => @target }, []] + else + @app.call(env) + end + end + + def asset?(env) + env['PATH_INFO'] =~ %r{^/assets} + end + + def cloudfront?(env) + env['HTTP_USER_AGENT'] == 'Amazon CloudFront' + end +end +``` + +```rb +# config/initializers/cloudfront.rb +Rails.application.config.middleware.use CloudfrontDenier, + target: 'https://www.yoursite.com/' +``` + +If you miss this step, you'll be able to access the rest of your site in your CloudFront URL. While those aren't public, you'd best have them secured as it can open up security flaws and possibly lead to SEO penalties. diff --git a/posts/2015/rails-inline-partials.md.disabled b/posts/2015/rails-inline-partials.md.disabled new file mode 100644 index 00000000..bd7e5c24 --- /dev/null +++ b/posts/2015/rails-inline-partials.md.disabled @@ -0,0 +1,44 @@ +--- +date: '2015-02-18' +title: Inline partials in Rails +tags: [Ruby] +description: Organize long views into smaller sub-views with this tip. +layout: simple +--- + +Ever find it annoying to have really long blocks of code being hard to parse in your head? + +```haml +- if @list.any? + - @list.each do |item| + - # really long code + - # ... + +- else + - # also long code + - # ... +``` + +You can break them into partials, or like so: + +```ruby +- body, list, empty = nil + +- body = Proc.new do + - if @list.any? + - list.call + - else + - empty.call + +-# List of items +- list = Proc.new do + %div + - @venues.each do |venue| + = venue + +-# No items available +- empty = Proc.new do + %div nothing here + +- body.call +``` diff --git a/posts/2015/refactoring-long-calls.md.disabled b/posts/2015/refactoring-long-calls.md.disabled new file mode 100644 index 00000000..a4aba4a8 --- /dev/null +++ b/posts/2015/refactoring-long-calls.md.disabled @@ -0,0 +1,72 @@ +--- +date: '2015-02-24' +title: Refactoring long calls +tags: [Development] +description: Clean up long code reasonably with these tips. +--- + +### + + + +Consider seeing this code in a view. It seems a little long, and may be a candidate for refactoring. Lets see how we can make it better. + +```ruby +<%= + event.ticket + .custom_message + .to_s.strip + .gsub("[URL]", site_url) +%> +``` + +## First attempt + +### + + + +One way to rewrite this is to make it as short as possible, ie, store all the complexity in a helper function. In my opinion, this isn't really ideal for the following reasons: + +```ruby +<%= custom_message_for(event) %> +``` + +```ruby +def custom_message_for(event) + event.ticket + .custom_messag + .to_s.strip + .gsub("[URL]", site_url) +end +``` + +1. **Same complexity, new location.** The complexity was simply moved from one part to another, instead of broken down into more easily-understandable chunks. + +2. **Tight coupling.** The method `custom_message_for` is too tightly coupled. To test this, you will need to create a mock event, with a mock ticket, with a mock custom message. + +## Second attempt + +### + + + +I would prefer this to be written as a helper that takes in a string, such as so. The view code may be a little longer, but you get these advantages: + +```ruby += format_message(event.ticket.custom_message) +``` + +```ruby +def format_message(message) + message.to_s.strip.gsub("[URL]", site_url) +end +``` + +1. **Simple and obvious.** The method `format_message` is simple and its purpose is immediately obvious. + +2. **Understandable at a glance.** Glancing at the view code (`format_message(event...)`), it's apparent how the message is being derived. + +3. **Easy testing.** It can be easily tested—just need to pass it a string. + +Another way to implement this is with a presenter, which may not be a bad idea either. diff --git a/posts/2015/relative-paths-in-jekyll.md.disabled b/posts/2015/relative-paths-in-jekyll.md.disabled new file mode 100644 index 00000000..ec91223b --- /dev/null +++ b/posts/2015/relative-paths-in-jekyll.md.disabled @@ -0,0 +1,108 @@ +--- +date: '2015-03-13' +title: Relative paths in Jekyll +tags: [Development, Jekyll] +description: Fix the problem with GitHub Pages's relative URLs with this snippet. +--- + +[Jekyll] is a great static site tool. If you're using [GitHub Pages][gh-pages], it comes with it for free, making it a very useful tool for any public blog or website. It comes, though, with one common problem that I've yet to come across a good solution for: relative paths. + + + +## The problem + +### The mess of relative paths + +Let's say you have an innocent URL in your layout like the one below. This is a relative path, not an absolute one that begins with `/`. It resolves based on wherever it's included from. + +```html + + +``` + +This works well in pages of your site placed in the root directory. Once you path one level deep though, you'll encounter problems. + +| From this page... | Base | Final URL | Result | +| --------------------- | -------- | ------------------------- | ------: | +| `/index.html` | `/` | `/assets/style.css` | Good ✓ | +| `/me.html` | `/` | `/assets/style.css` | Good ✓ | +| `/about/profile.html` | `/about` | `/about/assets/style.css` | Wrong ✗ | + + + + + +## A naive workaround + +One workaround is to use absolute paths by adding a `/` in the beginning. + +```html + ^ +``` + + + +### Why absolute URLs suck + +This works great for sites that live on its own domain. When your site will be hosted in a sub-directory (such as the case with [GitHub Project Pages][gh-pages]), this absolute path will not resolve to `/project/assets/style.css` as you probably would have intended. + +| If your site is in... | It resolves to... | | +| ------------------------- | ------------------- | ------: | +| `user.github.io/` | `/assets/style.css` | Good ✓ | +| `user.github.io/project/` | `/assets/style.css` | Wrong ✗ | + + + + + +## A better workaround + +This snippet below automatically determines the relative base and stores it in the variable `base`. Place it in your partials path, and include the partial in your layouts. + +
+ + +```html + +{% assign base = '' +%}{% assign depth = page.url | split: '/' | size | minus: 1 +%}{% if depth <= 1 +%}{% assign base = '.' +%}{% elsif depth == 2 +%}{% assign base = '..' +%}{% elsif depth == 3 +%}{% assign base = '../..' +%}{% elsif depth == 4 +%}{% assign base = '../../..' +%}{% endif +%} +``` + +
Tip: You can also collapse this into one line.
+
+ + + +### Use it as a prefix + +You can then use it as a prefix to URLs, like the examples below. You don't need to `include` it all the time—just include it once in your layouts and it will be available everywhere. + +```html +{% include base.html %} + +``` + +```html +Back to home +``` + +```html +About me +``` + +```html +Read "{{ post.title }}" +``` + +[jekyll]: http://jekyllrb.com/ +[gh-pages]: http://pages.github.com/ diff --git a/posts/2015/releasing-packages.md.disabled b/posts/2015/releasing-packages.md.disabled new file mode 100644 index 00000000..13612b69 --- /dev/null +++ b/posts/2015/releasing-packages.md.disabled @@ -0,0 +1,60 @@ +--- +date: '2015-03-09' +title: Tools to make releasing packages easier +tags: [Development] +description: Make releasing packages easier with these 2 open-source tools. +--- + +Releasing packages involves the same repetitive tasks: updating the change log, bumping versions, tagging a release, and so on. Here are a few tools to make this chore easier. + +## Tools I recommend + +### 1. Bump versions via [bump-cli](http://npmjs.com/bump-cli) + + + +This utility allows you to increment versions of files through the command line. For JavaScript packages, this simply means bumping your `.json` files. This works with any file type with [semver](http://semver.org/) tags. + +
+ +
+ +### 2. Continuously maintain the change log + + + +There are many change log tools available, but I've found that the sanest solution is to update the log as features get implemented. Maintain an "unreleased" list on top of the change log—on release time, you'll simply need to add a date. + +``` +## v0.1.0 - unreleased + +* Added feature Y +* Fixed bug Z +* ... +``` + +### 3. Tag and release via [git-extras](https://github.com/tj/git-extras) + + + +Git Extras comes with the `git release` command, which automates creating a release commit, tags it with the right version, and pushes it. + +```bash +git release "v1.0.0" +``` + +## All together now + +### + + + +Here's how it would work for a typical [npm](http://npmjs.com) package. These tools are language-agnostic, though—this process will also work for non-JavaScript packages. + +```bash +vim HISTORY.md +bump *.json && +npm test && # Run tests +npm publish && # Publish to npm +git release "v1.0.0" # Publish to GitHub +``` diff --git a/posts/2015/releasing-packages/screenshot.png b/posts/2015/releasing-packages/screenshot.png new file mode 100644 index 00000000..5f682582 Binary files /dev/null and b/posts/2015/releasing-packages/screenshot.png differ diff --git a/posts/2015/repeat-tmux-from-vim.md.disabled b/posts/2015/repeat-tmux-from-vim.md.disabled new file mode 100644 index 00000000..505e6add --- /dev/null +++ b/posts/2015/repeat-tmux-from-vim.md.disabled @@ -0,0 +1,42 @@ +--- +date: '2015-05-24' +title: Running tests using Vim and Tmux +tags: [Vim] +description: The most intuitive way for me to run tests is to use tmux split panes. +layout: simple +attachments: + - ./repeat-tmux-from-vim/tmux-repeat.gif +--- + +
+Screencast demo of tmux and vim +
+ +### Using split panes + +The most intuitive way for me to run tests is to use a tmux split pane. I simply split my screen between my editor and a shell that runs tests (`npm test` in this example). I've then set up a vim hotkey (`,r` for me) to quickly send "up enter" to the last tmux pane used. + +```vim +" Repeat last command in the next tmux pane. +nnoremap r :call TmuxRepeat() + +function! s:TmuxRepeat() + silent! exec "!tmux select-pane -l && tmux send up enter && tmux select-pane -l" + redraw! +endfunction +``` + +### Save-and-repeat + +Even better, I've set up `Ctrl-S` as a "save-and-repeat" key to make it all happen in one keystroke: + +```vim +inoremap :w:call TmuxRepeat()a +noremap :w:call TmuxRepeat() +``` + +### Alternative: vim-dispatch + +Alternatively, you can use [vim-dispatch], but it doesn't support colors and can be disorienting. + +[vim-dispatch]: https://github.com/tpope/vim-dispatch diff --git a/posts/2015/repeat-tmux-from-vim/tmux-repeat.gif b/posts/2015/repeat-tmux-from-vim/tmux-repeat.gif new file mode 100644 index 00000000..dd4fed56 Binary files /dev/null and b/posts/2015/repeat-tmux-from-vim/tmux-repeat.gif differ diff --git a/posts/2015/static-sites-in-heroku.md.disabled b/posts/2015/static-sites-in-heroku.md.disabled new file mode 100644 index 00000000..e2dd3b7c --- /dev/null +++ b/posts/2015/static-sites-in-heroku.md.disabled @@ -0,0 +1,17 @@ +--- +date: '2015-03-10' +title: 'Hosting static sites in Heroku' +tags: [Development, Heroku] +description: Serve simple websites for free using Heroku with this one simple tip. +--- + +Just add an `index.php` to your repository. +It tells [Heroku](http://heroku.com) that the site is a PHP site and that it will be hosted using +Apache. + +``` +touch index.php +``` + +And yes, you can still have an `index.html` file. The `.html` file takes +precedence over the `.php` one. Have fun! diff --git a/posts/2015/testing-static-sites.md.disabled b/posts/2015/testing-static-sites.md.disabled new file mode 100644 index 00000000..e5e88c45 --- /dev/null +++ b/posts/2015/testing-static-sites.md.disabled @@ -0,0 +1,51 @@ +--- +date: '2015-05-29' +title: Testing generated static sites +tags: [Development, Jekyll] +description: Ever feel guilty that your Jekyll sites don't have tests? Here's a way around that. +--- + +You can make a test script use `grep` and `test` to check for generated output. Since grep will die with an error code if it doesn't match anything, the test will be considered a failure. + +```bash +#!/usr/bin/env sh +set -o errexit # die on errors + +grep "" _site/index.html >/dev/null +grep display _site/style.css >/dev/null +grep function _site/script.js >/dev/null +test -f _site/image.jpg #check for file existence +``` + +<!-- {.-wide} --> + +### Travis support + +You can even integrate these with your [Travis-CI] tests. (Learn more about build configuration from their [documentation](http://docs.travis-ci.com/user/build-configuration/).) + +```yaml +# .travis.yml +before_script: bundle exec jekyll build +script: ./test/test.sh +``` + +<!-- {.-wide} --> + +### Makefiles + +Another good way is to use a `Makefile` so you can simply use `make test` to invoke tests. + +```bash +test: _site + grep display _site/style.css >/dev/null + grep function _site/script.js >/dev/null + test -f _site/image.jpg #check for file existence + +_site: + bundle + bundle exec jekyll build --safe +``` + +<!-- {.-wide} --> + +[travis-ci]: http://travis-ci.org diff --git a/posts/2015/use-bower-in-npm.md.disabled b/posts/2015/use-bower-in-npm.md.disabled new file mode 100644 index 00000000..39d1c59e --- /dev/null +++ b/posts/2015/use-bower-in-npm.md.disabled @@ -0,0 +1,35 @@ +--- +date: '2015-02-23' +title: Use Bower packages like npm packages +tags: [JavaScript] +description: Ever wanted to use some frontend packages but they're not npm-compatible? +layout: simple +--- + +<blockquote class='notice'> + +**Update (Jan 2019):** Bower hasn't been maintained for years, and using Bower packages should now be considered a discouraged practice. + +</blockquote> + +[Napa] is a package that lets you install arbitrary projects (without package.json) into `node_modules`. + +Bower packages are repos without package.json manifests. Hence, you can use Napa to use Bower packages as if they were npm packages... sort of. + +```js +{ + "scripts": { + "install": "napa" + }, + "napa": { + "hint": "chinchang/hint.css#v1.3.1", + "colorbox": "jackmoore/colorbox#1.5.9" + } +``` + +Promising idea, but I'd like to see it developed more. Some caveats as of 1.2.0: + +- not compatible with `npm shrinkwrap` +- upgrading packages are not automatic + +[napa]: https://www.npmjs.com/package/napa diff --git a/posts/2015/use-browserify-to-precompile.md.disabled b/posts/2015/use-browserify-to-precompile.md.disabled new file mode 100644 index 00000000..71e6e931 --- /dev/null +++ b/posts/2015/use-browserify-to-precompile.md.disabled @@ -0,0 +1,94 @@ +--- +date: '2015-02-14' +title: Use Browserify to precompile npm libraries +tags: [JavaScript] +description: Keep large libraries sane using Browserify. With ES6 and CoffeeScript support! +layout: simple +--- + +<blockquote class='notice'> + +**Update (Jan 2019):** This article was written with an outdated version of Babel (Babel 5). Also, please consider replacing Browserify with something more recent like Rollup. + +</blockquote> + +Remove the hassle in writing npm libraries in a transpiled language ([babeljs], [CoffeeScript], etc) by using browserify. + +With this technique, there's no need to maintain a full new directory of compiled files... just one pre-built `dist/` file. + +### Files + +``` +. +├─ lib +│ └─ index.js - actual entry point +├─ dist +│ └─ js2coffee.js - built package +└─ index.js - entry point (used in development) +``` + +### Install the requisite packages + +```bash +npm install --save-dev browserify babelify babel-preset-es2015 +``` + +### Make the entry point + +Put your _actual_ main entry point as, say, `./lib/index.js`. Then create an entry point `./index.js` like this for development: + +```js +require('babel/register') +module.exports = require('./lib/index') +``` + +### Set up compilation + +Set up a compliation script in the prepublish hook: + +```js +{ + "scripts": { + "prepublish": "browserify -s js2coffee --bare -t [ babelify --presets [ es2015 ] ] ./lib/index.js > dist/js2coffee.js" + } +} +``` + +For CoffeeScript support, use [coffeeify](https://github.com/jnordberg/coffeeify) for CoffeeScript (`-t [ coffeeify -x .coffee ]`). + +Options used: + +- `-s` - standalone (uses a UMD wrapper) +- `--bare` - don't stub node builtins +- `-t` - define transformations to use + +For packages targeting Node.js, use `--node --no-bundle-external`. This will disable browser-field resolution in package.json and not bundle node_modules. + +### Point the package + +Set `main` in `package.json` to the precompiled version: + +```js +{ + "main": "dist/js2coffee.js" +} +``` + +### All done + +- Every time `npm publish` is called, the pre-built version (dist/js2coffee.js) gets built +- Doing `require('js2coffee')` will point to the `dist/` version +- Doing `require('../index')` in your tests will point to the source version +- You can also do `require('js2coffee/index')` from other packages + +### Babel + +For [babeljs], I recommend using `--loose` for libraries that will target legacy IE. + +``` +-t [ babelify --loose all ] +``` + +[babeljs]: http://babeljs.io/ +[coffeescript]: http://coffeescript.org/ +[browserify]: https://github.com/substack/node-browserify diff --git a/posts/2015/use-macvim-with-lua.md.disabled b/posts/2015/use-macvim-with-lua.md.disabled new file mode 100644 index 00000000..4295fd37 --- /dev/null +++ b/posts/2015/use-macvim-with-lua.md.disabled @@ -0,0 +1,20 @@ +--- +date: '2015-04-14' +title: Speed up MacVim with Lua +description: Make your OSX Vim experience better with this simple switch. +tags: [Vim] +--- + +It's always a good idea to install [MacVim] with Lua support. [Homebrew] does not compile with Lua support by default. This gets you the benefit of a noticeably-faster [Unite.vim]. + +``` +brew install macvim --with-cscope --with-lua --override-system-vim --with-luajit --with-python3 +``` + +<!-- {.-wide} --> + +Also, the option `--override-system-vim` will allow you to use `vim` in the command line apart from the GUI... also a great idea! + +[macvim]: https://github.com/macvim-dev/macvim +[homebrew]: http://brew.sh/ +[unite.vim]: https://github.com/Shougo/unite.vim diff --git a/posts/2015/use-markdown-documentation.md.disabled b/posts/2015/use-markdown-documentation.md.disabled new file mode 100644 index 00000000..b61fd06c --- /dev/null +++ b/posts/2015/use-markdown-documentation.md.disabled @@ -0,0 +1,24 @@ +--- +date: '2015-02-19' +title: Use Markdown for documentation +tags: [Development] +description: Forget complex documentation schemes... it's really just this easy. +layout: simple +--- + +The most universal documentation "format" is a bunch of Markdown files inside a project's `doc/` directory. Here are some projects that follow that convention: + +- [io.js](https://github.com/iojs/io.js/tree/v1.x/doc/api) (and Node.js) +- [stylus](https://github.com/LearnBoost/stylus/tree/master/docs) +- [puppet](https://github.com/puppetlabs/puppet/tree/master/docs) +- [salt](https://github.com/saltstack/salt/tree/develop/doc) (uses .rst) +- [pip](https://github.com/pypa/pip/tree/develop/docs) (uses .rst) +- [npm](https://github.com/npm/npm/tree/master/doc) +- [composer](https://github.com/composer/composer/tree/master/doc) + +In my opinion, "a bunch of Markdown files" is good because: + +- It's easily viewable in GitHub +- It can be made into a Jekyll (GitHub pages) site easily +- It can be made into a Sphinx site easily +- It's a common documentation format that you can get some tools to generate to diff --git a/posts/2015/use-vendor-bundle.md.disabled b/posts/2015/use-vendor-bundle.md.disabled new file mode 100644 index 00000000..94f60a9d --- /dev/null +++ b/posts/2015/use-vendor-bundle.md.disabled @@ -0,0 +1,55 @@ +--- +date: '2015-06-22' +title: Bundle your gems inside your project +description: Here's a tip to get the most out of your Ruby development experience. +layout: simple +tags: [Ruby] +--- + +When working on a Ruby project, I always put the files in `vendor/bundle/`. This has the benefit of having a greppable index of all the gems of your project, while keeping your global gemset tidy. + +```bash +bundle install -j3 --path=vendor/bundle +``` + +You only need to do this once in your project. The `--path` setting will be persisted in your project's Bundler configuration (`.bundle/config`). + +<div><NextBlock title="Why would we want to do this?" /></div> + +## Inspecting your gems + +Doing this will make inspecting your gem code easier. This can be conveniently done with something like ack or [the silver searcher]: + +```bash +$ cd vendor/bundle/ruby/*/gems +$ ag all_application_helpers + + actionpack-4.2.1/lib/action_controller/metal/helpers.rb + 106: def all_application_helpers +``` + +<div><NextBlock title="How do we ignore this from all repos?" /></div> + +## Globally ignoring + +I recommend placing `vendor/bundle/` on a global gitignore. If you haven't set up a global gitignore list yet, it's pretty easy. + +```bash +git config --global core.excludesfile ~/.gitignore +echo vendor/bundle >> ~/.gitignore +``` + +## Goodbye, rvm! + +This removes the need for managing gemsets via [rvm]. In fact, if your project always uses the latest Ruby (which you also should, in my opinion!), you won't even need rvm at all. + +Even if you don't use this tip, you actually don't need rvm gemsets at all. Bundler solves the same problem. + +<div><NextBlock title="Bonus: let's speed up our installations!" /></div> + +## Parallel installs + +Bonus: the `-j3` flag makes your installations faster by allowing 3 installs in parallel. + +[rvm]: http://rvm.io/ +[the silver searcher]: https://github.com/ggreer/the_silver_searcher diff --git a/posts/2015/using-bower-and-rails-in-heroku.md.disabled b/posts/2015/using-bower-and-rails-in-heroku.md.disabled new file mode 100644 index 00000000..8c6fb8eb --- /dev/null +++ b/posts/2015/using-bower-and-rails-in-heroku.md.disabled @@ -0,0 +1,42 @@ +--- +date: '2015-02-24' +title: Using Bower and Rails in Heroku +tags: [Ruby] +description: Use the Bower package manager in Rails projects when deploying to Heroku. +layout: simple +--- + +<blockquote class='notice'> + +**Update (Jan 2019):** Bower hasn't been maintained for years, and using Bower packages should now be considered a discouraged practice. + +</blockquote> + +This seems pretty easy until you realize that Bower (a Node.js package) is not available in the Heroku Ruby build pack. +The gem [bower-rails](http://rubygems.org/gems/bower-rails) allows for easy Bower integration into Rails, but it won't work out of the box in Heroku. + +### Solution: add execjs + +The [Ruby buildpack](https://github.com/heroku/heroku-buildpack-ruby) has a magic behavior where if it detects the `execjs` gem, it will [bundle Node.js](https://github.com/heroku/heroku-buildpack-ruby#assets) with the build. This should make it work. + +```ruby +gem 'execjs' +``` + +### Alternative solution + +Use the [Multi buildpack](https://github.com/ddollar/heroku-buildpack-multi) to combine Ruby and Node buildpacks. + +``` +heroku config:add BUILDPACK_URL=https://github.com/ddollar/heroku-buildpack-multi.git +``` + +``` +# vim .buildpacks +https://github.com/heroku/heroku-buildpack-nodejs.git#v60 +https://github.com/heroku/heroku-buildpack-ruby.git#v127 +``` + +I recommend locking the versions (`#v60`) to ensure that you get versions that interoperate together (in case one upgrades and breaks interoperability). + +The last pack defined (Ruby in this case) is the "main" one that defines the process types (web, console, etc). diff --git a/posts/2015/using-git-merge-base.md.disabled b/posts/2015/using-git-merge-base.md.disabled new file mode 100644 index 00000000..a5166d14 --- /dev/null +++ b/posts/2015/using-git-merge-base.md.disabled @@ -0,0 +1,66 @@ +--- +date: '2015-12-10' +title: Use git merge-base for big merges +tags: [Git] +description: Use this one command to help you with big Git merges. +--- + +When trying to merge two Git branches that have diverged a lot, it's a little difficult to make sense of what happened on both sides. + +Enter [git merge-base]--a simple built-in utility to tell you where two branches diverged. It will tell you the commit where the two branches split off: + +```bash +$ git merge-base develop master +b8ac838cad3266f6a7e414181875831fd9b86ed5 +``` + +<!-- {.-wide} --> + +### Set up git tags + +This command will create a tag `_base` that will point to where they both diverged. + +```bash +git tag _base `git merge-base develop master` +``` + +<!-- {.-wide} --> + +### Inspecting changes + +You can then use `git diff`, `git log`, or [tig] to inspect changes on either side: + +```bash +# Inspect changes in Gemfile on each side +tig _base...develop -- Gemfile +tig _base...master -- Gemfile + +git diff _base...develop -- Gemfile +git diff _base...master -- Gemfile +``` + +<!-- {.-wide} --> + +### Check if both branches diverged + +You can also use the `--independent` flag which will show commits that can't be reached by any other. If it prints 2 commits, it means that there are changes on both sides. + +```bash +$ git merge-base develop master --independent +46978182cc8d90439b862e772e99a3f71889901a +8501118e0d958115caff692abda0f29ad530db4f +``` + +<!-- {.-wide} --> + +If it only prints 1, it means only one side has changes. + +```bash +$ git merge-base develop master --independent +8501118e0d958115caff692abda0f29ad530db4f +``` + +<!-- {.-wide} --> + +[git merge-base]: http://git-scm.com/docs/git-merge-base +[tig]: https://jonas.nitro.dk/tig diff --git a/posts/2017/less-cpu-on-development.md.disabled b/posts/2017/less-cpu-on-development.md.disabled new file mode 100644 index 00000000..8dfbe82a --- /dev/null +++ b/posts/2017/less-cpu-on-development.md.disabled @@ -0,0 +1,22 @@ +--- +date: '2017-07-12' +title: Make Rails (and other dev servers) use less CPU +tags: [Development, Linux] +description: Is Rails eating your CPU in development? Try this nifty trick. +layout: simple +--- + +Is Rails eating your CPU in development? Try lowering its priority using [renice(1)](http://manpages.ubuntu.com/manpages/zesty/en/man1/renice.1.html), a standard BSD utility that should be available in OS X and most Linux distributions. Here's a shell script that will automatically reset the priority to `+15` to common development processes: + +```sh +#!/usr/bin/env sh +sudo renice +15 -p $(ps ax | grep -E 'ruby|node|watchman|postgres' | grep -v grep | awk '{print $1}' | tr '\n' ' ') +``` + +Save this as `renice-dev` into one of your `bin` paths, and give it a `chmod +x renice-dev`. You can type `renice-dev` after you start your development processes to "renice" them. + +The lowest priority is `+19` (only run when nothing else is running), the default is `0`, and the highest is `-20` (makes things go very fast). + +### References + +- [Throttling processes in OS X](https://tinyapps.org/blog/mac/201107230700_throttle_process_os_x.html) _(tinyapps.org)_ diff --git a/posts/2017/life-in-side-quests.md.disabled b/posts/2017/life-in-side-quests.md.disabled new file mode 100644 index 00000000..e8b96930 --- /dev/null +++ b/posts/2017/life-in-side-quests.md.disabled @@ -0,0 +1,176 @@ +--- +date: '2017-02-19' +title: Your life is a series of side quests +tags: [Productivity] +image: images/side-quest-example.jpg +description: Video games organize tasks in quests. There's no reason this can't work in real life, too. +--- + +<figure class='-panorama'> + +![Example image](./life-in-side-quests/side-quest-example.jpg) + +</figure> + +Video games tell you what to do using _quests_ or _missions_. There's no reason this can't work in real life, too. Books tell us to lead purpose-driven lives with long-term and short-term goals. Seems like a perfect way to make your life an RPG. + +## Missions + +A _mission_ is an achievable goal within 3 months or less. A mission should be broken down into sub-tasks. In other words, it's an actionable short-term goal. + +<figure class='-panorama -transparent'> +<div class='mission-list'> +<div class='mission-item'> +<h3>1703 Start a blog <em>#project</em></h3> +<ul> +<li>Figure out the design</li> +<li>Write a few articles</li> +<li>Publishing</li> +</ul> +</div> +<div class='mission-item'> +<h3>1704 Couch to 5K <em>#fitness</em></h3> +<ul> +<li>Study the running program</li> +<li>Week 1 - 0.5km</li> +<li>Week 2 - 1km</li> +<li>Week 3 - 2.5km</li> +<li>Week 4 - 5km</li> +</ul> +</div> +<div class='mission-item'> +<h3>1704 Get a job <em>#career</em></h3> +<ul> +<li>Send 4 resumes</li> +<li>Get an interview</li> +<li>Go through negotiation</li> +<li>Get hired</li> +</ul> +</div> +<div class='mission-item'> +<h3>1706 Plan vacation <em>#life</em></h3> +<ul> +<li>Decide on destination</li> +<li>Book tickets</li> +<li>Book accomodations</li> +<li>Bon voyage!</li> +</ul> +</div> +</div> +</figure> + +I prefix each mission with a projected date of completion (`1702` is 2017, February), but this is totally up to you. I do this to get a good retrospective at the end of the year of all the missions I've completed. + +<figure class='-transparent'> +<div class='mission-list'> +<div class='mission-item -goals'> +<ul> +<li>1703 Start a blog</li> +<li>1704 Couch to 5K</li> +<li>1704 Job at Hyperfisk</li> +<li>1706 Vacation to Paris</li> +<li>1707 Remodel kitchen</li> +<li>1707 Learn guitar</li> +</ul> +</div> +</div> +</figure> + +## Keeping track + +### + +<!-- {.-literate-style} --> + +This system is so simple, you can use anything to keep track of it. As an Android user, I prefer using [Google Keep](https://keep.google.com). It lets me view all my missions at a glance, both ongoing and completed. + +<figure class='-transparent'> +<img src='./life-in-side-quests/keep-missions.png' /> +</figure> + +There are a variety of other ways to keep track of your missions, including using plain notebooks and pens. What's important is you break your goals into manageable pieces, and give yourself a way to look back on your progress. + +<style> +.mission-list { + box-sizing: border-box; + font-size: .86em; +} +.mission-item { + box-sizing: border-box; + margin: 8px; + padding: 16px; + border: solid 1px rgba(30, 144, 255, 0.2); + box-shadow: 0 4px 8px rgba(30, 144, 255, 0.07); + border-radius: 3px; + max-width: 320px; + background: white; +} +@media (min-width: 481px) { + .mission-list { + display: flex; + margin-left: 128px; + margin-right: 64px; + justify-content: center; + } + .mission-item { + flex: 0 1 320px; + margin: 4px; + } +} +.mission-item > h3, +.mission-item > ul, +.mission-item > ul > li { + margin: 0; + padding: 0; + border: 0; + list-style-type: none; + background: transparent; +} +.mission-item > h3 { + padding-bottom: 8px; + margin-bottom: 8px; + border-bottom: solid 1px rgba(30, 144, 255, 0.2); +} +.mission-item.mission-item > h3 > em { + font-style: normal; + font-weight: 400; + font-size: .86em; + font-family: sans-serif; + color: rgba(30, 144, 255, 0.5); + margin-left: 4px; +} +.mission-item > ul > li { + margin-top: 2px; +} +.mission-item > ul > li::before { + content: ''; + box-sizing: border-box; + position: relative; + left: 0; + top: 1px; + display: inline-block; + width: 12px; + height: 12px; + border: solid 2px rgba(30, 144, 255, 0.2); + border-radius: 2px; + margin: 0 8px 0 0; +} +@media (min-width: 481px) { + .mission-list.-single { + margin-left: 100px; + margin-right: 100px; + } + .mission-list.-single > .mission-item { + flex: 1 0 auto; + } +} +.mission-item.-goals > ul > li + li { + border-top: solid 1px rgba(30, 144, 255, 0.2); + padding-top: 4px; + margin: 4px 0; +} +.mission-item.-goals > ul > li::before { + background: rgba(30, 144, 255, 0.2); + border-color: transparent; +} +</style> diff --git a/posts/2017/life-in-side-quests/keep-missions.png b/posts/2017/life-in-side-quests/keep-missions.png new file mode 100644 index 00000000..ac4f17f4 Binary files /dev/null and b/posts/2017/life-in-side-quests/keep-missions.png differ diff --git a/posts/2017/life-in-side-quests/side-quest-example.jpg b/posts/2017/life-in-side-quests/side-quest-example.jpg new file mode 100644 index 00000000..0b67b8f3 Binary files /dev/null and b/posts/2017/life-in-side-quests/side-quest-example.jpg differ diff --git a/posts/2017/redux-side-effects.md.disabled b/posts/2017/redux-side-effects.md.disabled new file mode 100644 index 00000000..faf46984 --- /dev/null +++ b/posts/2017/redux-side-effects.md.disabled @@ -0,0 +1,155 @@ +--- +date: '2017-03-14' +title: Managing side effects in Redux +tags: [React] +layout: simple +description: Learn to manage impure side effects in a Redux store with middleware. +--- + +[Redux] is the preferred state management pattern for React apps today. Being a very "functional" library, it doesn't like side effects much. This means doing asynchronous actions in a Redux reducer is not just a bad idea, it simply won't work. + +```js +/* ✗ Warning: This won't work! */ + +function reducer (state, action) { + switch (action.type) { + case 'profile:load': + fetch('/my_profile') + .then(res => res.json()) + .then(res => { + dispatch({ + type: 'profile:set', + payload: res.body + }) + // ✗ Error: dispatch is not defined + }) + + ... +``` + +You can't modify state here in an async callback. In fact, you can't even `dispatch()` in a reducer! This won't work and is a bad idea; you break the purity of the store reducer. + +## Using Middleware + +### Middleware for side effects + +Fortunately, Redux has built-in provisions for managing side effects: [Middleware](http://redux.js.org/docs/advanced/Middleware.html)! You can write your own middleware with business logic. You don't need to use 3rd-party packages other than Redux itself. + +```js +// Redux middleware +function ProfileLoader () { + return store => dispatch => action { + // First pass them through to the reducers. + dispatch(action) + + switch (action.type) { + case 'profile:load!': + fetch('/my_profile') + .then(res => res.json()) + .then(res => dispatch({ type: 'profile:set', payload: res.body })) + .catch(err => dispatch({ type: 'profile:error', payload: err })) + } + } +} + +// Use the middleware in your store +store = createStore(reducers, {}, applyMiddleware( + ProfileLoader() +) +``` + +Redux middleware is simply a decorator for `dispatch()`. Here's an example where we extend `dispatch()` to perform certain side effects (an AJAX call, in this case) when certain actions come in. + +## Other solutions + +### Using redux-thunk + +Perhaps the most well-known solution to this is [redux-thunk](https://www.npmjs.com/package/redux-thunk), which allows you to dispatch functions ("thunks"). + +```js +// Using a function as an action via redux-thunk +store.dispatch(dispatch => { + fetch('/my_profile') + .then(res => res.json()) + .then(res => dispatch({ type: 'profile:set', payload: res.body })) + .catch(err => dispatch({ type: 'profile:error', payload: err })) +}) +``` + +However, I personally advise against this approach for a number of reasons: + +- It moves logic to your action creators, which were supposed to be very simple pieces of code. + +- It makes actions complicated, when they can just be simple JSON instructions (eg, `{ type: 'profile:load' }`). + +- It can't interact with other side effects. For instance, you can't make a side effect to send `profile:error`s to an error tracking service. Middleware can do this. + +## Naming convention + +You may have noticed I named my action `profile:load!`. This is my preferred convention of choice, where action names are simply strings, and "side effect" actions are suffixed with an exclamation mark, just as it would in Ruby or Elixir. + +## Other examples + +### Error tracker + +How about a middleware that tracks errors as they come in? + +```js +function ErrorTracker () { + return store => dispatch => action { + dispatch(action) + + switch (action.type) { + case 'profile:error': + case 'account:error': + case 'other:error': + Rollbar.track(action.payload) + } + } +} +``` + +### Ticker + +Or a middleware that sends `tick` events every second? Great for timers or for RPG's. + +```js +function Ticker (options) { + let timer + + return store => dispatch => action { + dispatch(action) + + switch (action.type) { + case 'ticker:start!': + timer = setInterval(() => { + store.dispatch({ type: 'ticker:tick', now: new Date() }) + }, options.interval) + break + + case 'ticker:stop!': + if (timer) clearInterval(timer) + timer = null + break + } + } +} +``` + +### Combining them + +And to put them all together: + +```js +store = createStore(reducers, {}, applyMiddleware( + ProfileLoader(), + ErrorTracker(), + Ticker({ interval: 1000 }) +) +``` + +## See also + +Check out [Redux on devguides.io](http://devguides.io/redux)! Devguides.io is my new pet project where I make pocket-sized explainers for web development concepts. + +[redux]: http://redux.js.org/