rally_workspace_utils 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/Manifest.txt ADDED
@@ -0,0 +1,17 @@
1
+ Manifest.txt
2
+ README.txt
3
+ Rakefile
4
+ setup.rb
5
+ bin/translate_workspace
6
+ bin/copy_workspace
7
+ lib/translate/copy.rb
8
+ lib/translate/version.rb
9
+ lib/translate/translate.rb
10
+ lib/translate/translation_audit.rb
11
+ lib/translate/copy/rest_object.rb
12
+ lib/translate/copy/story_copy.rb
13
+ lib/translate/copy/test_case_result_copy.rb
14
+ lib/translate/copy/defect_copy.rb
15
+ lib/translate/copy/object_copy.rb
16
+ lib/translate/copy/test_case_copy.rb
17
+ lib/rally_workspace_utils.rb
data/README.txt ADDED
@@ -0,0 +1,3 @@
1
+ README for rally_workspace_utils
2
+ ================================
3
+
data/Rakefile ADDED
@@ -0,0 +1,55 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rake/clean'
4
+ require 'rake/testtask'
5
+ require 'rake/packagetask'
6
+ require 'rake/gempackagetask'
7
+ require 'rake/rdoctask'
8
+ require 'rake/contrib/rubyforgepublisher'
9
+ require 'fileutils'
10
+ require 'hoe'
11
+ include FileUtils
12
+ require File.join(File.dirname(__FILE__), 'lib', 'translate', 'version')
13
+
14
+ AUTHOR = "bcotton" # can also be an array of Authors
15
+ EMAIL = "bcotton@rallydev.com"
16
+ DESCRIPTION = "A utility to translate a UseCase workspace to a UserStory workspace"
17
+ GEM_NAME = "rally_workspace_utils" # what ppl will type to install your gem
18
+ RUBYFORGE_PROJECT = "rally-rest-api" # The unix name for your project
19
+ HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
20
+ RELEASE_TYPES = %w( gem ) # can use: gem, tar, zip
21
+
22
+
23
+ NAME = "rally_workspace_utils"
24
+ REV = nil # UNCOMMENT IF REQUIRED: File.read(".svn/entries")[/committed-rev="(d+)"/, 1] rescue nil
25
+ VERS = ENV['VERSION'] || (Translate::VERSION::STRING + (REV ? ".#{REV}" : ""))
26
+ CLEAN.include ['**/.*.sw?', '*.gem', '.config']
27
+ RDOC_OPTS = ['--quiet', '--title', "translate documentation",
28
+ "--opname", "index.html",
29
+ "--line-numbers",
30
+ "--main", "README",
31
+ "--inline-source"]
32
+
33
+ # Generate all the Rake tasks
34
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
35
+ hoe = Hoe.new(GEM_NAME, VERS) do |p|
36
+ p.author = AUTHOR
37
+ p.description = DESCRIPTION
38
+ p.email = EMAIL
39
+ p.summary = DESCRIPTION
40
+ p.url = HOMEPATH
41
+ p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
42
+ p.test_globs = ["test/**/*_test.rb"]
43
+ p.clean_globs = CLEAN #An array of file patterns to delete on clean.
44
+
45
+ p.extra_deps = []
46
+ p.extra_deps << ["rally_rest_api", ">= 0.6.2"]
47
+ p.extra_deps << ["activesupport"]
48
+
49
+
50
+ # == Optional
51
+ #p.changes - A description of the release's latest changes.
52
+ #p.extra_deps - An array of rubygem dependencies.
53
+ #p.spec_extras - A hash of extra values to set in the gemspec.
54
+ end
55
+
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env ruby
2
+
@@ -0,0 +1,90 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.dirname(__FILE__) + '/../lib'
4
+
5
+ require 'getoptlong'
6
+ require 'logger'
7
+ require 'rubygems'
8
+ require 'rally_rest_api'
9
+
10
+ require 'rally_workspace_utils'
11
+
12
+ opts = GetoptLong.new(
13
+ ["--base_url", "-b", GetoptLong::REQUIRED_ARGUMENT],
14
+ ["--debug", "-d", GetoptLong::NO_ARGUMENT],
15
+ ["--from_workspace", "-f", GetoptLong::REQUIRED_ARGUMENT],
16
+ ["--to_workspace", "-t", GetoptLong::REQUIRED_ARGUMENT],
17
+ ["--username", "-u", GetoptLong::REQUIRED_ARGUMENT],
18
+ ["--password", "-p", GetoptLong::REQUIRED_ARGUMENT],
19
+ ["--audit", "-a", GetoptLong::NO_ARGUMENT],
20
+ ["--logfile", "-l", GetoptLong::REQUIRED_ARGUMENT],
21
+ ["--help", "-h", GetoptLong::NO_ARGUMENT]
22
+ )
23
+
24
+ def usage
25
+ puts "usage: #{$0}"
26
+ puts " -b|--base_url <url> The base URL of the system to translate. Defaults to 'https://rally1.rallydev.com/slm'"
27
+ puts " -d|--debug Turn debugging on"
28
+ puts " -f|--from_workspace <workspace name> The name of the workspace we are translating. Must be a UseCase style workspace"
29
+ puts " -t|--to_workspace <workspace name> The name of the workspace we are tranlating to. Must be a UserStory style workspace"
30
+ puts " -a|--audit Compare the old workspace against the new one"
31
+ puts " -l|--logfile <filename> A file the logger should log to. Defaults to STDOUT"
32
+ puts " -u|--username <username> The rally login of a subscription administrator."
33
+ puts " -p|--password <password> The password of the above user."
34
+ puts " -h|--help This help"
35
+ exit
36
+ end
37
+
38
+ args = {:base_url => "https://rally1.rallydev.com/slm"}
39
+
40
+ debug = false
41
+ logfile = STDOUT
42
+
43
+ opts.each do |opt, arg|
44
+ case opt
45
+ when "--base_url"
46
+ args[:base_url] = arg
47
+ when "--from_workspace"
48
+ puts "arg = #{arg}"
49
+ args[:from_workspace] = arg
50
+ when "--to_workspace"
51
+ args[:to_workspace] = arg
52
+ when "--username"
53
+ args[:username] = arg
54
+ when "--password"
55
+ args[:password] = arg
56
+ when "--debug"
57
+ debug = true
58
+ RestBuilder::DEBUG = true
59
+ when "--logfile"
60
+ logfile = arg
61
+ when "--help"
62
+ usage
63
+ else
64
+ puts "Unknown argument #{opt}"
65
+ usage
66
+ end
67
+ end
68
+
69
+
70
+
71
+ [:base_url, :to_workspace, :from_workspace, :username, :password].each do |arg|
72
+ unless args[arg]
73
+ puts "#{arg} is a required argument"
74
+ usage
75
+ end
76
+ end
77
+
78
+ logger = Logger.new(logfile)
79
+ if debug
80
+ logger.level = Logger::DEBUG
81
+ else
82
+ logger.level = Logger::INFO
83
+ end
84
+ args[:logger] = logger
85
+
86
+ translate = Translate.new(args)
87
+ puts translate.inspect
88
+ translate.run
89
+
90
+
@@ -0,0 +1,2 @@
1
+ require File.dirname(__FILE__) + '/translate/translate'
2
+
@@ -0,0 +1,75 @@
1
+ class DefectCopy < ObjectCopy
2
+
3
+ def shallow_copy(object, new_workspace)
4
+ values = super
5
+ values.delete(:submitted_by) unless user_exists? @object.submitted_by
6
+ values
7
+ end
8
+
9
+ def tangle(new_workspace)
10
+ values = {}
11
+ new_defect = fetch_object(:defect, @object.oid, new_workspace)
12
+
13
+ # If the original defect is in a different project from the stories' card,
14
+ # then create a new user story, as the parent of this new user story, and make
15
+ # the requirement on the defect this new story
16
+ if @object.requirement != nil && @object.requirement.cards != nil && # The defect has a requirement, that is scheduled
17
+ @object.project != nil && # The defect in not in the parent project
18
+ @object.requirement.cards.first.project != @object.project # The card in in a different project from the defect
19
+
20
+ story_name = @object.requirement.name
21
+ new_requirement = fetch_object(:artifact, @object.requirement.oid, new_workspace)
22
+ if new_requirement.parent && new_requirement.parent.project.nil?
23
+ parent_story = new_requirement.parent
24
+ else
25
+ parent_story = new_workspace.rally_rest.create(:hierarchical_requirement,
26
+ :workspace => new_workspace,
27
+ :name => "Parent story for UserStory '#{story_name}' to hold defects in project #{@object.project.name}")
28
+ @object.rally_rest.logger.info "During the tangle of defect '#{@object.name}' we needed to create a new UserStory and make it the parent of this defect's requirement. This is because the original Story was scheduled into a project that was diffetent than the project this defect is in."
29
+ end
30
+ new_requirement.update(:parent => parent_story)
31
+ values[:requirement] = parent_story
32
+ else
33
+
34
+ # if the old defect has a test case
35
+ unless @object.test_case.nil?
36
+ # get the new test_case
37
+ new_test_case = fetch_object(:test_case, @object.test_case.oid, new_workspace)
38
+ # update
39
+ values[:test_case] = new_test_case
40
+
41
+ # if the new defect is in the Parent Project and
42
+ # the its related test_case is not in the same project as the defect,
43
+ # then put the defect in the same project as the test_case
44
+ if new_defect.project.nil? && new_defect.project != new_test_case.project
45
+ values[:project] = new_test_case.project
46
+ end
47
+ end
48
+
49
+ # if the old defect has a requirement
50
+ if !@object.requirement.nil? && @object.requirement.type == "Story"
51
+ # get the requiement, copied from this defect's requirement
52
+ new_story = fetch_object(:artifact, @object.requirement.oid, new_workspace)
53
+ # update the new defect
54
+ values[:requirement] = new_story
55
+
56
+ # if the new defect is in the Parent Project and
57
+ # the new requirement is not in the same project as the defect,
58
+ # then put the defect in the same project as the requirement
59
+ if new_defect.project.nil? && new_defect.project != new_story.project
60
+ values[:project] = new_story.project
61
+ end
62
+ end
63
+ end
64
+
65
+ # tangle the duplicate defects
66
+ unless @object.duplicates.nil?
67
+ dups = []
68
+ @object.duplicates.values.each do |dup|
69
+ dups << fetch_object(:defect, dup.oid, new_workspace)
70
+ end
71
+ values[:duplicates] = dups
72
+ end
73
+ new_defect.update(values) unless values.length == 0
74
+ end
75
+ end
@@ -0,0 +1,97 @@
1
+ require 'pp'
2
+
3
+ class ObjectCopy
4
+ def initialize(object)
5
+ @object = object
6
+ end
7
+
8
+ def copy(new_workspace, additional_values = {})
9
+ values = shallow_copy(@object, new_workspace)
10
+ values.merge! additional_values
11
+
12
+ values[:workspace] = new_workspace
13
+ values[:project] = find_project_in_new_workspace(new_workspace)
14
+
15
+ @new_object = create_object(new_workspace.rally_rest, values)
16
+ remember @object, @new_object
17
+ copy_children(new_workspace)
18
+
19
+ @new_object
20
+ end
21
+
22
+ def remember old, new
23
+ $TRANSLATED ||= {}
24
+ $TRANSLATED[old.oid] = new.oid
25
+ end
26
+
27
+ def create_object(rally_rest, values)
28
+ rally_rest.create(@object.type_as_symbol, values)
29
+ end
30
+
31
+ def find_project_in_new_workspace(new_workspace)
32
+ # I'm assuming (after talking to nate) that if the project is nil, there there is more then one project in the
33
+ # workspace. At least for single project workspaces that were migrated for project scoping, this is true.
34
+ if @object.project.nil?
35
+ return nil
36
+ else
37
+ fetch_project(@object.project.name, new_workspace)
38
+ end
39
+ end
40
+
41
+ def fetch_project(project_name, workspace)
42
+ @@cached_projects ||= {}
43
+ return @@cached_projects[project_name] if @@cached_projects.key? project_name
44
+ project = workspace.rally_rest.find(:project, :workspace => workspace) { equal :name, project_name }.first
45
+ @@cached_projects[project_name] = project
46
+ end
47
+
48
+ def shallow_copy(object, new_workspace)
49
+ keys_to_exclude = excluded_attributes(object)
50
+ values = object.elements.reject { |key, value| keys_to_exclude.include? key }
51
+ values.delete(:owner) unless user_exists? @object.owner
52
+
53
+ # Here we need to fix any deleted custom dropdown values that have been deleted
54
+ values.each do |attribute, value|
55
+ if @object.typedef.custom_dropdown_attributes.key? attribute
56
+ attrdef = @object.typedef.custom_dropdown_attributes[attribute]
57
+ unless attrdef.allowed_values.include? value
58
+ values[attribute] = nil
59
+ puts "Deleteing #{attribute} = #{value} from @{object.name} because it no longer exists"
60
+ end
61
+ end
62
+ end
63
+
64
+ values
65
+ end
66
+
67
+ def excluded_attributes(object)
68
+ object.typedef.reference_attributes(true).keys
69
+ end
70
+
71
+ def copy_children(new_workspace)
72
+ end
73
+
74
+ def user_exists?(username)
75
+ @@cached_users ||= {}
76
+
77
+ return true if username.nil?
78
+ return @@cached_users[username] if @@cached_users.key? username
79
+
80
+ result = @object.rally_rest.find(:user) { equal :login_name, username }
81
+ if result.total_result_count > 0
82
+ @@cached_users[username] = true
83
+ else
84
+ @@cached_users[username] = false
85
+ end
86
+ end
87
+
88
+ def fetch_object(type, oid, workspace)
89
+ @@cached_objects ||= {}
90
+ new_oid = $TRANSLATED[oid]
91
+ return @@cached_objects[new_oid] if @@cached_objects.key? oid
92
+
93
+ object = @object.rally_rest.find(type, :workspace => workspace) { equal :object_i_d, new_oid }.first
94
+ @@cached_objects[new_oid] = object
95
+ end
96
+ end
97
+
@@ -0,0 +1,29 @@
1
+ require 'rubygems'
2
+ require 'rally_rest_api'
3
+
4
+ class RestObject
5
+ def copy(new_workspace, additional_values = {})
6
+ copy_class.new(self).copy(new_workspace, additional_values)
7
+ end
8
+
9
+ def tangle(new_workspace)
10
+ copy_class.new(self).tangle(new_workspace)
11
+ end
12
+
13
+ def excepted_attributes
14
+ [:object_id, :creation_date, :formatted_id, :owner] + self.typedef.reference_attributes(true).keys + self.typedef.custom_dropdown_attributes.keys
15
+ end
16
+
17
+ def copy_class
18
+ case self.type_as_symbol
19
+ when :card : CardCopy
20
+ when :story : StoryCopy
21
+ when :test_case : TestCaseCopy
22
+ when :defect : DefectCopy
23
+ when :test_case_result : TestCaseResultCopy
24
+ else
25
+ ObjectCopy
26
+ end
27
+ end
28
+ end
29
+
@@ -0,0 +1,44 @@
1
+ class StoryCopy < ObjectCopy
2
+ def shallow_copy(object, new_workspace)
3
+ values = super
4
+
5
+ unless object.cards.nil?
6
+ values[:rank] = object.cards.first.rank
7
+ values[:schedule_state] = object.cards.first.state
8
+
9
+ unless object.cards.first.iteration.nil?
10
+ iteration_name = object.cards.first.iteration.name
11
+ iteration = fetch_object(:iteration, object.cards.first.iteration.oid, new_workspace)
12
+ values[:iteration] = iteration if iteration
13
+ end
14
+
15
+ unless object.cards.first.release.nil?
16
+ release_name = object.cards.first.release.name
17
+ release = fetch_object(:release, object.cards.first.release.oid, new_workspace)
18
+ values[:release] = release if release
19
+ end
20
+
21
+ end
22
+ values
23
+ end
24
+
25
+ def find_project_in_new_workspace(new_workspace)
26
+ if @object.cards.nil?
27
+ return super
28
+ elsif @object.cards.first.project.nil?
29
+ return super
30
+ else
31
+ fetch_project(@object.cards.first.project.name, new_workspace)
32
+ end
33
+ end
34
+
35
+ def create_object(rally_rest, values)
36
+ rally_rest.create(:hierarchical_requirement, values)
37
+ end
38
+
39
+ def copy_children(new_workspace)
40
+ @object.cards.first.tasks.values.flatten.each do |task|
41
+ task.copy(new_workspace, :work_product => @new_object)
42
+ end if !@object.cards.nil? && !@object.cards.first.tasks.nil?
43
+ end
44
+ end
@@ -0,0 +1,53 @@
1
+
2
+ class TestCaseCopy < ObjectCopy
3
+ def copy_children(new_workspace)
4
+ @object.results.each do |result|
5
+ result.copy(new_workspace, :test_case => @new_object)
6
+ end unless @object.results.nil?
7
+
8
+ @object.steps.each do |step|
9
+ step.copy(new_workspace, :test_case => @new_object)
10
+ end unless @object.steps.nil?
11
+ end
12
+
13
+ def tangle(new_workspace)
14
+
15
+ values = {}
16
+ if !@object.work_product.nil? && @object.work_product.type == "Story"
17
+ # if the old test case has a work product
18
+ new_test_case = fetch_object(:test_case, @object.oid, new_workspace)
19
+ # get the new test case copied from this testcase
20
+ new_work_product = fetch_object(:artifact, @object.work_product.oid, new_workspace)
21
+
22
+ values[:work_product] = new_work_product
23
+
24
+
25
+
26
+ # If the original test_case is in a different project from the stories' card,
27
+ # then create a new user story, as the parent of this new user story, and make
28
+ # the work_product on the test case the new parent
29
+ if @object.work_product != nil && @object.work_product.cards != nil && # The TC has a work_product, that is scheduled
30
+ @object.project != nil && # The TC in not in the parent project
31
+ @object.work_product.cards.first.project != @object.project # The card in in a different project from the test_case
32
+
33
+ story_name = @object.work_product.name
34
+ parent_story = new_workspace.rally_rest.create(:hierarchical_requirement,
35
+ :workspace => new_workspace,
36
+ :project => nil,
37
+ :name => "Parent story for #{story_name} to hold test cases in project #{@object.project.name}")
38
+ new_work_product.update(:parent => parent_story)
39
+ values[:work_product] = parent_story
40
+
41
+ # if the new test case is in the Parent Project and
42
+ # the new work_product is not in the same project as the test case,
43
+ # then put the test case in the same project as the work_product
44
+ elsif new_test_case.project.nil? && new_test_case.project != new_work_product.project
45
+ values[:project] = new_work_product.project
46
+ end
47
+
48
+
49
+ # update the new test case
50
+ new_test_case.update(values) unless values.length == 0
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,7 @@
1
+ class TestCaseResultCopy < ObjectCopy
2
+ def shallow_copy(object, new_workspace)
3
+ values = super
4
+ values.delete(:tester) unless user_exists? @object.tester
5
+ values
6
+ end
7
+ end
@@ -0,0 +1,9 @@
1
+ require 'rubygems'
2
+ require 'rally_rest_api'
3
+
4
+ require 'translate/copy/rest_object'
5
+ require 'translate/copy/object_copy'
6
+ require 'translate/copy/defect_copy'
7
+ require 'translate/copy/story_copy'
8
+ require 'translate/copy/test_case_copy'
9
+ require 'translate/copy/test_case_result_copy'
@@ -0,0 +1,69 @@
1
+ require File.dirname(__FILE__) + '/copy'
2
+ require File.dirname(__FILE__) + '/translation_audit'
3
+
4
+ class Translate
5
+ include TranslationAudit
6
+
7
+ def initialize(args)
8
+ @base_url = args[:base_url]
9
+ @username = args[:username]
10
+ @password = args[:password]
11
+ @from_workspace_name = args[:from_workspace]
12
+ @to_workspace_name = args[:to_workspace]
13
+ @logger = args[:logger] if args[:logger]
14
+ $TRANSLATED ||= {}
15
+ end
16
+
17
+ def run
18
+ @slm = RallyRestAPI.new(:base_url => @base_url, :username => @username, :password => @password, :logger => @logger)
19
+
20
+ @from_workspace = @slm.user.subscription.workspaces.values.flatten.find { |w| w.name == @from_workspace_name }
21
+ @to_workspace = @slm.user.subscription.workspaces.values.flatten.find { |w| w.name == @to_workspace_name }
22
+
23
+ raise "No such workspace #{@from_workspace_name}" unless @from_workspace
24
+ raise "No such workspace #{@to_workspace_name}" unless @to_workspace
25
+
26
+
27
+ if File::exists? "TRANSLATED"
28
+ File.open("TRANSLATED", "r") do |f|
29
+ $TRANSLATED = Marshal.load(f)
30
+ end
31
+
32
+ if $TRANSLATED[:from] != @from_workspace.oid && $TRANSLATED[:to] != @to_workspace.oid
33
+ $TRANSLATED = {}
34
+ $TRANSLATED[:from] = @from_workspace.oid
35
+ $TRANSLATED[:to] = @to_workspace.oid
36
+ end
37
+ end
38
+
39
+ # now do the copy
40
+ [:iteration, :release, :story, :defect, :test_case].each do |type|
41
+ @slm.find_all(type, :workspace => @from_workspace).each do |o|
42
+ @logger.info "Copying #{o.type} -- #{o.name}"
43
+ o.copy(@to_workspace)
44
+ end
45
+ end unless $TRANSLATED[:copy]
46
+ # mark these workspaces as copied
47
+ $TRANSLATED[:copy] = true
48
+ # dump the oids to disk
49
+ dump_oids
50
+
51
+ # Now tangle the objects
52
+ [:test_case, :defect].each do |type|
53
+ @slm.find_all(type, :workspace => @from_workspace).each do |o|
54
+ @logger.info "Tangle #{o.type} -- #{o.name}"
55
+ o.tangle(@to_workspace)
56
+ end
57
+ end unless $TRANSLATED[:tangle]
58
+
59
+ $TRANSLATED[:tangle] = true
60
+ dump_oids
61
+ end
62
+
63
+ def dump_oids
64
+ File.open("TRANSLATED", "w+") do |f|
65
+ Marshal.dump($TRANSLATED, f)
66
+ end
67
+ end
68
+
69
+ end
@@ -0,0 +1,76 @@
1
+
2
+ module TranslationAudit
3
+ def audit
4
+ all_copied_fields_should_match_for :iteration
5
+ all_copied_fields_should_match_for :release
6
+ all_copied_fields_should_match_for :defect
7
+ all_copied_fields_should_match_for :defect, [:last_run]
8
+ all_copied_fields_should_match_for :test_case_result, [:test_case]
9
+ compare_each :test_case_result do |old, new|
10
+ if old.test_case.name != new.test_case.name
11
+ puts "#{old.type} -- #{old.name} is different"
12
+ end
13
+ end
14
+
15
+ all_copied_fields_should_match_for :test_case_step, [:test_case]
16
+ compare_each :test_case_step do |old, new|
17
+ if old.test_case.name != new.test_case.name
18
+ puts "#{old.type} -- #{old.name} is different"
19
+ end
20
+ end
21
+ all_copied_fields_should_match_for :task, [:work_product]
22
+ compare_each :task do |old, new|
23
+ if old.card.work_product.name != new.work_product.name
24
+ puts "#{old.type} -- #{old.name} is different"
25
+ end
26
+ end
27
+
28
+ compare_each :defect do |old, new|
29
+ if !old.requirement.nil? && old.requirement.type == "Story"
30
+ if new.requirement.children
31
+ old.requirement.name.should == new.requirement.children.values.first.name
32
+ else
33
+ old.requirement.name.should == new.requirement.name
34
+ end
35
+ end
36
+ end
37
+
38
+ compare_each :test_case do |old, new|
39
+ if !old.work_product.nil? && old.work_product.type == "Story"
40
+ if new.work_product.children
41
+ old.work_product.name.should == new.work_product.children.values.first.name
42
+ else
43
+ old.work_product.name.should == new.work_product.name
44
+ end
45
+
46
+ end
47
+ end
48
+ end
49
+
50
+ def compare_each(type)
51
+ @slm.find_all(type, :workspace => @from_workspace).each do |old_object|
52
+ new_object = @slm.find(type, :workspace => @to_workspace) { equal :object_i_d, $TRANSLATED[old_object.oid] }.first
53
+ if new_object.nil?
54
+ puts "Cound not find an copy for object #{old_object.type} -- #{old_object.name} -- #{old_object.oid}"
55
+ next
56
+ end
57
+ yield old_object, new_object
58
+ end
59
+ end
60
+
61
+ def all_copied_fields_should_match_for(type, additional_exceptions = [])
62
+ compare_each(type) do |old_object, new_object|
63
+ excepted_attributes = old_object.excepted_attributes + additional_exceptions
64
+ old_hash = old_object.elements.reject { |k, v| excepted_attributes.include?(k) }
65
+ new_hash = {}
66
+ old_hash.each_key do |k|
67
+ new_hash[k] = new_object.elements[k]
68
+ end
69
+ diff = old_hash.diff(new_hash)
70
+ if diff.length > 0
71
+ puts "#{old_hash.inspect} shoud == #{new_hash.inspect}"
72
+ puts "all_copied_fields_should_match_for #{type} diff = #{diff.inspect}"
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,9 @@
1
+ module Translate #:nodoc:
2
+ module VERSION #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 0
5
+ TINY = 1
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end