Core Elixir: File.Stat

Last time, we talked about the ‘File.stat’ function.

This time — not to be too confusing — we’re going to talk about the File.Stat library. Notice the capitalization on “Stat”. I know, I have to double check myself every time I write it, too.

The File.Stat library is not something you’ll likely use directly all that much, if ever, but it’s very much necessary for the File.stat command. There are all of two functions in the module, and they’re both meant to translate the stat information between Erlang and Elixir, who use different types to hold the info.

  • File.Stat.to_record will translate the information from an Elixir struct to an Erlang record.

  • File.Stat.from_record will take an Erlang record and give you back an Elixir struct, which you can then match against and have all sorts of fun with.

Let’s start by getting the file information directly from the Erlang :file module:

iex> {:ok, f} = :file.read_file_info("index2.html")
{:ok,
 {:file_info, 3348, :regular, :read_write, { {2015, 5, 15}, {23, 53, 45} },
  { {2011, 7, 7}, {0, 52, 13} }, { {2011, 7, 7}, {0, 52, 15} }, 33188, 1, 16777218,
  0, 2556026, 501, 20} }

You’ll see all the information in there, but it doesn’t include the names as atoms attached next to them. Who knows which one is which? (Yeah, you can look it up, but laziness is a virtue of programming, remember. Thanks, Larry Wall.)

Instead, convert the Erlang file info (in its record form) into an Elixir File struct with File.Stat.from_record/1

iex> f_elixir = File.Stat.from_record(f)
%File.Stat{access: :read_write, atime: { {2015, 5, 15}, {23, 53, 45} },
 ctime: { {2011, 7, 7}, {0, 52, 15}}, gid: 20, inode: 2556026, links: 1,
 major_device: 16777218, minor_device: 0, mode: 33188,
 mtime: { {2011, 7, 7}, {0, 52, 13} }, size: 3348, type: :regular, uid: 501}

Ah, that’s better. We’re back to the Elixir goodness there. When you use the File.stat function, Elixir handles both those steps for you. You end up with the struct. In fact, the only other thing File.stat does for you is convert your Elixir character data into an Erlang string type.

Going the other way: If you want to use an Erlang command to deal with that data, you can transform it back to Erlang’s format:

iex> f_erlang = File.Stat.to_record(f_elixir)
{:file_info, 3348, :regular, :read_write, { {2015, 5, 15}, {23, 53, 45} },
 { {2011, 7, 7}, {0, 52, 13} }, { {2011, 7, 7}, {0, 52, 15} }, 33188, 1, 16777218,
 0, 2556026, 501, 20}

This source code on this is also a classic case where there are twice as many lines to comment the code as there are lines of code. It’s worth it if you need to remind yourself of the definitions of things like inode and ctime. Those Unix terms aren’t always straight forward.

And judging from this discussion on the Elixir-lang mailing list, there’s room for improvement in the documentation here with the data types and whatnot. Take this as a challenge, if you wish…

Core Elixir will return later this week to delve into the world of odds and evens. It’s never as simple as you may think…

If you have any comments, questions, complaints, criticisms, or corrections, catch me on Twitter, @AugieDB. That handle is the same as my GMail account, if you need to type more characters. I want these articles to be factually correct and will update them as necessary.

(3)

Core Elixir: File.stat

(Note: Do not confuse the function File.stat with the module File.Stat. Note the capitalization on the second word there. We’ll be covering that one in the near future.)

File.stat, at its heart, is a struct that holds the filesystem information of a given file.

If you have any experience with programming on a Linux or Unix box, a lot of this struct will look very familiar to you. Its functional equivalent is the command line stat command:

[AugieDB] ~ $ stat index.html
16777218 26895214 -rw-r--r-- 1 augiedb staff 0 4437 "May 15 23:53:44 2015" 
"Aug  9 22:47:11 2012" "Aug  9 22:47:11 2012" "Aug  9 22:47:11 2012" 4096 
16 0 index.html

Those values include the file’s permissions, size, the file system id, and more. You can, of course, man stat for further information at the command line.

The Source

In the Elixir world, that command is rewritten as File.stat/2. Other languages have similar things. Perl and Ruby, for example, both have their own File::Stat modules.

