Best / cleanest DSL for manipulating data files?

D

Dreamcat Four

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!
 
B

Brian Candler

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"
:)
 
D

Dreamcat Four

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.

http://github.com/dreamcat4/yamldoc

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 :)

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
 
D

Dreamcat Four

Brian said:
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:

http://github.com/bleything/plist
http://gist.github.com/303378
http://github.com/ckruse/CFPropertyList

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.
 
D

Dreamcat Four

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)
 
D

Dreamcat Four

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.
 
D

Dreamcat Four

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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,067
Latest member
HunterTere

Latest Threads

Top