I was following an old thread regarding Accessing SVN through Ruby,
using SWIG or DL.  I cobbled together the following to see how hard it
would be to do this in DL.  I implemented the svn_client_status2
function as that is one API not implemented in the current SWIG/Ruby
binding to SVN (and it was the one I needed).
I did both a command line parsing version and the Ruby/DL calling
/usr/local/lib/libsvn_client-1.so.
The latter was far easier to deal with even though the DL stuff is not
really well documented and examples are far and few between, I hope
this can be added to the samples, as it involves callbacks, structures
and pointer types.  (I can send the command line parsing version if
anyone is interested).
I suspect this Ruby/DL version will work on win32 given the correct
path to the equivalent .DLL
Anyway there is a huge amount of effort required to get the Ruby/DL to
the same point that the Swig/Ruby bindings are currently at, but this
example may get someone started if they are so inclined.
The one thing I have tried to do with this approach is to make it more
ruby'ish, by hiding the SVN pool and context stuff in the class
SvnClient, and using a Proc Block for the callback.
I'm interested in any feedback on this approach as I suspect I will be
playing with Ruby/DL a lot more. Also any improvements and/or
suggestions on how to handle the large number of enums the c version
of SVN uses.
--- snip Svnrb.rb ----
require 'dl/import'
require 'dl/struct'
module Svnrb
    extend DL::Importable
    # NOTE you may have to change this path depending on where your svn 
is installed
    # On win32 it will need to point to the relevant .DLL
    dlload "/usr/local/lib/libsvn_client-1.so" 
#,"/usr/local/lib/libsvn_subr-1.so"
    typealias("apr_pool_t*", "void*")
    typealias("apr_status_t", "int")
    # define some convenient structures used by SVN
    Svn_opt_revision_t= struct [
        "int kind",
        "int revision"
    ]
    Svn_error_t= struct [
        "int apr_err",
        "char *message",
        "void *child",
        "apr_pool_t *pool",
        "char *file",
        "long line"
    ]
    # used where we pass a pointer to a long which gets modified in the call
    # and where we need to actually read the modified value within Ruby
    LongArg= struct [
        "long val"
    ]
    Svn_status_t = struct [
        "void *entry",
        "int text_status",
        "int prop_status",
        "int locked",
        "int copied",
        "int switched",
        "int repos_text_status",
        "int repos_prop_status",
        "void *repos_lock"
    ]
    # an experimental way to match standard SVN status enum with the value
    # Could also use Constants here
    Svn_wc_status_kind= {
        # does not exist
        "svn_wc_status_none" => 1,
        # is not a versioned thing in this wc
        "svn_wc_status_unversioned" => 2,
        # exists, but uninteresting.
        "svn_wc_status_normal" => 3,
        # is scheduled for addition
        "svn_wc_status_added" => 4,
        # under v.c., but is missing
        "svn_wc_status_missing" => 5,
        # scheduled for deletion
        "svn_wc_status_deleted" => 6,
        # was deleted and then re-added
        "svn_wc_status_replaced" => 7,
        # text or props have been modified
        "svn_wc_status_modified" => 8,
        # local mods received repos mods
        "svn_wc_status_merged" => 9,
        # local mods received conflicting repos mods
        "svn_wc_status_conflicted" => 10,
        #  resource marked as ignored
        "svn_wc_status_ignored" => 11,
        # an unversioned resource is in the way of the versioned resource
        "svn_wc_status_obstructed" => 12,
        # an unversioned path populated by an svn:external property
        "svn_wc_status_external" => 13,
        #  directory doesn't contain a complete entries list
        "svn_wc_status_incomplete" => 14
    }
    # the functions in various svn libraries we call in this example
    extern "int svn_cmdline_init(char *, void*)"
    extern "apr_pool_t *svn_pool_create_ex(apr_pool_t *, void *)"
    extern "void *svn_config_ensure(char *, apr_pool_t *)"
    extern "void *svn_client_create_context(void **, apr_pool_t *)"
    extern "void *svn_stream_for_stdout(void **, apr_pool_t *)"
    extern "void *svn_client_cat(void *, char *, Svn_opt_revision_t *, 
