powirb 1.1 → 1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -8,7 +8,7 @@
8
8
 
9
9
  # Intro
10
10
 
11
- Often when using Polarion ALM and you refine (and redefine) step-by-step you process, or perhaps you need a fine-tune over the way you defines wor items the workflows and so and so. Other time you need to import data from other tools and - again - you need an adjustment on some workitems (eg: author name, status, etc).
11
+ Often when using Polarion ALM you refine (and redefine) step-by-step your process, or perhaps you need a fine-tune over the way you defines work items, workflows and so and so. Other time you need to import data from other tools and - again - you need an adjustment on some workitems (eg: author name, status, external references, etc).
12
12
 
13
13
  I've fonud extremely valuable and fast to do all this operation in [CLI](http://en.wikipedia.org/wiki/Command-line_interface) with ruby.
14
14
 
@@ -29,21 +29,26 @@ Only basic operation on workitems are implemented, see the example above:
29
29
 
30
30
  # this referes to a project working copy of the subversion repository
31
31
  h = Powirb::Handler.new('./SampleProject')
32
-
33
- h.workitems.each do |wi|
34
- if wi['status'] == 'closed' and wi[:type] == 'action'
35
- wi[:resolution] = 'done'
36
- wi.save!
37
- end
32
+ # now the handler has loaded all workitems data in memory (very fast)
33
+
34
+ # we can access a particular workitem with its "wid"
35
+ wi = h.workitems['WI-1234']
36
+
37
+ # or by and AND'ed set of conditions, eg: all closed requirements
38
+ h.workitems(:type => 'requirements', :status => 'closed').each do |wid,wi|
39
+ puts "#{wid} - #{wi[:title]}"
40
+ wi[:resolution] = 'done'
41
+ wi.save!
38
42
  end
39
43
 
40
- Please, *remember*: In order to "save" the modification we have to hit a commit with subversion.
44
+ Please, *remember*: In order to "save" the modification we have to hit a subsequent *commit* with subversion.
41
45
 
42
46
 
43
47
  # Notes
44
48
 
45
- * it does work with Polarion 2011
46
- * it does **not** support the "old" *LiveDoc* technology
49
+ * Powirb can't create new workitems, only updating on existing ones is allowed
50
+ * it does **not** support the "old" *LiveDoc* technology, but it's ok with the new one
51
+ * the only fields you can change are "string/text" (more work have to be done here!)
47
52
  * the actual version was tested under Linux and Mac OS X only
48
53
 
49
54
 
@@ -51,7 +56,4 @@ Please, *remember*: In order to "save" the modification we have to hit a commit
51
56
 
52
57
  **Powirb** is written by [Carlo Pecchia](mailto:info@carlopecchia.eu) and released under the terms of Apache License (see LICENSE file).
53
58
 
54
-
55
59
  ----
56
-
57
-
@@ -7,6 +7,7 @@ end
7
7
 
8
8
  require 'powirb/workitem'
9
9
  require 'powirb/handler'
10
+ require 'powirb/version'
10
11
  require 'logger'
11
12
 
12
13
  # This module represent a container for Powirb classes.
@@ -14,35 +14,32 @@ class Handler
14
14
  # working copy)
15
15
  def initialize(project_path)
16
16
  unless File.exist?(project_path)
17
- msg = "Invalid project path '#{project_path}'"
18
- Powirb.log.error(msg)
19
- raise msg
20
- end
17
+ msg = "Invalid project path '#{project_path}'"
18
+ Powirb.log.error(msg)
19
+ raise msg
20
+ end
21
21
  @project_path = project_path
22
- Powirb.log.debug("Initialized handler for #{@project_path}")
22
+ Powirb.log.debug("Initialized handler for #{@project_path}")
23
23
 
