mingle4r 0.3.0 → 0.4.0
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/History.txt +6 -0
- data/README +53 -20
- data/TODO.txt +8 -4
- data/lib/mingle4r.rb +9 -23
- data/lib/mingle4r/api/card.rb +137 -0
- data/lib/mingle4r/api/card/attachment.rb +47 -0
- data/lib/mingle4r/api/card/comment.rb +12 -0
- data/lib/mingle4r/api/card/transition.rb +48 -0
- data/lib/mingle4r/api/murmur.rb +12 -0
- data/lib/mingle4r/api/project.rb +64 -0
- data/lib/mingle4r/api/property_definition.rb +7 -0
- data/lib/mingle4r/api/user.rb +7 -0
- data/lib/mingle4r/api/wiki.rb +10 -0
- data/lib/mingle4r/common_class_methods.rb +0 -1
- data/lib/mingle4r/mingle_client.rb +16 -14
- data/lib/mingle4r/version.rb +1 -1
- metadata +11 -22
- data/lib/mingle4r/api.rb +0 -31
- data/lib/mingle4r/api/v1.rb +0 -25
- data/lib/mingle4r/api/v1/card.rb +0 -173
- data/lib/mingle4r/api/v1/card/attachment.rb +0 -51
- data/lib/mingle4r/api/v1/project.rb +0 -71
- data/lib/mingle4r/api/v1/property_definition.rb +0 -16
- data/lib/mingle4r/api/v1/transition_execution.rb +0 -15
- data/lib/mingle4r/api/v1/user.rb +0 -9
- data/lib/mingle4r/api/v1/wiki.rb +0 -12
- data/lib/mingle4r/api/v2.rb +0 -25
- data/lib/mingle4r/api/v2/card.rb +0 -157
- data/lib/mingle4r/api/v2/card/attachment.rb +0 -51
- data/lib/mingle4r/api/v2/card/comment.rb +0 -16
- data/lib/mingle4r/api/v2/card/transition.rb +0 -52
- data/lib/mingle4r/api/v2/murmur.rb +0 -14
- data/lib/mingle4r/api/v2/project.rb +0 -90
- data/lib/mingle4r/api/v2/property_definition.rb +0 -14
- data/lib/mingle4r/api/v2/user.rb +0 -9
- data/lib/mingle4r/api/v2/wiki.rb +0 -12
- data/lib/mingle4r/common_dyn_class_instance_methods.rb +0 -5
data/History.txt
CHANGED
data/README
CHANGED
@@ -8,6 +8,8 @@ This gem is a wrapper around active resource to access the rest api exposed by m
|
|
8
8
|
(http://studios.thoughtworks.com/mingle-agile-project-management).It provides a easy
|
9
9
|
way to communicate with mingle. For the library to work you need to enable basic authentication
|
10
10
|
(not enabled by default) in Mingle. See below to enable basic authentication in Mingle.
|
11
|
+
The typical use-case for this gem is to help someone getting started with writing code
|
12
|
+
to integrate with Mingle.
|
11
13
|
|
12
14
|
However if you are planning to connect and work with mingle from the terminal, then there
|
13
15
|
is another gem mingle-mingle which you should look at. This library provides a very easy
|
@@ -29,10 +31,9 @@ attachments for a particular card.
|
|
29
31
|
|
30
32
|
== SYNOPSIS:
|
31
33
|
|
32
|
-
The api now supports
|
33
|
-
|
34
|
-
|
35
|
-
<mingle instance host>/help/index)
|
34
|
+
The api now supports only mingle 3. Mingle 2 is no longer supported. If you need to connect
|
35
|
+
to both Mingle 2 and Mingle 3 then try 0.3.0 of the gem. To see the api for mingle 3, check
|
36
|
+
the api documentation for the mingle instance(located at <mingle instance host>/help/index)
|
36
37
|
|
37
38
|
In all the documentation below you can replace Mingle4r::MingleClient with MingleClient.
|
38
39
|
Its an alias for easy use.
|
@@ -70,10 +71,12 @@ you are trying to access. It should be something like 'http://localhost:8080/pro
|
|
70
71
|
|
71
72
|
m_c = Mingle4r::MingleClient.new('http://localhost:8080', 'testuser', 'password')
|
72
73
|
m_c.proj_id = 'great_mingle_project'
|
73
|
-
m_c.project
|
74
|
+
project = m_c.project
|
75
|
+
|
76
|
+
project is a single activeresource object
|
74
77
|
|
75
78
|
C) Getting cards for a particular project
|
76
|
-
|
79
|
+
-----------------------------------------
|
77
80
|
|
78
81
|
Get a mingle client object initialized as in SECTION B. Then call the cards method.
|
79
82
|
|
@@ -113,6 +116,7 @@ m_c = Mingle4r::MingleClient.new('http://localhost:8080', 'testuser', 'password'
|
|
113
116
|
m_c.proj_id = 'great_mingle_project'
|
114
117
|
defect_card = m_c.project.cards.first
|
115
118
|
defect_card.property_value('Status', 'Closed')
|
119
|
+
defect_card.save
|
116
120
|
|
117
121
|
G) Adding comment to a particular card
|
118
122
|
--------------------------------------
|
@@ -122,14 +126,42 @@ m_c.proj_id = 'great_mingle_project'
|
|
122
126
|
defect_card = m_c.project.cards[0]
|
123
127
|
defect_card.add_comment('Not able to reproduce')
|
124
128
|
|
125
|
-
H) Getting
|
126
|
-
|
129
|
+
H) Getting a particular version of a card
|
130
|
+
-----------------------------------------
|
131
|
+
|
132
|
+
Mingle maintains the different versions of a card. It always shows the latest version
|
133
|
+
by default. However if you want to access a different version you can do so in the
|
134
|
+
following ways.
|
135
|
+
|
136
|
+
m_c = Mingle4r::MingleClient.new('http://localhost:8080', 'testuser', 'password')
|
137
|
+
m_c.proj_id = 'great_mingle_project'
|
138
|
+
defect_card = m_c.project.cards.first
|
139
|
+
supposing the latest version of the card is 42
|
140
|
+
|
141
|
+
1) Get the previous version
|
142
|
+
---------------------------
|
143
|
+
|
144
|
+
defect_card.version(:previous) # return version 41
|
145
|
+
|
146
|
+
2) Get the next version
|
147
|
+
-----------------------
|
148
|
+
|
149
|
+
defect_card.version(:next) # returns version 42 since it is the latest version
|
150
|
+
|
151
|
+
3) Get an arbitrary version
|
152
|
+
---------------------------
|
153
|
+
|
154
|
+
defect_card.version(21) # returns version 21
|
155
|
+
|
156
|
+
I) Getting all comments for a card
|
157
|
+
----------------------------------
|
158
|
+
|
127
159
|
m_c = Mingle4r::MingleClient.new('http://localhost:8080', 'testuser', 'password')
|
128
160
|
m_c.proj_id = 'great_mingle_project'
|
129
161
|
defect_card = m_c.project.cards.first
|
130
162
|
defect_cards.comments
|
131
163
|
|
132
|
-
|
164
|
+
J) Attachments
|
133
165
|
--------------
|
134
166
|
m_c = Mingle4r::MingleClient.new('http://localhost:8080', 'testuser', 'password')
|
135
167
|
m_c.proj_id = 'great_mingle_project'
|
@@ -140,15 +172,15 @@ defect_card.attachments
|
|
140
172
|
--------------------------------------
|
141
173
|
|
142
174
|
attachment = defect_card.attachments.first
|
143
|
-
attachment.
|
175
|
+
attachment.download('page.css')
|
144
176
|
|
145
177
|
2) Uploading an attachment
|
146
178
|
--------------------------
|
147
179
|
|
148
180
|
defect_card.upload_attachment('page-screenshot.jpg')
|
149
181
|
|
150
|
-
|
151
|
-
|
182
|
+
K) Murmurs
|
183
|
+
----------
|
152
184
|
|
153
185
|
1) Get the murmurs for a project
|
154
186
|
--------------------------------
|
@@ -168,16 +200,16 @@ J) Murmurs(only in Mingle 3.0)
|
|
168
200
|
-------------------------
|
169
201
|
project.post_murmur('my first murmur, I am excited!')
|
170
202
|
|
171
|
-
|
172
|
-
|
203
|
+
L) Get all transitions for a card
|
204
|
+
---------------------------------
|
173
205
|
|
174
206
|
m_c = Mingle4r::MingleClient.new('http://localhost:8080', 'testuser', 'password')
|
175
207
|
m_c.proj_id = 'great_mingle_project'
|
176
208
|
defect_card = m_c.project.cards.first
|
177
209
|
|
178
|
-
defect_card.transitions
|
210
|
+
defect_card.transitions # array of active resource objects
|
179
211
|
|
180
|
-
|
212
|
+
M) Execute a transition on a card
|
181
213
|
---------------------------------
|
182
214
|
|
183
215
|
m_c = Mingle4r::MingleClient.new('http://localhost:8080', 'testuser', 'password')
|
@@ -185,11 +217,9 @@ m_c.proj_id = 'great_mingle_project'
|
|
185
217
|
defect_card = m_c.project.cards.first
|
186
218
|
|
187
219
|
defect_card.execute_transition(
|
188
|
-
'name'/'transition' => name of the transition to execute exactly s in Mingle(required in 2,3 but
|
189
|
-
not required in 3.0)
|
190
220
|
'comment' => comment for the transition, required only if the transition requires a comment
|
191
221
|
'Property Name as in Mingle exactly' => 'Property value to set for the property', required only
|
192
|
-
if the transition requires to be set manually.
|
222
|
+
if the transition requires to be set manually, multiple properties might need to be set.
|
193
223
|
)
|
194
224
|
|
195
225
|
== REQUIREMENTS:
|
@@ -199,7 +229,10 @@ during gem install.
|
|
199
229
|
|
200
230
|
== INSTALL:
|
201
231
|
|
202
|
-
|
232
|
+
since github no longer archives gems, I am hosting the gem at gemcutter. So you would need to
|
233
|
+
add http://gemcutter.org to your gem sources : gem sources -a 'http://gemcutter.org'. Then do
|
234
|
+
|
235
|
+
gem install mingle4r
|
203
236
|
|
204
237
|
== LICENSE:
|
205
238
|
|
data/TODO.txt
CHANGED
@@ -1,7 +1,11 @@
|
|
1
|
-
update documentation
|
2
|
-
write tests for
|
1
|
+
update documentation - write about executing transitions directly on the transition
|
2
|
+
write tests for project class
|
3
|
+
|
3
4
|
|
4
5
|
refactorings
|
5
6
|
------------
|
6
|
-
|
7
|
-
|
7
|
+
|
8
|
+
maybe nice to nave
|
9
|
+
-------------------
|
10
|
+
|
11
|
+
should transtions give the list of properties to be changed directly
|
data/lib/mingle4r.rb
CHANGED
@@ -13,29 +13,15 @@ end
|
|
13
13
|
require 'mingle_resource'
|
14
14
|
require 'mingle4r/version'
|
15
15
|
require 'mingle4r/common_class_methods'
|
16
|
-
require 'mingle4r/common_dyn_class_instance_methods'
|
17
16
|
require 'mingle4r/helpers'
|
18
17
|
require 'mingle4r/mingle_client'
|
19
|
-
require 'mingle4r/api'
|
20
18
|
|
21
|
-
|
22
|
-
require 'mingle4r/api/
|
23
|
-
require 'mingle4r/api/
|
24
|
-
require 'mingle4r/api/
|
25
|
-
require 'mingle4r/api/
|
26
|
-
require 'mingle4r/api/
|
27
|
-
require 'mingle4r/api/
|
28
|
-
require 'mingle4r/api/
|
29
|
-
require 'mingle4r/api/
|
30
|
-
|
31
|
-
# version 2 of api
|
32
|
-
require 'mingle4r/api/v2'
|
33
|
-
require 'mingle4r/api/v2/card'
|
34
|
-
require 'mingle4r/api/v2/card/attachment'
|
35
|
-
require 'mingle4r/api/v2/card/comment'
|
36
|
-
require 'mingle4r/api/v2/card/transition'
|
37
|
-
require 'mingle4r/api/v2/murmur'
|
38
|
-
require 'mingle4r/api/v2/project'
|
39
|
-
require 'mingle4r/api/v2/property_definition'
|
40
|
-
require 'mingle4r/api/v2/user'
|
41
|
-
require 'mingle4r/api/v2/wiki'
|
19
|
+
require 'mingle4r/api/card'
|
20
|
+
require 'mingle4r/api/card/attachment'
|
21
|
+
require 'mingle4r/api/card/comment'
|
22
|
+
require 'mingle4r/api/card/transition'
|
23
|
+
require 'mingle4r/api/murmur'
|
24
|
+
require 'mingle4r/api/project'
|
25
|
+
require 'mingle4r/api/property_definition'
|
26
|
+
require 'mingle4r/api/user'
|
27
|
+
require 'mingle4r/api/wiki'
|
@@ -0,0 +1,137 @@
|
|
1
|
+
module Mingle4r
|
2
|
+
module API
|
3
|
+
class Card
|
4
|
+
extend Mingle4r::CommonClassMethods
|
5
|
+
|
6
|
+
# overwrite the default find in CommonClassMethods
|
7
|
+
def self.find(*args)
|
8
|
+
scope = args.slice!(0)
|
9
|
+
options = args.slice!(0) || {}
|
10
|
+
@resource_class.find_without_pagination(scope, options)
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
def find_without_pagination(*args)
|
15
|
+
scope = args.slice!(0)
|
16
|
+
options = args.slice!(0) || {}
|
17
|
+
options[:params] ||= {}
|
18
|
+
options[:params].merge!({:page => 'all'})
|
19
|
+
# call ActiveResource::Base::find with proper options
|
20
|
+
find(scope, options)
|
21
|
+
end
|
22
|
+
|
23
|
+
# applies an mql filter on card types. Look at https://mingle05.thoughtworks.com/help/mql_reference.html
|
24
|
+
# for reference
|
25
|
+
def apply_filter(filter_string)
|
26
|
+
find_without_pagination(:all, :params => {'filters[mql]'.to_sym => filter_string})
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
module InstanceMethods
|
31
|
+
# so that active resource tries to find by number
|
32
|
+
def id
|
33
|
+
number()
|
34
|
+
end
|
35
|
+
|
36
|
+
def attachments(refresh = false)
|
37
|
+
return @attachments if(!refresh && @attachments)
|
38
|
+
set_attributes_for(Attachment)
|
39
|
+
@attachments = Attachment.find(:all)
|
40
|
+
end
|
41
|
+
|
42
|
+
def comments(refresh = false)
|
43
|
+
return @comments if(!refresh && @comments)
|
44
|
+
set_attributes_for(Comment)
|
45
|
+
@comments = Card::Comment.find(:all)
|
46
|
+
end
|
47
|
+
|
48
|
+
def transitions(refresh = false)
|
49
|
+
return @transitions if(!refresh && @transitions)
|
50
|
+
set_attributes_for(Transition)
|
51
|
+
@transitions = Card::Transition.find(:all)
|
52
|
+
end
|
53
|
+
|
54
|
+
def murmurs(refresh = false)
|
55
|
+
return @murmurs if(!refresh && @murmurs)
|
56
|
+
set_attributes_for(Murmur)
|
57
|
+
@murmurs = Murmur.find(:all)
|
58
|
+
end
|
59
|
+
|
60
|
+
def upload_attachment(file_path)
|
61
|
+
attachment_uri = URI.parse(File.join(self.class.site.to_s, "cards/#{self.number()}/attachments.xml"))
|
62
|
+
http = Net::HTTP.new(attachment_uri.host, attachment_uri.port)
|
63
|
+
http.use_ssl = attachment_uri.is_a?(URI::HTTPS)
|
64
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE if http.use_ssl
|
65
|
+
basic_encode = 'Basic ' + ["#{self.class.user}:#{self.class.password}"].pack('m').delete("\r\n")
|
66
|
+
|
67
|
+
post_headers = {
|
68
|
+
'Authorization' => basic_encode,
|
69
|
+
'Content-Type' => 'multipart/form-data; boundary=----------XnJLe9ZIbbGUYtzPQJ16u1'
|
70
|
+
}
|
71
|
+
|
72
|
+
file_content = IO.read(file_path)
|
73
|
+
|
74
|
+
post_body = <<EOS
|
75
|
+
------------XnJLe9ZIbbGUYtzPQJ16u1\r
|
76
|
+
Content-Disposition: form-data; name="file"; filename="#{File.basename(file_path)}"\r
|
77
|
+
Content-Type: application/octet-stream\r
|
78
|
+
Content-Length: #{file_content.size}\r
|
79
|
+
\r
|
80
|
+
#{file_content}\r
|
81
|
+
------------XnJLe9ZIbbGUYtzPQJ16u1--\r
|
82
|
+
EOS
|
83
|
+
|
84
|
+
http.post(attachment_uri.path, post_body, post_headers)
|
85
|
+
end
|
86
|
+
|
87
|
+
def add_comment(str)
|
88
|
+
set_attributes_for(Comment)
|
89
|
+
comment = Comment.new(:content => str.to_s)
|
90
|
+
comment.save
|
91
|
+
end
|
92
|
+
|
93
|
+
def execute_transition(args)
|
94
|
+
trans_name = args.symbolize_keys[:name]
|
95
|
+
transition = transitions.detect { |t| t.name == trans_name}
|
96
|
+
transition.execute(args)
|
97
|
+
end
|
98
|
+
|
99
|
+
# returns back the version of the card given. If an invalid version is given, the latest
|
100
|
+
# version is returned, takes a number or :next or :before
|
101
|
+
def version(version_no)
|
102
|
+
version_2_find = 0
|
103
|
+
case version_no
|
104
|
+
when :previous
|
105
|
+
version_2_find = self.version.to_i - 1
|
106
|
+
when :next
|
107
|
+
version_2_find = self.version.to_i + 1
|
108
|
+
else
|
109
|
+
version_2_find = version_no.to_i
|
110
|
+
end
|
111
|
+
self.class.find(self.number, :params => {:version => version_2_find})
|
112
|
+
end
|
113
|
+
|
114
|
+
# Gets and sets the value of a property. The property name given should be the same
|
115
|
+
# as the mingle property name. the value is optional
|
116
|
+
def property_value(name, val = nil)
|
117
|
+
property = properties.detect { |p| p.name == name }
|
118
|
+
val ? property.value = val : property.value
|
119
|
+
end
|
120
|
+
|
121
|
+
# Gets the custom properties in the form of an array of hashes with the property names as keys and
|
122
|
+
# property values as the value
|
123
|
+
def custom_properties
|
124
|
+
properties.map { |p| {p.name => p.value} }
|
125
|
+
end
|
126
|
+
|
127
|
+
private
|
128
|
+
def set_attributes_for(klass)
|
129
|
+
resource_site = File.join(self.class.site.to_s, "cards/#{self.number()}").to_s
|
130
|
+
klass.site = resource_site
|
131
|
+
klass.user = self.class.user
|
132
|
+
klass.password = self.class.password
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Mingle4r
|
2
|
+
module API
|
3
|
+
class Attachment
|
4
|
+
module InstanceMethods
|
5
|
+
# downloads the attachment. It an additional file path is given it saves it at the
|
6
|
+
# given path. The given path should be writable
|
7
|
+
def download(file_name = nil)
|
8
|
+
collection_uri = self.class.site
|
9
|
+
rel_down_url = self.url
|
10
|
+
base_url = "#{collection_uri.scheme}://#{collection_uri.host}:#{collection_uri.port}/"
|
11
|
+
down_uri = URI.join(base_url, rel_down_url)
|
12
|
+
req = Net::HTTP::Get.new(down_uri.path)
|
13
|
+
req.basic_auth self.class.user, self.class.password
|
14
|
+
begin
|
15
|
+
res = Net::HTTP.start(down_uri.host, down_uri.port) { |http| http.request(req) }
|
16
|
+
file_name ||= self.file_name()
|
17
|
+
File.open(file_name, 'w') { |f| f.print(res.body) }
|
18
|
+
rescue Exception => e
|
19
|
+
e.message
|
20
|
+
end
|
21
|
+
end # download
|
22
|
+
|
23
|
+
# alias for file_name
|
24
|
+
def name
|
25
|
+
file_name()
|
26
|
+
end
|
27
|
+
|
28
|
+
# so that active resource tries to find by proper id
|
29
|
+
def id
|
30
|
+
name()
|
31
|
+
end
|
32
|
+
|
33
|
+
# This method had to be overriden.
|
34
|
+
# normal active resource destroy doesn't work as mingle site for deleting attachments doesn't end with .xml.
|
35
|
+
def destroy
|
36
|
+
connection = self.send(:connection)
|
37
|
+
# deletes the attachment by removing .xml at the end
|
38
|
+
connection.delete(self.send(:element_path).gsub(/\.xml\z/, ''))
|
39
|
+
end
|
40
|
+
alias_method :delete, :destroy
|
41
|
+
end #module InstanceMethods
|
42
|
+
|
43
|
+
extend Mingle4r::CommonClassMethods
|
44
|
+
|
45
|
+
end # class Attachment
|
46
|
+
end # class API
|
47
|
+
end # module Mingle4r
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Mingle4r
|
2
|
+
module API
|
3
|
+
class Transition
|
4
|
+
extend Mingle4r::CommonClassMethods
|
5
|
+
|
6
|
+
module InstanceMethods
|
7
|
+
def execute(args = {})
|
8
|
+
args.symbolize_keys!
|
9
|
+
trans_exec_xml = convert_to_xml(args)
|
10
|
+
# set_transition_execution_attributes
|
11
|
+
conn = self.class.connection
|
12
|
+
url_path =URI.parse(transition_execution_url()).path
|
13
|
+
conn.post(url_path, trans_exec_xml, self.class.headers)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
def convert_to_xml(args)
|
18
|
+
hash = create_transition_exec_hash(args)
|
19
|
+
xmlize_trans_exec(hash)
|
20
|
+
end
|
21
|
+
|
22
|
+
def create_transition_exec_hash(args)
|
23
|
+
transition_hash = {}
|
24
|
+
transition_hash['card'] = (args.delete(:card) || associated_card_number).to_i
|
25
|
+
args.delete(:name) || args.delete(:transition)
|
26
|
+
|
27
|
+
comment = args.delete(:comment)
|
28
|
+
transition_hash['comment'] = comment if comment
|
29
|
+
properties = []
|
30
|
+
args.each do |name, value|
|
31
|
+
property = {'name' => name.to_s, 'value' => value}
|
32
|
+
properties.push(property)
|
33
|
+
end
|
34
|
+
transition_hash['properties'] = properties unless properties.empty?
|
35
|
+
transition_hash
|
36
|
+
end
|
37
|
+
|
38
|
+
def xmlize_trans_exec(hash)
|
39
|
+
hash.to_xml(:root => 'transition_execution', :dasherize => false)
|
40
|
+
end
|
41
|
+
|
42
|
+
def associated_card_number
|
43
|
+
File.basename(self.class.site.to_s).to_i
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|