Helix Core is now P4. Product user interfaces will be updated soon. Learn more >>

P4 API for Ruby Documentation | 2025.2

# Class P4

Main interface to the P4 Server client API. Each `P4` object provides you with a thread-safe API level interface to P4 Server. The basic model is to:

1. Instantiate your `P4` object.

2. Specify your P4 Server client environment.

   - `client`
   - `host`
   - `password`
   - `port`
   - `user`

3. Set any options to control output or error handling:

   - `exception_level`

4. Connect to the Perforce service.

   The P4 Server protocol is not designed to support multiple concurrent queries over the same connection. Multithreaded applications that use the C++ API or derived APIs (including P4 API for Ruby) should ensure that a separate connection is used for each thread, or that only one thread may use a shared connection at a time.

5. Run your P4 Server commands.
6. Disconnect from the Perforce service.

## Class Methods

### P4.identify -> aString

Return the version of P4 API for Ruby that you are using. Also reports the version of the OpenSSL library used for building the underlying P4 API for C/C++ with which P4 API for Ruby was built.

    ruby -rP4 -e 'puts( P4.identify )'

Some of this information is already made available through the predefined constants `P4::VERSION`, `P4::OS`, and `P4::PATCHLEVEL`.

### P4.new -> aP4

Constructs a new P4 object.

    p4 = P4.new()

## Instance Methods

### p4.api_level= anInteger -> anInteger

Sets the API compatibility level desired. This is useful when writing scripts using P4 Server commands that do not yet support tagged output. In these cases, upgrading to a later server that supports tagged output for the commands in question can break your script. Using this method allows you to lock your script to the output format of an older P4 Server release and facilitate seamless upgrades. This method must be called prior to calling `P4#connect()`.

    p4 = P4.new
    p4.api_level = 67 # Lock to 2010.1 format
    p4.connect
    ...

For more information about the API integer levels, see Protocol levels: server/client in the P4 Server Administration Documentation.

### p4.api_level -> anInteger

Returns the current P4 API for C/C++ compatibility level. Each iteration of the P4 Server is given a level number. As part of the initial communication, the client protocol level is passed between client application and the P4 Server. This value, defined in the P4 API for C/C++, determines the communication protocol level that the P4 Server client will understand. All subsequent responses from the P4 Server can be tailored to meet the requirements of that client protocol level.

For more information, see:

http://kb.perforce.com/article/512

### p4.at_exception_level( lev ) { …​ } -> self

Executes the associated block under a specific exception level. Returns to the previous exception level when the block returns.

    p4 = P4.new
    p4.client = "www"
    p4.connect

    p4.at_exception_level( P4::RAISE_ERRORS ) do
        p4.run_sync
    end

    p4.disconnect

### p4.charset= aString -> aString

Sets the character set to use when connect to a Unicode enabled server. Do not use when working with non-Unicode-enabled servers. By default, the character set is the value of the `P4CHARSET` environment variable. If the character set is invalid, this method raises a `P4Exception`.

    p4 = P4.new
    p4.client = "www"
    p4.charset = "iso8859-1"
    p4.connect
    p4.run_sync
    p4.disconnect

### p4.charset -> aString

Get the name of the character set in use when working with Unicode-enabled servers.

    p4 = P4.new
    p4.charset = "utf8"
    puts( p4.charset )

### p4.client= aString -> aString

Set the name of the client workspace you wish to use. If not called, defaults to the value of `P4CLIENT` taken from any `P4CONFIG` file present, or from the environment as per the usual P4 Server convention. Must be called before connecting to the P4 Server.

    p4 = P4.new
    p4.client = "www"
    p4.connect
    p4.run_sync
    p4.disconnect

### p4.client -> aString

Get the name of the P4 Server client currently in use.

    p4 = P4.new
    puts( p4.client )

### p4.connect -> aBool

Connect to the P4 Server. You must connect before you can execute commands. Raises a `P4Exception` if the connection attempt fails.

    p4 = P4.new
    p4.connect

### p4.connected? -> aBool

Test whether or not the session has been connected, and if the connection has not been dropped.

    p4 = P4.newp4.connected?

### p4.cwd= aString -> aString

Sets the current working directly. Can be called prior to executing any P4 Server command. Sometimes necessary if your script executes a `chdir()` as part of its processing.

    p4 = P4.new
    p4.cwd = "/home/bruno"

### p4.cwd -> aString

Get the current working directory.

    p4 = P4.new
    puts( p4.cwd )

### p4.delete_<spectype>( [options], name ) -> anArray

The delete methods are simply shortcut methods that allow you to quickly delete the definitions of clients, labels, branches, etc. These methods are equivalent to:

    p4.run( "<spectype>", '-d', [options], "spec name" )

For example:

    require "P4"
    require "parsedate"
    include ParseDate
    now = Time.now
    p4 = P4.new
    begin
      p4.connect
      p4.run_clients.each do
        |client|
        atime = parsedate( client[ "Access" ] )
        if( (atime + 24 * 3600 * 365 ) < now )
          p4.delete_client( '-f', client[ "client" ] )
        end
      end
    rescue P4Exception
      p4.errors.each { |e| puts( e ) }
    ensure
      p4.disconnect
    end

### p4.disconnect -> true

Disconnect from the P4 Server.

    p4 = P4.new
    p4.connect
    p4.disconnect

### p4.each_<spectype<( arguments ) -> anArray

The `each_<spectype>` methods are shortcut methods that allow you to quickly iterate through clients, labels, branches, etc. Valid <spectype>s are `clients`, `labels`, `branches`, `changes`, `streams`, `jobs`, `users`, `groups`, `depots` and `servers`. Valid arguments are any arguments that would be valid for the corresponding `run_<spectype>` command.

For example, to iterate through clients:

    p4.each_clients do
    |c|
      # work with the retrieved client spec
    end

is equivalent to:

    clients = p4.run_clients
    clients.each do
    |c|
      client = p4.fetch_client( c['client'] )
      # work with the retrieved client spec
    end

### p4.env -> string

Get the value of a P4 Server environment variable, taking into account `P4CONFIG` files and (on Windows and macOS) the registry or user preferences.

    p4 = P4.new
    puts p4.env( "P4PORT" )

### p4.errors -> anArray

Returns the array of errors which occurred during execution of the previous command.

    p4 = P4.new
    begin
      p4.connect
      p4.exception_level( P4::RAISE_ERRORS ) # ignore "File(s) up-to-date"
      files = p4.run_sync
    rescue P4Exception
      p4.errors.each { |e| puts( e ) }
    ensure
      p4.disconnect
    end

### p4.exception_level= anInteger -> anInteger

Configures the events which give rise to exceptions. The following three levels are supported:

- `P4::RAISE_NONE` disables all exception raising and makes the interface completely procedural.
- `P4::RAISE_ERRORS` causes exceptions to be raised only when errors are encountered.

- `P4::RAISE_ALL` causes exceptions to be raised for both errors and warnings. This is the default.

      p4 = P4.new
      p4.exception_level = P4::RAISE_ERRORS
      p4.connect   # P4Exception on failure
      p4.run_sync  # File(s) up-to-date is a warning so no exception is raised
      p4.disconnect

### p4.exception_level -> aNumber

Returns the current exception level.

### p4.fetch_<spectype>( [name] ) -> aP4::Spec

The `fetch_<spectype>` methods are shortcut methods that allow you to quickly fetch the definitions of clients, labels, branches, etc. They’re equivalent to:

    p4.run( "<spectype>", '-o', ... ).shift

For example:

    p4 = P4.new
    begin
      p4.connect
      client       = p4.fetch_client()
      other_client = p4.fetch_client( "other" )
      label        = p4.fetch_label( "somelabel" )
    rescue P4Exception
      p4.errors.each { |e| puts( e ) }
    ensure
      p4.disconnect
    end

### p4.format_spec( "<spectype>", aHash )-> aString

Converts the fields in a hash containing the elements of a P4 Server form (spec) into the string representation familiar to users.

The first argument is the type of spec to format: for example, `client`, `branch`, `label`, and so on. The second argument is the hash to parse.

There are shortcuts available for this method. You can use:

    p4.format_<spectype>( hash )

instead of:

    p4.format_spec( "<spectype>", hash )

where <spectype> is the name of a P4 Server spec, such as `client`, `label`, etc.

### p4.format_<spectype> aHash -> aHash

The `format_<spectype>` methods are shortcut methods that allow you to quickly fetch the definitions of clients, labels, branches, etc. They’re equivalent to:

    p4.format_spec( "<spectype>", aHash )

### p4.graph= -> aBool

Enable or disable support for graph depots. By default, support for depots of type graph is enabled at 2017.1 or higher (`P4#api_level()` >= 82). Raises a `P4Exception` if you attempt to enable graph depots on a pre-2017.1 server. You can enable or disable support for graph depots both before and after connecting to the server.

    p4 = P4.new
    p4.graph = false

### p4.graph? -> aBool

Detects whether or not support for P4 Server graph depots is enabled.

    p4 = P4.new
    puts ( p4.graph? )
    p4.graph = false
    puts ( p4.graph? )

### p4.handler= aHandler -> aHandler

Set the current output handler. This should be a subclass of `P4::OutputHandler`.

### p4.handler -> aHandler

Get the current output handler.

### p4.host= aString -> aString

Set the name of the current host. If not called, defaults to the value of `P4HOST` taken from any `P4CONFIG` file present, or from the environment as per the usual P4 Server convention. Must be called before connecting to the P4 Server.

    p4 = P4.new
    p4.host = "workstation123.perforce.com"
    p4.connect
    ...
    p4.disconnect

### p4.host -> aString

Get the current hostname.

    p4 = P4.new
    puts( p4.host )

### p4.input= ( aString|aHash|anArray ) -> aString|aHash|anArray

Store input for the next command.

Call this method prior to running a command requiring input from the user. When the command requests input, the specified data will be supplied to the command. Typically, commands of the form `p4 cmd -i` are invoked using the `P4#save_<spectype>()` methods, which call `P4#input()` internally; there is no need to call `P4#input()` when using the `P4#save_<spectype>()` shortcuts.

You may pass a string, a hash, or (for commands that take multiple inputs from the user) an array of strings or hashes. If you pass an array, note that the array will be shifted each time P4 Server asks the user for input.

    p4 = P4.new
    p4.connect

    change = p4.run_change( "-o" ).shift
    change[ "Description" ] = "Autosubmitted changelist"

    p4.input = change
    p4.run_submit( "-i" )

    p4.disconnect

### p4.maxlocktime= anInteger -> anInteger

Limit the amount of time (in milliseconds) spent during data scans to prevent the server from locking tables for too long. Commands that take longer than the limit will be aborted. The limit remains in force until you disable it by setting it to zero. See `p4 help maxlocktime` for information on the commands that support this limit.

    p4 = P4.new
    begin
      p4.connect
      p4.maxlocktime = 10000 # 10 seconds
      files = p4.run_sync
    rescue P4Exception => ex
      p4.errors.each { |e| $stderr.puts( e ) }
    ensure
      p4.disconnectend

### p4.maxlocktime -> anInteger

Get the current `maxlocktime` setting.

    p4 = P4.new
    puts( p4.maxlocktime )

### p4.maxresults= anInteger -> anInteger

Limit the number of results P4 Server permits for subsequent commands. Commands that produce more than this number of results will be aborted. The limit remains in force until you disable it by setting it to zero. See `p4 help maxresults` for information on the commands that support this limit.

    p4 = P4.new
    begin
      p4.connect
      p4.maxresults = 100
      files = p4.run_sync
    rescue P4Exception => ex
      p4.errors.each { |e| $stderr.puts( e ) }
    ensure
      p4.disconnect
    end

### p4.maxresults -> anInteger

Get the current `maxresults` setting.

    p4 = P4.new
    puts( p4.maxresults )

### p4.maxscanrows= anInteger -> anInteger

Limit the number of database records P4 Server will scan for subsequent commands. Commands that attempt to scan more than this number of records will be aborted. The limit remains in force until you disable it by setting it to zero. See `p4 help maxscanrows` for information on the commands that support this limit.

    p4 = P4.new
    begin
      p4.connect
      p4.maxscanrows = 100
      files = p4.run_sync
    rescue P4Exception => ex
      p4.errors.each { |e| $stderr.puts( e ) }
    ensure
      p4.disconnectend

### p4.maxscanrows -> anInteger

Get the current `maxscanrows` setting.

    p4 = P4.new
    puts( p4.maxscanrows )

### p4.messages -> aP4::Message

Returns a message from the P4 Server in the form of a `P4::Message` object.

    p4 = P4.new
    p4.exception_level = P4::RAISE_NONE
    p4.run_sync
    p4.run_sync    # this second sync should return "File(s) up-to-date."
    w = p4.messages[0]
    puts ( w.to_s )

### p4.p4config_file -> aString

Get the path to the current `P4CONFIG` file.

    p4 = P4.new
    puts( p4.p4config_file )

### p4.parse_<spectype>( aString ) -> aP4::Spec

This is equivalent to:

    p4.parse_spec( "<spectype>", aString )

### p4.parse_spec( "<spectype>", aString ) -> aP4::Spec

Parses a P4 Server form (spec) in text form into a Ruby hash using the spec definition obtained from the server.

The first argument is the type of spec to parse: `client`, `branch`, `label`, and so on. The second argument is the string buffer to parse.

Note that there are shortcuts available for this method. You can use:

    p4.parse_<spectype>( buf )

instead of:

    p4.parse_spec( "<spectype>", buf )

Where <spectype> is one of `client`, `branch`, `label`, and so on.

### p4.password= aString -> aString

Set your P4 Server password, in plain text. If not used, takes the value of `P4PASSWD` from any `P4CONFIG` file in effect, or from the environment according to the normal P4 Server conventions. This password will also be used if you later call `p4.run_login` to login using the 2003.2 and later ticket system.

    p4 = P4.new
    p4.password = "mypass"
    p4.connect
    p4.run_login

### p4.password -> aString

Get the current password or ticket. This may be the password in plain text, or if you’ve used `P4#run_login()`, it’ll be the value of the ticket you’ve been allocated by the server.

    p4 = P4.new
    puts( p4.password )

### p4.port= aString -> aString

Set the host and port of the P4 Server you want to connect to. If not called, defaults to the value of `P4PORT` in any `P4CONFIG` file in effect, and then to the value of `P4PORT` taken from the environment.

    p4 = P4.new
    p4.port = "localhost:1666"
    p4.connect
    ...
    p4.disconnect

### p4.port -> aString

Get the host and port of the current P4 Server.

    p4 = P4.new
    puts( p4.port )

### p4.prog= aString -> aString

Set the name of the program, as reported to P4 Server system administrators running `p4 monitor show -e` in P4 Server 2004.2 or later releases.

    p4 = P4.new
    p4.prog = "sync-script"
    p4.connect
    ...
    p4.disconnect

### p4.prog -> aString

Get the name of the program as reported to the P4 Server.

    p4 = P4.new
    p4.prog = "sync-script"
    puts( p4.prog )

### p4.progress= aProgress -> aProgress

Set the current progress indicator. This should be a subclass of `P4::Progress`.

### p4.progress -> aProgress

Get the current progress indicator.

### p4.run_<cmd>( arguments ) -> anArray

This is equivalent to:

    p4.run( "cmd", arguments... )

### p4.run( aCommand, arguments…​ ) -> anArray

Base interface to all the run methods in this API. Runs the specified P4 Server command with the arguments supplied. Arguments may be in any form as long as they can be converted to strings by `to_s`. However, each command's options should be passed as quoted and comma-separated strings, with no leading space. For example:

    p4.run("print","-o","test-print","-q","//depot/Jam/MAIN/src/expand.c")

Failing to pass options in this way can result in confusing error messages.

The `P4#run()` method returns an array of results whether the command succeeds or fails; the array may, however, be empty. Whether the elements of the array are strings or hashes depends on (a) server support for tagged output for the command, and (b) whether tagged output was disabled by calling `p4.tagged = false`.

In the event of errors or warnings, and depending on the exception level in force at the time, `P4#run()` will raise a `P4Exception`. If the current exception level is below the threshold for the error/warning, `P4#run()` returns the output as normal and the caller must explicitly review `P4#errors()` and `P4#warnings()` to check for errors or warnings.

    p4 = P4.new
    p4.connect
    spec = p4.run( "client", "-o" ).shift
    p4.disconnect

Shortcuts are available for `P4#run()`. For example:

    p4.run_command( args )

is equivalent to:

    p4.run( "command", args )

There are also some shortcuts for common commands such as editing P4 Server forms and submitting. Consequently, this:

    p4 = P4.new
    p4.connect
    clientspec = p4.run_client( "-o" ).shift
    clientspec[ "Description" ] = "Build client"
    p4.input = clientspec
    p4.run_client( "-i" )
    p4.disconnect

