/******************************************************************************* Copyright (c) 1997-2004, Perforce Software, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTR IBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL PERFORCE SOFTWARE, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *******************************************************************************/ /******************************************************************************* * Name : p4.cc * * Author : Tony Smith <tony@perforce.com> or <tony@smee.org> * * Description : Ruby bindings for the Perforce API. * ******************************************************************************/ #include <ruby.h> #include "undefdups.h" #include <clientapi.h> #include <strtable.h> #include <spec.h> #include "p4rb_version.h" #include "p4result.h" #include "clientuserruby.h" #include "p4clientapi.h" #include "p4mergedata.h" #include "extconf.h" /******************************************************************************* * Utility class for self identification ******************************************************************************/ class Ident : public StrBuf { public: Ident( const char *fmt ) { this->fmt = fmt; } void AddVar( const char *name, const char *value ) { char buf[ 256 ]; sprintf( buf, fmt, name, value ); Append( buf ); } VALUE ToValue() { return rb_str_new2( Text() ); } private: const char *fmt; }; /******************************************************************************* * Our Ruby classes. ******************************************************************************/ VALUE cP4; // Base P4 Class VALUE eP4; // Exception class VALUE cP4MD; // P4::MergeData class extern "C" { // // Construction/destruction // static void p4_free( P4ClientApi *p4 ) { delete p4; } static void p4_mark( P4ClientApi *p4 ) { p4->GCMark(); } static VALUE p4_new( VALUE pClass ) { VALUE argv[ 1 ]; P4ClientApi *p4 = new P4ClientApi(); VALUE self; self = Data_Wrap_Struct( pClass, p4_mark, p4_free, p4 ); rb_obj_call_init( self, 0, argv ); return self; } // // Session connect/disconnect // static VALUE p4_connect( VALUE self ) { P4ClientApi *p4; Data_Get_Struct( self, P4ClientApi, p4 ); return p4->Connect(); } static VALUE p4_disconnect( VALUE self ) { P4ClientApi *p4; Data_Get_Struct( self, P4ClientApi, p4 ); return p4->Disconnect(); } // // Protocol settings // static VALUE p4_set_tagged( int argc, VALUE *argv, VALUE self ) { P4ClientApi *p4; int flag = 1; Data_Get_Struct( self, P4ClientApi, p4 ); if( argc ) flag = NUM2INT( *argv ); p4->Tagged( flag ); return INT2NUM( flag ); } static VALUE p4_set_parse_forms( VALUE self ) { P4ClientApi *p4; Data_Get_Struct( self, P4ClientApi, p4 ); p4->ParseForms(); return Qtrue; } static VALUE p4_set_api_level( VALUE self, VALUE level ) { P4ClientApi *p4; Data_Get_Struct( self, P4ClientApi, p4 ); p4->SetApiLevel( NUM2INT( level ) ); return self; } // // Getting/Setting Perforce environment // static VALUE p4_get_charset( VALUE self ) { P4ClientApi *p4; Data_Get_Struct( self, P4ClientApi, p4 ); StrPtr c = p4->GetCharset(); return rb_str_new2( c.Text() ); } static VALUE p4_set_charset( VALUE self, VALUE c ) { P4ClientApi *p4; Data_Get_Struct( self, P4ClientApi, p4 ); return p4->SetCharset( STR2CSTR( c ) ); } static VALUE p4_get_cwd( VALUE self ) { P4ClientApi *p4; Data_Get_Struct( self, P4ClientApi, p4 ); StrPtr cwd = p4->GetCwd(); return rb_str_new2( cwd.Text() ); } static VALUE p4_set_cwd( VALUE self, VALUE cwd ) { P4ClientApi *p4; Data_Get_Struct( self, P4ClientApi, p4 ); p4->SetCwd( STR2CSTR( cwd ) ); return Qtrue; } static VALUE p4_get_client( VALUE self ) { P4ClientApi *p4; Data_Get_Struct( self, P4ClientApi, p4 ); StrPtr client = p4->GetClient(); return rb_str_new2( client.Text() ); } static VALUE p4_set_client( VALUE self, VALUE client ) { P4ClientApi *p4; Data_Get_Struct( self, P4ClientApi, p4 ); p4->SetClient( STR2CSTR( client ) ); return Qtrue; } static VALUE p4_get_host( VALUE self ) { P4ClientApi *p4; Data_Get_Struct( self, P4ClientApi, p4 ); StrPtr host = p4->GetHost(); return rb_str_new2( host.Text() ); } static VALUE p4_set_host( VALUE self, VALUE host ) { P4ClientApi *p4; Data_Get_Struct( self, P4ClientApi, p4 ); p4->SetHost( STR2CSTR( host ) ); return Qtrue; } static VALUE p4_set_maxresults( VALUE self, VALUE val ) { P4ClientApi *p4; Data_Get_Struct( self, P4ClientApi, p4 ); p4->SetMaxResults( NUM2INT( val ) ); return Qtrue; } static VALUE p4_set_maxscanrows( VALUE self, VALUE val ) { P4ClientApi *p4; Data_Get_Struct( self, P4ClientApi, p4 ); p4->SetMaxScanRows( NUM2INT( val ) ); return Qtrue; } static VALUE p4_get_password( VALUE self ) { P4ClientApi *p4; Data_Get_Struct( self, P4ClientApi, p4 ); StrPtr passwd = p4->GetPassword(); return rb_str_new2( passwd.Text() ); } static VALUE p4_set_password( VALUE self, VALUE passwd ) { P4ClientApi *p4; Data_Get_Struct( self, P4ClientApi, p4 ); p4->SetPassword( STR2CSTR( passwd ) ); return Qtrue; } static VALUE p4_get_port( VALUE self ) { P4ClientApi *p4; Data_Get_Struct( self, P4ClientApi, p4 ); StrPtr port = p4->GetPort(); return rb_str_new2( port.Text() ); } static VALUE p4_set_port( VALUE self, VALUE port ) { P4ClientApi *p4; Data_Get_Struct( self, P4ClientApi, p4 ); p4->SetPort( STR2CSTR( port ) ); return Qtrue; } static VALUE p4_set_prog( VALUE self, VALUE prog ) { P4ClientApi *p4; Data_Get_Struct( self, P4ClientApi, p4 ); p4->SetProg( STR2CSTR( prog ) ); return Qtrue; } static VALUE p4_get_user( VALUE self ) { P4ClientApi *p4; Data_Get_Struct( self, P4ClientApi, p4 ); StrPtr user = p4->GetUser(); return rb_str_new2( user.Text() ); } static VALUE p4_set_user( VALUE self, VALUE user ) { P4ClientApi *p4; Data_Get_Struct( self, P4ClientApi, p4 ); p4->SetUser( STR2CSTR( user ) ); return Qtrue; } /******************************************************************************* * Running commands. General purpose Run method and method for supplying * input to "p4 xxx -i" commands ******************************************************************************/ static VALUE p4_run( VALUE self, VALUE args ) { int i; int argc = 0; ID idFlatten = rb_intern( "flatten" ); ID idLength = rb_intern( "length" ); ID idTo_S = rb_intern( "to_s" ); P4ClientApi *p4; Data_Get_Struct( self, P4ClientApi, p4 ); // Flatten the args array, and extract the Perforce command leaving // the remaining args in the array. VALUE flatArgs = rb_funcall( args, idFlatten, 0 ); if ( ! NUM2INT( rb_funcall( flatArgs, idLength, 0 ) ) ) rb_raise( eP4, "P4#run requires an argument" ); char *cmd = STR2CSTR( rb_funcall( flatArgs, rb_intern( "shift" ), 0 ) ); argc = NUM2INT( rb_funcall( flatArgs, idLength, 0 ) ); // Allocate storage on the stack so it's automatically reclaimed // when we exit. char **p4args = ALLOCA_N( char *, argc + 1 ); // Copy the args across for ( i = 0; i < argc; i++ ) { VALUE entry = rb_ary_entry( flatArgs, i ); p4args[ i ] = STR2CSTR( rb_funcall( entry, idTo_S, 0 ) ); } p4args[ i ] = 0; // Run the command VALUE res = p4->Run( cmd, argc, p4args ); return res; } static VALUE p4_set_input( VALUE self, VALUE input ) { P4ClientApi *p4; Data_Get_Struct( self, P4ClientApi, p4 ); return p4->SetInput( input ); } static VALUE p4_get_output( VALUE self ) { P4ClientApi *p4; Data_Get_Struct( self, P4ClientApi, p4 ); return p4->GetOutput(); } static VALUE p4_get_errors( VALUE self ) { P4ClientApi *p4; Data_Get_Struct( self, P4ClientApi, p4 ); return p4->GetErrors(); } static VALUE p4_get_warnings( VALUE self ) { P4ClientApi *p4; Data_Get_Struct( self, P4ClientApi, p4 ); return p4->GetWarnings(); } static VALUE p4_set_except_level( VALUE self, VALUE level ) { P4ClientApi *p4; Data_Get_Struct( self, P4ClientApi, p4 ); p4->ExceptionLevel( NUM2INT(level) ); return level; } static VALUE p4_get_except_level( VALUE self ) { P4ClientApi *p4; Data_Get_Struct( self, P4ClientApi, p4 ); return INT2NUM( p4->ExceptionLevel() ); } static VALUE p4_parse_spec( VALUE self, VALUE type, VALUE form ) { P4ClientApi *p4; Check_Type( form, T_STRING ); Check_Type( type, T_STRING ); Data_Get_Struct( self, P4ClientApi, p4 ); return p4->ParseSpec( STR2CSTR(type), STR2CSTR(form) ); } static VALUE p4_format_spec( VALUE self, VALUE type, VALUE hash ) { P4ClientApi *p4; Check_Type( type, T_STRING ); Check_Type( hash, T_HASH ); Data_Get_Struct( self, P4ClientApi, p4 ); return p4->FormatSpec( STR2CSTR(type), hash ); } /******************************************************************************* * Self identification ******************************************************************************/ static VALUE p4_identify( VALUE self ) { Ident ident( "%24s : %s\n" ); ident.Append( "\nP4/Ruby Build Summary:\n\n" ); ident.AddVar( "Author","Tony Smith <tony@smee.org> or <tony@perforce.com>"); ident.AddVar( "Version", P4RUBY_VERSION ); ident.AddVar( "Perforce API Version", P4APIVER_STRING ); ident.AddVar( "CFLAGS", P4CFLAGS ); ident.AddVar( "LIBS", P4LIBS ); ident.AddVar( "LIBPATH", P4LIBPATH ); ident.Append( "\n" ); return ident.ToValue(); } /******************************************************************************* * Debugging support ******************************************************************************/ static VALUE p4_debug( VALUE self, VALUE debug ) { P4ClientApi *p4; Data_Get_Struct( self, P4ClientApi, p4 ); p4->SetDebug( NUM2INT(debug) ); return Qtrue; } /******************************************************************************* * P4::MergeData methods. Construction/destruction defined elsewhere ******************************************************************************/ static VALUE p4md_getyourname( VALUE self ) { P4MergeData *md = 0; Data_Get_Struct( self, P4MergeData, md ); return md->GetYourName(); } static VALUE p4md_gettheirname( VALUE self ) { P4MergeData *md = 0; Data_Get_Struct( self, P4MergeData, md ); return md->GetTheirName(); } static VALUE p4md_getbasename( VALUE self ) { P4MergeData *md = 0; Data_Get_Struct( self, P4MergeData, md ); return md->GetBaseName(); } static VALUE p4md_getyourpath( VALUE self ) { P4MergeData *md = 0; Data_Get_Struct( self, P4MergeData, md ); return md->GetYourPath(); } static VALUE p4md_gettheirpath( VALUE self ) { P4MergeData *md = 0; Data_Get_Struct( self, P4MergeData, md ); return md->GetTheirPath(); } static VALUE p4md_getbasepath( VALUE self ) { P4MergeData *md = 0; Data_Get_Struct( self, P4MergeData, md ); return md->GetBasePath(); } static VALUE p4md_getresultpath( VALUE self ) { P4MergeData *md = 0; Data_Get_Struct( self, P4MergeData, md ); return md->GetResultPath(); } static VALUE p4md_getmergehint( VALUE self ) { P4MergeData *md = 0; Data_Get_Struct( self, P4MergeData, md ); return md->GetMergeHint(); } static VALUE p4md_runmerge( VALUE self ) { P4MergeData *md = 0; Data_Get_Struct( self, P4MergeData, md ); return md->RunMergeTool(); } /****************************************************************************** * Extension initialisation ******************************************************************************/ void Init_P4() { // Ruby instantiation eP4 = rb_define_class( "P4Exception", rb_eRuntimeError ); cP4 = rb_define_class( "P4", rb_cObject ); rb_define_singleton_method( cP4, "new", RUBY_METHOD_FUNC(p4_new), 0 ); // Protocol options rb_define_method( cP4, "tagged", RUBY_METHOD_FUNC(p4_set_tagged), -1 ); rb_define_method( cP4, "parse_forms", RUBY_METHOD_FUNC(p4_set_parse_forms), 0); rb_define_method( cP4, "api=", RUBY_METHOD_FUNC(p4_set_api_level), 1 ); // Perforce client settings. Two sets of interfaces. One with // parameter passing, and one as assignment. e.g. // // p4.port( "localhost:1666" ) // or // p4.port = "localhost:1666" // // the latter is preferred. // rb_define_method( cP4, "charset=", RUBY_METHOD_FUNC(p4_set_charset) , 1 ); rb_define_method( cP4, "cwd=", RUBY_METHOD_FUNC(p4_set_cwd) , 1 ); rb_define_method( cP4, "client=", RUBY_METHOD_FUNC(p4_set_client) , 1 ); rb_define_method( cP4, "host=", RUBY_METHOD_FUNC(p4_set_host) , 1 ); rb_define_method( cP4, "maxresults=", RUBY_METHOD_FUNC(p4_set_maxresults), 1 ); rb_define_method( cP4, "maxscanrows=", RUBY_METHOD_FUNC(p4_set_maxscanrows), 1 ); rb_define_method( cP4, "password=", RUBY_METHOD_FUNC(p4_set_password), 1 ); rb_define_method( cP4, "port=", RUBY_METHOD_FUNC(p4_set_port) , 1 ); rb_define_method( cP4, "prog=", RUBY_METHOD_FUNC(p4_set_prog) , 1 ); rb_define_method( cP4, "user=", RUBY_METHOD_FUNC(p4_set_user) , 1 ); rb_define_method( cP4, "charset?", RUBY_METHOD_FUNC(p4_get_charset) , 0 ); rb_define_method( cP4, "cwd?", RUBY_METHOD_FUNC(p4_get_cwd) , 0 ); rb_define_method( cP4, "client?", RUBY_METHOD_FUNC(p4_get_client) , 0 ); rb_define_method( cP4, "host?", RUBY_METHOD_FUNC(p4_get_host) , 0 ); rb_define_method( cP4, "password?", RUBY_METHOD_FUNC(p4_get_password), 0 ); rb_define_method( cP4, "port?", RUBY_METHOD_FUNC(p4_get_port) , 0 ); rb_define_method( cP4, "user?", RUBY_METHOD_FUNC(p4_get_user) , 0 ); // Session Connect/Disconnect rb_define_method( cP4, "connect", RUBY_METHOD_FUNC(p4_connect) , 0 ); rb_define_method( cP4, "disconnect", RUBY_METHOD_FUNC(p4_disconnect) , 0 ); // Running commands - general purpose commands rb_define_method( cP4, "run", RUBY_METHOD_FUNC(p4_run) ,-2 ); rb_define_method( cP4, "input", RUBY_METHOD_FUNC(p4_set_input) , 1 ); rb_define_method( cP4, "output", RUBY_METHOD_FUNC(p4_get_output) , 0 ); rb_define_method( cP4, "errors", RUBY_METHOD_FUNC(p4_get_errors) , 0 ); rb_define_method( cP4, "warnings", RUBY_METHOD_FUNC(p4_get_warnings), 0 ); rb_define_method( cP4, "exception_level?", RUBY_METHOD_FUNC(p4_get_except_level), 0 ); rb_define_method( cP4, "exception_level=", RUBY_METHOD_FUNC(p4_set_except_level), 1 ); // Spec parsing rb_define_method( cP4, "parse_spec", RUBY_METHOD_FUNC(p4_parse_spec), 2 ); rb_define_method( cP4, "format_spec", RUBY_METHOD_FUNC(p4_format_spec), 2 ); // Identification rb_define_const( cP4, "VERSION", rb_str_new2( P4RUBY_VERSION ) ); rb_define_singleton_method( cP4, "identify", RUBY_METHOD_FUNC(p4_identify), 0 ); // Debugging support rb_define_method( cP4, "debug=", RUBY_METHOD_FUNC(p4_debug), 1 ); // P4::MergeData class cP4MD = rb_define_class_under( cP4, "MergeData", rb_cObject ); rb_define_method( cP4MD, "your_name", RUBY_METHOD_FUNC(p4md_getyourname),0); rb_define_method( cP4MD, "their_name", RUBY_METHOD_FUNC(p4md_gettheirname),0); rb_define_method( cP4MD, "base_name", RUBY_METHOD_FUNC(p4md_getbasename),0); rb_define_method( cP4MD, "your_path", RUBY_METHOD_FUNC(p4md_getyourpath),0); rb_define_method( cP4MD, "their_path", RUBY_METHOD_FUNC(p4md_gettheirpath),0); rb_define_method( cP4MD, "base_path", RUBY_METHOD_FUNC(p4md_getbasepath),0); rb_define_method( cP4MD, "result_path", RUBY_METHOD_FUNC(p4md_getresultpath),0); rb_define_method( cP4MD, "merge_hint", RUBY_METHOD_FUNC(p4md_getmergehint),0); rb_define_method( cP4MD, "run_merge", RUBY_METHOD_FUNC(p4md_runmerge),0); } } // Extern C
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#15 | 5791 | Tony Smith |
Add experimental support for passing a block to P4#run_resolve. The block is passed a P4::MergeData object encapsulating the context of each merge performed. The block should evaluate to a string indicating the desired result of the merge: 'ay', 'at', 'am', 's', etc. The P4::MergeData object contains information about the files involved in the merge and can invoke an external merge tool. This is still experimental at this stage so the interface may change as it evolves. |
||
#14 | 5693 | Tony Smith |
Update p4conf.rb to define const_char on all platforms if building against a 2006.1 or later API. Also squelched some compiler noise. No functional change |
||
#13 | 5311 | Tony Smith |
Add new P4#api= method to allow users to lock scripts to a particular API level. This helps when upgrading to new servers that extend support for tagged output to hitherto unsupported commands (2005.2 did a lot of that). See the C/C++ API Release Notes for the full details, but by way of example, to lock scripts to the 2005.1 interface use: p4.api = 57 |
||
#12 | 4942 | Tony Smith |
Add support for Unicode servers to P4Ruby. This change adds two new interfaces, P4#charset= and P4#charset? to set and get the charset respectively. |
||
#11 | 4940 | Tony Smith |
Add (undoc'd) support for enabling/disabling tagged mode on a per-command basis. Also fixed a minor typo which was rendering an error message less useful than intended. |
||
#10 | 4809 | Tony Smith |
Add P4#maxresults= and P4#maxscanrows= methods to allow you to place explicit limits on the execution of individual commands. These limits remain in force for all subsequent commands until they are removed by setting them to zero. Port of new functionality from P4Perl. |
||
#9 | 4651 | Tony Smith |
Add format_spec() method and format_* shortcuts to make it easy to convert a spec in a hash back to its string form without sending it to the server. |
||
#8 | 4589 | Tony Smith |
Update P4Ruby to support the new SetProg() method in the 2004.2 API. Whilst the new 'P4#prog=' method is always available, it's only functional if P4Ruby is built with a 2004.2 or later API. The build system got a bit of tidying up to support this change and the API version is now detected automatically if possible. I've also removed the --apilibdir and --apiincludedir flags as they complicate matters and I never use them and I don't believe anyone else does either. There are also some minor doc formatting tweaks to go along with the added documentation for prog=. |
||
#7 | 4261 | Tony Smith |
Add support for parsing arbitrary specs from strings in Ruby space. Useful with spec depots. You might obtain the spec by running a "p4 print -q" against a file in a spec depot, but want to parse it into a Ruby hash. i.e. p4 = P4.new p4.parse_forms # Required! p4.connect buf = p4.run_print( "-q", "//specs/client/myclient" ) spec = p4.parse_client( buf ) # Or equivalently spec = p4.parse_spec( "client", buf ) |
||
#6 | 4157 | Tony Smith |
Copyright notice update. No functional change |
||
#5 | 3888 | Tony Smith |
Bug fix. Off-by-one error in memory allocation. Where there were no arguments passed to P4#run(), an access violation could result. Thanks to Jeff Bowles for pointing this one out. |
||
#4 | 2408 | Tony Smith |
Build process tweak. Rename config.h to extconf.h so that it will be removed by "make distclean" as that's what mkmf expects it to be called. |
||
#3 | 2388 | Tony Smith |
Rework p4conf.rb script to improve the build process. There are now two mandatory parms and if you omit them you will be prompted for their values. --apidir <dir> - Path to the Perforce API --apiver <string> - API version string (e.g. 2002.2) I've also renamed the .so file from "P4api.so" to just P4.so and I've improved the self identification code to include the build flags and the API version to help with diagnosis. |
||
#2 | 1934 | Tony Smith |
Bug fix. On some lame Linux distro's which ship with beta gcc's p4.cpp would fail to compile because of a prototype mismatch in the arguments to the rb_define_method() function (and others). This change makes use of Ruby's handy RUBY_METHOD_FUNC(func) macro to make sure the cast is always correct |
||
#1 | 1750 | Tony Smith | Build environment tweaks. | ||
//guest/tony_smith/perforce/API/Ruby/main/p4.cc | |||||
#13 | 1529 | Tony Smith |
Remove useless initialize method from the P4 class. Previously it did nothing, and it's not required, so there's no functional change |
||
#12 | 1426 | Tony Smith |
Cleaned up the debug output a little. Introduced some debug levels so you can decide (roughly) what output you want to see. Level 1 shows command execution, connect and disconnect. Level 2 includes Level 1 and also shows the RPC callbacks as they happen. Level 3 includes 1 and 2 and also shows when Ruby garbage collection takes place. Converted all the simple methods of the form P4#meth( arg ) to aliases for P4#meth=. Added P4#debug= to complete the scheme. The P4#meth( arg ) forms are now deprecated. i.e. you should use: p4.user = "tony" and not: p4.user( "tony" ) It's just more Ruby-like. |
||
#11 | 1423 | Tony Smith |
Refine the argument handling in P4#run. Now take args as a Ruby array rather than a C array which makes dealing with arguments which are themselves arrays easier. P4#run just calls Array#flatten() to sort out any nested arrays and then Array#length gives us the true argument count which is used to construct the C array for passing to the Perforce API. Much simpler. |
||
#10 | 1275 | Tony Smith |
Added synonym methods for: p4.cwd / p4.cwd= p4.client / p4.client= p4.host / p4.host= p4.password / p4.password= p4.port / p4.port= p4.user / p4.user= And for exceptions: p4.exception_level / p4.exception_level= and p4.exception_level? so you can test the current exception level p4.exception_level and p4.exception_level= now return the exception level rather than true. |
||
#9 | 1168 | Tony Smith |
Bug fix. P4#run was not handling arrays passed as arguments - which is what P4#method_missing does. It was just calling to_s on all its args, and Array#to_s joins all the elements of the array so if you passed more than one argument to a run_xxx method, it was joining them together. This change involves flattening an array of Ruby objects (some of which could be arrays themselves) into a single C++ array before executing the command. |
||
#8 | 1166 | Tony Smith |
Followup to previous change. Simplify the interface to getting results/errors and warnings. No need for the P4Result class anymore so that's gone (though it's still there as a C++ class because it's useful) and so is P4#result. Now you get your errors/warnings and results using P4#errors, P4#warnings and P4#output all of which return arrays. |
||
#7 | 1165 | Tony Smith |
Minor reshuffle. Added the ability to disable exceptions completely if you don't like them or to have them raised only for errors (and not for warnings). Removed P4#warnings interface and replaced it with P4#exception_level. Some minor doc tweaks to go with the above change |
||
#6 | 1164 | Tony Smith |
Reworked exception handling (hopefully for the last time) in P4/Ruby. Now exceptions are raised on completion of Perforce commands if any errors or warnings were received as part of executing the command. This change also adds documentation, and indexes the Ruby interface off my main page. Bad form to combine so many changes in one changelist, but it's getting late and I want to get them submitted! |
||
#5 | 1083 | Tony Smith |
Sweeping change to exception handling and garbage collection. Exceptions are no longer raised for errors encoutered during execution of Perforce commands as that was causing processing to abort at the first error when several success messages may have been close behind. Now exceptions are raised for events which are fatal to the execution of commands - such as failure to connect to the Perforce server for example. For other errors, the user must call "p4.errors? " to determine whether or not errors occured and "p4.errors" to get an array of error messages. You can of course then raise exceptions yourself if you want to: begin client = p4.fetch_client if p4.errors? raise P4Exception, "p4 client -o failed" end rescue P4Exception => m puts( m ) p4.errors.each { |e| puts( e ) } end version.h got renamed because it conflicts with ruby's own version.h file. We may need to look in there at some point for ruby's version so I'm getting it out of the way now. Added gc_hack.h to make sure that GC works properly on all platforms now so Ruby shouldn't nuke any objects we're holding now. |
||
#4 | 1081 | Tony Smith |
Debugging and identification support. Adds two new methods: P4#identify() P4#debug( int ) |
||
#3 | 1027 | Tony Smith |
Rework structure slightly. What was P4.so is now called P4api.so and a new P4.rb module is added. P4api.so contains the raw bridge to the Perforce API whilst P4.rb contains extensions to it written purely in Ruby. Both define and extend the P4 class and P4.rb requires P4api.so so user code is unaffected. The intent is to give me a place to write Ruby code rather than having to work solely in C++. The first method added using this new structure is P4#method_missing, so now Perforce commands all appear as methods on the P4 object (prefixed by "run_" ). e.g p4 = P4.new p4.run_info() p4.run_users() p4.run_protect( "-o" ) This change also adds support for shortcut methods for form editing. fetch* and save* methods have been added as shortcuts for "p4 <cmd> -o" and "p4 <cmd> -i" respectively. For example: p4 = P4.new p4.parse_forms() client_spec = p4.fetch_client() client_spec[ "Owner" ] = tony p4.save_client( client_spec ) Note that unlike the run* methods, these accessor methods do not return an array, but return a single result - a string normally, or a hash in parse_forms mode. Ruby's arcane build/install system means that you have to install the P4.rb file yourself. "make install" doesn't take care of it. |
||
#2 | 1019 | Tony Smith |
Some porting work to get it to compile on NT. mkmf stuff is still rubbish on NT (<smug>as I told ruby-talk it would be</smug>) and the generated makefile doesn't work at all. Had to figure it out and build by hand - yuk. |
||
#1 | 1015 | Tony Smith |
First cut of Perforce bindings for the Ruby scripting language. Similar functionality to the Perl API stuff, but "rubyfied". Supports error reporting via exceptions, and presents tagged output and parsed forms as hash structures, with nested arrays where required. Still early days so the docs are thin on the ground. See the example.pl for a brief guide. Built with Ruby 1.6.4 on Linux. May still be some memory management issues as the Ruby Garbage Collection API has changed a little since the docs I've got and I've just dodged garbage collection for now. Not indexing this just yet. |