{ "version": "https://jsonfeed.org/version/1", "title": "Fernando Paredes", "home_page_url": "https://fdp.io", "feed_url": "https://fdp.io/feed.json", "description": "My random thoughts on software", "icon": "https://fdp.io/apple-touch-icon.png", "favicon": "https://fdp.io/favicon.ico", "expired": false, "author": { "name": "Fernando Paredes", "url": "https://fdp.io", "avatar": null }, "items": [ { "id": "https://fdp.io/blog/2020/07/01/2020-what-a-year/", "title": "2020 - What A Year", "summary": "We are officially halfway through 2020 and boy has it been a wild ride.\n", "content_text": "We are officially halfway through 2020 and boy has it been a wild ride.šŸ’” Fires burned throughout Australia. 46 million acres have been destroyed, one billion animals dead, hundreds of species required emergency intervention.šŸ˜· COVID-19 spread throughout the world causing countries to shutter to prevent an epidemic. Millions lost their jobs, economy tanked, and our healthcare workers had limited access to PPE. Weā€™re still not out of the woods yet; we have yet to develop a vaccine that immunizes us to it.šŸ”Ø 45 was impeached on the grounds of abuse of power and obstruction of Congress; nothing happened.āœŠšŸæ George Floyd was murdered by a policeman. The US saw protests in most cities, police brutality ensued. No police reform, police budgets are going up.šŸš° Flint Michigan still doesnā€™t have clean water.šŸ‡µšŸ‡· Puerto Rico was hit with many earthquakes.šŸ Murder Hornets in the Pacific Northwest.Plus so many other situations. 2020 feels like the year that just wonā€™t end.Black Lives Matter.", "content_html": "

We are officially halfway through 2020 and boy has it been a wild ride.

šŸ’” Fires burned throughout Australia. 46 million acres have been destroyed, one billion animals dead, hundreds of species required emergency intervention.

šŸ˜· COVID-19 spread throughout the world causing countries to shutter to prevent an epidemic. Millions lost their jobs, economy tanked, and our healthcare workers had limited access to PPE. Weā€™re still not out of the woods yet; we have yet to develop a vaccine that immunizes us to it.

šŸ”Ø 45 was impeached on the grounds of abuse of power and obstruction of Congress; nothing happened.

āœŠšŸæ George Floyd was murdered by a policeman. The US saw protests in most cities, police brutality ensued. No police reform, police budgets are going up.

šŸš° Flint Michigan still doesnā€™t have clean water.

šŸ‡µšŸ‡· Puerto Rico was hit with many earthquakes.

šŸ Murder Hornets in the Pacific Northwest.

Plus so many other situations. 2020 feels like the year that just wonā€™t end.

Black Lives Matter.

", "url": "https://fdp.io/blog/2020/07/01/2020-what-a-year/", "date_published": "2020-07-01T14:00:00+00:00", "date_modified": "2020-07-01T14:00:00+00:00", "author": { "name": "Fernando Paredes", "url": "https://fdp.io", "avatar": null } }, { "id": "https://fdp.io/blog/2020/01/21/show-me-what-you-got/", "title": "Show Me What You Got", "summary": "See the hardware and software I use daily", "content_text": "Inspired by others, Iā€™ve listed the hardware and software that I use daily.", "content_html": "

\"Show

Inspired by others, Iā€™ve listed the hardware and software that I use daily.

", "url": "https://fdp.io/blog/2020/01/21/show-me-what-you-got/", "date_published": "2020-01-21T11:12:00+00:00", "date_modified": "2020-01-21T11:12:00+00:00", "author": { "name": "Fernando Paredes", "url": "https://fdp.io", "avatar": null } }, { "id": "https://fdp.io/blog/2018/04/14/50-things-that-made-the-modern-economy/", "title": "50 Things That Made the Modern Economy", "summary": "An interesting podcast I liked about how the modern economy came to be.", "content_text": "I came across the 50 Things That Made the Modern Economy podcast while browsing Overcast. Itā€™s a short podcast about 50 inventions, ideas, and innovations that helped created the modern economy. Each episode is about 10 minutes long with just enough information.I would highly recommend the podcast šŸ¤“. I learned a lot about processes and things that I hadnā€™t gave much thought to.Favorites iPhone - Explores the technologies, components, and entities that made the iPhone a possibility. Paper - The establishment of such a small thing increased literacy, access, and comfort. Gramaphone - The synthesization of our voice into an analog medium.", "content_html": "

I came across the 50 Things That Made the Modern Economy podcast while browsing Overcast. Itā€™s a short podcast about 50 inventions, ideas, and innovations that helped created the modern economy. Each episode is about 10 minutes long with just enough information.

I would highly recommend the podcast šŸ¤“. I learned a lot about processes and things that I hadnā€™t gave much thought to.

Favorites

", "url": "https://fdp.io/blog/2018/04/14/50-things-that-made-the-modern-economy/", "date_published": "2018-04-14T18:00:00+00:00", "date_modified": "2018-04-14T18:00:00+00:00", "author": { "name": "Fernando Paredes", "url": "https://fdp.io", "avatar": null } }, { "id": "https://fdp.io/blog/2018/03/22/supporting-compactmap-in-swift-4/", "title": "Supporting compactMap in Swift 4", "summary": "Define an extension on Sequence to make compactMap backwards compatible.", "content_text": "Swift 4.1 is shipping soon with many wonderful things! One of the changes that may affect you is usage of flatMap to remove nil from your Sequence.12let names = [\"Maria\", nil, \"Daniel\"]names.flatMap { $0 } //=> [\"Maria\", \"Daniel\"]Backwards CompatibilityI wanted to ensure I avoided dealing with migration errors/assistant as much as possible. Lucky for us, Swift is a powerful language.We can define an extension Sequence.compactMap for any version below 4.1 šŸŽ‰.123456789#if swift(>=4.1)// This will be provided by the stdlib#else extension Sequence { func compactMap<T>(_ transform: (Self.Element) throws -> T?) rethrows -> [T] { return try flatMap(transform) } }#endifWith the extension in place, we can change our usage of .flatMap { $0 } to .compactMap { $0 } šŸ¤“.", "content_html": "

Swift 4.1 is shipping soon with many wonderful things! One of the changes that may affect you is usage of flatMap to remove nil from your Sequence.

12
let names = [\"Maria\", nil, \"Daniel\"]names.flatMap { $0 } //=> [\"Maria\", \"Daniel\"]

Backwards Compatibility

I wanted to ensure I avoided dealing with migration errors/assistant as much as possible. Lucky for us, Swift is a powerful language.We can define an extension Sequence.compactMap for any version below 4.1 šŸŽ‰.

123456789
#if swift(>=4.1)// This will be provided by the stdlib#else  extension Sequence {    func compactMap<T>(_ transform: (Self.Element) throws -> T?) rethrows -> [T] {      return try flatMap(transform)    }  }#endif

With the extension in place, we can change our usage of .flatMap { $0 } to .compactMap { $0 } šŸ¤“.

", "url": "https://fdp.io/blog/2018/03/22/supporting-compactmap-in-swift-4/", "date_published": "2018-03-22T19:50:00+00:00", "date_modified": "2018-03-22T19:50:00+00:00", "author": { "name": "Fernando Paredes", "url": "https://fdp.io", "avatar": null } }, { "id": "https://fdp.io/blog/2018/03/18/active-compilation-conditions-for-xcode/", "title": "Active Compilation Conditions for Xcode", "summary": "Pass flags to your iOS App without prepending -D", "content_text": "Previously, declaring conditional compilation flags was done in the OTHER_SWIFT_FLAGS build setting. The tricky part when adding a flag was remembering to prepend -D like -DBETA. Forgetting the flag could lead to unexpected results or time wasted to debugging.Enter Active Compilation Conditions.Active Compilation ConditionsAs of Xcode 8, we have the new SWIFT_ACTIVE_COMPILATION_CONDITIONS build setting which allows us to define our flags without the need to prefix them. Under the hood, each element is passed to swiftc prefixed with -D šŸ‘šŸ½! This matches the behavior we had with Objective-C and Preprocessor Macros.UsageRegardless of which setting you choose to add it to, using the flag in your Swift project is the same:12345#if BETA let backgroundQueue = \"com.company.appname.background.beta\"#else let backgroundQueue = \"com.company.appname.background\"#endif", "content_html": "

Previously, declaring conditional compilation flags was done in the OTHER_SWIFT_FLAGS build setting. The tricky part when adding a flag was remembering to prepend -D like -DBETA. Forgetting the flag could lead to unexpected results or time wasted to debugging.

\"Other

Enter Active Compilation Conditions.

\"\"

Active Compilation Conditions

As of Xcode 8, we have the new SWIFT_ACTIVE_COMPILATION_CONDITIONS build setting which allows us to define our flags without the need to prefix them. Under the hood, each element is passed to swiftc prefixed with -D šŸ‘šŸ½! This matches the behavior we had with Objective-C and Preprocessor Macros.

\"Active

Usage

Regardless of which setting you choose to add it to, using the flag in your Swift project is the same:

12345
#if BETA  let backgroundQueue = \"com.company.appname.background.beta\"#else  let backgroundQueue = \"com.company.appname.background\"#endif
", "url": "https://fdp.io/blog/2018/03/18/active-compilation-conditions-for-xcode/", "date_published": "2018-03-18T19:30:00+00:00", "date_modified": "2018-03-18T19:30:00+00:00", "author": { "name": "Fernando Paredes", "url": "https://fdp.io", "avatar": null } }, { "id": "https://fdp.io/blog/2018/03/11/the-comment-comeback/", "title": "The Comment Comeback", "summary": "Adding comments via utteranc.es", "content_text": "Iā€™ve wanted to add comments to my blog for a long time now. Initially, I started out with Disqus until I became more aware of how much tracking they added.Over the past few years, there have been a plethora of new contenders in the comments arena: gh-commentify, Isso, Hashover, and lambda-comments to name a few. They all had their pros/cons but I wanted something really simple for my blog where I didnā€™t have to put much effort into getting it to work.UtterancesUtterances proved to be the right commenting system for me. It requires a public repo on GitHub, adding a file to said repo, and a script tag where you want comments to show up.Comments are then tracked via GitHub Issues per page using the userā€™s github account. If the page does not have any comments, the first comment can trigger the creation of the issue! Best of all, Utterances is also open source", "content_html": "

Iā€™ve wanted to add comments to my blog for a long time now. Initially, I started out with Disqus until I became more aware of how much tracking they added.

Over the past few years, there have been a plethora of new contenders in the comments arena: gh-commentify, Isso, Hashover, and lambda-comments to name a few. They all had their pros/cons but I wanted something really simple for my blog where I didnā€™t have to put much effort into getting it to work.

Utterances

Utterances proved to be the right commenting system for me. It requires a public repo on GitHub, adding a file to said repo, and a script tag where you want comments to show up.

Comments are then tracked via GitHub Issues per page using the userā€™s github account. If the page does not have any comments, the first comment can trigger the creation of the issue! Best of all, Utterances is also open source

\"\"

", "url": "https://fdp.io/blog/2018/03/11/the-comment-comeback/", "date_published": "2018-03-11T12:18:00+00:00", "date_modified": "2018-03-11T12:18:00+00:00", "author": { "name": "Fernando Paredes", "url": "https://fdp.io", "avatar": null } }, { "id": "https://fdp.io/blog/2018/01/18/setting-up-absolute-paths-in-create-react-app/", "title": "Setting Up Absolute Paths in create-react-app", "summary": "Avoid using relative imports via import {} from '../../../components with CRA.", "content_text": "Setting up a React project can be a fun exercise until you decide to move things around and noticed that all of local imports are broken. To avoid that, you can use absolute paths for imports.NODE_PATH is a special environment variable that instructs Webpack to resolve from this folder. Assuming a project containing two folders in the src , containers and components the difference would look like:Current Code12import { Header } from '../../components/Header'import { FancyContainer } from '../FancyContainer'New Code12import { Header } from 'components/Header'import { FancyContainer } from 'containers/FancyContainer'With the new structure, we can safely move our files around without worrying about where we are in the directory stack. To achieve this, we create a .env file in the root of our directory with NODE_PATH set to src1NODE_PATH=srcNext time we run the start command, it will pick up the path automatically šŸŽ‰", "content_html": "

Setting up a React project can be a fun exercise until you decide to move things around and noticed that all of local imports are broken. To avoid that, you can use absolute paths for imports.

NODE_PATH is a special environment variable that instructs Webpack to resolve from this folder. Assuming a project containing two folders in the src , containers and components the difference would look like:

Current Code

12
import { Header } from '../../components/Header'import { FancyContainer } from '../FancyContainer'

New Code

12
import { Header } from 'components/Header'import { FancyContainer } from 'containers/FancyContainer'

With the new structure, we can safely move our files around without worrying about where we are in the directory stack. To achieve this, we create a .env file in the root of our directory with NODE_PATH set to src

1
NODE_PATH=src

Next time we run the start command, it will pick up the path automatically šŸŽ‰

", "url": "https://fdp.io/blog/2018/01/18/setting-up-absolute-paths-in-create-react-app/", "date_published": "2018-01-18T00:00:00+00:00", "date_modified": "2018-01-18T00:00:00+00:00", "author": { "name": "Fernando Paredes", "url": "https://fdp.io", "avatar": null } }, { "id": "https://fdp.io/blog/2017/05/29/release-alfred-pods-workflow/", "title": "Release: Alfred Pods Workflow", "summary": "Search CocoaPods and copy pod 'Pod' to your clipboard with Alfred", "content_text": "Iā€™m a big fan of productivity tools. A particular tool that I use every day is Alfred. Alfred is customizable and supports Workflows to further enhance your experience. When I work on iOS apps, I find myself going to a dependencyā€™s source or searching for a certain iOS library.The Pods workflow helps with searching for pods and copying the pod 'Pod' stanza to your clipboard.UsageTo use the workflow, you use the pods prefix plus your query pods rxPressing Enter or āŒ˜+[1-9] will open the page for that CocoaPod.Copying To PodfileOnce you have a pod, you can press āŒ˜+C to copy the code to your clipboard. For example, if you selected RxCocoa, the clipboard contents would look like:1pod 'RxCocoa'Opening the SourceAlfred Workflows support alternative actions when a user has Option/Alt, Control, Command, or Shift pressed. To open the source for the current Pod, press Option+Enter.How Do I Get It?123npm install -g alfred-pods# or with yarnyarn global add alfred-podsBinary versionAfter a few months, I decided to rewrite it in Rust to make installation much simpler. Now you can download the pure alfredworkflow without installing anything else.Download the latest version", "content_html": "

Iā€™m a big fan of productivity tools. A particular tool that I use every day is Alfred. Alfred is customizable and supports Workflows to further enhance your experience. When I work on iOS apps, I find myself going to a dependencyā€™s source or searching for a certain iOS library.

The Pods workflow helps with searching for pods and copying the pod 'Pod' stanza to your clipboard.

Usage

To use the workflow, you use the pods prefix plus your query

pods rx

\"Searching

Pressing Enter or āŒ˜+[1-9] will open the page for that CocoaPod.

Copying To Podfile

Once you have a pod, you can press āŒ˜+C to copy the code to your clipboard. For example, if you selected RxCocoa, the clipboard contents would look like:

1
pod 'RxCocoa'

Opening the Source

Alfred Workflows support alternative actions when a user has Option/Alt, Control, Command, or Shift pressed. To open the source for the current Pod, press Option+Enter.

How Do I Get It?

\"\"

123
npm install -g alfred-pods# or with yarnyarn global add alfred-pods

Binary version

After a few months, I decided to rewrite it in Rust to make installation much simpler. Now you can download the pure alfredworkflow without installing anything else.

Download the latest version

", "url": "https://fdp.io/blog/2017/05/29/release-alfred-pods-workflow/", "date_published": "2017-05-29T14:30:00+00:00", "date_modified": "2017-05-29T14:30:00+00:00", "author": { "name": "Fernando Paredes", "url": "https://fdp.io", "avatar": null } }, { "id": "https://fdp.io/blog/2017/03/22/persisting-last-visited-url-in-android-webview/", "title": "Persisting the Last Visited URL in an Android WebView", "summary": "Where I use Kotlin to load the last visited URL", "content_text": "I had a problem in Android where I wanted the user to resume their WebView session and pick up where they left off. I also didnā€™t want to use WebView.saveState or WebView.restoreState due to the side effects that could happen. The example code below uses Kotlin.Saving the current URLWe can hook into the onPause lifecycle event to look at the current URL and save it to SharedPreferences:1234567891011val prefs: SharedPreferences by lazy { applicationContext.getSharedPreferences(applicationContext.packageName, Activity.MODE_PRIVATE)}override fun onPause() { super.onPause() val edit = prefs.edit() edit.putString(\"lastUrl\", webView.url) edit.commit() // You could also use `.apply` here _ćø__(ā€¾ā—”ā— )>}Loading the URLTo retrieve the value, we can pull it out from SharedPreferences when the activity gets created:123456789override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // Pull saved URL out of SharedPreferences val lastOrMainUrl = prefs.getString(lastUrl, BuildConfig.URL) webview.loadUrl(lastOrMainUrl)}Example CodeYou can find the full activity with some refactoring in this gist", "content_html": "

I had a problem in Android where I wanted the user to resume their WebView session and pick up where they left off. I also didnā€™t want to use WebView.saveState or WebView.restoreState due to the side effects that could happen. The example code below uses Kotlin.

Saving the current URL

We can hook into the onPause lifecycle event to look at the current URL and save it to SharedPreferences:

1234567891011
val prefs: SharedPreferences by lazy {  applicationContext.getSharedPreferences(applicationContext.packageName, Activity.MODE_PRIVATE)}override fun onPause() {    super.onPause()    val edit = prefs.edit()    edit.putString(\"lastUrl\", webView.url)    edit.commit() // You could also use `.apply` here _ćø__(ā€¾ā—”ā— )>}

Loading the URL

To retrieve the value, we can pull it out from SharedPreferences when the activity gets created:

123456789
override fun onCreate(savedInstanceState: Bundle?) {  super.onCreate(savedInstanceState)  setContentView(R.layout.activity_main)  // Pull saved URL out of SharedPreferences  val lastOrMainUrl = prefs.getString(lastUrl, BuildConfig.URL)  webview.loadUrl(lastOrMainUrl)}

Example Code

You can find the full activity with some refactoring in this gist

", "url": "https://fdp.io/blog/2017/03/22/persisting-last-visited-url-in-android-webview/", "date_published": "2017-03-22T16:20:00+00:00", "date_modified": "2017-03-22T16:20:00+00:00", "author": { "name": "Fernando Paredes", "url": "https://fdp.io", "avatar": null } }, { "id": "https://fdp.io/blog/2017/03/12/using-citext-with-knex-js/", "title": "Using CITEXT With Knex.js", "summary": "Using the CITEXT PostgreSQL data type with Knex.js", "content_text": "Iā€™ve been working on the backend for a side project with Express, Objection.js,and Knex. As with most projects, users needed to have the ability to register with an email.I wanted my database to enforce the constraints and have valid indexes on the email column.bob@example.com and BOB@example.com are the same per RFC822. A usersshould not be blocked from logging in due to the difference in casing on their email. We could call lower(email) ourselves but that skips the index we already have. Thankfully, we have access to CITEXT.CITEXTCITEXT is a PostgreSQL data type for case insensitive text. When dealing with emails, I have found it better to use CITEXT. Running lower(column) manually is not ideal on every SQL query. This is compounded when you have a unique index on a column as the index will reference the case sensitive value.Limitations It doesnā€™t handle unicode case insensitivity well. Not as efficient as text, due to making copies of the data to make it lower case. Doesnā€™t work well if you want case sensitivity sometimes.Even with these limitations, email is still a good fit for citext.Knex MigrationI decided to use Objection.js for my ORM which in turn uses Knex for queries and migrations. Letā€™s create a new migration via:1knex migrate:make ChangeEmailToCITextOnUsersLetā€™s update our existing table to use citext on email. To do that, we need to enable the extension. Knex doesnā€™t have a way to do that directly but they do allow you to pass raw SQL.1234567891011exports.up = knex => { return knex.schema .raw('CREATE EXTENSION IF NOT EXISTS CITEXT') // Enables CITEXT on this database without throwing errors .alterTable('User', t => { t.specificType('email', 'CITEXT').notNullable().alter() })}exports.down = knex => knex.schema.alterTable('User', t => { t.string('email').notNullable().alter()})To use a type not available via Knexā€™s DSL, we can use specificType(column, type). Running the migration using knex migrate:latest will update our table to use CITEXT:", "content_html": "

Iā€™ve been working on the backend for a side project with Express, Objection.js,and Knex. As with most projects, users needed to have the ability to register with an email.I wanted my database to enforce the constraints and have valid indexes on the email column.

bob@example.com and BOB@example.com are the same per RFC822. A usersshould not be blocked from logging in due to the difference in casing on their email. We could call lower(email) ourselves but that skips the index we already have. Thankfully, we have access to CITEXT.

CITEXT

CITEXT is a PostgreSQL data type for case insensitive text. When dealing with emails, I have found it better to use CITEXT. Running lower(column) manually is not ideal on every SQL query. This is compounded when you have a unique index on a column as the index will reference the case sensitive value.

Limitations

Even with these limitations, email is still a good fit for citext.

Knex Migration

I decided to use Objection.js for my ORM which in turn uses Knex for queries and migrations. Letā€™s create a new migration via:

1
knex migrate:make ChangeEmailToCITextOnUsers

Letā€™s update our existing table to use citext on email. To do that, we need to enable the extension. Knex doesnā€™t have a way to do that directly but they do allow you to pass raw SQL.

1234567891011
exports.up = knex => {  return knex.schema    .raw('CREATE EXTENSION IF NOT EXISTS CITEXT') // Enables CITEXT on this database without throwing errors    .alterTable('User', t => {      t.specificType('email', 'CITEXT').notNullable().alter()    })}exports.down = knex => knex.schema.alterTable('User', t => {  t.string('email').notNullable().alter()})

To use a type not available via Knexā€™s DSL, we can use specificType(column, type). Running the migration using knex migrate:latest will update our table to use CITEXT:

\"Describing

", "url": "https://fdp.io/blog/2017/03/12/using-citext-with-knex-js/", "date_published": "2017-03-12T17:00:00+00:00", "date_modified": "2017-03-12T17:00:00+00:00", "author": { "name": "Fernando Paredes", "url": "https://fdp.io", "avatar": null } }, { "id": "https://fdp.io/blog/2017/03/04/setup-phoenix-router-with-a-spa/", "title": "Setup Phoenix Router with a Single Page App", "summary": "Allow SPA routers to handle your frontend routes.", "content_text": "Iā€™ve been playing around with Phoenix for a personal project. Phoenix by default comessetup with a basic PageController. I wanted to use Elmā€™s Navigation package to handle routingon my Elm app.With the default setup, Phoenix will attempt to serve the route, ending in a 404. I had to change my router.ex to the following to allow any path to defer to the PageController.index function:123456789defmodule AppliedAt.Router do use AppliedAt.Web, :router scope \"/\", AppliedAt do pipe_through :browser # Use the default browser stack get \"/*path\", PageController, :index # <-- Allows any number of path items endendThis should work for any router in JavaScript, Elm, or < insert language here > šŸ˜„.", "content_html": "

Iā€™ve been playing around with Phoenix for a personal project. Phoenix by default comessetup with a basic PageController. I wanted to use Elmā€™s Navigation package to handle routingon my Elm app.

With the default setup, Phoenix will attempt to serve the route, ending in a 404. I had to change my router.ex to the following to allow any path to defer to the PageController.index function:

123456789
defmodule AppliedAt.Router do  use AppliedAt.Web, :router  scope \"/\", AppliedAt do    pipe_through :browser # Use the default browser stack    get \"/*path\", PageController, :index # <-- Allows any number of path items  endend

This should work for any router in JavaScript, Elm, or < insert language here > šŸ˜„.

", "url": "https://fdp.io/blog/2017/03/04/setup-phoenix-router-with-a-spa/", "date_published": "2017-03-04T20:19:00+00:00", "date_modified": "2017-03-04T20:19:00+00:00", "author": { "name": "Fernando Paredes", "url": "https://fdp.io", "avatar": null } }, { "id": "https://fdp.io/blog/2016/12/25/atom-plugins-for-elm/", "title": "Atom Plugins for Elm", "summary": "I've gotten back into using Elm for a pet project of mine. Here are some of the plugins I found most useful for Atom.", "content_text": "I havenā€™t used Elm since version 0.12. During my vacation, I wanted to get a deeper dive into the effort people have put into it since then. Elmā€™s compiler is great at identifying errors in your code, but it happened after I tried to run my code or use elm-make on it.Below are some of the plugins I found useful when writing Elm:language-elmlanguage-elm provides syntax highlighting and snippets. It forms the basis for every other plugin described.1apm install language-elmelm-formatelm-format is like gofmt in that it formats Elm source code according to a standard set of rules based on the official Elm Style Guide. Installing the apm package is not enough though, you need to install the tool. Iā€™d recommend using the following brew command which grabs the recipe from the homebrew-devel tap.1brew install homebrew/devel-only/elm-format --develThe recipe by default installs elm-format for the last three(?) versions of Elm. We will need to create a symlink so that the atom package calls out to the right binary.12# Remember to substitute the version number if >= 0.19 is outln -s \"$(brew --prefix)/bin/elm-format-0.18\" /usr/local/bin/elm-formatOnce installed, Elm code will format to match the styling guide on save.1apm install elm-formatlinter-elm-makelinter-elm-make brings in helpful error messages to Atom. It has options to lint as you type or on save.AutocompletionTo enable autocompletion, we will need to install elm-oracle via npm. elm-oracle queries for information about values in Elm source files. Iā€™d recommend to install it globally via [Yarn][yarn]:1yarn global add elm-oracleelmjutsuOnce elm-oracle is available, we will need elmjutsu to enable proper autocomplete.1apm install elmjutsuAfter installing elmjutsu, we need to disable autocomplete for language-elm and enable it for elmjutsu.", "content_html": "

I havenā€™t used Elm since version 0.12. During my vacation, I wanted to get a deeper dive into the effort people have put into it since then. Elmā€™s compiler is great at identifying errors in your code, but it happened after I tried to run my code or use elm-make on it.

Below are some of the plugins I found useful when writing Elm:

language-elm

language-elm provides syntax highlighting and snippets. It forms the basis for every other plugin described.

1
apm install language-elm

elm-format

\"elm-format

elm-format is like gofmt in that it formats Elm source code according to a standard set of rules based on the official Elm Style Guide. Installing the apm package is not enough though, you need to install the tool. Iā€™d recommend using the following brew command which grabs the recipe from the homebrew-devel tap.

1
brew install homebrew/devel-only/elm-format --devel

The recipe by default installs elm-format for the last three(?) versions of Elm. We will need to create a symlink so that the atom package calls out to the right binary.

12
# Remember to substitute the version number if >= 0.19 is outln -s \"$(brew --prefix)/bin/elm-format-0.18\" /usr/local/bin/elm-format

Once installed, Elm code will format to match the styling guide on save.

1
apm install elm-format

linter-elm-make

\"elm-make

linter-elm-make brings in helpful error messages to Atom. It has options to lint as you type or on save.

Autocompletion

To enable autocompletion, we will need to install elm-oracle via npm. elm-oracle queries for information about values in Elm source files. Iā€™d recommend to install it globally via [Yarn][yarn]:

1
yarn global add elm-oracle

elmjutsu

Once elm-oracle is available, we will need elmjutsu to enable proper autocomplete.

1
apm install elmjutsu

After installing elmjutsu, we need to disable autocomplete for language-elm and enable it for elmjutsu.

", "url": "https://fdp.io/blog/2016/12/25/atom-plugins-for-elm/", "date_published": "2016-12-25T00:00:00+00:00", "date_modified": "2016-12-25T00:00:00+00:00", "author": { "name": "Fernando Paredes", "url": "https://fdp.io", "avatar": null } }, { "id": "https://fdp.io/blog/2016/11/21/es6-using-set-for-unique-arrays/", "title": "ES6: Using Set For Unique Arrays", "summary": "A quick way to get an array of unique values", "content_text": "In the past, getting unique values meant pulling an external library or creating your own.Now, you can easily do that with Set and Array.from.123const books = ['Matilda', 'Charlie and the Chocolate Factory', 'Matilda', 'James and the Giant Peach', 'Matilda']const uniqueBooks = Array.from(new Set(books))// => ['Matilda', 'Charlie and the Chocolate Factory', 'James and the Giant Peach']Set lets you store unique values and accepts any Object or primitive to be passed in to itā€™s initializer.When the data passed in has multiple items, it strips them out automatically.Spread OperatorA simpler syntax exists if you use the spread operator:12const books = ['Matilda', 'Charlie and the Chocolate Factory', 'Matilda', 'James and the Giant Peach', 'Matilda']const uniqueBooks = [...new Set(books)]", "content_html": "

In the past, getting unique values meant pulling an external library or creating your own.Now, you can easily do that with Set and Array.from.

123
const books = ['Matilda', 'Charlie and the Chocolate Factory', 'Matilda', 'James and the Giant Peach', 'Matilda']const uniqueBooks = Array.from(new Set(books))// => ['Matilda', 'Charlie and the Chocolate Factory', 'James and the Giant Peach']

Set lets you store unique values and accepts any Object or primitive to be passed in to itā€™s initializer.When the data passed in has multiple items, it strips them out automatically.

Spread Operator

A simpler syntax exists if you use the spread operator:

12
const books = ['Matilda', 'Charlie and the Chocolate Factory', 'Matilda', 'James and the Giant Peach', 'Matilda']const uniqueBooks = [...new Set(books)]
", "url": "https://fdp.io/blog/2016/11/21/es6-using-set-for-unique-arrays/", "date_published": "2016-11-21T00:00:00+00:00", "date_modified": "2016-11-21T00:00:00+00:00", "author": { "name": "Fernando Paredes", "url": "https://fdp.io", "avatar": null } }, { "id": "https://fdp.io/blog/2016/11/20/adding-a-user-to-an-existing-gpg-key/", "title": "Adding A User To An Existing GPG key", "summary": "Where I fix commits that were signed but unverified in Github.", "content_text": "I recently came across an issue when I was signing commits that also used my business email.I gladly learned that itā€™s not very hard to add an additional user id to a GPG key.Get Current Secret Key IDTo start, we need to get our current secret key id. Weā€™re looking for the sec key.We can ignore the bit length1234567gpg --list-secret-keys --keyid-format LONG#=> ~/.gnupg/secring.gpg#=> ------------------------------------------#=> sec 4096R/THISISMYKEY 2016-11-04#=> uid Fernando Paredes <email@example.com>#=> ssb 4096R/THISISMYSSB 2016-11-04Edit Key1gpg --edit-key THISISMYKEY--edit-key will drop us into gpgā€™s REPL. From here we can add our additional user ID:123456gpg> adduid#=> Real Name: Newt Scamander#=> Email address: newt@scamander.com#=> Comment: Fantastic Beasts#=> Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? OGithubWeā€™ll need to add our key to Github. If youā€™ve already added the key to Github, youā€™ll need to delete it and paste in your new key.1gpg --armor --export | pbcopy", "content_html": "

I recently came across an issue when I was signing commits that also used my business email.

\"github

I gladly learned that itā€™s not very hard to add an additional user id to a GPG key.

Get Current Secret Key ID

To start, we need to get our current secret key id. Weā€™re looking for the sec key.We can ignore the bit length

1234567
gpg --list-secret-keys --keyid-format LONG#=> ~/.gnupg/secring.gpg#=> ------------------------------------------#=> sec   4096R/THISISMYKEY 2016-11-04#=> uid                          Fernando Paredes <email@example.com>#=> ssb   4096R/THISISMYSSB 2016-11-04

Edit Key

1
gpg --edit-key THISISMYKEY

--edit-key will drop us into gpgā€™s REPL. From here we can add our additional user ID:

123456
gpg> adduid#=> Real Name: Newt Scamander#=> Email address: newt@scamander.com#=> Comment: Fantastic Beasts#=> Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O

Github

Weā€™ll need to add our key to Github. If youā€™ve already added the key to Github, youā€™ll need to delete it and paste in your new key.

1
gpg --armor --export | pbcopy
", "url": "https://fdp.io/blog/2016/11/20/adding-a-user-to-an-existing-gpg-key/", "date_published": "2016-11-20T00:00:00+00:00", "date_modified": "2016-11-20T00:00:00+00:00", "author": { "name": "Fernando Paredes", "url": "https://fdp.io", "avatar": null } }, { "id": "https://fdp.io/blog/2016/11/08/theming-via-css-properties/", "title": "Theming Via CSS Properties", "summary": "Where I talk about adding a new feature to my blog where you can switch between a Night and Day theme.", "content_text": "CSS PropertiesCSS Properties are fairly new. Chrome 49, Firefox 42, Safari 9.1, and iOS Safari 9.3 support custom variables. They are incredible for setting up themes at runtime when coupled with a little bit of JavaScript.Adding Custom PropertiesCustom properties must be prefixed with two dashes. To demonstrate, lets add a custom property for our main and background colors:1234:root { --main-color: red; --bg-color: #FFF;}Weā€™ve set these colors on the documentā€™s root element. To use a custom property, we need to use var(). Letā€™s say that we wanted to replace the background color for our body. We could do that with the following:123body { background-color: var(--bg-color);}Changing A PropertyWith JavaScript, we can update a property to anything weā€™d like using setProperty. Hereā€™s an example that changes our main color to blue:1document.body.style.setProperty('--main-color', 'blue')The downside to custom properties is that they allow you to write invalid CSS. If you are using --main-color in places that expect a color but you inadvertently set it to 1.25rem, it wouldnā€™t alert you that itā€™s invalid.To learn more about CSS custom properties, I strongly recommend reading Googleā€™s article on it.Declaring ThemesNow that we know about CSS custom properties, we can shift to the code that enables theme switching. To declare a theme, I used a basic JavaScript Object to match properties to values.1234567891011const LightTheme = { '--main-color': '#1b70de', '--bg-color': '#FFF', '--text-color': '#000', '--button-color': 'rgba(0, 0, 0, 0.8)', '--header-color': '#424242', '--metadata-color': '#828282', '--table-border-color': '#cccccc', '--table-header-color': '#e8e8e8', themeName: 'LightTheme'}Once you declare a theme, you should also make another version with alternate values.Toggle ThemeUpdating code at runtime is made easy by JavaScriptā€™s Object.entries, we can traverse the objectā€™s keys and values to save to our stylesheet.12345const updateTheme = (theme) => { Object .entries(theme) .forEach(([key, value]) => document.body.style.setProperty(key, value))}When this is ran, anything that is using a var(--custom-property) will immediately update to the new color.PersistenceAn issue with our approach is that the selected theme is lost when a user navigates away from the page. To remedy this, I utilized localStorage to persist the current theme.Saving to localStorageSaving to localStorage provides two methods: setItem(key, value) and a subscript method (localStorage['key'] = value). We also want to make sure weā€™re dealing with the same kind of object. To do this, we can use JSON.stringify to store our theme as a JSON string. For good measure, we also need to save our current theme name.123456const saveTheme = (theme) => { if (window.localStorage) { localStorage['theme'] = JSON.stringify(theme) localStorage['currentTheme'] = theme.themeName }}Loading Saved ThemeTo pull the theme back out, weā€™ll use JSON.parse() to convert our JSON string to a JSON object.1234567if (window.localStorage) { const maybeTheme = localStorage['theme'] if (maybeTheme) { const theme = JSON.parse(maybeTheme) updateTheme(theme) }}Next, we want to check if thereā€™s a currently saved theme on every page load. What Iā€™ve done is created an inline script that occurs at the end of a page load and attempts to load the saved theme that is persisted in localStorage:123<script>checkForSavedTheme()</script>WiringNow that we have a mechanism for using different themes, we need to wire up a toggle for users. I chose to add my toggle to the navigation bar.We had previously saved the currentThemeā€™s name to localStorage for exactly this reason.123456789101112131415161718// First we need to look for the empty nav toggle// I called mine #theme-switcherconst el = document.getElementById('theme-switcher')// From here we need to check if we have a saved themeconst theme = loadSavedTheme()// We'll also need the theme's nameconst currentTheme = localStorage['currentTheme']// We'll use a rudimentary if clause to check if theme and currentTheme is equalif (theme && currentTheme === NightTheme.themeName) { updateTheme(LightTheme) // Here we set the FontAwesome classes for our toggle el.className = 'fa fa-moon-o'} else { updateTheme(NightTheme) el.className = 'fa fa-sun-o'}Whatā€™s NextWith custom properties, we were able to make our page react to changes in CSS with a little JavaScript. In this post, I focused mostly on changing colors but you could also change it to any valid CSS šŸ˜¬.", "content_html": "

CSS Properties

CSS Properties are fairly new. Chrome 49, Firefox 42, Safari 9.1, and iOS Safari 9.3 support custom variables. They are incredible for setting up themes at runtime when coupled with a little bit of JavaScript.

Adding Custom Properties

Custom properties must be prefixed with two dashes. To demonstrate, lets add a custom property for our main and background colors:

1234
:root {  --main-color: red;  --bg-color: #FFF;}

Weā€™ve set these colors on the documentā€™s root element. To use a custom property, we need to use var(). Letā€™s say that we wanted to replace the background color for our body. We could do that with the following:

123
body {  background-color: var(--bg-color);}

Changing A Property

With JavaScript, we can update a property to anything weā€™d like using setProperty. Hereā€™s an example that changes our main color to blue:

1
document.body.style.setProperty('--main-color', 'blue')

The downside to custom properties is that they allow you to write invalid CSS. If you are using --main-color in places that expect a color but you inadvertently set it to 1.25rem, it wouldnā€™t alert you that itā€™s invalid.

To learn more about CSS custom properties, I strongly recommend reading Googleā€™s article on it.

Declaring Themes

Now that we know about CSS custom properties, we can shift to the code that enables theme switching. To declare a theme, I used a basic JavaScript Object to match properties to values.

1234567891011
const LightTheme = {  '--main-color': '#1b70de',  '--bg-color': '#FFF',  '--text-color': '#000',  '--button-color': 'rgba(0, 0, 0, 0.8)',  '--header-color': '#424242',  '--metadata-color': '#828282',  '--table-border-color': '#cccccc',  '--table-header-color': '#e8e8e8',  themeName: 'LightTheme'}

Once you declare a theme, you should also make another version with alternate values.

Toggle Theme

Updating code at runtime is made easy by JavaScriptā€™s Object.entries, we can traverse the objectā€™s keys and values to save to our stylesheet.

12345
const updateTheme = (theme) => {  Object    .entries(theme)    .forEach(([key, value]) => document.body.style.setProperty(key, value))}

When this is ran, anything that is using a var(--custom-property) will immediately update to the new color.

Persistence

An issue with our approach is that the selected theme is lost when a user navigates away from the page. To remedy this, I utilized localStorage to persist the current theme.

Saving to localStorage

Saving to localStorage provides two methods: setItem(key, value) and a subscript method (localStorage['key'] = value). We also want to make sure weā€™re dealing with the same kind of object. To do this, we can use JSON.stringify to store our theme as a JSON string. For good measure, we also need to save our current theme name.

123456
const saveTheme = (theme) => {  if (window.localStorage) {    localStorage['theme'] = JSON.stringify(theme)    localStorage['currentTheme'] = theme.themeName  }}

Loading Saved Theme

To pull the theme back out, weā€™ll use JSON.parse() to convert our JSON string to a JSON object.

1234567
if (window.localStorage) {  const maybeTheme = localStorage['theme']  if (maybeTheme) {    const theme = JSON.parse(maybeTheme)    updateTheme(theme)  }}

Next, we want to check if thereā€™s a currently saved theme on every page load. What Iā€™ve done is created an inline script that occurs at the end of a page load and attempts to load the saved theme that is persisted in localStorage:

123
<script>checkForSavedTheme()</script>

Wiring

Now that we have a mechanism for using different themes, we need to wire up a toggle for users. I chose to add my toggle to the navigation bar.

We had previously saved the currentThemeā€™s name to localStorage for exactly this reason.

123456789101112131415161718
// First we need to look for the empty nav toggle// I called mine #theme-switcherconst el = document.getElementById('theme-switcher')// From here we need to check if we have a saved themeconst theme = loadSavedTheme()// We'll also need the theme's nameconst currentTheme = localStorage['currentTheme']// We'll use a rudimentary if clause to check if theme and currentTheme is equalif (theme && currentTheme === NightTheme.themeName) {  updateTheme(LightTheme)  // Here we set the FontAwesome classes for our toggle  el.className = 'fa fa-moon-o'} else {  updateTheme(NightTheme)  el.className = 'fa fa-sun-o'}

Whatā€™s Next

With custom properties, we were able to make our page react to changes in CSS with a little JavaScript. In this post, I focused mostly on changing colors but you could also change it to any valid CSS šŸ˜¬.

", "url": "https://fdp.io/blog/2016/11/08/theming-via-css-properties/", "date_published": "2016-11-08T00:00:00+00:00", "date_modified": "2016-11-08T00:00:00+00:00", "author": { "name": "Fernando Paredes", "url": "https://fdp.io", "avatar": null } }, { "id": "https://fdp.io/blog/2016/10/13/fix-nokogiri-v1-6-8-1-installation-failure-on-macOS-sierra/", "title": "Fix Nokogiri v1.6.8.1 Installation Failure on macOS Sierra", "summary": "A fix for nokogiri 1.6.8.1 on macOS Sierra by updating libxml2", "content_text": "On macOS Sierra 10.12, I ran into issues updating my gems that relied on Nokogiri due to the following error with the OS libxml2 version.1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859Building native extensions. This could take a while...ERROR: Error installing nokogiri: ERROR: Failed to build gem native extension. current directory: ~/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/nokogiri-1.6.8.1/ext/nokogiri~/.rbenv/versions/2.3.1/bin/ruby -r ./siteconf20161013-4537-ttdfzl.rb extconf.rbchecking if the C compiler accepts ... yeschecking if the C compiler accepts -Wno-error=unused-command-line-argument-hard-error-in-future... noBuilding nokogiri using packaged libraries.Using mini_portile version 2.1.0checking for iconv.h... yeschecking for gzdopen() in -lz... yeschecking for iconv... yes************************************************************************IMPORTANT NOTICE:Building Nokogiri with a packaged version of libxml2-2.9.4.Team Nokogiri will keep on doing their best to provide securityupdates in a timely manner, but if this is a concern for you and wantto use the system library instead; abort this installation process andreinstall nokogiri as follows: gem install nokogiri -- --use-system-libraries [--with-xml2-config=/path/to/xml2-config] [--with-xslt-config=/path/to/xslt-config]If you are using Bundler, tell it to use the option: bundle config build.nokogiri --use-system-libraries bundle installNote, however, that nokogiri is not fully compatible with arbitraryversions of libxml2 provided by OS/package vendors.************************************************************************Extracting libxml2-2.9.4.tar.gz into tmp/x86_64-apple-darwin15.5.0/ports/libxml2/2.9.4... OKRunning 'configure' for libxml2 2.9.4... OKRunning 'compile' for libxml2 2.9.4... ERROR, review '~/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/nokogiri-1.6.8.1/ext/nokogiri/tmp/x86_64-apple-darwin15.5.0/ports/libxml2/2.9.4/compile.log' to see what happened. Last lines are:======================================================================== unsigned short* in = (unsigned short*) inb; ^~~~~~~~~~~~~~~~~~~~~encoding.c:815:27: warning: cast from 'unsigned char *' to 'unsigned short *' increases required alignment from 1 to 2 [-Wcast-align] unsigned short* out = (unsigned short*) outb; ^~~~~~~~~~~~~~~~~~~~~~4 warnings generated. CC error.lo CC parserInternals.lo CC parser.lo CC tree.lo CC hash.lo CC list.lo CC xmlIO.loxmlIO.c:1450:52: error: use of undeclared identifier 'LZMA_OK' ret = (__libxml2_xzclose((xzFile) context) == LZMA_OK ) ? 0 : -1; ^1 error generated.make[2]: *** [xmlIO.lo] Error 1make[1]: *** [all-recursive] Error 1make: *** [all] Error 2Installing libxml2Iā€™ve found installing libxml2 from homebrew to work in the past:1brew install libxml2Finally, youā€™ll need to do one of two things depending on how you are performing the update:BundlerWith Bundler, you need to instruct it to configure Nokogiri builds to use our newly installed libxml2 library:1bundle config build.nokogiri --use-system-libraries --with-xml2-include=/usr/local/opt/libxml2/include/libxml2On the next run of bundle update, it should install correctlyRubygemsA gem install allows you to pass in the configuration options to the native extension using --:123gem install nokogiri -- \\ --use-system-libraries \\ --with-xml2-include=/usr/local/opt/libxml2/include/libxml2", "content_html": "

On macOS Sierra 10.12, I ran into issues updating my gems that relied on Nokogiri due to the following error with the OS libxml2 version.

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859
Building native extensions.  This could take a while...ERROR:  Error installing nokogiri:        ERROR: Failed to build gem native extension.    current directory: ~/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/nokogiri-1.6.8.1/ext/nokogiri~/.rbenv/versions/2.3.1/bin/ruby -r ./siteconf20161013-4537-ttdfzl.rb extconf.rbchecking if the C compiler accepts ... yeschecking if the C compiler accepts -Wno-error=unused-command-line-argument-hard-error-in-future... noBuilding nokogiri using packaged libraries.Using mini_portile version 2.1.0checking for iconv.h... yeschecking for gzdopen() in -lz... yeschecking for iconv... yes************************************************************************IMPORTANT NOTICE:Building Nokogiri with a packaged version of libxml2-2.9.4.Team Nokogiri will keep on doing their best to provide securityupdates in a timely manner, but if this is a concern for you and wantto use the system library instead; abort this installation process andreinstall nokogiri as follows:    gem install nokogiri -- --use-system-libraries        [--with-xml2-config=/path/to/xml2-config]        [--with-xslt-config=/path/to/xslt-config]If you are using Bundler, tell it to use the option:    bundle config build.nokogiri --use-system-libraries    bundle installNote, however, that nokogiri is not fully compatible with arbitraryversions of libxml2 provided by OS/package vendors.************************************************************************Extracting libxml2-2.9.4.tar.gz into tmp/x86_64-apple-darwin15.5.0/ports/libxml2/2.9.4... OKRunning 'configure' for libxml2 2.9.4... OKRunning 'compile' for libxml2 2.9.4... ERROR, review '~/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/nokogiri-1.6.8.1/ext/nokogiri/tmp/x86_64-apple-darwin15.5.0/ports/libxml2/2.9.4/compile.log' to see what happened. Last lines are:========================================================================    unsigned short* in = (unsigned short*) inb;                         ^~~~~~~~~~~~~~~~~~~~~encoding.c:815:27: warning: cast from 'unsigned char *' to 'unsigned short *' increases required alignment from 1 to 2 [-Wcast-align]    unsigned short* out = (unsigned short*) outb;                          ^~~~~~~~~~~~~~~~~~~~~~4 warnings generated.  CC       error.lo  CC       parserInternals.lo  CC       parser.lo  CC       tree.lo  CC       hash.lo  CC       list.lo  CC       xmlIO.loxmlIO.c:1450:52: error: use of undeclared identifier 'LZMA_OK'    ret =  (__libxml2_xzclose((xzFile) context) == LZMA_OK ) ? 0 : -1;                                                   ^1 error generated.make[2]: *** [xmlIO.lo] Error 1make[1]: *** [all-recursive] Error 1make: *** [all] Error 2

Installing libxml2

Iā€™ve found installing libxml2 from homebrew to work in the past:

1
brew install libxml2

Finally, youā€™ll need to do one of two things depending on how you are performing the update:

Bundler

With Bundler, you need to instruct it to configure Nokogiri builds to use our newly installed libxml2 library:

1
bundle config build.nokogiri --use-system-libraries --with-xml2-include=/usr/local/opt/libxml2/include/libxml2

On the next run of bundle update, it should install correctly

Rubygems

A gem install allows you to pass in the configuration options to the native extension using --:

123
gem install nokogiri -- \\  --use-system-libraries \\  --with-xml2-include=/usr/local/opt/libxml2/include/libxml2
", "url": "https://fdp.io/blog/2016/10/13/fix-nokogiri-v1-6-8-1-installation-failure-on-macOS-sierra/", "date_published": "2016-10-13T00:00:00+00:00", "date_modified": "2016-10-13T00:00:00+00:00", "author": { "name": "Fernando Paredes", "url": "https://fdp.io", "avatar": null } }, { "id": "https://fdp.io/blog/2016/09/21/macos-sierra-hang-on-react-native-init/", "title": "macOS Sierra: Hang on React Native Init", "summary": "A fix for hangs that occur on react-native init on macOS Sierra.", "content_text": "With the recent release of macOS Sierra, running react-native init app hangs until the process is exited. After spending some time diagnosing the issue, I narrowed it down to watchman hanging. To verify, run watchman version.To fix the issue, you need to delete watchman and reinstall it:1rm -rf /usr/local/var/run/watchman/ && brew uninstall watchman && brew install watchman", "content_html": "

With the recent release of macOS Sierra, running react-native init app hangs until the process is exited. After spending some time diagnosing the issue, I narrowed it down to watchman hanging. To verify, run watchman version.

To fix the issue, you need to delete watchman and reinstall it:

1
rm -rf /usr/local/var/run/watchman/ && brew uninstall watchman && brew install watchman
", "url": "https://fdp.io/blog/2016/09/21/macos-sierra-hang-on-react-native-init/", "date_published": "2016-09-21T00:00:00+00:00", "date_modified": "2016-09-21T00:00:00+00:00", "author": { "name": "Fernando Paredes", "url": "https://fdp.io", "avatar": null } }, { "id": "https://fdp.io/blog/2016/09/02/migration-to-jekyll/", "title": "Migration to Jekyll", "summary": "My experience transitioning from Octopress 2.0 to Jekyll 3", "content_text": "After many years of neglecting my blog, I decided I should start writing again.Before I could do that, I had to transition my blog from Octopress 2 to Jekyll.Why I MovedOctopress was a great blogging system. It worked wonderfully when I first started using it.A few months in, I wanted to update my Octopress instance and ran into a roadblock. Before I canget into that, I have to explain how I understand the project to be structured. It was a bootstrapproject where many Octopress internals were in the folder you needed to get started. To update,you needed to add an additional remote and pull down the changes, many of which overwrote yourown customizations :(.JekyllJekyll was a natural progression as it was the blogging engine that Octopress was built on. It usesLiquid as itā€™s templating language. With it, you can compose layouts and partials like in erb.Combined with jekyll-compose, you get many of the helpfulfeatures of Octopress like draft, post, publish, and unpublish.Other TechnologiesIā€™ve added Circle CI to use Danger to lint my prose and correct my spelling.šŸŽ‰Additional, the site is now hosted on S3 with a Cloudflare CDN šŸŽ‰.Things To Come Comments Footnotes Favicon šŸ˜‚", "content_html": "

After many years of neglecting my blog, I decided I should start writing again.Before I could do that, I had to transition my blog from Octopress 2 to Jekyll.

Why I Moved

Octopress was a great blogging system. It worked wonderfully when I first started using it.A few months in, I wanted to update my Octopress instance and ran into a roadblock. Before I canget into that, I have to explain how I understand the project to be structured. It was a bootstrapproject where many Octopress internals were in the folder you needed to get started. To update,you needed to add an additional remote and pull down the changes, many of which overwrote yourown customizations :(.

Jekyll

Jekyll was a natural progression as it was the blogging engine that Octopress was built on. It usesLiquid as itā€™s templating language. With it, you can compose layouts and partials like in erb.Combined with jekyll-compose, you get many of the helpfulfeatures of Octopress like draft, post, publish, and unpublish.

Other Technologies

Iā€™ve added Circle CI to use Danger to lint my prose and correct my spelling.šŸŽ‰Additional, the site is now hosted on S3 with a Cloudflare CDN šŸŽ‰.

Things To Come

", "url": "https://fdp.io/blog/2016/09/02/migration-to-jekyll/", "date_published": "2016-09-02T00:00:00+00:00", "date_modified": "2016-09-02T00:00:00+00:00", "author": { "name": "Fernando Paredes", "url": "https://fdp.io", "avatar": null } }, { "id": "https://fdp.io/blog/2013/09/15/rails-4-easily-add-reference-to-migration/", "title": "Rails 4: Easily Add Reference to Migration", "summary": "A look into Rails 4's add_reference", "content_text": "While working on a Rails 4 application, I came across a new addition to migrations add_reference. add_reference makes it easier to create migrations for foreign keys.Rails 3In Rails 3, to add a foreign key and an index you would write a migration like this:123456class AddUserToBooks < ActiveRecord::Migration def change add_column :books, :user_id, :integer add_index :books, :user_id endendIt requires two method calls, one to add the user_id column to the Books table, and another to add an index to user_id.Rails 4In Rails 4, you can accomplish the same thing with add_reference:12345class AddUserToBooks < ActiveRecord::Migration def change add_reference :books, :user, index: true endendWhile it isnā€™t a major change, it further illustrates the foundation that Ruby was built upon. ā€œRuby is designed to make programmers happy.ā€ - Yukihiro Matsumoto", "content_html": "

While working on a Rails 4 application, I came across a new addition to migrations add_reference. add_reference makes it easier to create migrations for foreign keys.

Rails 3

In Rails 3, to add a foreign key and an index you would write a migration like this:

123456
class AddUserToBooks < ActiveRecord::Migration  def change    add_column :books, :user_id, :integer    add_index  :books, :user_id  endend

It requires two method calls, one to add the user_id column to the Books table, and another to add an index to user_id.

Rails 4

In Rails 4, you can accomplish the same thing with add_reference:

12345
class AddUserToBooks < ActiveRecord::Migration  def change    add_reference :books, :user, index: true  endend

While it isnā€™t a major change, it further illustrates the foundation that Ruby was built upon.

ā€œRuby is designed to make programmers happy.ā€ - Yukihiro Matsumoto

", "url": "https://fdp.io/blog/2013/09/15/rails-4-easily-add-reference-to-migration/", "date_published": "2013-09-15T19:29:00+00:00", "date_modified": "2013-09-15T19:29:00+00:00", "author": { "name": "Fernando Paredes", "url": "https://fdp.io", "avatar": null } }, { "id": "https://fdp.io/blog/2013/09/01/how-to-view-open-connections-on-files/", "title": "How to view open connections on files", "summary": "Use lsof to learn what your processes use behind the scenes", "content_text": "During a pairing session on Ruby, we had an issue with what file the API was stuck on. Luckily, there is a command lsof which stands for list open files.UsageWhen lsof is ran without parameters, it will show all the files opened by any process. It can get a bit messy, so I recommend greping through to find the file/process you want.To find out what files Ruby has opened you can use lsof -c. Here is what it looks like when running rails server:123$ lsof -c rubyruby 55222 nano 10w REG 1,1 1214625 34875337 ~/Developer/Projects/contact-manager/log/development.logruby 55222 nano 11u REG 1,1 49152 34875460 ~/Developer/Projects/contact-manager/db/development.sqlite3To find out what is accessing any file in a directory, use the lsof +D /usr/directory12$ lsof +D /Users/nano/Developer/Projects/contact-managervim 44957 nano cwd DIR 1,1 748 34873022 ~/Developer/Projects/contact-managerYou can list all opened internet sockets using lsof -i.1234567$ lsof -i :62658Rdio 63484 nano 23u IPv4 0x2674679fdf954c9b 0t0 TCP 10.0.1.14:62658->8.18.203.81:sunproxyadmin (ESTABLISHED)# We can find out the process id for Unicorn running on port 8080$ lsof -i :8080COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAMEruby 54171 nano 9u IPv4 0x2674679fe1b36243 0t0 TCP *:http-alt (LISTEN)ruby 54172 nano 9u IPv4 0x2674679fe1b36243 0t0 TCP *:http-alt (LISTEN)After finding the process id, you can find out what that process is accessing using lsof +p PID12345678$ lsof +p 79766COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAMEruby 54172 nano cwd DIR 1,1 748 34873022 ~/Developer/Projects/contact-managerruby 54172 nano txt REG 1,1 9184 17640590 ~/.rvm/rubies/ruby-2.0.0-p247/bin/rubyruby 54172 nano txt REG 1,1 2793108 17640592 ~/.rvm/rubies/ruby-2.0.0-p247/lib/libruby.2.0.0.dylibruby 54172 nano txt REG 1,1 118244 615195 /usr/local/Cellar/libyaml/0.1.4/lib/libyaml-0.2.dylibruby 54172 nano txt REG 1,1 378152 797957 /usr/local/Cellar/openssl/1.0.1e/lib/libssl.1.0.0.dylibruby 54172 nano txt REG 1,1 1663200 797956 /usr/local/Cellar/openssl/1.0.1e/lib/libcrypto.1.0.0.dylibUsing several commands leads you to any files that may be stuck in processing. I have found that it is an essential tool in debugging a multitude of issues.References A Unix Utility You Should Know About: lsof man lsof lsof Survival Guide", "content_html": "

During a pairing session on Ruby, we had an issue with what file the API was stuck on. Luckily, there is a command lsof which stands for list open files.

Usage

When lsof is ran without parameters, it will show all the files opened by any process. It can get a bit messy, so I recommend greping through to find the file/process you want.

To find out what files Ruby has opened you can use lsof -c. Here is what it looks like when running rails server:

123
$ lsof -c rubyruby    55222 nano   10w   REG    1,1   1214625 34875337 ~/Developer/Projects/contact-manager/log/development.logruby    55222 nano   11u   REG    1,1     49152 34875460 ~/Developer/Projects/contact-manager/db/development.sqlite3

To find out what is accessing any file in a directory, use the lsof +D /usr/directory

12
$ lsof +D /Users/nano/Developer/Projects/contact-managervim     44957 nano  cwd    DIR    1,1      748 34873022 ~/Developer/Projects/contact-manager

You can list all opened internet sockets using lsof -i.

1234567
$ lsof -i :62658Rdio      63484 nano   23u  IPv4 0x2674679fdf954c9b      0t0  TCP 10.0.1.14:62658->8.18.203.81:sunproxyadmin (ESTABLISHED)# We can find out the process id for Unicorn running on port 8080$ lsof -i :8080COMMAND   PID USER   FD   TYPE             DEVICE SIZE/OFF NODE NAMEruby    54171 nano    9u  IPv4 0x2674679fe1b36243      0t0  TCP *:http-alt (LISTEN)ruby    54172 nano    9u  IPv4 0x2674679fe1b36243      0t0  TCP *:http-alt (LISTEN)

After finding the process id, you can find out what that process is accessing using lsof +p PID

12345678
$ lsof +p 79766COMMAND   PID USER   FD   TYPE             DEVICE  SIZE/OFF     NODE NAMEruby    54172 nano  cwd    DIR                1,1       748 34873022 ~/Developer/Projects/contact-managerruby    54172 nano  txt    REG                1,1      9184 17640590 ~/.rvm/rubies/ruby-2.0.0-p247/bin/rubyruby    54172 nano  txt    REG                1,1   2793108 17640592 ~/.rvm/rubies/ruby-2.0.0-p247/lib/libruby.2.0.0.dylibruby    54172 nano  txt    REG                1,1    118244   615195 /usr/local/Cellar/libyaml/0.1.4/lib/libyaml-0.2.dylibruby    54172 nano  txt    REG                1,1    378152   797957 /usr/local/Cellar/openssl/1.0.1e/lib/libssl.1.0.0.dylibruby    54172 nano  txt    REG                1,1   1663200   797956 /usr/local/Cellar/openssl/1.0.1e/lib/libcrypto.1.0.0.dylib

Using several commands leads you to any files that may be stuck in processing. I have found that it is an essential tool in debugging a multitude of issues.

References

", "url": "https://fdp.io/blog/2013/09/01/how-to-view-open-connections-on-files/", "date_published": "2013-09-01T20:01:00+00:00", "date_modified": "2013-09-01T20:01:00+00:00", "author": { "name": "Fernando Paredes", "url": "https://fdp.io", "avatar": null } }, { "id": "https://fdp.io/blog/2013/08/04/make-iterm-2-work-with-mac-osx-movement-keyboard-shortcuts/", "title": "Make iTerm 2 work with Mac OSX movement keyboard shortcuts", "summary": "Add support to iTerm2 for ⌥ and ⌘ modifiers to move between words", "content_text": "After transitioning from Terminal to iTerm 2, you may notice commands like āŒ„ + ā† (moves left one word) and āŒ˜ + ā† (moves to the beginning of the line) no longer work.It is very simple process to add the keyboard shortcuts to your profile. Here are the keyboard shortcuts we are adding: Keyboard Shortcut Description āŒ„ + ā† Move one word to the left āŒ„ + ā†’ Move one word to the right āŒ˜ + ā† Move to the beginning of the line āŒ˜ + ā†’ Move to the end of the line 1. Open Preferences2. Click on ProfilesFind the Keys header and click on the + sign to add a new shortcut key.3. Add Keyboard shortcutsEnter each of the following shortcuts below:Congratulations, you can now move around in iTerm 2 just like Terminal!", "content_html": "

After transitioning from Terminal to iTerm 2, you may notice commands like āŒ„ + ā† (moves left one word) and āŒ˜ + ā† (moves to the beginning of the line) no longer work.

It is very simple process to add the keyboard shortcuts to your profile. Here are the keyboard shortcuts we are adding:

Keyboard Shortcut Description
āŒ„ + ā† Move one word to the left
āŒ„ + ā†’ Move one word to the right
āŒ˜ + ā† Move to the beginning of the line
āŒ˜ + ā†’ Move to the end of the line

1. Open Preferences

\"iTerm

2. Click on Profiles

Find the Keys header and click on the + sign to add a new shortcut key.

\"\"

3. Add Keyboard shortcuts

Enter each of the following shortcuts below:

\"Option

\"Option

\"Command

\"Command

Congratulations, you can now move around in iTerm 2 just like Terminal!

", "url": "https://fdp.io/blog/2013/08/04/make-iterm-2-work-with-mac-osx-movement-keyboard-shortcuts/", "date_published": "2013-08-04T12:17:00+00:00", "date_modified": "2013-08-04T12:17:00+00:00", "author": { "name": "Fernando Paredes", "url": "https://fdp.io", "avatar": null } }, { "id": "https://fdp.io/blog/2013/07/06/devops-for-ruby-part-2-using-nginx-to-host-rails-applications/", "title": "DevOps for Ruby Part 2: Using Nginx to Host Rails Applications", "summary": "Continue setting up Rails to use Nginx on a VPS", "content_text": "In DevOps for Ruby Part 1, we set up a new VPS with all of the software needed to serve Ruby on Rails applications. In this post we will be setting up two Rails application with separate PostgreSQL databases.ObjectivesThe goal of this tutorial is to: Create the necessary folders to host our web applications Use Git to sync our applications from Github onto the VPS. Create PostgreSQL databases for our applications and modify the database.yml file. Set up Nginx to serve our application.Initial setupFirst, we must create a /var/www folder. We can do that by ssh-ing into our VPS. We can create the folder using:1sudo mkdir -p /var/wwwmkdir makes the directory and the -p flag adds intermediate folders if they donā€™t exist.GitHub setupIn order to avoid typing passwords all the time, we must generate SSH keys and tie them to our Github account. You can follow this guide to get started.Next, lets fetch our Rails application using the git clone command:123cd /var/wwwgit clone git@github.com:NanoXD/fdpio.gitgit clone git@github.com:NanoXD/testapp.gitVerify that you have two folders within /var/www/ for your two Rails apps.Database SetupWe are going to create a separate user for each application as it is good security practice to limit how much access a user has. Letā€™s start off by logging into the PostgreSQL shell1sudo -u postgres psqlWe can create users and databases for each of our applications using this syntax12postgres=# create user REPLACE_WITH_USER with password 'ENTER_PASSWORD';postgres=# create database DATABASE_NAME owner USERNAME_FOR_DATABASE;Configuring your database.ymlEach Rails application comes with a database.yml file like I described in Setup PostgreSQL for Rails on a Mac. Letā€™s modify this file to connect to our new PostgreSQL database.12345678910# /var/www/fdpio/config/database.ymlproduction: adapter: postgresql encoding: unicode host: localhost database: fdpio_db pool: 5 username: fdpio password: password-manDo the same for your secondary Rails app.Configure NginxWe now have to setup Nginx to allow us to serve our two applications using one server. If you followed Part 1 of this tutorial, you can find your nginx.conf in the /opt/. Letā€™s open it up using vim:1sudo vim /opt/nginx/conf/nginx.confKeep in mind that this configuration is suited to my two applications. Ensure that you change the paths and names to fit your application. I will describe the changes below.1234567891011121314151617181920212223242526272829303132333435363738worker_processes 1;events { worker_connections 1024; multi_accept on;}http { passenger_root /home/nano/.rvm/gems/ruby-2.0.0-p195/gems/passenger-4.0.5; passenger_ruby /home/nano/.rvm/wrappers/ruby-2.0.0-p195/ruby; include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; gzip on; gzip_vary on; server { listen 80; server_name fdp.io; charset utf-8; root /var/www/fdpio/public; passenger_enabled on; rails_env production; } server { listen 80; server_name testapp.com; charset utf-8; root /var/www/testapp/public; passenger_enabled on; rails_env production; }}The two Passenger settings that have to suit your machine are:passenger_root - Specifies where Passenger ispassenger_ruby - Tells Passenger what version of Ruby to use.We created two server settings for each application. Here is what each setting means:1234567891011121314151617# What port does this server listen to?listen 80;# Default charset to use.charset utf-8;# What domain will I route to this application?server_name fdp.io;# The root of the application, for Rails apps it's always the public folder.root /var/www/fdpio/public;# Let Passenger handle the Rails application.passenger_enabled on;# Set the RAILS_ENV variable to 'production'.rails_env production;As long as these domains are in your possession and you have set up the DNS to point to your VPS, you can now visit the domains and see your application.The only caveat left is whenever you update your application, you will need to let Passenger know to reload the application. You can alleviate this in one of two ways: Create/Modify the tmp/restart.txt in your Rails application folder. Restart Nginx manuallyWe are officially done with the manual work! We will be going over ways to automate this setup using tools like Capistrano and Chef in DevOps Part 3.", "content_html": "

In DevOps for Ruby Part 1, we set up a new VPS with all of the software needed to serve Ruby on Rails applications. In this post we will be setting up two Rails application with separate PostgreSQL databases.

Objectives

The goal of this tutorial is to:

Initial setup

First, we must create a /var/www folder. We can do that by ssh-ing into our VPS. We can create the folder using:

1
sudo mkdir -p /var/www

mkdir makes the directory and the -p flag adds intermediate folders if they donā€™t exist.

GitHub setup

In order to avoid typing passwords all the time, we must generate SSH keys and tie them to our Github account. You can follow this guide to get started.

Next, lets fetch our Rails application using the git clone command:

123
cd /var/wwwgit clone git@github.com:NanoXD/fdpio.gitgit clone git@github.com:NanoXD/testapp.git

Verify that you have two folders within /var/www/ for your two Rails apps.

Database Setup

We are going to create a separate user for each application as it is good security practice to limit how much access a user has. Letā€™s start off by logging into the PostgreSQL shell

1
sudo -u postgres psql

We can create users and databases for each of our applications using this syntax

12
postgres=# create user REPLACE_WITH_USER with password 'ENTER_PASSWORD';postgres=# create database DATABASE_NAME owner USERNAME_FOR_DATABASE;

Configuring your database.yml

Each Rails application comes with a database.yml file like I described in Setup PostgreSQL for Rails on a Mac. Letā€™s modify this file to connect to our new PostgreSQL database.

12345678910
# /var/www/fdpio/config/database.ymlproduction:  adapter: postgresql  encoding: unicode  host: localhost  database: fdpio_db  pool: 5  username: fdpio  password: password-man

Do the same for your secondary Rails app.

Configure Nginx

We now have to setup Nginx to allow us to serve our two applications using one server. If you followed Part 1 of this tutorial, you can find your nginx.conf in the /opt/. Letā€™s open it up using vim:

1
sudo vim /opt/nginx/conf/nginx.conf

Keep in mind that this configuration is suited to my two applications. Ensure that you change the paths and names to fit your application. I will describe the changes below.

1234567891011121314151617181920212223242526272829303132333435363738
worker_processes  1;events {    worker_connections  1024;    multi_accept on;}http {    passenger_root /home/nano/.rvm/gems/ruby-2.0.0-p195/gems/passenger-4.0.5;    passenger_ruby /home/nano/.rvm/wrappers/ruby-2.0.0-p195/ruby;    include       mime.types;    default_type  application/octet-stream;    sendfile        on;    keepalive_timeout  65;    gzip  on;    gzip_vary on;    server {        listen       80;        server_name  fdp.io;        charset utf-8;        root /var/www/fdpio/public;        passenger_enabled on;        rails_env production;    }    server {        listen       80;        server_name  testapp.com;        charset utf-8;        root /var/www/testapp/public;        passenger_enabled on;        rails_env production;    }}

The two Passenger settings that have to suit your machine are:passenger_root - Specifies where Passenger ispassenger_ruby - Tells Passenger what version of Ruby to use.

We created two server settings for each application. Here is what each setting means:

1234567891011121314151617
# What port does this server listen to?listen       80;# Default charset to use.charset utf-8;# What domain will I route to this application?server_name  fdp.io;# The root of the application, for Rails apps it's always the public folder.root /var/www/fdpio/public;# Let Passenger handle the Rails application.passenger_enabled on;# Set the RAILS_ENV variable to 'production'.rails_env production;

As long as these domains are in your possession and you have set up the DNS to point to your VPS, you can now visit the domains and see your application.

The only caveat left is whenever you update your application, you will need to let Passenger know to reload the application. You can alleviate this in one of two ways:

  1. Create/Modify the tmp/restart.txt in your Rails application folder.
  2. Restart Nginx manually

We are officially done with the manual work! We will be going over ways to automate this setup using tools like Capistrano and Chef in DevOps Part 3.

", "url": "https://fdp.io/blog/2013/07/06/devops-for-ruby-part-2-using-nginx-to-host-rails-applications/", "date_published": "2013-07-06T19:26:00+00:00", "date_modified": "2013-07-06T19:26:00+00:00", "author": { "name": "Fernando Paredes", "url": "https://fdp.io", "avatar": null } }, { "id": "https://fdp.io/blog/2013/06/23/devops-for-ruby-part-1-vps-and-ubuntu-installation/", "title": "DevOps for Ruby Part 1: VPS & Ubuntu Installation", "summary": "Tutorial on how to move your Rails project to a VPS", "content_text": "When you first start learning Ruby or Rails you are expected to learn many different languages at a time. When it comes to deploying that application you hit a crossroad. Will you use a site like Heroku or try your luck at a VPS? For small applications, a cost-comparison will yield Heroku the prime candidate but once you need more dynos it quickly becomes cost-prohibitive. VPSs are an unyielding dragon that quickly breathe fire at a newcomer with itā€™s extensibility. This guide aims to help in lowering the burden and making VPS more accessible to everyone.Notice: This guide is only meant as a starting point.Picking a Virtual Private Server (VPS)There are a plethora of VPS offerings out there. Some are inexpensive, use a different OS, include more ram, offer more bandwidth. Look around and find one that fits you and your application needs best.The three VPS providers I hold dear to my heart are DigitalOcean, Linode, and MediaTemple DV. DigitalOcean provides SSD cloud servers that can be built in minutes, Linode has quick and painless customization, and MediaTemple while being the most expensive, offers RAID-10. All three providers have excellent customer support and extensive documentation for any error you may encounter.Try to pick a server with at least: 1GB RAM 500GB Bandwidth 20GB StorageUbuntu Setup and ConfigurationI will cover Ubuntu 13.04 x64 as it is the latest version available on DigitalOcean and Linode.After selecting the operating system, you will be given a root password and IP to connect to your server. We will be using a terminal (e.g. Terminal or iTerm2) to connect using the following command:123ssh 10.0.0.1 root@10.0.0.1 password: TYPE_PASSWORD_HERE Welcome to Ubuntu 13.04 (GNU/Linux 3.8.0-19-generic x86_64)UpdatesThe first thing we want to do is make sure our package manager has the latest updates and perform any upgrades.123# The first command pulls package lists while the second command runs the install scriptsudo apt-get updatesudo apt-get upgradeSecurityNext we want to disable root access to the server. We do this by creating a new user using the adduser command. It will ask for a password (ideally different than the root password) and optional personal information.1234567891011$ adduser sampleAdding user `sample' ...Adding new group `sample' (1001) ...Adding new user `sample' (1001) with group `sample' ...Creating home directory `/home/sample' ...Copying files from `/etc/skel' ...Enter new UNIX password:Retype new UNIX password:passwd: password updated successfullyChanging the user information for sampleIs the information correct? [Y/n] YThen we want to allow our new user to have root capabilities by adding it to the sudo list:1$ sudo visudovisudo will bring up a document with lots of information. The only part you need to focus on is User privilege specification. We will add our new user to this section like so:123# User privilege specificationroot ALL=(ALL:ALL) ALLsample ALL=(ALL:ALL) ALLPress Ctrl + X and enter Y to save the document. Next we will disable SSH login to the root login. Note: We will be logging into our newly created account soon!Letā€™s open up the SSH configuration file and find PermitRootLogin under the Authentication tab and change it to look like below:123456789$ vim /etc/ssh/sshd_config# You can also use nano if you don't like vim$ nano /etc/ssh/sshd_config# Authentication:LoginGraceTime 120PermitRootLogin noStrictModes yesFor the changes to take effect you need to run1$ /etc/init.d/ssh restartFinally, letā€™s logout from the root account using the logout command.Now that we are logged out, lets use our new user sample using the following:1$ ssh sample@ip-addressSoftware InstallationLetā€™s install some basic software before we install rvm. We need curl to download the script for rvm and git for version control.1$ sudo apt-get install curl git-coreRVM InstallationNext, letā€™s install RVM. RVM stands for Ruby Version Manager and as itā€™s name implies, it manages different versions of Ruby. There are a few alternatives such as rbenv and chruby. You are welcome to pick whichever one you like but the following instructions are for RVM.First, letā€™s grab RVM:1curl -L get.rvm.io | bash -s stableAfter it is done installing, we will need to load it into our environment.1234$ source ~/.rvm/scripts/rvm# We can verify the install using:$ rvm -v#=> rvm 1.21.2 (stable) by Wayne E. Seguin <wayneeseguin@gmail.com>, Michal Papis <mpapis@gmail.com> [https://rvm.io/]In order to work, RVM has some of its own dependencies that need to be installed. You can install and see what they are using:12$ rvm requirements#=> Installing required packages: gawk, g++, gcc, make, libc6-dev, libreadline6-dev, zlib1g-dev, libssl-dev, libyaml-dev, libsqlite3-dev, sqlite3, libxml2-dev, libxslt1-dev, autoconf, libc6-dev, libgdbm-dev, libncurses5-dev, automake, libtool, bison, pkg-config, libffi-devRuby InstallationNow that RVM is installed we can install the latest patch level of Ruby 2 using:123$ rvm install 2.0.0# To install 1.9.3$ rvm install 1.9.3Once the version of Ruby you selected is finished installing, we can make it the default Ruby version.1$ rvm --default use 2.0.0Rails InstallationFeel free to skip this section if you are using Sinatra.Installing Rails is simple, the only hard part is deciding what version you want to use.Rails 3.2Great! You picked the battle-tested Rails 3.2, which means less typing!1$ gem install rails --version 3.2.13To verify the version installed run rails -vRails 4Update: Rails 4 was released today on June 25th, 2013So you want to see what the hype is about? What are Turbolinks and Strong Parameters? Maybe you like to play with the Russian Doll Caching. Whatever it is, we can install it using:1$ gem install railsLetā€™s run rails -v to verify our version number matches 4.0.0 RC2.PostgreSQLPostgreSQL is a highly regarded database in the Rails community as an object-relational database management system. To install it on Ubuntu run the following:1$ sudo apt-get install postgresqlFor security purposes, we will change the postgres user password using:1$ sudo -u postgres psql postgresThe command will drop us into a psql session. Enter the following to change the default password:123\\password postgres# Enter new password:# Enter it again:Once you are finished, press Ctrl + D to terminate the psql session. Next we need to allow communication to the Postgres server from our Rails app. Letā€™s open up the default configuration:1$ sudo nano /etc/postgresql/9.1/main/postgresql.confWe are looking for a block that is commented out that allows localhost connections. Uncomment the line like so:12- #listen_addresses = 'localhost'+ listen_addresses = 'localhost'All we need now is a Postgres server reboot:1$ sudo service postgresql restartPassenger + Nginx InstallationMy favorite app-server + web-server are Passenger and Nginx due to the ease of installation. First we will install the Passenger gem and setup the Nginx module.12gem install passengerrvmsudo passenger-install-nginx-moduleYou will be presented with three choices. Choose Option 1 to automate the installation of Nginx. You might have some missing package dependencies, follow the installation instructions for them and rerun rvmsudo passenger-install-nginx-moduleNginx is now installed and configured on your machine but it is recommended to install the following script to start, stop, or restart nginx.1234wget -O init-deb.sh http://library.linode.com/assets/660-init-deb.shmv init-deb.sh /etc/init.d/nginxchmod +x /etc/init.d/nginx/usr/sbin/update-rc.d -f nginx defaultsThe following commands will allow you to easily start or stop Nginx:12sudo /etc/init.d/nginx stopsudo /etc/init.d/nginx startCongratulations, you are done with the first part of your VPS deployment. If you visit the ip address of your VPS, you will be greeted with the standard ā€œWelcome to nginx!ā€ page.Check back for Part two next week!References Ubuntu PostgreSQL Nginx Init Script", "content_html": "

When you first start learning Ruby or Rails you are expected to learn many different languages at a time. When it comes to deploying that application you hit a crossroad. Will you use a site like Heroku or try your luck at a VPS? For small applications, a cost-comparison will yield Heroku the prime candidate but once you need more dynos it quickly becomes cost-prohibitive. VPSs are an unyielding dragon that quickly breathe fire at a newcomer with itā€™s extensibility. This guide aims to help in lowering the burden and making VPS more accessible to everyone.

Notice: This guide is only meant as a starting point.

Picking a Virtual Private Server (VPS)

There are a plethora of VPS offerings out there. Some are inexpensive, use a different OS, include more ram, offer more bandwidth. Look around and find one that fits you and your application needs best.

The three VPS providers I hold dear to my heart are DigitalOcean, Linode, and MediaTemple DV. DigitalOcean provides SSD cloud servers that can be built in minutes, Linode has quick and painless customization, and MediaTemple while being the most expensive, offers RAID-10. All three providers have excellent customer support and extensive documentation for any error you may encounter.

Try to pick a server with at least:

Ubuntu Setup and Configuration

I will cover Ubuntu 13.04 x64 as it is the latest version available on DigitalOcean and Linode.

After selecting the operating system, you will be given a root password and IP to connect to your server. We will be using a terminal (e.g. Terminal or iTerm2) to connect using the following command:

123
ssh 10.0.0.1  root@10.0.0.1 password: TYPE_PASSWORD_HERE  Welcome to Ubuntu 13.04 (GNU/Linux 3.8.0-19-generic x86_64)

Updates

The first thing we want to do is make sure our package manager has the latest updates and perform any upgrades.

123
# The first command pulls package lists while the second command runs the install scriptsudo apt-get updatesudo apt-get upgrade

Security

Next we want to disable root access to the server. We do this by creating a new user using the adduser command. It will ask for a password (ideally different than the root password) and optional personal information.

1234567891011
$ adduser sampleAdding user `sample' ...Adding new group `sample' (1001) ...Adding new user `sample' (1001) with group `sample' ...Creating home directory `/home/sample' ...Copying files from `/etc/skel' ...Enter new UNIX password:Retype new UNIX password:passwd: password updated successfullyChanging the user information for sampleIs the information correct? [Y/n] Y

Then we want to allow our new user to have root capabilities by adding it to the sudo list:

1
$ sudo visudo

visudo will bring up a document with lots of information. The only part you need to focus on is User privilege specification. We will add our new user to this section like so:

123
# User privilege specificationroot    ALL=(ALL:ALL) ALLsample  ALL=(ALL:ALL) ALL

Press Ctrl + X and enter Y to save the document. Next we will disable SSH login to the root login. Note: We will be logging into our newly created account soon!

Letā€™s open up the SSH configuration file and find PermitRootLogin under the Authentication tab and change it to look like below:

123456789
$ vim /etc/ssh/sshd_config# You can also use nano if you don't like vim$ nano /etc/ssh/sshd_config# Authentication:LoginGraceTime 120PermitRootLogin noStrictModes yes

For the changes to take effect you need to run

1
$ /etc/init.d/ssh restart

Finally, letā€™s logout from the root account using the logout command.

Now that we are logged out, lets use our new user sample using the following:

1
$ ssh sample@ip-address

Software Installation

Letā€™s install some basic software before we install rvm. We need curl to download the script for rvm and git for version control.

1
$ sudo apt-get install curl git-core

RVM Installation

Next, letā€™s install RVM. RVM stands for Ruby Version Manager and as itā€™s name implies, it manages different versions of Ruby. There are a few alternatives such as rbenv and chruby. You are welcome to pick whichever one you like but the following instructions are for RVM.

First, letā€™s grab RVM:

1
curl -L get.rvm.io | bash -s stable

After it is done installing, we will need to load it into our environment.

1234
$ source ~/.rvm/scripts/rvm# We can verify the install using:$ rvm -v#=> rvm 1.21.2 (stable) by Wayne E. Seguin <wayneeseguin@gmail.com>, Michal Papis <mpapis@gmail.com> [https://rvm.io/]

In order to work, RVM has some of its own dependencies that need to be installed. You can install and see what they are using:

12
$ rvm requirements#=> Installing required packages: gawk, g++, gcc, make, libc6-dev, libreadline6-dev, zlib1g-dev, libssl-dev, libyaml-dev, libsqlite3-dev, sqlite3, libxml2-dev, libxslt1-dev, autoconf, libc6-dev, libgdbm-dev, libncurses5-dev, automake, libtool, bison, pkg-config, libffi-dev

Ruby Installation

Now that RVM is installed we can install the latest patch level of Ruby 2 using:

123
$ rvm install 2.0.0# To install 1.9.3$ rvm install 1.9.3

Once the version of Ruby you selected is finished installing, we can make it the default Ruby version.

1
$ rvm --default use 2.0.0

Rails Installation

Feel free to skip this section if you are using Sinatra.

Installing Rails is simple, the only hard part is deciding what version you want to use.

Rails 3.2

Great! You picked the battle-tested Rails 3.2, which means less typing!

1
$ gem install rails --version 3.2.13

To verify the version installed run rails -v

Rails 4

Update: Rails 4 was released today on June 25th, 2013So you want to see what the hype is about? What are Turbolinks and Strong Parameters? Maybe you like to play with the Russian Doll Caching. Whatever it is, we can install it using:

1
$ gem install rails

Letā€™s run rails -v to verify our version number matches 4.0.0 RC2.

PostgreSQL

PostgreSQL is a highly regarded database in the Rails community as an object-relational database management system. To install it on Ubuntu run the following:

1
$ sudo apt-get install postgresql

For security purposes, we will change the postgres user password using:

1
$ sudo -u postgres psql postgres

The command will drop us into a psql session. Enter the following to change the default password:

123
\\password postgres# Enter new password:# Enter it again:

Once you are finished, press Ctrl + D to terminate the psql session. Next we need to allow communication to the Postgres server from our Rails app. Letā€™s open up the default configuration:

1
$ sudo nano /etc/postgresql/9.1/main/postgresql.conf

We are looking for a block that is commented out that allows localhost connections. Uncomment the line like so:

12
- #listen_addresses = 'localhost'+ listen_addresses = 'localhost'

All we need now is a Postgres server reboot:

1
$ sudo service postgresql restart

Passenger + Nginx Installation

My favorite app-server + web-server are Passenger and Nginx due to the ease of installation. First we will install the Passenger gem and setup the Nginx module.

12
gem install passengerrvmsudo passenger-install-nginx-module

You will be presented with three choices. Choose Option 1 to automate the installation of Nginx. You might have some missing package dependencies, follow the installation instructions for them and rerun rvmsudo passenger-install-nginx-module

Nginx is now installed and configured on your machine but it is recommended to install the following script to start, stop, or restart nginx.

1234
wget -O init-deb.sh http://library.linode.com/assets/660-init-deb.shmv init-deb.sh /etc/init.d/nginxchmod +x /etc/init.d/nginx/usr/sbin/update-rc.d -f nginx defaults

The following commands will allow you to easily start or stop Nginx:

12
sudo /etc/init.d/nginx stopsudo /etc/init.d/nginx start

Congratulations, you are done with the first part of your VPS deployment. If you visit the ip address of your VPS, you will be greeted with the standard ā€œWelcome to nginx!ā€ page.

Check back for Part two next week!

References

", "url": "https://fdp.io/blog/2013/06/23/devops-for-ruby-part-1-vps-and-ubuntu-installation/", "date_published": "2013-06-23T21:53:00+00:00", "date_modified": "2013-06-23T21:53:00+00:00", "author": { "name": "Fernando Paredes", "url": "https://fdp.io", "avatar": null } }, { "id": "https://fdp.io/blog/2013/05/23/setup-postgresql-for-rails-on-a-mac/", "title": "Setup PostgreSQL for Rails on a Mac", "summary": "Learn how to setup PostgreSQL on a Mac via CLI or an application", "content_text": "Pick your elephant poisonThere are a myriad of ways to install PostgreSQL on a Mac. I will be going over brew and Postgres.app. I like to go with the app as it eases into my workflow but does require itā€™s path to be set in your profile (e.g .zshrc, .bashrc, or fish.config)Homebrew InstallationThe homebrew installation is straightforward and can be done by following the guide below. Run a brew command to install PG and take note of the documentation that follows it.1brew install postgresql If this is your first install run the following command to create your first database1initdb /usr/local/var/postgres -E utf8 Create aliases to simplify the start and stop of the Postgres service12alias pg-start='pg_ctl -D /usr/local/var/postgres -l /usr/local/var/postgres/server.log start'alias pg-stop='pg_ctl -D /usr/local/var/postgres stop -s -m fast'Postgres.app Installation Visit http://postgresapp.com Download the latest version which as of this writing is 9.2.4.1 Drag, drop, and open the elephant icon into the Application folder. Set the following in your .bashrc or.zshrc 123 PATH=\"/Applications/Postgres.app/Contents/MacOS/bin:$PATH\" # If you are using Fish, use the following: set PATH /Applications/Postgres.app/Contents/MacOS/bin $PATH Open the elephant and verify it is referencing the app using which psqlThe path should look like what was entered above but if all else fails restart your terminal session.Use Postgres in RailsNow that youā€™ve successfully installed PG, you can utilize it in rails by including it in your gemfile.New Rails AppIf you have yet to generate your rails app, you can set Postgresql as your database by running rails new blog -d postgresqlYou will then have to run rake db:create:all to create the databases in the database.yml file.Existing Rails AppFor an existing rails app you will need to add the pg gem to your gemfile like so.12345678910source 'https://rubygems.org'gem 'rails', '4.0.0.rc1'gem \"pg\", \"~> 0.15.1\"gem 'sass-rails', '~> 4.0.0.rc1'gem 'uglifier', '>= 1.3.0'gem 'coffee-rails', '~> 4.0.0'gem 'jquery-rails'gem 'turbolinks'You will also need to change your database.yml file to look something like this:12345678910111213141516development: adapter: postgresql encoding: utf8 database: blog_dev host: localhosttest: adapter: postgresql encoding: utf8 database: blog_test host: localhostproduction: adapter: postgresql encoding: utf8 database: blog_production host: supersecretserverFinally, you can run rake db:create:all followed by rake db:migrate and continue editing your amazing rails app!Resources Railscasts Ep. 342 Postgres.app PG Gem", "content_html": "

Pick your elephant poison

There are a myriad of ways to install PostgreSQL on a Mac. I will be going over brew and Postgres.app. I like to go with the app as it eases into my workflow but does require itā€™s path to be set in your profile (e.g .zshrc, .bashrc, or fish.config)

Homebrew Installation

The homebrew installation is straightforward and can be done by following the guide below.

1
brew install postgresql
1
initdb /usr/local/var/postgres -E utf8
12
alias pg-start='pg_ctl -D /usr/local/var/postgres -l /usr/local/var/postgres/server.log start'alias pg-stop='pg_ctl -D /usr/local/var/postgres stop -s -m fast'

Postgres.app Installation

  1. Visit http://postgresapp.com
  2. Download the latest version which as of this writing is 9.2.4.1
  3. Drag, drop, and open the elephant icon into the Application folder.
  4. Set the following in your .bashrc or.zshrc

    123
     PATH=\"/Applications/Postgres.app/Contents/MacOS/bin:$PATH\" # If you are using Fish, use the following: set PATH /Applications/Postgres.app/Contents/MacOS/bin $PATH
  5. Open the elephant and verify it is referencing the app using which psql

The path should look like what was entered above but if all else fails restart your terminal session.

Use Postgres in Rails

Now that youā€™ve successfully installed PG, you can utilize it in rails by including it in your gemfile.

New Rails App

If you have yet to generate your rails app, you can set Postgresql as your database by running rails new blog -d postgresqlYou will then have to run rake db:create:all to create the databases in the database.yml file.

Existing Rails App

For an existing rails app you will need to add the pg gem to your gemfile like so.

12345678910
source 'https://rubygems.org'gem 'rails', '4.0.0.rc1'gem \"pg\", \"~> 0.15.1\"gem 'sass-rails', '~> 4.0.0.rc1'gem 'uglifier', '>= 1.3.0'gem 'coffee-rails', '~> 4.0.0'gem 'jquery-rails'gem 'turbolinks'

You will also need to change your database.yml file to look something like this:

12345678910111213141516
development:  adapter: postgresql  encoding: utf8  database: blog_dev  host: localhosttest:  adapter: postgresql  encoding: utf8  database: blog_test  host: localhostproduction:  adapter: postgresql  encoding: utf8  database: blog_production  host: supersecretserver

Finally, you can run rake db:create:all followed by rake db:migrate and continue editing your amazing rails app!

Resources

", "url": "https://fdp.io/blog/2013/05/23/setup-postgresql-for-rails-on-a-mac/", "date_published": "2013-05-23T14:57:00+00:00", "date_modified": "2013-05-23T14:57:00+00:00", "author": { "name": "Fernando Paredes", "url": "https://fdp.io", "avatar": null } }, { "id": "https://fdp.io/blog/2013/05/06/venture-into-tmux/", "title": "Venture into tmux", "summary": "A look into how to install and use tmux", "content_text": "Iā€™ve recently gotten into tmux after experimenting with vim as my main editor. I initially had a frustrating experience until I changed the keybindings and used it to have my editor, guard, and rails server running on a single screen.Definitiontmux is defined as a Terminal multiplexer that allows you to switch easily between several programs in one terminal, detach them (they keep running in the background) and reattach them to a different terminal. And do a lot more.InstallationOn a Mac you can use Homebrew to install the latest version using the following command:1brew install tmuxNext we want to verify that it was installed properly. We can start up tmux and name the session using the tmux new -s commandCustomize tmux.confI like to customize my tmux configuration to change some of the default tasks and add Powerline to the display bar.123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778##### Basic Usage ###### First things first: Remap the prefix keyunbind C-b# By default, we'll use Control-A as the prefix key.set -g prefix 'C-a' ; bind 'C-a' send-prefix# Reload tmux config so we can pick up changes to this file without needing to restart tmuxbind r source-file ~/.tmux.conf \\; display \"Reloaded tmux configuration!\"# Index windows from 1, not 0, so they line up a little better# with the order of the number keys on the keyboardset -g base-index 1setw -g pane-base-index 1# Patch for OS X pbpaste and pbcopy under tmux.set-option -g default-command \"reattach-to-user-namespace -l zsh\"##### Scrollback Navigation ###### Use vi-style navigation in Copy mode (which is also scrollback mode)setw -g mode-keys vi# No escape time for vi modeset -sg escape-time 0# Allow proper copy/pasteset-option -g default-command \"reattach-to-user-namespace -l $SHELL -l\"##### Window/Pane Management ###### Split windows more intuitivelybind | split-window -h # horizontal columnsbind - split-window -v # vertical rows# Navigate panes vim-style!bind h select-pane -Lbind j select-pane -Dbind k select-pane -Ubind l select-pane -R# And windows too!bind -r C-l select-window -t :+bind -r C-h select-window -t :-# Quickly jump between two windowsbind i last-window# Resizing panesbind -r H resize-pane -L 5bind -r J resize-pane -D 5bind -r K resize-pane -U 5bind -r L resize-pane -R 5##### Colors || Visual ###### Ensure we're using 256 colorsset -g default-terminal \"screen-256color\"# color scheme (styled as vim-powerline)set -g status onset -g status-utf8 onset -g status-interval 2set -g status-fg colour231set -g status-bg colour234set -g status-left-length 20set -g status-left '#[fg=colour16,bg=colour254,bold] #S #[fg=colour254,bg=colour234,nobold]ī‚°#(/usr/local/share/python/powerline tmux left)'set -g status-right '#(/usr/local/share/python/powerline tmux right)'set -g status-right-length 150set -g window-status-format \"#[fg=colour244,bg=colour234]#I #[fg=colour240]ī‚± #[fg=colour249]#W \"set -g window-status-current-format \"#[fg=colour234,bg=colour31]ī‚°#[fg=colour117,bg=colour31] #I ī‚± #[fg=colour231,bold]#W #[fg=colour31,bg=colour234,nobold]ī‚°\"# Ring the bell if any background window rang a bellset -g bell-action any# Bigger historyset -g history-limit 10000Useful commands Action Commands Zoom into a single pane Prefix + Z Split the window into two vertical panes tmux split-window or Prefix + - Split the window into two horizontal panes tmux split-window -h or Prefix + | Create a new window tmux new-window or Prefix + c Select a window tmux select-window -t :0-9 or Prefix + 0-9 Rename the current window tmux rename-window or Prefix + , Split the window into two vertical panes Prefix + | Split the window into two horizontal panes Prefix + - Resources thoughtbot Hawk Host Blog", "content_html": "

Iā€™ve recently gotten into tmux after experimenting with vim as my main editor. I initially had a frustrating experience until I changed the keybindings and used it to have my editor, guard, and rails server running on a single screen.

Definition

tmux is defined as a Terminal multiplexer that allows you to switch easily between several programs in one terminal, detach them (they keep running in the background) and reattach them to a different terminal. And do a lot more.

Installation

On a Mac you can use Homebrew to install the latest version using the following command:

1
brew install tmux

Next we want to verify that it was installed properly. We can start up tmux and name the session using the tmux new -s command

Customize tmux.conf

I like to customize my tmux configuration to change some of the default tasks and add Powerline to the display bar.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
##### Basic Usage ###### First things first:  Remap the prefix keyunbind C-b# By default, we'll use Control-A as the prefix key.set -g prefix 'C-a' ; bind 'C-a' send-prefix# Reload tmux config so we can pick up changes to this file without needing to restart tmuxbind r source-file ~/.tmux.conf \\; display \"Reloaded tmux configuration!\"# Index windows from 1, not 0, so they line up a little better# with the order of the number keys on the keyboardset -g base-index 1setw -g pane-base-index 1# Patch for OS X pbpaste and pbcopy under tmux.set-option -g default-command \"reattach-to-user-namespace -l zsh\"##### Scrollback Navigation ###### Use vi-style navigation in Copy mode (which is also scrollback mode)setw -g mode-keys vi# No escape time for vi modeset -sg escape-time 0# Allow proper copy/pasteset-option -g default-command \"reattach-to-user-namespace -l $SHELL -l\"##### Window/Pane Management ###### Split windows more intuitivelybind | split-window -h # horizontal columnsbind - split-window -v # vertical rows# Navigate panes vim-style!bind h select-pane -Lbind j select-pane -Dbind k select-pane -Ubind l select-pane -R# And windows too!bind -r C-l select-window -t :+bind -r C-h select-window -t :-# Quickly jump between two windowsbind i last-window# Resizing panesbind -r H resize-pane -L 5bind -r J resize-pane -D 5bind -r K resize-pane -U 5bind -r L resize-pane -R 5##### Colors || Visual ###### Ensure we're using 256 colorsset -g default-terminal \"screen-256color\"# color scheme (styled as vim-powerline)set -g status onset -g status-utf8 onset -g status-interval 2set -g status-fg colour231set -g status-bg colour234set -g status-left-length 20set -g status-left '#[fg=colour16,bg=colour254,bold] #S #[fg=colour254,bg=colour234,nobold]ī‚°#(/usr/local/share/python/powerline tmux left)'set -g status-right '#(/usr/local/share/python/powerline tmux right)'set -g status-right-length 150set -g window-status-format \"#[fg=colour244,bg=colour234]#I #[fg=colour240]ī‚± #[fg=colour249]#W \"set -g window-status-current-format \"#[fg=colour234,bg=colour31]ī‚°#[fg=colour117,bg=colour31] #I ī‚± #[fg=colour231,bold]#W #[fg=colour31,bg=colour234,nobold]ī‚°\"# Ring the bell if any background window rang a bellset -g bell-action any# Bigger historyset -g history-limit 10000

Useful commands

Action Commands
Zoom into a single pane Prefix + Z
Split the window into two vertical panes tmux split-window or Prefix + -
Split the window into two horizontal panes tmux split-window -h or Prefix + |
Create a new window tmux new-window or Prefix + c
Select a window tmux select-window -t :0-9 or Prefix + 0-9
Rename the current window tmux rename-window or Prefix + ,
Split the window into two vertical panes Prefix + |
Split the window into two horizontal panes Prefix + -

Resources

", "url": "https://fdp.io/blog/2013/05/06/venture-into-tmux/", "date_published": "2013-05-06T09:00:00+00:00", "date_modified": "2013-05-06T09:00:00+00:00", "author": { "name": "Fernando Paredes", "url": "https://fdp.io", "avatar": null } }, { "id": "https://fdp.io/blog/2013/04/12/extremely-useful-git-commands/", "title": "Amending git commits", "summary": "Learn how to amend your git commits with a simple fix", "content_text": "During the past few years of using git, Iā€™ve come across several commands which have become pivotal in my workflow. One of them is git commit --amend.Amending a commitHow many times have you inadvertently forgotten to include a file in your last commit? I know my Github can attest to my forgetfulness. We will first start out by creating a file named help.md123# HelpNothing can stop the man with the right mental attitude from achieving his goal; nothing onearth can help the man with the wrong mental attitude.12git add help.mdgit commit -m \"Add help file\"It seems we forgot to include some text in the help file. To fix it we will append the information to the end of the file using the echo command.1echo 'You can also contact me at foo@bar.com' >> hello.mdYou can now add it back to the list of tracked files and amend your commit using the following12git add help.mdgit commit --amend -m \"Add contact information to help file\"Congratulations, you have successfully amended a commit!References Git-SCM", "content_html": "

During the past few years of using git, Iā€™ve come across several commands which have become pivotal in my workflow. One of them is git commit --amend.

Amending a commit

How many times have you inadvertently forgotten to include a file in your last commit? I know my Github can attest to my forgetfulness. We will first start out by creating a file named help.md

123
# HelpNothing can stop the man with the right mental attitude from achieving his goal; nothing onearth can help the man with the wrong mental attitude.
12
git add help.mdgit commit -m \"Add help file\"

It seems we forgot to include some text in the help file. To fix it we will append the information to the end of the file using the echo command.

1
echo 'You can also contact me at foo@bar.com' >> hello.md

You can now add it back to the list of tracked files and amend your commit using the following

12
git add help.mdgit commit --amend -m \"Add contact information to help file\"

Congratulations, you have successfully amended a commit!

References

", "url": "https://fdp.io/blog/2013/04/12/extremely-useful-git-commands/", "date_published": "2013-04-12T08:11:00+00:00", "date_modified": "2013-04-12T08:11:00+00:00", "author": { "name": "Fernando Paredes", "url": "https://fdp.io", "avatar": null } } ] }