void *, apr_pool_t *)"
    extern "void *svn_client_status2(int *, char *, Svn_opt_revision_t 
*, void *, void *, int, int, int, int, int, void *,  apr_pool_t *)"
    # a wrapper around access to the SVN Client library, to make it more 
"ruby-like" to the user
    class SvnClient
        @ctx= nil    # active context
        @pool= nil   # active pool
        # this initializes the svn library and gets the contect and pool 
for use in other calls
        def initialize(name)
            err= Svnrb::svn_cmdline_init(name, nil)
            raise "svn_cmdline_init failed" if err != 0
            @pool= Svnrb::svn_pool_create_ex(nil, nil)
            raise "svn_pool_create_ex" if @pool == nil
            err= Svnrb::svn_config_ensure("", @pool)
            raise "svn_config_ensure failed" if err != nil;
            # effectively void *, passed in as void **, will get the 
pointer to the context
            # Ruby does not need to every read this value, it just 
passes it through to subsequent calls
            # ditto for the pool
            tctx= DL.malloc(DL.sizeof('P'))
            err = Svnrb::svn_client_create_context(tctx, @pool);
            raise "svn_client_create_context failed" if err != nil
            @ctx= tctx.ptr # this becomes the contect to use for other calls
        end
        # gets the stdout stream for use in cat
        def getStdoutStream
            sto= DL.malloc(DL.sizeof('P')) # long *
            err= Svnrb::svn_stream_for_stdout(sto, @pool)
            sto.ptr
        end
        # implements the client cat call
        # rev is -1 to get HEAD, and a rev number for any other revision
        def cat(stream, path, rev)
            revision= Svn_opt_revision_t.malloc
            if rev >= 0
                revision.kind= 1 # revision #
                revision.revision= rev
            else
                revision.kind= 7 # HEAD
            end
            err= Svnrb::svn_client_cat(stream, path, revision, @ctx, @pool)
            if err != nil
                terr= Svn_error_t.new(err)
                raise "svn_client_cat failed: (#{terr.apr_err}) 
#{terr.message}"
            end
        end
        # call sthe Client Status2 function
        # url is the WC path
        # rev is -1 to get HEAD, and a rev number for any other revision
        # proc is the proc method callback for each resource which will 
get two parameters: path and status
        # status is a structure of type Svn_status_t
        # returns the current Youngest revision in the Repository
        def status(url, rev, &proc)
            resrev= LongArg.malloc
            revision= Svn_opt_revision_t.malloc
            if rev >= 0
                revision.kind= 1 # revision #
                revision.revision= rev
            else
                revision.kind= 7 # HEAD
            end
            # process callback and call the supplied Proc
            mycb= DL.callback('0PSP'){ |baton,path,pstatus|
                if pstatus
                    status= Svn_status_t.new(pstatus)
                    proc.call(path, status)
                end
            }
            err= Svnrb::svn_client_status2(resrev, url, revision, mycb, 
nil, 1, 1, 1, 0, 1, @ctx, @pool)
            resrev.val
        end
    end
end
if $0 == __FILE__
        # test it, NOTE these test are specific to paths in my WC, yours 
may vary
        svn= Svnrb::SvnClient.new("testsvn")
        #outStream= svn.getStdoutStream
        #svn.cat(outStream, "/home/morris/work/perl/vcvs.pl", -1)
        #svn.cat(outStream, "/home/morris/work/perl/.dddd", 7)
        # get the status of the specified WC path, gets the HEAD 
revision, and only prints out the status of versioned resources
        rev= svn.status("/home/morris/work/perl", -1) {
            |path, status|
            print path, "-> ", status.text_status, " - ", 
status.prop_status, "\n" if  status.text_status != 
Svnrb::Svn_wc_status_kind["svn_wc_status_unversioned"]
        }
        print "rev= ", rev, "\n"
end
--- end snip ----
---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@subversion.tigris.org
For additional commands, e-mail: users-help@subversion.tigris.org
Received on Wed Jun  8 04:36:10 2005