Well, we've covered all the basics, but there are a few more odds and ends worth being familiar with, if only in name, that we haven't yet looked at. You'll find them here.

12.1 | curl

We did see curl used already, but only briefly.

Like, very briefly.

"curl is a command line tool for transferring data with URL syntax, supporting DICT, FILE, FTP, FTPS, Gopher, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, POP3, POP3S, RTMP, RTSP, SCP, SFTP, SMTP, SMTPS, Telnet and TFTP. curl supports SSL certificates, HTTP POST, HTTP PUT, FTP uploading, HTTP form based upload, proxies, cookies, user+password authentication (Basic, Digest, NTLM, Negotiate, kerberos...), file transfer resume, proxy tunneling and a busload of other useful tricks." - curl.haxx.se/

So, that's a lot of functionality, but the takeaway for us is that it can do a lot of stuff with HTTP.

Earlier, we performed an HTTP GET and an HTTP POST using curl to programmatically hit against our Rails application and view the returned JSON.

Though useful in that capacity alone, curl can also be used as a general system utility to grab things from the Internet:

$ curl http://www.example.com/some/random/lolcats/picture.jpg > image.jpg

The line above would download the image at that fictional URL and save it to disk as image.jpg in the working directory.

Having only seen curl usage in examples from the Rails community, you might never expect that this is possible with curl, but it just goes to show the incredible flexibility of the tools we have access to. ...for free no less.

Another applications with comparable functionality is wget, but the Rails community seems to greatly favor curl.

12.2 | SSH

SSH stands for "Secure Shell" and is used to securely log into a remote server and run a remote shell for that machine. This is often the method used to manage things server-side, but since we're using Heroku and Heroku makes use of a distributed environment, we can't ssh into our Heroku server.