Erlang has File.read_file_info in its File library. It populates a data type named file_info with all the information. That’s what Elixir calls on for the sake of File.stat. It then converts it from Erlang’s data structure to a native Elixir struct via File.Stat.from_record/1. (Again, we’ll talk more about that next time.)

In the end, your Elixir request gets a chunk of data like this in response:

 %File.Stat{access: :read_write, atime: { {2015, 5, 15}, {23, 53, 44} },
    ctime: { {2012, 8, 9}, {22, 47, 11 }, gid: 20, inode: 26895214, links: 1,
    major_device: 16777218, minor_device: 0, mode: 33188,
    mtime: { {2012, 8, 9}, {22, 47, 11} }, size: 4437, type: :regular, uid: 501} }

Of the three languages I’ve mentioned in this post, I like this one the best. It just gives you all the results along with the descriptive names in one big shot. You can parse/pattern match and transform all you like from there. No need to remember which values come in which order, or what the naming convention for the new object is. It’s all laid out for you right there in the returned value.

The Options

You can pass along an option list to go along with that file path. You don’t get a lot of options, but they might come in handy for your time-keeping needs:

stat_options :: [{:time, :local | :universal | :posix}]

Basically, you can format the time stamp of the file in whichever way you’d like. Let’s run all the options against the same file and see what the differences are.

We’ll start with the default case, which is local. If you want local, you don’t need to even specify it. (Thus, ‘default’.) Leave the options blank and just pass in the filename. In the interests of your curiosity, though, here’s what it looks like when you include the option:

iex> f = "welcome.html"
iex> File.stat(f, [time: :local])
{:ok,
 %File.Stat{access: :read_write, atime: { {2015, 5, 15}, {23, 53, 45} },
  ctime: { {2014, 2, 16}, {21, 51, 46} }, gid: 20, inode: 50093003, links: 1,
  major_device: 16777218, minor_device: 0, mode: 33188,
  mtime: { {2014, 2, 16}, {21, 51, 46} }, size: 2839, type: :regular, uid: 501} }

The date/time stamp is structured as { {YYYY, MM, DD}, {HH, MM, SS} }.

I’m on the east coast of the United States, so that’s a 9:51:46 p.m. time stamp on the ctime and mtime there.

