[ANN] "files" gem - for creating temporary files and dirs

Ever want to create a whole bunch of files at once? Like when you're
writing tests for a tool that processes files? The Files gem lets you
cleanly specify those files and their contents inside your test code,
instead of forcing you to create a fixture directory and check it in
to your repo. It puts them in a temporary directory and cleans up when
your test is done.

The API currently has two modes -- mixin and function -- and I would
absolutely adore feedback on which mode is more useful, or other
advice about naming the methods and especially the instance var in
mixin mode.

Usage (mixin mode):

    require "files"
    include Files

    file "hello.txt" # creates file "hello.txt" containing
"contents of hello.txt"

    dir "web" do # creates directory "web"
      file "snippet.html", # creates file "web/snippet.html"...
        "<h1>Fix this!</h1>" # ...containing "<h1>Fix this!</h1>"
      dir "img" do # creates directory "web/img"
        file File.new("data/hello.png") # containing a copy
of hello.png
        file "hi.png", File.new("data/hello.png") # and a copy of
hello.png named hi.png
      end
    end

Usage (bare function mode)

    require "files"

    temp_dir = Files do # creates a temporary directory inside
Dir.tmpdir
      file "hello.txt" # creates file "hello.txt" containing
"contents of hello.txt"
      dir "web" do # creates directory "web"
        file "snippet.html", # creates file "web/snippet.html"...
          "<h1>Fix this!</h1>" # ...containing "<h1>Fix this!</h1>"
        dir "img" do # creates directory "web/img"
          file File.new("data/hello.png") # containing a
copy of hello.png
          file "hi.png", File.new("data/hello.png") # and a copy of
hello.png named hi.png
        end
      end
    end # "Files" returns a string with the
path to the directory

More docs, examples and tests at https://github.com/alexch/files

···

--
Alex Chaffee - alex@stinky.com
http://alexchaffee.com
http://twitter.com/alexch

Here's another way to do it. (FSDB does a lot of other stuff, too...)

require 'fsdb' # gem install fsdb
require 'tmpdir'

data = FSDB::Database.new "data"

formats = [
   FSDB::TEXT_FORMAT.when(/\.txt$|\.html$/),
   FSDB::BINARY_FORMAT.when(/\.png$/)
]

data.formats = formats

Dir.mktmpdir do |dir|
   #dir = "tmp1" # for testing, use a dir that won't be deleted

   tmp = FSDB::Database.new(dir)
   tmp.formats = formats

   tmp["hello.txt"] = "contents of hello.txt"
   tmp["web/snippet.html"] = "<h1>Fix this!</h1>"

   img = tmp.subdb("img")
   img["hello.png"] = data["hello.png"]
   img["hi.png"] = data["hello.png"]
end

# you can automatically serialize objects in yaml, json, or marshal:
require 'yaml'
require 'json'

Dir.mktmpdir do |dir|
   #dir = "tmp2" # for testing, use a dir that won't be deleted

   tmp = FSDB::Database.new(dir)
   json_format = FSDB::Format.new(
     /\.json$/, /\.js$/,
     :name => "JSON_FORMAT",
     :load => proc {|f| JSON.load(f)},
     :dump => proc {|object, f| f.syswrite(JSON.dump(object))}
   )
   tmp.formats = formats + [FSDB::YAML_FORMAT, json_format]

   tmp["a.yaml"] = {:some => ["complex", Object]}
   tmp["b.json"] = ["foo", 2, 3]
end

···

On 01/24/2012 12:03 PM, Alex Chaffee wrote:

Ever want to create a whole bunch of files at once? Like when you're
writing tests for a tool that processes files? The Files gem lets you
cleanly specify those files and their contents inside your test code,
instead of forcing you to create a fixture directory and check it in
to your repo. It puts them in a temporary directory and cleans up when
your test is done.

The API currently has two modes -- mixin and function -- and I would
absolutely adore feedback on which mode is more useful, or other
advice about naming the methods and especially the instance var in
mixin mode.

Usage (mixin mode):

     require "files"
     include Files

     file "hello.txt" # creates file "hello.txt" containing