24
- @workitems_count = 0
25
- self.workitems_paths.each do |path|
26
- @workitems_count += Dir[File.join(@project_path, path, "/**/workitem.xml")].size
27
- end
28
- Powirb.log.debug("Found #{@workitems_count} workitems.")
24
+ # workitems are stored in a big hash, indexed by 'wid's
25
+ @space = {}
26
+ self.workitems_paths.each do |path|
27
+ Dir[File.join(@project_path, path, "/**/workitem.xml")].each do |filename|
28
+ wid = File.dirname(filename).split(/\//).last
29
+ @space[wid] = Workitem.new(filename)
30
+ Powirb.log.debug("Added workitem from #{filename}")
31
+ end
32
+ end
33
+ Powirb.log.debug("Found #{@space.keys.size} workitems.")
29
34
  end
30
-
35
+
31
36
  # Return all workitems in the project
32
- def workitems
33
- tmp = []
34
- self.workitems_paths.each do |path|
35
- Dir[File.join(@project_path, path, "/**/workitem.xml")].each do |filename|
36
- tmp << Workitem.new(filename)
37
- Powirb.log.debug("Added workitem from #{filename}")
38
- end
39
- end
40
- tmp
41
- end
42
-
43
- # Return the number of total workitems found in the project
44
- def workitems_count
45
- @workitems_count
37
+ def workitems(conditions=nil)
38
+ return @space if conditions.nil?
39
+ # Return only workitems for which *all* specified conditions are true
40
+ @space.select do |wid,wi|
41
+ conditions.map{|k,v| wi[k] == v}.reduce(:&)
42
+ end
46
43
  end
47
44
 
48
45
  # Returns the path for workitems in the specified project
@@ -1,4 +1,4 @@
1
1
  module Powirb
2
- VERSION = '1.1'
2
+ VERSION = '1.2'
3
3
  end
4
4
 
@@ -10,51 +10,110 @@ module Powirb
10
10
  # Copyright:: Copyright (c) 2011 Carlo Pecchia
11
11
  # License:: See LICENSE file
12
12
  class Workitem
13
+ attr_reader :filename
13
14
 
14
15
  # Create a new instance of a workitem belonging to its representation
15
16
  # in the standard way Polarion does it
16
17
  def initialize(filename)
17
18
  @filename = filename
18
- Powirb.log.debug("Initializing workitem with #{filename}")
19
- end
20
-
21
- # Read workitem content
22
- def read
23
19
  Powirb.log.debug("Retrieving workitem from #{@filename}")
24
20
  begin
25
- @doc = Nokogiri::XML(open(@filename))
26
- rescue Exception => e
27
- Powirb.log.error(e)
28
- end
21
+ @doc = Nokogiri::XML(open(@filename))
22
+ rescue Exception => e
23
+ Powirb.log.error(e)
24
+ end
29
25
  end
30
26
 
31
27
  # Return a field value
32
28
  def [](fname)
33
29
  fname = fname.to_s
34
30
  node = @doc.xpath("//field[@id=\"#{fname}\"]")
35
- return nil if node.text.empty?
36
- node.text
31
+ return nil if node.text.empty?
32
+ node.text
37
33
  end
38
34
 
39
- # Set/remove a field
40
- def []=(fname, fvalue)
41
- fname = fname.to_s
42
- if self[fname].nil?
43
- # inserting new field
44
- Powirb.log.debug("[#{wid}] adding new field '#{fname}' with value '#{fvalue}'")
45
- @doc.xpath('//field[@id="type"]').last.add_next_sibling("\n <field id=\"#{fname}\">#{fvalue}</field>")
46
- else
47
- if fvalue.nil?
48
- # removing existing field
49
- Powirb.log.debug("[#{wid}] removing field '#{fname}'")
50
- @doc.xpath("//field[@id=\"#{fname}\"]").last.remove
51
- else
52
- # updating existing field
53
- Powirb.log.debug("[#{wid}] updating field '#{fname}' with value '#{fvalue}'")
54
- e = @doc.xpath("//field[@id=\"#{fname}\"]").last
55
- e.content = fvalue
56
- end
57
- end
35
+ # Set/remove a field, optionally is possible to specify a 'type'
36
+ # eg: wi[:foo] = nil -> remove the field 'foo'
37
+ # wi[:foo] = 'bar' -> add/update the field 'foo'
38
+ # wi[:foo] = {:value => 'bar', :type => 'enum:foo'} -> the same as above
39
+ def []=(f,v)
40
+ f = f.to_s
41
+
42
+ # with a null value we remove and existing field
43
+ if v.nil?
44
+ # Delete
45
+ Powirb.log.debug("[#{wid}] removing field '#{f}'")
46
+ @doc.xpath("//field[@id=\"#{f}\"]").last.remove
47
+ return
48
+ end
49
+ # assert: v is defined (String or Hash)
50
+
51
+ # retrieve the 'value' and the optional 'type'
52
+ if v.instance_of?(Hash)
53
+ value = v[:value]
54
+ type = v[:type]
55
+ else
56
+ value = v
57
+ type = nil
58
+ end
59
+ # assert: 'value' and 'type' are defined
60
+
61
+ if self[f].nil?
62
+ # Create: the field is not already present
63
+ Powirb.log.debug("[#{wid}] adding new field '#{f}' with value '#{value}'")
64
+ e = Nokogiri::XML::Node.new('field', @doc)
65
+ e['id'] = f
66
+ e['type'] = type unless type.nil?
67
+ e.content = value
68
+ # we are sure the 'type' field always exists for any workitem, so attach after that
69
+ @doc.xpath('//field[@id="type"]').last.add_next_sibling(e)
70
+ else
71
+ # Update: the field is already present
72
+ Powirb.log.debug("[#{wid}] updating existing field '#{f}' with value '#{value}'")
73
+ e = @doc.xpath("//field[@id=\"#{f}\"]").last
74
+ e['type'] = type unless type.nil?
75
+ e.content = value
76
+ end
77
+ end
78
+
79
+
80
+ # Return the list of linked workitems ['role1:wid1', ..., 'roleN:widN']
81
+ def links
82
+ tmp = []
83
+ @doc.xpath('//field[@id="linkedWorkItems"]/list/struct').each do |struct|
84
+ linked_wid = struct.xpath('item[@id="workItem"]').text
85
+ role = struct.xpath('item[@id="role"]').text
86
+ tmp << "#{role}:#{linked_wid}"
87
+ end
88
+ return tmp
89
+ end
90
+
91
+ # Add a link to another workitem with specified role
92
+ def add_link(lh)
93
+ lnk_wid = lh[:wid]
94
+ lnk_role = lh[:role]
95
+
96
+ # find or create the attach node
97
+ if @doc.xpath('//field[@id="linkedWorkItems"]/list').last.nil?
98
+ Nokogiri::XML::Builder.with(@doc.xpath('//work-item').last) do
99
+ field(:id => 'linkedWorkItems') {
100
+ list {}
101
+ }
102
+ end
103
+ end
104
+
105
+ # build and attach the link struct
106
+ Nokogiri::XML::Builder.with(@doc.xpath('//field[@id="linkedWorkItems"]/list').last) do
107
+ struct {
108
+ item(:id => 'revision')
109
+ item(:id => 'workItem') {
110
+ text lnk_wid
111
+ }
112
+ item(:id => 'role') {
113
+ text lnk_role
114
+ }
115
+ }
116
+ end
58
117
  end
59
118
 
60
119
  # Save workitem on filesystem
@@ -76,7 +135,16 @@ class Workitem
76
135
  # Return a list with all field names
77
136
  def fields
78
137
  @doc.xpath("//field").map{|node| node['id']}.sort
79
- end
138
+ end
139
+
140
+ # Return the "space" under which the workitem lives (tracker xor document)
141
+ def space
142
+ if @filename.include?('/.polarion/tracker/')
143
+ return 'tracker'
144
+ else
145
+ File.dirname(@filename).split(/\//)[4]
146
+ end
147
+ end
80
148
  end
81
149
 
82
150
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = "powirb"
5
- s.version = "1.1"
5
+ s.version = "1.2"
6
6
  s.summary = "POlarion WorkItems handling with RuBy"
7
7
  s.description = <<-EOF
8
8
  Ruby interface to Polarion workitems content, for fast manipulation.
@@ -11,6 +11,6 @@ EOF
11
11
  s.email = ["info@carlopecchia.eu"]
12
12
  s.homepage = "http://github.com/carlopecchia/powirb"
13
13
  s.add_dependency('nokogiri')
14
- s.files = ["LICENSE", "README.markdown", "Rakefile", "lib/powirb/handler.rb", "lib/powirb/version.rb", "lib/powirb/workitem.rb", "lib/powirb.rb", "powirb.gemspec", "test/test_helper.rb", "test/test_powirb.rb", "test/test_powirb_handler.rb", "test/test_powirb_workitem.rb"]
14
+ s.files = ["LICENSE", "README.markdown", "Rakefile", "lib/powirb.rb", "lib/powirb/version.rb", "lib/powirb/handler.rb", "lib/powirb/workitem.rb", "powirb.gemspec", "test/test_helper.rb", "test/test_powirb_workitem.rb", "test/test_powirb.rb", "test/test_powirb_handler.rb"]
15
15
  end
16
16
 
@@ -1,6 +1,5 @@
1
1
  require 'rubygems'
2
2
  require 'test/unit'
3
- #require 'ftools'
4
3
  require 'fileutils'
5
4
 
6
5
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__),'..','lib'))
@@ -12,4 +12,7 @@ class PowirbTest < Test::Unit::TestCase
12
12
  assert_not_nil Powirb.log
