Class: Vcs
- Inherits:
-
Object
- Object
- Vcs
- Defined in:
- lib/vcs/vcs.rb,
lib/vcs/edit.rb,
lib/vcs/mail.rb,
lib/vcs/news.rb,
lib/vcs/script.rb,
lib/vcs/message.rb,
lib/vcs/message.rb,
lib/vcs/conflict.rb,
lib/vcs/diffstat.rb,
lib/vcs/mycommit.rb,
lib/vcs/changelog.rb
Overview
The abstract class for a Vcs wrapper. Conventions:
example:
svn checkout http://foo.bar/proj # alias
vcs-svn checkout http://foo.bar/proj # wrapper
vcs --vcs Svn checkout http://foo.bar/proj # manual
checkout checkout_ checkout! checkout_!
Defined Under Namespace
Classes: Failure, MustBeFilled, VcsCmdData, VcsCmdDataFactory
Constant Summary collapse
- MAIL =
Sendmail::MAIL_FILE
- MAILER =
Sendmail.new
- DEFAULT_OPTIONS =
%w[ --confirm --sign --mime ]
- NEWS =
Pathname.new(',news')
- COMMITED =
Pathname.new(',commited')
- @@all_commands =
{}
- @@all_aliases =
Set.new
- @@checkers =
Set.new
- @@message =
Pathname.new(',message')
- @@diffstat =
'diffstat'.to_cmd
- @@subject_format =
'<%= rev %>: <%= title %>'
Instance Attribute Summary collapse
-
#cmd_data ⇒ Object
readonly
Returns the value of attribute cmd_data.
Class Method Summary collapse
Instance Method Summary collapse
- #call_conf_checkers ⇒ Object
- #call_handlers ⇒ Object
- #changelog_failed ⇒ Object
- #check_diffstat ⇒ Object
- #cmd_data_factory ⇒ Object
- #cmd_data_factory=(aFactory) ⇒ Object
- #commit_failed ⇒ Object
- #common_commit!(subject_format, *args, &block) ⇒ Object
- #concat_changelog!(*args) ⇒ Object
-
#diffstat!(*a) ⇒ Object
Use the diffstat command to display statitics on your patch.
- #diffw!(*args) ⇒ Object
- #edit!(*files) ⇒ Object
- #edit_conflicts! ⇒ Object
- #error_handling(meth) ⇒ Object
- #help!(*args) ⇒ Object
-
#initialize(aCmd) ⇒ Vcs
constructor
class VcsCmdDataFactory.
-
#mail!(*args) ⇒ Object
Mail.
- #mail_conf_checker ⇒ Object
- #mail_failed ⇒ Object
- #message(*args) ⇒ Object
- #message_failed ⇒ Object
- #method_missing(meth, *args) ⇒ Object
- #mk_conflicts_list ⇒ Object
- #mkchangelog!(*args) ⇒ Object
-
#news!(*args) ⇒ Object
Post the news.
- #news_conf_checker ⇒ Object
- #news_failed ⇒ Object
- #puts(*a, &b) ⇒ Object
- #resolve_conflicts! ⇒ Object
- #run(*args) ⇒ Object
- #run!(*args) ⇒ Object
- #run_missing!(name, orig, *args) ⇒ Object
- #script!(code, *args) ⇒ Object
- #sub_vcs(out, err) ⇒ Object
- #sub_vcs_with_name(name) ⇒ Object
- #with(io) ⇒ Object
Constructor Details
#initialize(aCmd) ⇒ Vcs
class VcsCmdDataFactory
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
# File 'lib/vcs/vcs.rb', line 63 def initialize ( aCmd ) @cmd = aCmd.to_cmd @handlers = Set.new @runner = Commands::Runners::System.new @h = HighLine.new self.cmd_data_factory = VcsCmdDataFactory.new(:output => STDOUT, :error => STDERR) @runner.subscribe_hook(:failure) do |data| if data.output == STDOUT LOG.debug { raise data.to_yaml } exit((data.status)? data.status.exitstatus : 1) else raise data.to_yaml end end @runner.subscribe_hook(:display_command) do |cmd| LOG.debug { "running: #{cmd.to_sh}" } end end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(meth, *args) ⇒ Object
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 |
# File 'lib/vcs/vcs.rb', line 172 def method_missing ( meth, *args ) meth = meth.to_s if meth =~ /^(.*)!$/ no_bang = $1 super if respond_to? no_bang run_missing!(no_bang, meth, *args) else with_bang = meth + '!' return run_missing!(meth, meth, *args) unless respond_to? with_bang copy = sub_vcs_with_name(meth) res = copy.send(with_bang, *args) return res unless res.nil? out = copy.cmd_data out.out_io.flush out end end |
Instance Attribute Details
#cmd_data ⇒ Object (readonly)
Returns the value of attribute cmd_data.
127 128 129 |
# File 'lib/vcs/vcs.rb', line 127 def cmd_data @cmd_data end |
Class Method Details
.add_basic_method(meth) ⇒ Object
83 84 85 86 87 88 89 90 91 92 |
# File 'lib/vcs/vcs.rb', line 83 def self.add_basic_method ( meth ) class_eval <<-end_eval def #{meth}! ( *args ) run!("#{meth}", *args) end def #{meth}_! ( *args ) run!("#{meth}", *args) end end_eval end |
.add_conf_checker(meth) ⇒ Object
271 272 273 |
# File 'lib/vcs/vcs.rb', line 271 def self.add_conf_checker ( meth ) @@checkers << meth end |
.alias_command(m1, m2) ⇒ Object
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
# File 'lib/vcs/vcs.rb', line 97 def self.alias_command ( m1, m2 ) m2 = m2.to_s @@all_commands[m2] ||= Set.new @@all_commands[m2] << m1.to_s @@all_aliases << m1 class_eval <<-end_eval def #{m1} ( *args ) #{m2}(*args) end def #{m1}_ ( *args ) #{m2}_(*args) end def #{m1}! ( *args ) #{m2}!(*args) end def #{m1}_! ( *args ) #{m2}_!(*args) end end_eval end |
Instance Method Details
#call_conf_checkers ⇒ Object
275 276 277 |
# File 'lib/vcs/vcs.rb', line 275 def call_conf_checkers @@checkers.each { |meth| send(meth) } end |
#call_handlers ⇒ Object
267 268 269 |
# File 'lib/vcs/vcs.rb', line 267 def call_handlers @handlers.each { |meth| send(meth) } end |
#changelog_failed ⇒ Object
142 143 144 145 146 147 148 |
# File 'lib/vcs/changelog.rb', line 142 def changelog_failed if TMP_CL.exist? LOG.info "Restoring `#{CL}' from `#{TMP_CL}' ..." TMP_CL.rename(CL) end LOG.info "#{ADD_CL}: Contains your ChangeLog entry" if ADD_CL.exist? end |
#check_diffstat ⇒ Object
19 20 21 22 23 |
# File 'lib/vcs/diffstat.rb', line 19 def check_diffstat unless `diffstat -V` =~ /diffstat version/ raise ArgumentError, 'The `diffstat\' tool is needed by Vcs.diffstat' end end |
#cmd_data_factory ⇒ Object
118 119 120 |
# File 'lib/vcs/vcs.rb', line 118 def cmd_data_factory @runner.command_data_factory end |
#cmd_data_factory=(aFactory) ⇒ Object
122 123 124 125 |
# File 'lib/vcs/vcs.rb', line 122 def cmd_data_factory= ( aFactory ) @runner.command_data_factory = aFactory @cmd_data = aFactory.new_command_data end |
#commit_failed ⇒ Object
83 84 85 86 87 88 |
# File 'lib/vcs/mycommit.rb', line 83 def commit_failed LOG.info "#{COMMITED}: Contains your ChangeLog entry" if COMMITED.exist? LOG.error 'Aborting' LOG.info 'You can rerun the same command to continue the commit' raise 'Commit failed' end |
#common_commit!(subject_format, *args, &block) ⇒ Object
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 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 77 78 79 80 |
# File 'lib/vcs/mycommit.rb', line 14 def common_commit! ( subject_format, *args, &block ) opts, args = args.partition { |a| a =~ /^-/ } @@subject_format = subject_format update! unless COMMITED.exist? begin mkchangelog(*args) rescue MustBeFilled => ex edit! ex.file end (*args) edit! @@message unless @h.agree 'Committing, are you sure? (y/n)', true commit_failed end cl_entry = concat_changelog!(*args) #pager! diff #pager! status args << 'ChangeLog' unless args.grep(/^[^-]/).empty? # FIXME simplify cl_entry contents begin commit!('--message', cl_entry, *(opts + args)) ADD_CL.rename(COMMITED) TMP_CL.delete if TMP_CL.exist? rescue commit_failed end update! else (*args) edit! @@message end header ||= YAML::load(META.read) block[header['subject']] if block_given? LOG.info 'Deleting junk files...' TMP_CL.delete if TMP_CL.exist? ADD_CL.delete if ADD_CL.exist? COMMITED.delete if COMMITED.exist? META.delete if META.exist? = Pathname.new(',messages') .mkpath unless .directory? = + "#{@@message}.#{header['rev']}" LOG.info "Moving `#{@@message}' to `#{}'..." @@message.rename() LOG.info "You can remove `#{}' if everything is ok." end |
#concat_changelog!(*args) ⇒ Object
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
# File 'lib/vcs/changelog.rb', line 120 def concat_changelog! ( *args ) error_handling :changelog_failed if cl_entry = mkchangelog(*args) unless TMP_CL.exist? LOG.info "Moving `#{CL}' to `#{TMP_CL}' ..." CL.rename(TMP_CL) end CL.open('w') do |file| LOG.info "Prepending `#{ADD_CL}' to `#{CL}' ..." file.print cl_entry file.puts file.print TMP_CL.read end return cl_entry end end |
#diffstat!(*a) ⇒ Object
Use the diffstat command to display statitics on your patch.
13 14 15 16 |
# File 'lib/vcs/diffstat.rb', line 13 def diffstat! ( *a ) check_diffstat (diff(*a) | @@diffstat).run(@runner) end |
#diffw!(*args) ⇒ Object
12 13 14 |
# File 'lib/vcs/message.rb', line 12 def diffw! ( *args ) diff!(*args) end |
#edit!(*files) ⇒ Object
10 11 12 13 14 |
# File 'lib/vcs/edit.rb', line 10 def edit! ( *files ) # stringify cmd = EDITOR + files.flatten.map { |x| x.to_s } > [STDOUT, STDERR] cmd.run(@runner) end |
#edit_conflicts! ⇒ Object
9 10 11 |
# File 'lib/vcs/conflict.rb', line 9 def edit_conflicts! edit! mk_conflicts_list end |
#error_handling(meth) ⇒ Object
263 264 265 |
# File 'lib/vcs/vcs.rb', line 263 def error_handling ( meth ) @handlers << meth end |
#help!(*args) ⇒ Object
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 |
# File 'lib/vcs/vcs.rb', line 190 def help! ( *args ) return help_!(*args) unless args.empty? puts "usage: #{@cmd.command} <subcommand> [options] [args] |Type '#{@cmd.command} help <subcommand>' for help on a specific subcommand. | |Most subcommands take file and/or directory arguments, recursing |on the directories. If no arguments are supplied to such a |command, it recurses on the current directory (inclusive) by default. | |Available subcommands:".head_cut! cmds = [] methods.each do |meth| next if meth =~ /_!?$/ next unless meth =~ /^(.+)!$/ cmd = $1 next if cmd == 'run' next if @@all_aliases.include? cmd.to_sym cmds << cmd end cmds.sort! cmds.each do |cmd| if @@all_commands.has_key? cmd aliases = @@all_commands[cmd].sort.join(', ') puts " - #{cmd} (alias #{aliases})" else puts " - #{cmd}" end end end |
#mail!(*args) ⇒ Object
Mail.
18 19 20 21 22 23 24 25 26 |
# File 'lib/vcs/mail.rb', line 18 def mail! ( *args ) error_handling :mail_failed unless MAIL.exist? print_body(MAIL, MAILER.(*(DEFAULT_OPTIONS + args))) end MAILER.sendmail MAIL.delete puts 'Mail: Sent.' end |
#mail_conf_checker ⇒ Object
35 36 37 38 39 40 41 42 43 44 45 46 47 |
# File 'lib/vcs/mail.rb', line 35 def mail_conf_checker %w[ EMAIL FULLNAME SMTPSERVER ].each do |var| if ENV[var].nil? or ENV[var].empty? LOG.error "environment variable `#{var}' not set" end end unless `gpg --version` =~ /^gpg \(GnuPG\)/ LOG.error 'command not found: gpg' end unless File.exist?("#{ENV['HOME']}/.gnupg/secring.gpg") LOG.error 'no private key: in your ~/.gnupg' end end |
#mail_failed ⇒ Object
28 29 30 31 32 33 |
# File 'lib/vcs/mail.rb', line 28 def mail_failed if defined? MAIL and MAIL.exist? LOG.info "#{MAIL}: Contains the generated mail " + "(generated from #{@@message})" end end |
#message(*args) ⇒ Object
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 |
# File 'lib/vcs/message.rb', line 38 def ( *args ) error_handling :message_failed unless @@message.exist? cl_entry = mkchangelog(*args) TempPath.new('message') do |tmp| tmp.open('w') do |f| with(f).url! if defined? COLLABOA f.puts f.puts 'You can also view this changeset here:' f.puts next_rev = rev.output.read.to_i next_rev += 1 unless ',commited'.to_path.exist? f.puts "http://#{COLLABOA}/repository/changesets/#{next_rev}" end f.puts f.puts 'Index: ChangeLog' f.print cl_entry.sub(/^\d+-\d+-\d+/, 'from') f.puts with(f).diffstat!(*args) f.puts diffw_from_status(*args).each_line do |line| f.print line if line !~ /^=+$/ end end tmp.mv(@@message) end end @@message.open('r') end |
#message_failed ⇒ Object
72 73 74 75 76 77 |
# File 'lib/vcs/message.rb', line 72 def if @@message.exist? LOG.info "#{@@message}: Contains the generated message" LOG.info ' (the ChangeLog entry, the diffstat, the diff)' end end |
#mk_conflicts_list ⇒ Object
3 4 5 6 7 |
# File 'lib/vcs/conflict.rb', line 3 def mk_conflicts_list ls = status.output.readlines.grep(/^C/).map! { |s| s[/^C\s+(.*)/, 1] } raise "no conflicts" if ls.empty? ls end |
#mkchangelog!(*args) ⇒ Object
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/vcs/changelog.rb', line 53 def mkchangelog! ( *args ) error_handling :changelog_failed unless CL.exist? raise Failure, "No `#{CL}', you are probably not in a valid directory." end cl = ADD_CL # Already filled if ,ChangeLog.add exists and not begin by --- if cl.exist? raise MustBeFilled, cl if cl.read =~ /\A---/ require 'erb' ls = [] YAML.each_document("--- |\n" + cl.read) { |x| (ls.size == 2)? break : ls << x } title_subject, input = ls header = { 'title' => title_subject[/^title: (.*)$/, 1], 'subject' => title_subject[/^subject: (.*)$/, 1] } rev = revision.read.to_i + 1 header.merge!('rev' => rev, 'revision' => rev) raise "no title: reopen #{ADD_CL}" if header['title'].nil? header['title'].sub!(/\.$/, '') b = getBinding(header) header = ERB.new(header.to_yaml, $SAFE, '<-%->', '$erbout_').result(b) META.open('w') { |f| f.puts header } output = ERB.new(input, $SAFE, '<-%->', '$erbout_').result(b) return output end cl_add = mkchangelog_from_status(*args) LOG.info "Creating a new `#{cl}' file" cl.open('w') do |f| head = Time.now.strftime("%Y-%m-%d #{FULL_EMAIL}") f.puts " |--- | ########## Fill this file correctly and remove this line ########## | --- |title: |subject: #{@@subject_format}. | |--- | ###################### Your ChangeLog entrie ###################### | --- |#{head} | |\t<%= title %>. | |".head_cut! cl_add.each do |file, str| f.puts "\t* #{file}: #{str}." end f.puts "| |--- | ########### This line, and those below, will be ignored ########### | --- |Instructions: |\t* The first line must be removed when this file is filled. |\t* After you must specify a title, for the news/mail subject. |\t* The line containing <%= title %> will be replaced by your title, |\t <%= subject %> by the subject line, <%= rev %> by the revision... |\t* Everywhere in the document you can get/compute some values with |\t these tags <%= aRubyExpression %> even some vcs specific call. |\t For example <%= status.read %> will include the `svn status' output. | |".head_cut! with(f).diff!(*args) end raise MustBeFilled, cl end |
#news!(*args) ⇒ Object
Post the news.
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
# File 'lib/vcs/news.rb', line 62 def news! ( *args ) error_handling :news_failed print_body(NEWS, (*args)) unless NEWS.exist? @news_status = 'Sent.' NEWS.open('r') do |file| opt = YAML::chop_header(file) server, port = opt[:server].split(/:/) port ||= 119 LOG.info('news') { "Nntp Server: #{server}:#{port}" } unless @h.agree("Post a news, with this subject: #{opt[:subject]}\n" + " to #{opt[:groups].join(', ')}\n from #{opt[:from]}\n" + 'Are you sure? (y/n)', true) LOG.error('news') { 'Aborting' } exit end require 'socket' TCPSocket.open(server, port) do |f| check_line(f, /^200/) f.puts 'post' check_line(f, /^340/) f.puts "Newsgroups: #{opt[:groups].join(', ')}" f.puts "From: #{opt[:from]}" f.puts "Subject: #{opt[:subject]}" f.puts file.each do |line| f.print line.gsub(/^\./, ' .') end f.puts '.' check_line(f, /^240/) f.puts 'quit' check_line(f, /^205/) end end NEWS.delete puts @news_status end |
#news_conf_checker ⇒ Object
108 109 110 111 112 113 114 |
# File 'lib/vcs/news.rb', line 108 def news_conf_checker %w[ NNTPSERVER ].each do |var| if ENV[var].nil? or ENV[var].empty? LOG.error "environment variable `#{var}' not set" end end end |
#news_failed ⇒ Object
101 102 103 104 105 106 |
# File 'lib/vcs/news.rb', line 101 def news_failed if defined? NEWS and NEWS.exist? LOG.info "#{NEWS}: Contains the generated news" + "(generated from #{@@message})" end end |
#puts(*a, &b) ⇒ Object
150 151 152 |
# File 'lib/vcs/vcs.rb', line 150 def puts ( *a, &b ) @cmd_data.puts(*a, &b) end |
#resolve_conflicts! ⇒ Object
13 14 15 16 17 18 19 |
# File 'lib/vcs/conflict.rb', line 13 def resolve_conflicts! conflicts = mk_conflicts_list question = "Resolve these conflicts?: \n - #{conflicts.join("\n - ")}\n(y/n)" if @h.agree question, true return resolved(conflicts) end end |
#run(*args) ⇒ Object
154 155 156 |
# File 'lib/vcs/vcs.rb', line 154 def run ( *args ) sub_vcs.run!(*args) end |
#run!(*args) ⇒ Object
131 132 133 |
# File 'lib/vcs/vcs.rb', line 131 def run! ( *args ) (@cmd + args).run(@runner) end |
#run_missing!(name, orig, *args) ⇒ Object
158 159 160 161 162 163 164 165 166 |
# File 'lib/vcs/vcs.rb', line 158 def run_missing! ( name, orig, *args ) return help!(*args) if name == '--help' if name =~ /^(.*)_$/ run!($1, *args) else LOG.warn { "unknown method #{orig}" } run!(name, *args) end end |
#script!(code, *args) ⇒ Object
10 11 12 13 14 15 16 17 |
# File 'lib/vcs/script.rb', line 10 def script! ( code, *args ) begin eval(code) rescue Exception => ex LOG.error { 'Vcs#script: during the client execution' } LOG.error { ex.long_pp } end end |
#sub_vcs(out, err) ⇒ Object
135 136 137 138 139 |
# File 'lib/vcs/vcs.rb', line 135 def sub_vcs ( out, err ) copy = self.class.new(@cmd) copy.cmd_data_factory = VcsCmdDataFactory.new(:output => out, :error => err) copy end |
#sub_vcs_with_name(name) ⇒ Object
141 142 143 |
# File 'lib/vcs/vcs.rb', line 141 def sub_vcs_with_name ( name ) sub_vcs(TempPath.new("#{name}-out"), TempPath.new("#{name}-err")) end |
#with(io) ⇒ Object
145 146 147 148 |
# File 'lib/vcs/vcs.rb', line 145 def with ( io ) io.flush if io.respond_to? :flush sub_vcs(io, io) end |