Best / cleanest DSL for manipulating data files?

Hi,

At the stage of implementing the interface for a new RubyGem. The main
goal is to make something that is intuitive, easy to use. Its a library
for manipulating apple plists. So really we are just manipulating a
file, BUT its of a certain type and predifined structure.

The current feature set includes:

* Read, write, and edit (modify-in-place)
* Can remember operations, but act later (passing in a block), then
calling 'finalize', 'close' or something when the work is to be done.
* Different *types* of plist. Like we can restrict our valid data if our
Plist is for Launchd, or an Appbundle's Info.plist
* Whether binary or xml can be autodetected on read. But want to provide
an option to specify how to write. I don't really know if that should be
a seperate class, method, or just an optional parameter?
* Maybe a couple of global options (or not!) Like whether to override /
enable the subtypes (Info,Launchd) and their validation. Another
possible option is to allow users to override the default backend and
specify their own "backend" to use.
* Have already decided / implemented for users to manipulate their plist
data through the familiar ruby block syntax. eg `plist do
setter_methods.. end`. So im not going to change that part.

This exercise is more about avoiding confusion and providing the neatest
possible interface. In previous work, i have never felt completely
comfortable passing hashes to the initializer for options. Its just
something im not very experienced at doing. But that, or anything else
to make life easier for my users.

Guessing that some of you might yawn and think "ive seen this all before
somewhere else". Well its exactly you guys i need to talk to!

···

--
Posted via http://www.ruby-forum.com/.

You did google for 'ruby plist' already?

I'd suggest you try out the various plist packages already out there,
see what you like and what you dislike from their APIs. Then you can
implement something which you like better.

As a side benefit, you can use this in your announcement. E.g. "the code
to do foo takes 20 lines using that package, but only 5 lines in mine"
:slight_smile:

···

--
Posted via http://www.ruby-forum.com/.

So i've been looking at the current API interface to my classes. To be
honest, Ive been playing with examples all morning, but cannot find a
way to make them user friendly enough. Hopefully at least one person
here would be kind enough to suggest to me a regular object to
initializer eg `.new()` with an options hash?

I wonder a lot how people have done this for other kinds of files. A
while ago I write a gem for manipulating yaml files called `yamldoc`
which tackled the problem of reading / writing yaml settings files.

Yamldoc uses a very different approach. So im not saying that its
necessarily applicable to this problem here. Just maybe another example
to draw from.

How best to solve a problem like this? Can any of you point to an
examples from elsewhere in the Ruby world?

These are my attempts so-far. Can't stress enough how im emphatically
*not at all* happy with them yet. So don't waste your time replying to
say how bad they are, or whats wrong with them. I already know :slight_smile:

But hopefully you can get the idea of what its trying to do.

# 1. First attempt (but none of the options)
launchd_plist filename do
  label "com.github.homebrew.myprogram"
  program_arguments ["/usr/bin/myprogram"]
  run_at_load true
  working_directory "/var/db/myprogram"
  standard_out_path "/var/log/myprogram.log"

  sockets do
    sock_service_name "netbios-ssn"
  end
  sockets do
    sock_service_name "netbios"
    bonjour ['smb']
  end
end
plist.finalize

# Module to avoid namespace conflicts
include ::Plist4r

# 2. This is not much different to 1 except the 'w' argument
# it doesnt really seem to work for me
p = plist4r("/Library/LaunchDaemons/org.cups.cupsd.plist",'w')
p.keys do
  key1 "value1"
  key2 true
end
p.finalize

# 3. If a plist is just a file then maybe ::File syntax?
::Plist4r.open(plist,'w') do
  key1 "value1"
  key2 true
end

::Plist4r.open(plist,'r') do |hash|
  puts hash.inspect
end

# 4. Like 3 but we are writing a binary plist?
::Plist4r.open(plist,'w', :binary =>true) do
  key1 "value1"
  key2 true
end

# 5. Different again
plist = ::Plist4r.open(plist,'w', :delayed_write => true) do
  key1 "value1"
  key2 true
end

plist.format = :binary # force to binary
plist.finalize # actually reads and writes the plist

···

--
Posted via http://www.ruby-forum.com/.

Hi,
For any of you who were interested, here is the interface which was
finally settled upon.

# Example 1
  Plist4r::Config.default_dir "/Library/LaunchDaemons"
  filename = "com.github.myservice"
  p = Plist4r.new(filename)

  p.<< do
    ProgramArguments ["/usr/local/bin/myservice"]
  end

  p.edit do
    WatchPaths ["/var/db/myservice"]
  end

  p.save

# Example 2
  class MyXmlReader < ::Plist4r::ReaderBase
  end
  Plist4r::Config.readers << MyXmlReader

  class MyBinaryWriter < ::Plist4r::WriterBase
  end
  Plist4r::Config.writers << MyBinaryWriter

  Plist4r::Config.default_dir "/Library/LaunchDaemons"

  car = Plist4r.new("car.plist")

  car.load # autodetects plist file_format and plist_type

  car.file_format :binary
  car.plist_type :car

  car.save

  car.<< do
    road_legal true
    brake_light_color "red"
  end

  car.save_as("car2.plist", :binary => true)

  car.<< do
    eyes "blue"
  end
  # => Exception, invalid plist key name "Eyes"

  car.<< do
    tyres "Pirelli"
  end

···

--
Posted via http://www.ruby-forum.com/.

Brian Candler wrote:

You did google for 'ruby plist' already?

Yes. Although none of them provide the same kind of options that I shall
be providing. Actually, this gem aims to do is use those as pluggable
`backends`. Eg for reading/writing a binary plist on OS-X, the gem can
detect and use RubyCocoa. (which may be faster / natively supported
Apply code). However for reading / writing a binary plist on Linux, it
might failover to either Ben's github gist or ckruse/CFPropertyList (a
ruby library).

They are here:



I also have my own xml based parser / writer which uses libxml / haml.
Its not known yet which implementations are the more stable, reliable
and effecient. Hence a pluggable backends strategy kindda seems to make
sense to me. I've already worked with something similar in the GeoKit
gem.

One thing I was hoping to find out by coming here was:

Maybe someone had written a ruby DSL for manipulating other kinds of
files. Like .jpeg images, pdf files, or some other neat interface for
writing their structured data. Then it might help this plist editing
interface better by taking their lessons learned.

···

--
Posted via http://www.ruby-forum.com/.

Standard initializers seem better. Leaning toward b)

  # a)
  plist_opts = {
    :filename => "car.plist",
    :type => :vehicle_plist,
    :save_format => binary,
    :autoload => true,
    :autosave => false
  }

  car = Plist4r.new(plist_opts) {
    road_legal true
    brake_light_color "red"
  }

  car.save

  # b)
  car = Plist4r.new("car.plist")

  car.type = :vehicle_plist
  car.load # also detects plist type

  # not sure what to call this method
  # it overwrites any existing keys,
  # appending the new/replacement keys
  car << do
    road_legal true
    brake_light_color "red"
  end

  car.save(:binary => true)

···

--
Posted via http://www.ruby-forum.com/.

Hmm,
This seems to be invalid:

p << do
  args << "evaluated"
end

But this is okay

p.<< do
  args << "evaluated"
end

Well, for REE 1.8.7 interpreter at least.

···

--
Posted via http://www.ruby-forum.com/.