{ "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": "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.
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\"]
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 }
š¤.
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.
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.
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
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 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:
12
import { Header } from '../../components/Header'import { FancyContainer } from '../FancyContainer'
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.
To use the workflow, you use the pods
prefix plus your query
pods rx
Pressing Enter
or ā+[1-9]
will open the page for that CocoaPod.
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'
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
.
123
npm install -g alfred-pods# or with yarnyarn global add alfred-pods
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.
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.
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 _ćø__(ā¾ā”ā )>}
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)}
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 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.
text
, due to making copies of the data to make it lower case.Even with these limitations, email is still a good fit for citext
.
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
:
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 provides syntax highlighting and snippets. It forms the basis for every other plugin described.
1
apm install language-elm
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
brings in helpful error messages to Atom. It has options to lint as you type or on save.
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
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
.
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.
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)]
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.
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
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
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
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.
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);}
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.
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.
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.
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
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 }}
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>
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'}
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
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:
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
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
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
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.
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 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
.
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 š.
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.
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
.
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.
", "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": "āRuby is designed to make programmers happy.ā - Yukihiro Matsumoto
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.
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 grep
ing 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.
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 |
Find the Keys header and click on the +
sign to add a new shortcut key.
Enter each of the following shortcuts below:
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.
The goal of this tutorial is to:
database.yml
file.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.
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.
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;
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.
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:
tmp/restart.txt
in your Rails application folder.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.
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:
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)
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
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
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
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
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
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.
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
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 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
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!
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
)
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'
9.2.4.1
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
which psql
The path should look like what was entered above but if all else fails restart your terminal session.
Now that youāve successfully installed PG, you can utilize it in rails by including it in your gemfile.
If you have yet to generate your rails app, you can set Postgresql as your database by running rails new blog -d postgresql
You will then have to run rake db:create:all
to create the databases in the database.yml
file.
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!
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.
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.
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
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
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 + - |
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
.
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!