‘Universal` gives back the Greenwich Prime Meridian time:

iex> File.stat(f, [time: :universal])
{:ok,
 %File.Stat{access: :read_write, atime: { {2015, 5, 16}, {3, 53, 45} },
  ctime: { {2014, 2, 17}, {2, 51, 46} }, gid: 20, inode: 50093003, links: 1,
  major_device: 16777218, minor_device: 0, mode: 33188,
  mtime: { {2014, 2, 17}, {2, 51, 46} }, size: 2839, type: :regular, uid: 501} }

Now you see it’s the following day at 2:51:46 a.m.

And posix gives you back that ridiculously long series of numbers that only a computer could love — or someone doing math with seconds:

iex> File.stat(f, [time: :posix])
{:ok,
 %File.Stat{access: :read_write, atime: 1431748425, ctime: 1392605506, gid: 20,
  inode: 50093003, links: 1, major_device: 16777218, minor_device: 0,
  mode: 33188, mtime: 1392605506, size: 2839, type: :regular, uid: 501}}

That time stamp equates to ‘run it through Google and hope for the best.’ (Here’s one.)

Bang!

There’s a variation on File.stat, too, with File.stat! The difference is that exclamation point at the end. It’s an Elixir convention that a module ending in an exclamation point will not return a tuple, but rather the information you requested, or an error.

For example:

iex> File.stat('File does not exist.md')
{:error, :enoent}

iex> File.stat!('File does not exist.md')
** (File.Error) could not read file stats File does not exist.md: no such file or directory
    (elixir) lib/file.ex:282: File.stat!/2

In the source code, File.stat! calls File.stat to get the job done, then pattern matches on the results to return back the appropriate File.error type if necessary, or just pass back the bare info.

The error codes are taken from POSIX error code standards. Here is a small sampling of those:

  • enoentError No Entry (i.e. no file)
  • eacces – You don’t have the right access rights (permissions) to that file
  • eisdir – Not a file. Might be a directory. Or a device.
  • enotdir – Not a directory
  • enospc – No space. The drive/disk partition is full.
  • eperm – Operation Not Permitted (You can’t do that; it doesn’t make sense.)
  • efbig – Your file is too effing big.

(For more details on the difference between “eperm” and “eacces”, check out this article. Thanks for the link, Jan!)

You can find more errors listed in the Erlang File module documentation. See if you can guess what they all mean, then compare them to this ridiculously exhaustive list that will make your eyes bleed.

And may your bathroom never have an epipe error…

Coming next: We’ll talk about the File.Stat module. It’s different from the File.stat function, I swear.

If you have any comments, questions, complaints, criticisms, or corrections, catch me on Twitter, @AugieDB. That handle is the same as my GMail account, if you need to type more characters. I want these articles to be factually correct and will update them as necessary.

(2)

Core Elixir: Introduction and the File Library

Welcome to Core Elixir.

I’ve been busy studying Elixir’s source code lately.

I’ve never looked too deeply into any language’s source code. Perl and Ruby scared me off with their C origins, I guess.

Looking more closely at the Elixir source code, though, I’m Iearning a lot. The code is easy to understand, clearly documented, and not too scary. Granted, I haven’t gone into the parts where stuff is compiled or macros are created or — well, the deeper stuff. I’ll save that for a rainy day. (Monsoon rainy, I should think.)

The outer libraries, though, are fair game.

That’s what “Core Elixir” will be — a series of articles looking at the core libraries of the Elixir language: How they work, what fundamentals of the programming language they expose, where Erlang fits into all of this, how it compares to other languages like Ruby and Perl (because I know something about those two), and more.

And, occasionally, you can watch my frustration or ignorance boil over into something approaching madness and disbelief at the way things work.

The biggest thing I’ve noticed so far is easy to sum up:

Elixir is Elixir

I’m amazed at how much of Elixir is written in Elixir. You can see smaller building blocks being written in Elixir, and then the slightly more complicated ones building themselves on top of those smaller bits. And when Elixir doesn’t have the answer, Erlang does and can be called out to. It’s a remarkably efficient way to create a language that improves on what’s there and doesn’t just copy it as an exercise. If the latter were true, Elixir wouldn’t be nearly as popular as it is today.

I’ve paid the most attention recently to the Elixir File module. As a Perl programmer, being able to manipulate files in various ways is of special interest to me. So much of the work I’ve done in my career has been in automating things that this module does the individual parts of. I can’t help but look here first.

The File Libraries

Elixir’s File library is mostly a wrapper around Erlang’s File module. Or, as we Elixir folks like to call it, :file. The effect is so strong that :file, itself, is aliased at the very top of the Elixir File module to save a lot of typing, like so:

  alias :file, as: F

Seriously, it’s the second line of non-commented code in the module. It’s just that important.

What’s In a Name?

Elixir creator, Jose Valim, has spoken out in that past on the importance of Elixir being about more than just wrapping Erlang functions. So what’s he adding here, specifically? From what I’ve seen, the biggest addition to the language that this module gives us is the ability to think in more Unix-y terms.

Erlang’s file manipulation code is much more florid in its prose. It’s descriptive, but not intuitive to a modern programmer, necessarily. Erlang has function names like get_cwd and set_cwd instead of cwd and cd. Elixir’s stat is Erlang’s read_file_info. It may feel like Erlang’s syntax here is more expressive and plainer in its English, but Elixir utilizes the programmer’s built-in knowledge of the Unix commands. As a programmer, you don’t need to translate Unix to Elixir, or learn a second set of commands to do the same things as the command you already know. The Unix and Elixir commands are identical, for the most part.

Code Pattern

Elixir has private functions, which are defined with a defp. Those, as that description might suggest to you, aren’t available to the programmer using the library.

The File library uses it in an interesting way. The regular function that’s publicly accessible has the simple and memorable name. The work that it does, however, is done in a private function that’s preceded by do_ in its name.

For example, if you want to to create a specific directory, you call on the mkdir_p function with the path name. Elixir will create that directory as well as any of the parent directories along the way. Plain old mkdir will handle creating only a single directory without the option for the parent directories.

Here’s what that function looks like:

def mkdir_p(path) do
  do_mkdir_p(IO.chardata_to_string(path))
end

The only thing it does is call out to a private function of similar name, translating the path name along the way to something Erlang would find more palatable. (We’ll get to the whole string versus list of characters things in a future installment, I’m sure.)

You’ve asked it to run a command, and now Elixir will do_ that command.

From there, Elixir can pattern match to handle the request across multiple versions of the code. In this case, there are two:

  defp do_mkdir_p("/") do
  defp do_mkdir_p(path) do

Spoiler: The first one returns :ok and does nothing, as that directory already exists. The second one actually creates a new directory with the Erlang function :file.make_dir.

Funny enough, in writing this article, I found that this command didn’t use the F alias like every other call to :file did in the program. I submitted a pull request on that over the weekend which got merged in. ( Use file alias ‘F’ for consistency #3412 )

You know how people tend to like short pull requests? That one deleted 5 characters and added 1. This beats my previous shortest pull request by a few characters. Only a few, though…

I <3 open source.

Coming Soon…

File.stat — Or, “inode for Elixir”. Or, File#stat for Elixir. Or, even :file.stat for Elixir.

After that, well, there’s lots of stuff ready to go: Collections and lists, Enumerables, dates and times, odds and evens, why IO.puts is the simplest and most complicated thing ever, where in Elixir There’s More Than One Way To Do It, a diversion into documentation formatting, and so very much more…

If you have any comments, questions, complaints, criticisms, or corrections, catch me on Twitter, @AugieDB. That handle is the same as my GMail account, if you need to type more characters. I want these articles to be factually correct and will update them as necessary.

(1)

Links of Pure Technological Randomness

  • It’s a blind spot that I couldn’t explain what Rack is if you asked me. This explainer looks promising.

  • Time for some futuristic Apple forecasting. It’s a fascinating look at what Apple might be doing with its ARM chips and how it has architected software to be relatively chip agnostic. It’s also another example of how Apple might be making big changes over the long term right under our nose without laying it out in detail. Exciting.

Here’s a quick excerpt:

When you see Bitcode for OS X on the App Store, it means that Bob’s team has created a CPU–almost certainly another ARM-derivative–good enough for the Mac. This week’s Metal on OS X announcement already lays the groundwork for ditching AMD and NVIDIA’s hot and bothersome components in favor of the increasingly first class graphics hardware in the iPad and iPhone.

As an early test to see if he could challenge the (Veterans Administration)’s protocol, he insisted, successfully, that his official government title be Rogue Leader. And so he is: Rogue Leader Weaver.

Awesome. As always, do not read the comments.

  • Naming things is hard. Don’t name objects with -ER words. I’ve heard that advice before, but this is one of the better explanations I’ve read.

  • 64 Bites is a new screencast series teaching you how to program a Commodore 64. Where was this in 1984 when I needed it, and had to rely on Jim Butterfield’s book, instead? ;–)

Sandi Metz: Nothing is Something

I don’t do any object oriented programming. Crazy, right? But I really don’t at this time.

Sure, there are some Perl modules I use that are OO, and I’ve created one or two that are OO, also, but I can’t claim that my daily duties at work consist of me figuring out whether to inject some dependency or compose some new objects.

Yet I’m fascinated by that stuff, probably because I don’t have to actually deal with it on a daily basis. Theory is easy; practice is hard.

Sandi Metz’s latest talk is awesome. If you’ve read POODR (and you should), you’ll love this. If you haven’t read POODR, you should like this, too.

Fair Warning: Pay close attention. Block off some time to pay attention to this. It’s worth it.

Why Apple Keynotes Attendees Are So Easily Excited

You know how Apple keynote attendees will cheer wildly for the simplest, littlest new features? It’s laughable at times when Craig Federighi gets a standing ovation for a new obscure two finger gesture on the trackpad in Mail on OS X. (OK, that’s a bit of an exaggeration, but not by much, really…)

How does that happen? Is the power of the Reality Distortion Field so great that otherwise smart and logical people lose their minds?

Well, yes, there’s some of that. There’s also sleep deprivation. Those people in the first twenty rows were probably camped out on a cold San Francisco sidewalk six hours before the keynote began. They want to be excited by something, so they’re eager to applaud.

It’s a similar thing to the way stand-up comedians often get easier laughs from people who come in expecting to laugh and are eager to laugh, or magicians wow people with tricks because the people want to be astounded, even if they could figure out how it was done with two seconds thought.

But I think there’s another bigger reason to all of this, and it’s something that’s been on a slide in the last couple WWDCs.

80% of attendees are first timers.

For these people, it’s like Christmas morning. They’ve wanted to attend a keynote for years. They won the Apple lottery to land a ticket for themselves. This is the Big Event of the week. Their excitement is doubled, compared to anyone else’s. And there’s more of them than anyone else.

I’m not saying the newbies are ruining WWDC or anything. Heck, I’ve never been to one and I’d likely be just as excitable and want to hoot and holler for anything. Glass houses, etc. etc. I’m just saying it helps to explain why the developers are so likely to cheer for relatively minor things.

And don’t think Apple Marketing doesn’t know this, too. They want that cheering at the Keynote. If the 80% won’t do it, they’ll have Apple employees scattered in the crowd to help kickstart it… ;–)

Apple WWDC Keynote 2015

Some quick thoughts after today’s WWDC Keynote, which I “watched” mostly through Twitter reactions and the first half hour of live streaming…

  • The split screen functionality they showed on Mac OS X will work great on the new larger iPads, won’t it? Can’t wait! I hope they make a larger screen iPad — I just want to see comic books at an even bigger size. The color and reproduction are already better digitally, but if the page size effectively gets larger than print, I’ll be very happy.

  • Swift 2 is going Open Source? Awesome! I’m busy with Elixir again, but I still really want to learn Swift, too. I even want to try my hand at a Mac app someday. Someday

Things we learned only after the event, but which are more exciting to me than all the bullet point feature lists:

  • There’s no separate developer’s program for iOS and Mac OS X. Now, it’s $99 to program for everything. That includes the Watch OS and, one might guess for the future, the Apple TV App Store. As Apple continues to spread the operating systems out and make them more interconnected, this is a smart move.

  • You can now load your apps on your iPhone without a developer’s license. But you could always do that before, with the exception of certain APIs which wouldn’t work — most notably for me, the Media stuff, like playing music from iTunes. Sounds like you can load everything now, which is pretty cool. It basically means you only need to pay the $99 when you’re read to ship an app to the App Store.

  • Apple believes nobody uses a case on their iPhone or iPad. The sight of 10 year old kids walking down the street with their unprotected iPhones made me laugh in this video. No parent is going to let that happen. I think the only case I saw in the whole video was on the iPad being used by the deaf boy near the end, and that’s just to help prop the iPad up. It is, of course, the official Apple iPad case…

I need to watch the rest of the event to see what I missed next…

If Only I Had a Use Case for an Apple Watch…

Time to get the kid dressed for school. What’s the weather? Check out the weather app on my phone to see.

I got up from my desk at noon for the first time that day. Spent three hours sitting down. Felt stiff. Back hurt. Need to remember to get up more often.

Since switching to Direct TV from the cable company, my receivers don’t have clocks anymore. So I find myself pulling my phone out of my front pocket to check the time a lot more. And when I’m playing a board game on the floor with my daughter and my wife texts me, I dig deep to pull the phone out to look at the message. When it comes time to put a “five minute” warning on the end of play time, I grab for the phone again.

I left the phone sitting on my desk when I walked downstairs and back. I lost those steps in my pedometer, not to mention the total count of stairs walked.

When I’m cooking dinner, I pull the phone out to set a timer. When I’m cleaning the dishes, I play a podcast off my phone, sometimes with ear buds in, but other times with the phone on the counter and the speakers up loud. When someone calls for me from the other room when I’m doing dishes, I have to dry off my hands to run over to the phone and hit the pause button.

Then I hopped on my stationary bike and pedaled for 45 minutes. According to my bike, I did about 12 miles. But I stupidly took the phone out of my pocket and so it didn’t count that exercise as “steps” towards my daily goal.

I love my iPhone 6 and I like its bigger size, except for taking it into and out of my pocket. I used to just throw it in there without thinking about it on my 4S. It was small enough to slide easily in. With the 6, I have to be more careful. If there’s anything else already in that pocket, I have to negotiate more carefully.

This is all stuff that happened to me the other day that made me think, “The Apple Watch would have made all of those situations easier, or fixed them, or simplified things….”

This may be a First World Problem, but it is interesting to realize there IS a use case to a device you thought of as unnecessary for a long while.