13
13
  end
14
14
 
15
+ def test_valid_version
16
+ assert_not_nil Powirb::VERSION
17
+ end
15
18
  end
@@ -19,10 +19,21 @@ class PowirbHandlerTest < Test::Unit::TestCase
19
19
  end
20
20
  end
21
21
 
22
+ def test_wired_workitems_sources
23
+ assert_not_nil @h.workitems_paths
24
+ assert_kind_of Array, @h.workitems_paths
25
+ end
26
+
22
27
  def test_workitems
23
- assert_kind_of Array, @h.workitems
24
- assert @h.workitems_count == 4
28
+ assert_kind_of Hash, @h.workitems
25
29
  assert @h.workitems.size == 4
30
+
31
+ assert_equal 1, @h.workitems(:type => 'task').size
32
+ assert_equal 3, @h.workitems(:type => 'action').size
33
+ assert_equal 0, @h.workitems(:type => 'fooz').size
34
+
35
+ assert_not_nil @h.workitems['WI-1']
36
+ assert_nil @h.workitems['non existant wid']
26
37
  end
27
38
 
28
39
  end
@@ -1,5 +1,5 @@
1
1
  require File.dirname(__FILE__) + '/test_helper'
2
- require 'Nokogiri'
2
+ #require 'Nokogiri'
3
3
 
4
4
  class PowirbWorkitemTest < Test::Unit::TestCase
5
5
 
@@ -9,11 +9,10 @@ class PowirbWorkitemTest < Test::Unit::TestCase
9
9
  FileUtils.rm_rf dst
10
10
  FileUtils.cp_r src, dst
11
11
 
12
- pp = File.join(File.dirname(__FILE__),'sample_project')
12
+ @pp = File.join(File.dirname(__FILE__),'sample_project')
13
13
  Powirb.set_logger(:debug,'/dev/null')
14
- @h = Powirb::Handler.new(pp)
15
- @wi = @h.workitems.first
16
- @wi.read
14
+ @h = Powirb::Handler.new(@pp)
15
+ @wi = @h.workitems[@h.workitems.keys.first]
17
16
  end
18
17
 
19
18
  def teardown
@@ -36,12 +35,18 @@ class PowirbWorkitemTest < Test::Unit::TestCase
36
35
  @wi['foo'] = 'bar'
37
36
  assert @wi['foo'] == 'bar'
38
37
  assert @wi[:foo] == 'bar'
38
+
39
+ @wi['foo'] = {:value => 'new foo', :type => 'enum:fooz'}
40
+ assert @wi.to_xml.include?("id=\"foo\" type=\"enum:fooz\"")
39
41
  end