"contents of hello.txt"

     dir "web" do # creates directory "web"
       file "snippet.html", # creates file "web/snippet.html"...
         "<h1>Fix this!</h1>" # ...containing"<h1>Fix this!</h1>"
       dir "img" do # creates directory "web/img"
         file File.new("data/hello.png") # containing a copy
of hello.png
         file "hi.png", File.new("data/hello.png") # and a copy of
hello.png named hi.png
       end
     end

Here's another way to do it. (FSDB does a lot of other stuff, too...)

Without looking, I'm going to guess that you created that library,
Joel. That immediately assures me of its quality, but I found some of
the API a little unwieldy.

formats = [
FSDB::TEXT_FORMAT.when(/\.txt$|\.html$/),
FSDB::BINARY_FORMAT.when(/\.png$/)
]

formats = { /\.txt$|\.html$/ => :text, /\.png$/ => :binary }
  # And should the programmer really have to specify these? Can't they
be baked in?

img["hello.png"] = data["hello.png"]

I don't get a sense of what that line is doing.

# you can automatically serialize objects in yaml, json, or marshal:
[...]
json_format = FSDB::Format.new(
/\.json$/, /\.js$/,
:name => "JSON_FORMAT",
:load => proc {|f| JSON.load(f)},
:dump => proc {|object, f| f.syswrite(JSON.dump(object))}
)
tmp.formats = formats + [FSDB::YAML_FORMAT, json_format]

Seems to me that building yaml, json and marshal capabilities into the
library would be justified and worthwhile.

I'm starting work on a project involving a lot of file and directory
manipulation, so I'll check out fsdb enthusiastically.

Gavin

···

On Thu, Jan 26, 2012 at 7:13 AM, Joel VanderWerf <joelvanderwerf@gmail.com> wrote:

Here's another way to do it. (FSDB does a lot of other stuff, too...)

Without looking, I'm going to guess that you created that library,
Joel. That immediately assures me of its quality, but I found some of
the API a little unwieldy.

Yeah, it's kind of ancient (c. 2003) and could use some redesign. I hadn't used it for a few years. and the formats api felt awkward to me, too.

Recently I needed it as a local alternative to Amazon S3, so at least it's 1.9.3 compatible, now.

formats = [
  FSDB::TEXT_FORMAT.when(/\.txt$|\.html$/),
  FSDB::BINARY_FORMAT.when(/\.png$/)
]

formats = { /\.txt$|\.html$/ => :text, /\.png$/ => :binary }
   # And should the programmer really have to specify these? Can't they
be baked in?

The defaults could be better (currently the default is that .txt maps to TEXT_FORMAT and everything else is marshalled), but many formats don't have a unique ruby object representation: yaml files could be strings or objects, png could be binary strings or instances of somebody's PNG class.... Also, file extensions are not always how you want to discriminate; you might not even have file extensions, and use subdir location instead.

But it should definitely be easier to turn on common formats.

  img["hello.png"] = data["hello.png"]

I don't get a sense of what that line is doing.

It's reading a binary string from data/hello.png and writing it back out to "#{dir}/img/hello.png". Which is potentially far less efficient than a file copy...

# you can automatically serialize objects in yaml, json, or marshal:
[...]
  json_format = FSDB::Format.new(
    /\.json$/, /\.js$/,
    :name => "JSON_FORMAT",
    :load => proc {|f| JSON.load(f)},
    :dump => proc {|object, f| f.syswrite(JSON.dump(object))}
  )
  tmp.formats = formats + [FSDB::YAML_FORMAT, json_format]

Seems to me that building yaml, json and marshal capabilities into the
library would be justified and worthwhile.

Sure...

I'm starting work on a project involving a lot of file and directory
manipulation, so I'll check out fsdb enthusiastically.

Great! Hope it's suitable, though it's not really a general purpose file/dir manager...

···

On 01/25/2012 04:50 PM, Gavin Sinclair wrote:

On Thu, Jan 26, 2012 at 7:13 AM, Joel VanderWerf > <joelvanderwerf@gmail.com> wrote: