Class: Perforce

Inherits:
Object show all
Defined in:
lib/perforce.rb

Overview

Perforce – class representing a connection to a perforce server.

Defined Under Namespace

Modules: Util Classes: Changelist

Constant Summary collapse

CYGWIN =

:nodoc:

(RUBY_PLATFORM =~ %r!cygwin!)

Class Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(spec = {}) ⇒ Perforce

Connect to a perforce depot.

The argument spec is a hash of (method, value) pairs sent to the underlying P4 object.

The given values override the P4PORT, P4CLIENT, etc. environment variables.

Example:

#
# This calls P4#user=, P4#password=, P4#client=, P4#port= with
# these values, then calls P4#connect.
#
Perforce.new(
  :user => "iggy_fenton",
  :password => "<password or ticket number>",
  :client => "iggy_fenton_project",
  :port => "server_name:1666")


32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/perforce.rb', line 32

def initialize(spec = {})
  #
  # Remove PWD during creation to avoid some troubles.  The user
  # probably wants perforce to use the absolute path of the current
  # directory, not a path infected by symlinks.
  # 
  # Since ~/project was a symlink to my perforce-based project,
  # perforce would not run when I got there via "cd project" from my
  # home directory.
  #
  @p4 = 
    if self.class.use_pwd_symlinks
      P4.new
    else
      Thread.exclusive {
        previous_pwd = ENV["PWD"]
        ENV.delete("PWD")
        begin
          P4.new
        ensure
          if previous_pwd
            ENV["PWD"] = previous_pwd
          end
        end
      }
    end
  spec.each_pair { |key, value|
    @p4.send("#{key}=", value)
  }
  unless spec.has_key?(:user)
    # guess user
    @p4.user = [
      ENV["USER"],
      ENV["USERNAME"],
    ].select { |name|
      name != nil and name != ""
    }.first.tap { |user|
      unless user 
        raise "Could not determine username"
      end
    }
  end
  @p4.exception_level = P4::RAISE_ERRORS
  @p4.connect
end

Class Attribute Details

Whether the current directory as reported to the perforce server can contain symlinks. Default is false.



282
283
284
# File 'lib/perforce.rb', line 282

def use_pwd_symlinks
  @use_pwd_symlinks
end

Instance Method Details

#add_unix_rootObject

Cygwin-only.

Add a UNIX-style AltRoot.

This allows P4Win and cygwin to work in the same client spec.



185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/perforce.rb', line 185

def add_unix_root #:nodoc:
  client = run(*%w(client -o)).first
  alt_roots = client["AltRoots"]
  if alt_roots and alt_roots.include?(Util.unix_path(client["Root"]))
    # has proper alt root
  else
    client["AltRoots"] =
      alt_roots.to_a + [Util.unix_path(client["Root"])]
    run_with_input(client, "client", "-i")
    puts("Note: added unix AltRoot to client")
  end
end

#chdir(dir) ⇒ Object

Change working directory (locally and remotely).

If a block is given, the original working directory is restored when the block exits. (Behavior is like Dir.chdir.)



205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
# File 'lib/perforce.rb', line 205

def chdir(dir)
  previous_dir = File.expand_path(".")

  Dir.chdir(dir)
  begin
    @p4.cwd = File.expand_path(".")
  rescue
    Dir.chdir(previous_dir)
    raise
  end

  if block_given?
    begin
      yield dir
    ensure
      Dir.chdir(previous_dir)
      @p4.cwd = previous_dir
    end
  end
end

#delete_empty_changelistsObject

Delete all empty changelists.



127
128
129
130
131
# File 'lib/perforce.rb', line 127

def delete_empty_changelists
  pending_changelists.each { |changelist|
    changelist.delete
  }
end

#edit_and_submit(changelist_description, *files) ⇒ Object

Edit and submit files in one step.

Example:

perforce.edit_and_submit("remove trailing whitespace", files) {
  #
  # Do stuff with the files.
  # ...
  #
}
#
# Changes are submitted when the block ends.
#


154
155
156
157
158
159
# File 'lib/perforce.rb', line 154

def edit_and_submit(changelist_description, *files)
  changelist = new_changelist(changelist_description)
  changelist.add_files(*files)
  yield
  changelist.submit
end

#new_changelist(desc) ⇒ Object

Create a Changelist with the given description (a string).



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/perforce.rb', line 88

def new_changelist(desc)
  input = {
    "Change" => "new",
    "Description" => desc,
  }

  number =
    run_with_input(input, "change", "-i").
    first.
    match(%r!\AChange (\d+) created\.!).
    captures.
    first

  Changelist.new(self, number)
end

#p4Object

The underlying P4 object from the P4Ruby package.



81
82
83
# File 'lib/perforce.rb', line 81

def p4
  @p4
end

#pending_changelistsObject

Return the pending changelists (as Changelist objects).



117
118
119
120
121
122
# File 'lib/perforce.rb', line 117

def pending_changelists
  command = %W(changelists -u #{@p4.user} -c #{@p4.client} -s pending)
  run(*command).map { |elem|
    Changelist.new(self, elem["change"].to_i)
  }
end

#revert_files(*files) ⇒ Object

Revert these files.



107
108
109
110
111
112
# File 'lib/perforce.rb', line 107

def revert_files(*files)
  files = files.flatten
  unless files.empty?
    run("revert", *files)
  end
end

#rootObject

Client root directory.



164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/perforce.rb', line 164

def root
  dir = run(*%w(client -o)).first["Root"]
  if CYGWIN
    Util.unix_path(dir).tap { |unix_dir|
      if dir != unix_dir
        add_unix_root
      end
    }
  else
    dir
  end
end

#run(*args) ⇒ Object

Run a general p4 command.

Example:

puts "Your server version is: "
puts perforce.run("info").first["serverVersion"]


241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
# File 'lib/perforce.rb', line 241

def run(*args)
  go = lambda {
    @p4.run(*args).tap {
      puts(@p4.warnings)
    }
  }
  if CYGWIN
    begin
      go.call
    rescue P4Exception => exception
      if @p4.connected? and
          exception.message =~ %r!not under client\'s root!
        # maybe unix root is not present; try again
        add_unix_root
        go.call
      else
        raise
      end
    end
  else
    # not CYGWIN
    go.call
  end
end

#run_with_input(input, *args) ⇒ Object

Call P4#input(input) and then P4#run(*args)



229
230
231
232
# File 'lib/perforce.rb', line 229

def run_with_input(input, *args)
  @p4.input = input
  run(*args)
end

#sync(*args) ⇒ Object

Calls run("sync", *args)



136
137
138
# File 'lib/perforce.rb', line 136

def sync(*args)
  run("sync", *args)
end