In order to obtain a remote shell using SSH, we just need the address of a machine running an SSH [daemon](http://en.wikipedia.org/wiki/Daemon_(computing))/server and valid login credentials for that machine.

Assuming I have an SSH daemon (sshd) running on my machine and that my username is brad, I can SSH into my machine like this:

$ ssh brad@localhost

I would then be prompted for my password and, if correct, I would be granted a Bash prompt.

This would be ridiculously unnecessary for running commands locally, but it is incredibly useful for running shell commands on a remote server.

12.3 | pry

Pry is a Ruby Gem and alternative to IRB and offers a lot of awesome features. It's definitely worth a look.

Pry can even be used for rails console in place of IRB.

12.4 | Units of Measurement

After you write code for a while, it becomes apparent that there are methods of numbering and units of measurement that come up often in a programming context. Let's start with numbering.

Base / Radix

We live in a society that most commonly uses a decimal numeral system, a system that has a base (or radix) of ten. In this base-10 system there are exactly ten ordered digits: 0123456789.

You could use any number as a base for a numeral system, but four bases are particularly common in computing:

Numeral System Name Base/Radix Ordered Digits Literal Prefix
Binary 2 01 0b
Octal 8 01234567 0
Decimal 10 0123456789 n/a
Hexidecimal (Hex) 16 0123456789ABCDEF 0x

Note that the lowercase characters abcdef may also be used for hexadecimal values.

When we call to_s on a Fixnum in Ruby, we can pass in a number base as an argument. For instance, if we call 64.to_s(2) we are asking for a string that is the binary representation of the decimal value 64.

Let's take a look in IRB:

> 128.to_s(2)
=> "10000000"

> 128.to_s(8)
=> "200"

> 128.to_s(10)
=> "128"

> 128.to_s(16)
=> "80"

Keep in mind that these returns are just string values, but Ruby also has several notations we can use to create binary, octal, and hexadecimal literals.

To take a look at these, let's use the Ruby Fixnum literal representation for each of values we just saw above:

# binary representation
> 0b10000000
=> 128

# octal representation
> 0200
=> 128

# hexidecimal representation
> 0x80
=> 128

And yes, alphabetical characters are used to represent certain hexadecimal values:

# lowercase
  > 0xff
  => 255

# uppercase
  > 0xFF
  => 255

# case doesn't matter

You can see that each of these values is quite varied from the others, but there is a special relationship between binary and hexadecimal: each hexadecimal digit represents four binary digits.

For example, decimal 10 can be represented as 0b1010 or 0xa, as can decimal 170 by 0b10101010 or 0xaa.

All of this is nothing that we'd normally have to think about during day to day web programming, but it does help us understand a number of different computing concepts.

In fact, it'll help us understand our very next topic.

Bits and Bytes

Internally, computers keep track of everything in binary.

As it turns out, computers run on electricity. And in electronics, logical values are either "high" or "low", "on" or "off". Computers use 1 to represent "on" and 0 to represent "off".

Character Encoding

But from our perspective, they keep track of much more than just numbers, so how do they do it?

Well, as you might expect, everything ends up being eventually mapped to some kind of numeral value. Take strings for example, they end up as something like ASCII- or UTF-encoded values.

Now we've mentioned these character encodings before, but have yet to see exactly what they are. As most of what we've seen in this book, they are topics unto themselves, but now that we have a basic understanding of binary we're now equipped to grasp their fundamentals.

Let's start by screwing around in IRB:

As is tradition.

> 'a'.ord
=> 97

> '1'.ord
=> 49

> '*'.ord
=> 42

> 97.chr
=> "a"

> 49.chr
=> "1"

> 42.chr
=> "*"

ord (from String#ord) stands for "ordinal" by the way.

Is the conversion above immediately useful? No.

Is it interesting? Yes.

Will everyone eventually run into a situation where they'll need to understand this? Absolutely.

The integer values above might look random, but there is some method to the madness. Take a look at the printable ASCII characters to see.

One interesting part of this mapping is that 0 to 9 are represented by 0x30 to 0x39.

Notice that the least significant digits line up.

Also, uppercase alphabetic characters begin at 0x40, while lowercase characters begin at 0x60. This means that we can down case a character by adding 0x20 and upcase by subtracting 0x20.

We can see this in IRB, making 'A' into an 'a' with some simple hex addition:

# the integer ASCII value for 'A' 
> up_ord = 'A'.ord
=> 65

# represented in hex as...
> up_ord.to_s(16)
=> "41"

# integer ASCII value for 'a'
> down_ord = up_ord + 0x20
=> 97

# represented in hex as...
> down_ord.to_s(16)
=> "61"

# convert it to a string
> down_ord.chr
=> "a"

So ASCII is pretty interesting, but it has a limit: it can only accommodate seven bits.

Okay, well how much data can be encoded with seven bits?

Not much. Each binary digit has only two possibilities (1 or 0), but with each additional bit, the possibilities are doubled.

In other words, 7 bits allow for 2 to 7th power permutations.

How many possibilities?

Let's ask IRB:

> 2 ** 7
=> 128

Powers can be calculated in Ruby using **.

128 might not seem like much, and it isn't. Especially when you consider how many different languages and writing systems there are.

To deal with this, UTF-8 (UCS Transformation Format 8-bit) was created. UTF-8 can be used to represent any Unicode character using one to four bytes.

8 bits in a byte, hence the "8" in "UTF-8".

Most of the text on the Web is UTF-8 encoded, so Ruby and Rails play quite nicely with it.

In fact, you'll find a <meta> tag in app/views/layout/application.html.erb that declares the charset as utf-8.

For instance, let's play with a Japanese Hiragana ("a") in IRB:

> "あ"
=> "あ"

> "あ".ord
=> 12354

> "あ".ord.to_s(16)
=> "3042"

> "あ".ord.to_s(2)
=> "11000001000010"

One interesting characteristic of UTF-8 is that its first 128 characters map directly to ASCII. This means that well-formed ASCII is also well-formed UTF-8.

There are certainly more encodings (e.g. Base64), but a cursory understanding of ASCII and UTF-8 is a good start.

More on Bytes

So character encodings are a helpful means of demonstrating bits and bytes, but keep in mind that computers use these concepts everywhere.

As for hardware, physical memory is hexadecimal addressable and written by the byte and processor registers have a specific byte size. When it comes to networking, data is also transfered by the byte.

For larger values, metric prefixes are used to make things more readable. Some common examples are as follows:

Name Abbreviation Number of Bytes Value
Byte b / B 1 1b (byte)
Kilobyte kb / KB 210 1024b (bytes)
Megabyte mb / MB 220 1024kb (kilobytes)
Gigabyte gb / GB 230 1024mb (megabytes)
Terabyte tb / TB 240 1024gb (gigabytes)

As seen in the table on the right of this page, decimal values are sometimes used for these values. This is done a lot by hard drive manufacturers, for example, because they can sell slightly smaller capacity hard drives and claim that they are larger without actually lying.

A base knowledge of these values is incredibly helpful in understanding how much data you're working with.

Time

Time can get rather complicated in computing, but fortunately for us, it's at least measured with a unit we're already familiar with: the second.

As you might have noticed, network requests are measured in Dev Tools and the rails server output using "ms" – milliseconds.

Let's go over some more metric prefixes real quick to help with this topic.

Name Representation
Millisecond 10-3 (.001) seconds
Microsecond 10-6 (.000001) seconds
Nanosecond 10-9 (.000000001) seconds

A millisecond is actually quite a while for a computer. In fact, if you're spending more than 1ms, you're probably performing some inherently time consuming task like a DB/file operation or a network transfer.

On the other side of the spectrum, processor and memory operations can take as little as a few nanoseconds.

Sadly, I can't tell you in absolute terms what is "fast" and what is "slow" though, because it's always relative. So stay aware and try to get an understanding of how long your code is taking and how long it should be taking.