40
-
42
+
41
43
  def test_updating_existing_field
42
44
  assert_not_nil @wi['title']
43
45
  @wi['title'] = 'Some silly Title here...'
44
46
  assert @wi['title'] == 'Some silly Title here...'
47
+
48
+ @wi['title'] = {:value => 'new title', :type => 'text/plain'}
49
+ assert @wi.to_xml.include?("id=\"title\" type=\"text/plain\"")
45
50
  end
46
51
 
47
52
  def test_remove_field
@@ -67,8 +72,8 @@ class PowirbWorkitemTest < Test::Unit::TestCase
67
72
  assert_nil @wi['priority']
68
73
  @wi.save!
69
74
 
70
- w = @h.workitems.first
71
- w.read
75
+ h2 = Powirb::Handler.new(@pp)
76
+ w = h2.workitems[@wi.wid]
72
77
  assert_nil w['priority']
73
78
  end
74
79
 
@@ -84,4 +89,32 @@ class PowirbWorkitemTest < Test::Unit::TestCase
84
89
  assert fields.include?('title')
85
90
  assert !fields.include?('foo')
86
91
  end
92
+
93
+ def test_space
94
+ assert_not_nil @wi.space
95
+ assert_kind_of String, @wi.space
96
+ assert_equal 'tracker', @wi.space
97
+ end
98
+
99
+ def test_links
100
+ wi = @h.workitems['WI-2']
101
+ assert_not_nil wi.links
102
+ assert_equal 1, wi.links.size
103
+ assert_equal 'parent:WI-1', wi.links.first
104
+ end
105
+
106
+ def test_add_link
107
+ wi1 = @h.workitems['WI-1']
108
+ assert_not_nil wi1
109
+ wi2 = @h.workitems['WI-2']
110
+ assert_not_nil wi2
111
+
112
+ # to empty link set
113
+ wi1.add_link(:wid => 'WI-2', :role => 'relates_to')
114
+ assert wi1.links.include?('relates_to:WI-2')
115
+
116
+ # to non empty links set
117
+ wi2.add_link(:wid => 'WI-1', :role => 'relates_to')
118
+ assert wi2.links.include?('relates_to:WI-1')
119
+ end
87
120
  end
metadata CHANGED
@@ -1,12 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: powirb
3
3
  version: !ruby/object:Gem::Version
4
- hash: 13
4
+ hash: 11
5
5
  prerelease: false
6
6
  segments:
7
7
  - 1
8
- - 1
9
- version: "1.1"
8
+ - 2
9
+ version: "1.2"
10
10
  platform: ruby
11
11
  authors:
12
12
  - Carlo Pecchia
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2011-04-18 00:00:00 +02:00
17
+ date: 2011-08-08 00:00:00 +02:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -44,15 +44,15 @@ files:
44
44
  - LICENSE
45
45
  - README.markdown
46
46
  - Rakefile
47
- - lib/powirb/handler.rb
47
+ - lib/powirb.rb
48
48
  - lib/powirb/version.rb
49
+ - lib/powirb/handler.rb
49
50
  - lib/powirb/workitem.rb
50
- - lib/powirb.rb
51
51
  - powirb.gemspec
52
52
  - test/test_helper.rb
53
+ - test/test_powirb_workitem.rb
53
54
  - test/test_powirb.rb
54
55
  - test/test_powirb_handler.rb
55
- - test/test_powirb_workitem.rb
56
56
  has_rdoc: true
57
57
  homepage: http://github.com/carlopecchia/powirb
58
58
  licenses: []