may be shortened to:

    p4 = P4.new
    p4.connect
    clientspec = p4.fetch_client
    clientspec[ "Description" ] = "Build client"
    p4.save_client( clientspec )
    p4.disconnect

The following are equivalent:

`p4.delete_<spectype>()` | `p4.run( "<spectype>", "-d" )`
`p4.fetch_<spectype>()` | `p4.run( "<spectype>", "-o" ).shift`
`p4.save_<spectype>( spec )` | `p4.input = specp4.run( "<spectype>", "-i" )`

As the commands associated with `P4#fetch_<spectype>()` typically return only one item, these methods do not return an array, but instead return the first result element.

For convenience in submitting changelists, changes returned by `P4#fetch_change()` can be passed to `P4#run_submit`. For example:

    p4 = P4.new
    p4.connect
    spec = p4.fetch_changespec[ "Description" ] = "Automated change"
    p4.run_submit( spec )
    p4.disconnect

### p4.run_filelog( fileSpec ) -> anArray

Runs a `p4 filelog` on the `fileSpec` provided and returns an array of `P4::DepotFile` results when executed in tagged mode, and an array of strings when executed in non-tagged mode. By default, the raw output of `p4 filelog` is tagged; this method restructures the output into a more user-friendly (and object-oriented) form.

    p4 = P4.new
    begin
      p4.connect
      p4.run_filelog( "index.html" ).shift.each_revision do
        |r|
        r.each_integration do
          |i|
          # Do something
        end
      end
    rescue P4Exception
      p4.errors.each { |e| puts( e ) }
    ensure
      p4.disconnect
    end

### p4.run_login( arg…​ ) -> anArray

Runs `p4 login` using a password or ticket set by the user.

### p4.run_password( oldpass, newpass ) -> anArray

A thin wrapper to make it easy to change your password. This method is (literally) equivalent to the following code:

    p4.input( [ oldpass, newpass, newpass ] )
    p4.run( "password" )

For example:

    p4 = P4.new
    p4.password = "myoldpass"
    begin
      p4.connect
      p4.run_password( "myoldpass", "mynewpass" )
    rescue P4Exception
      p4.errors.each { |e| puts( e ) }
    ensure
      p4.disconnect
    end

### p4.run_resolve( args ) [ block ] -> anArray

Interface to `p4 resolve`. Without a block, simply runs a non-interactive resolve (typically an automatic resolve).

    p4.run_resolve( "-at" )

When a block is supplied, the block is invoked once for each merge scheduled by P4 Server. For each merge, a `P4::MergeData` object is passed to the block. This object contains the context of the merge.

The block determines the outcome of the merge by evaluating to one of the following strings:

Block string | Meaning
--- | ---
`ay` | Accept Yours.
`at` | Accept Theirs.
`am` | Accept Merge result.
`ae` | Accept Edited result.
`s` | Skip this merge.
`q` | Abort the merge.

For example:

    p4.run_resolve() do
      |md|
      puts( "Merging..." )
      puts( "Yours: #{md.your_name}" )
      puts( "Theirs: #{md.their_name}" )
      puts( "Base: #{md.base_name}" )
      puts( "Yours file: #{md.your_path}" )
      puts( "Theirs file: #{md.their_path}" )
      puts( "Base file: #{md.base_path}" )
      puts( "Result file: #{md.result_path}" )
      puts( "Merge Hint: #{md.merge_hint}" )

      result = md.merge_hint
      if( result == "e" )
          puts( "Invoking external merge application" )
          result = "s"    # If the merge doesn't work, we'll skip
          result = "am" if md.run_merge()
      end
      result
    end

### p4.run_submit( [aHash], [arg…​] ) -> anArray

Submit a changelist to the server. To submit a changelist, set the fields of the changelist as required and supply any flags:.

    change = p4.fetch_change
    change._description = "Some description"
    p4.run_submit( "-r", change )

You can also submit a changelist by supplying the arguments as you would on the command line:

    p4.run_submit( "-d", "Some description", "somedir/..." )

### p4.run_tickets( ) -> anArray

Get a list of tickets from the local tickets file. Each ticket is a hash object with fields for `Host`, `User`, and `Ticket`.

### p4.save_<spectype>( hashOrString, [options] ) -> anArray

The `save_<spectype>` methods are shortcut methods that allow you to quickly update the definitions of clients, labels, branches, etc. They are equivalent to:

    p4.input = hashOrStringp4.run( "<spectype>", "-i" )

