<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DZone Snippets: ace2 code</title>
    <link>http://snippets.dzone.com/posts</link>
    <pubDate>Thu, 28 Aug 2008 16:12:24 GMT</pubDate>
    <description>DZone Snippets: ace2 code</description>
    <item>
      <title>Ace2 mod manager</title>
      <link>http://snippets.dzone.com/posts/show/4136</link>
      <description>&lt;code&gt;&lt;br /&gt;#!/usr/local/bin/ruby&lt;br /&gt;&lt;br /&gt;require 'open-uri'&lt;br /&gt;require 'fileutils'&lt;br /&gt;&lt;br /&gt;# should be placed inside WoW directory&lt;br /&gt;# usage:&lt;br /&gt;# ace_updater                  - updates all installed mods&lt;br /&gt;# ace_updater update           - updates all installed mods&lt;br /&gt;# ace_updater update Omen Grid - updates selected mods&lt;br /&gt;# ace_updater add Threat-1.0   - install a new mod&lt;br /&gt;# ace_updater delete FuBar     - uninstall an existing mod&lt;br /&gt;# add the -v flag to an update to show changes from the changelog.&lt;br /&gt;&lt;br /&gt;# You may need to adjust zip_extract for your system.&lt;br /&gt;# file is the zip file to be extracted&lt;br /&gt;# target_dir is the existing directory to extract into.&lt;br /&gt;&lt;br /&gt;class Updater&lt;br /&gt;	# requirers some OS customisation, this WFM on Mac OS X&lt;br /&gt;	def zip_extract(file,target_dir)&lt;br /&gt;		system(&lt;br /&gt;			"unzip", "-q", file, "-d", target_dir&lt;br /&gt;		) or raise "Error extracting (#{$?}): unzip #{file.inspect} -d #{target_dir.inspect}"&lt;br /&gt;	end	&lt;br /&gt;&lt;br /&gt;	def initialize(wow_path,opts,listing_url="http://files.wowace.com/")&lt;br /&gt;		@opts=opts&lt;br /&gt;		@wow_path = wow_path&lt;br /&gt;		@addons_dir = File.join(@wow_path,"Interface","Addons")&lt;br /&gt;		@addons_download_dir = File.join(@wow_path,"Interface","ace_updater")&lt;br /&gt;		@listing_url=listing_url&lt;br /&gt;	end&lt;br /&gt;&lt;br /&gt;	def mod_index&lt;br /&gt;		@mod_index ||= begin&lt;br /&gt;			puts "Fetching Ace mod index from #{@listing_url}..."&lt;br /&gt;			m={}&lt;br /&gt;			open(@listing_url) {|f| &lt;br /&gt;				f.read &lt;br /&gt;			}.scan(&lt;br /&gt;				%r{&lt;td&gt;\s*&lt;a href="([^"]+)"&gt;([^&gt;]+)&lt;/a&gt;\s*&lt;/td&gt;\s*&lt;td&gt;r(\d+)&lt;/td&gt;}&lt;br /&gt;			) {|url,mod,revision|&lt;br /&gt;				m[mod] = [revision.to_i,url]&lt;br /&gt;			} &lt;br /&gt;			puts "#{m.length} entries found."&lt;br /&gt;			puts&lt;br /&gt;			m&lt;br /&gt;		end&lt;br /&gt;	end&lt;br /&gt;&lt;br /&gt;	def each_installed_mod&lt;br /&gt;		Dir.foreach(@addons_dir) {|addon|&lt;br /&gt;			next if ["..","."].include? addon &lt;br /&gt;			yield addon,installed_revision(addon)&lt;br /&gt;		}&lt;br /&gt;	end&lt;br /&gt;&lt;br /&gt;	def installed_revision(addon)&lt;br /&gt;		addon_dir = File.join(@addons_dir,addon)&lt;br /&gt;		x = Dir.glob(File.join(addon_dir,"Changelog-#{addon}-r*.xml"))&lt;br /&gt;		return nil if x.empty?&lt;br /&gt;		x[0] =~ /Changelog-#{addon}-r(\d+)\.xml/&lt;br /&gt;		$1.to_i&lt;br /&gt;	end&lt;br /&gt;&lt;br /&gt;	def get_changes(changelog,local_revision) &lt;br /&gt;		changes=[]&lt;br /&gt;		current_rev = "?????"&lt;br /&gt;		reading_changes = false&lt;br /&gt;		File.open(changelog) {|f|&lt;br /&gt;			f.read&lt;br /&gt;		}.each_line{|l|&lt;br /&gt;			l.strip!&lt;br /&gt;			if l =~ /^r(\d+) \|/&lt;br /&gt;				current_rev = $1.to_i&lt;br /&gt;				break if current_rev &lt;= local_revision&lt;br /&gt;			elsif l=~/^-+$/&lt;br /&gt;				reading_changes = false&lt;br /&gt;			elsif l=~/^$/&lt;br /&gt;				reading_changes = true&lt;br /&gt;			else&lt;br /&gt;				changes &lt;&lt; [current_rev,l] if reading_changes&lt;br /&gt;			end&lt;br /&gt;		}&lt;br /&gt;		changes.stable_sort_by {|r,t| r}&lt;br /&gt;	end&lt;br /&gt;&lt;br /&gt;	def update_mods(list=nil)&lt;br /&gt;		list=nil if list.empty?&lt;br /&gt;&lt;br /&gt;		remote_mods = mod_index()&lt;br /&gt;&lt;br /&gt;		puts "Scanning #{list ? 'chosen' : 'all'} mods for updates..."&lt;br /&gt;		jobs = []&lt;br /&gt;		each_installed_mod {|mod,revision|&lt;br /&gt;			next if revision.nil? #non-ace&lt;br /&gt;			next if list and not list.include?(mod) #not on the list&lt;br /&gt;&lt;br /&gt;			remote_revision,url = remote_mods[mod]&lt;br /&gt;			if remote_revision.nil? or remote_revision &lt; revision&lt;br /&gt;				nonfatal("#{mod}: local is newer than remote (r#{revision.inspect} vs r#{remote_revision.inspect}). Skipping.")&lt;br /&gt;			elsif remote_revision &gt; revision&lt;br /&gt;				jobs &lt;&lt; [mod,revision,remote_revision,url]&lt;br /&gt;			end&lt;br /&gt;&lt;br /&gt;			list.delete mod if list&lt;br /&gt;		}&lt;br /&gt;		list.each {|x|&lt;br /&gt;			$stderr.puts "WARNING: #{x} is not an installed Ace addon, skipping update."&lt;br /&gt;		} if list&lt;br /&gt;&lt;br /&gt;		if jobs.length == 0&lt;br /&gt;			puts "#{list ? 'Chosen' : 'All'} mods up to date."&lt;br /&gt;		else&lt;br /&gt;			install_mods(jobs)&lt;br /&gt;		end&lt;br /&gt;	end&lt;br /&gt;&lt;br /&gt;	def add_mods(modlist)&lt;br /&gt;		remote_mods = mod_index()&lt;br /&gt;		jobs = modlist.map {|mod|&lt;br /&gt;			if remote_mods.key?(mod)&lt;br /&gt;				rev,url = remote_mods[mod]&lt;br /&gt;				[mod,nil,rev,url]&lt;br /&gt;			else&lt;br /&gt;				$stderr.puts "WARNING: #{mod} not found in mod index, skipping"&lt;br /&gt;			end&lt;br /&gt;		}.compact&lt;br /&gt;		install_mods(jobs)&lt;br /&gt;	end&lt;br /&gt;&lt;br /&gt;	def delete_mods(modlist)&lt;br /&gt;		modlist = modlist.map {|x|&lt;br /&gt;			next if ['.','..'].include?(x) # this would be lame&lt;br /&gt;			rev = installed_revision(x)&lt;br /&gt;			if rev&lt;br /&gt;				[x,rev]&lt;br /&gt;			else&lt;br /&gt;				nonfatal("#{x} is not an installed Ace addon, skipping")&lt;br /&gt;			end&lt;br /&gt;		}.compact&lt;br /&gt;&lt;br /&gt;		modlist.each_with_index {|(mod,rev),i|&lt;br /&gt;			status(i,modlist.length,"delete",mod,rev)&lt;br /&gt;			FileUtils.remove_dir(File.join(@addons_dir,mod))&lt;br /&gt;		}&lt;br /&gt;	end&lt;br /&gt;	&lt;br /&gt;	def install_mods(jobs)&lt;br /&gt;		FileUtils.remove_dir(@addons_download_dir) if File.directory?(@addons_download_dir)&lt;br /&gt;		Dir.mkdir(@addons_download_dir) &lt;br /&gt;		jobs.each_with_index{|(mod,revision,remote_revision,url),index|&lt;br /&gt;			status(index, jobs.length, (revision ? 'update' : 'add'), mod, *([revision,remote_revision].compact))&lt;br /&gt;&lt;br /&gt;			zipfile = File.join(@addons_download_dir,"#{mod}.zip")&lt;br /&gt;&lt;br /&gt;			File.open(zipfile,'w') {|f|&lt;br /&gt;				open(url) {|data|&lt;br /&gt;					FileUtils.copy_stream(data,f)&lt;br /&gt;				}&lt;br /&gt;			}&lt;br /&gt;&lt;br /&gt;			zip_extract(zipfile,@addons_download_dir)&lt;br /&gt;			expected_output_dir = File.join(@addons_download_dir,mod)&lt;br /&gt;			destination_dir = File.join(@addons_dir,mod)&lt;br /&gt;			backup_dir = "#{destination_dir}.backup"&lt;br /&gt;&lt;br /&gt;			unless File.directory?(expected_output_dir)&lt;br /&gt;				nonfatal("#{expected_output_dir} not created as expected")&lt;br /&gt;				nonfatal("Check #{zipfile}")&lt;br /&gt;				nonfatal("Skipping installation of #{mod}")&lt;br /&gt;			end&lt;br /&gt;&lt;br /&gt;			changelog = File.join(expected_output_dir,"Changelog-#{mod}-r#{remote_revision}.xml")&lt;br /&gt;			# for some reason these often get wrong privs&lt;br /&gt;			FileUtils.chmod 0644,changelog&lt;br /&gt;			if revision and @opts.include?('-v') # show changelog&lt;br /&gt;				changes = get_changes(changelog,revision)&lt;br /&gt;				changes.each {|revision,text|&lt;br /&gt;					puts("   * [r%d] %s" % [revision,text])&lt;br /&gt;				}&lt;br /&gt;			end&lt;br /&gt;&lt;br /&gt;			FileUtils.remove(zipfile)&lt;br /&gt;			FileUtils.mv(destination_dir,backup_dir) if revision&lt;br /&gt;			FileUtils.mv(expected_output_dir,destination_dir)&lt;br /&gt;			FileUtils.remove_dir(backup_dir) if revision&lt;br /&gt;		}&lt;br /&gt;		FileUtils.remove_dir(@addons_download_dir)&lt;br /&gt;	end&lt;br /&gt;&lt;br /&gt;	def status(sequence,total,action,mod,rev1,rev2=nil)&lt;br /&gt;		rev2 = " -&gt; r#{rev2}" if rev2&lt;br /&gt;		puts("[%2d/%2d] %6s %16s r%d%s" % [sequence+1,total,action,mod,rev1,rev2])&lt;br /&gt;	end&lt;br /&gt;&lt;br /&gt;	def nonfatal(s)&lt;br /&gt;		$stderr.puts("WARNING: #{s}")&lt;br /&gt;	end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;class Array&lt;br /&gt;  def stable_sort_by&lt;br /&gt;    n = 0&lt;br /&gt;    sort_by {|x| n+= 1; [yield(x), n]}&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;if __FILE__ == $0&lt;br /&gt;	opts,args = ARGV.partition {|text| text[0] == ?- }&lt;br /&gt;	args &lt;&lt; 'update' if args.empty?&lt;br /&gt;	command = args.shift.downcase&lt;br /&gt;&lt;br /&gt;	updater = Updater.new(File.dirname(__FILE__),opts)&lt;br /&gt;&lt;br /&gt;	case command&lt;br /&gt;	when 'update'&lt;br /&gt;		updater.update_mods(args)&lt;br /&gt;	when 'add'&lt;br /&gt;		raise "Must specify mods to add" if args.empty?&lt;br /&gt;		updater.add_mods(args)&lt;br /&gt;	when 'delete'&lt;br /&gt;		raise "Must specify mods to delete" if args.empty?&lt;br /&gt;		updater.delete_mods(args)&lt;br /&gt;	else&lt;br /&gt;		puts "Valid commands: update add delete"&lt;br /&gt;	end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;</description>
      <pubDate>Wed, 13 Jun 2007 10:51:27 GMT</pubDate>
      <guid>http://snippets.dzone.com/posts/show/4136</guid>
      <author>tunah (Sam McCall)</author>
    </item>
  </channel>
</rss>