For example:

    p4 = P4.new
    begin
      p4.connect
      client            = p4.fetch_client()
      client[ "Owner" ] = p4.user
      p4.save_client( client )
    rescue P4Exception
      p4.errors.each { |e| puts( e ) }
    ensure
      p4.disconnect
    end

### p4.server_case_sensitive? -> aBool

Detects whether or not the server is case-sensitive.

### p4.server_level -> anInteger

Returns the current P4 Server level. Each iteration of the P4 Server is given a level number. As part of the initial communication this value is passed between the client application and the P4 Server. This value is used to determine the communication that the P4 Server will understand. All subsequent requests can therefore be tailored to meet the requirements of this Server level.

For more information about the P4 Server version levels, see Protocol levels: server/client in the P4 Server Administration Documentation.

### p4.server_unicode? -> aBool

Detects whether or not the server is in unicode mode.

### p4.set_env= ( aString, aString ) -> aBool

On Windows or macOS, set a variable in the registry or user preferences. To unset a variable, pass an empty string as the second argument. On other platforms, an exception is raised.

    p4 = P4.new
    p4.set_env = ( "P4CLIENT", "my_workspace" )
    p4.set_env = ( "P4CLIENT", "" )

### p4.streams= -> aBool

Enable or disable support for streams. By default, streams support is enabled at 2011.1 or higher (`P4#api_level()` >= 70). Raises a `P4Exception` if you attempt to enable streams on a pre-2011.1 server. You can enable or disable support for streams both before and after connecting to the server.

    p4 = P4.new
    p4.streams = false

### p4.streams? -> aBool

Detects whether or not support for P4 Server Streams is enabled.

    p4 = P4.new
    puts ( p4.streams? )
    p4.streams = false
    puts ( p4.streams? )

### p4.tagged( aBool ) { block }

Temporarily toggles the use of tagged output for the duration of the block, and then resets it when the block terminates.

### p4.tagged= aBool -> aBool

Sets tagged output. By default, tagged output is on.

    p4 = P4.new
    p4.tagged = false

### p4.tagged? -> aBool

Detects whether or not you are in tagged mode.

    p4 = P4.new
    puts ( p4.tagged? )
    p4.tagged = false
    puts ( p4.tagged? )

### p4.ticketfile= aString -> aString

Sets the location of the `P4TICKETS` file.

    p4 = P4.new
    p4.ticketfile = "/home/bruno/tickets"

### p4.ticketfile -> aString

Get the path to the current `P4TICKETS` file.

    p4 = P4.new
    puts( p4.ticketfile )

### p4.track= -> aBool

Instruct the server to return messages containing performance tracking information. By default, server tracking is disabled.

    p4 = P4.new
    p4.track = true

### p4.track? -> aBool

Detects whether or not performance tracking is enabled.

    p4 = P4.new
    p4.track = true
    puts ( p4.track? )
    p4.track = false
    puts ( p4.track? )

### p4.track_output -> anArray

If performance tracking is enabled with `p4.track=`, returns a list of strings corresponding to the performance tracking output for the most recently-executed command.

    p4 = P4.new
    p4.track = true
    p4.run_info
    puts ( p4.track_output[0].slice(0,3) )   # should be "rpc"

### p4.user= aString -> aString

Set the P4 Server username. If not called, defaults to the value of `P4USER` taken from any `P4CONFIG` file present, or from the environment as per the usual P4 Server convention. Must be called before connecting to the P4 Server.

    p4 = P4.new
    p4.user = "bruno"
    p4.connect
    ...
    p4.disconnect

### p4.user -> aString

Returns the current P4 Server username.

    p4 = P4.new
    puts( p4.user )

### p4.version= aString -> aString

Set the version of your script, as reported to the P4 Server.

### p4.version -> aString

Get the version of your script, as reported to the P4 Server.

### p4.warnings -> anArray

Returns the array of warnings that arose during execution of the last command.

    p4 = P4.new
    begin
      p4.connect
      p4.exception_level( P4::RAISE_ALL ) # File(s) up-to-date is a warning
      files = p4.run_sync
    rescue P4Exception => ex
      p4.warnings.each { |w| puts( w ) }
    ensure
      p4.disconnect
    end

Your search for returned results