mingle-macro-development-toolkit 1.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.
Files changed (36) hide show
  1. data/LICENSE.txt +21 -0
  2. data/README.rdoc +66 -0
  3. data/Rakefile +36 -0
  4. data/bin/new_mingle_macro +107 -0
  5. data/example/Rakefile +3 -0
  6. data/example/deploy.rake +10 -0
  7. data/example/init.rb +10 -0
  8. data/example/integration_test.rb +13 -0
  9. data/example/integration_test_helper.rb +19 -0
  10. data/example/macro.rb +18 -0
  11. data/example/unit_test.rb +13 -0
  12. data/example/unit_test_helper.rb +12 -0
  13. data/getting_started.txt +253 -0
  14. data/lib/macro_development_toolkit.rb +22 -0
  15. data/lib/macro_development_toolkit/mingle/card_type.rb +41 -0
  16. data/lib/macro_development_toolkit/mingle/card_type_property_definition.rb +26 -0
  17. data/lib/macro_development_toolkit/mingle/project.rb +80 -0
  18. data/lib/macro_development_toolkit/mingle/project_variable.rb +23 -0
  19. data/lib/macro_development_toolkit/mingle/property_definition.rb +94 -0
  20. data/lib/macro_development_toolkit/mingle/property_value.rb +68 -0
  21. data/lib/macro_development_toolkit/mingle/user.rb +29 -0
  22. data/lib/macro_development_toolkit/mingle_model_loader.rb +169 -0
  23. data/tasks/test.rake +15 -0
  24. data/test/fixtures/sample/card_types.yml +29 -0
  25. data/test/fixtures/sample/card_types_property_definitions.yml +81 -0
  26. data/test/fixtures/sample/project_variables.yml +5 -0
  27. data/test/fixtures/sample/projects.yml +4 -0
  28. data/test/fixtures/sample/property_definitions.yml +41 -0
  29. data/test/fixtures/sample/users.yml +16 -0
  30. data/test/integration/integration_test_helper.rb +20 -0
  31. data/test/integration/rest_loader.rb +302 -0
  32. data/test/integration/rest_loader_test.rb +86 -0
  33. data/test/unit/fixture_loader.rb +180 -0
  34. data/test/unit/fixture_loader_test.rb +46 -0
  35. data/test/unit/unit_test_helper.rb +13 -0
  36. metadata +110 -0
data/tasks/test.rake ADDED
@@ -0,0 +1,15 @@
1
+ namespace :test do |ns|
2
+
3
+ Rake::TestTask.new(:units) do |t|
4
+ t.libs << "test/unit"
5
+ t.pattern = 'test/unit/*_test.rb'
6
+ t.verbose = true
7
+ end
8
+
9
+ Rake::TestTask.new(:integration) do |t|
10
+ t.libs << "test/integration"
11
+ t.pattern = 'test/integration/*_test.rb'
12
+ t.verbose = true
13
+ end
14
+
15
+ end
@@ -0,0 +1,29 @@
1
+ ---
2
+ - name: Story
3
+ position: 3
4
+ color: 'ff3300'
5
+ id: 339
6
+ - name: Defect
7
+ position: 5
8
+ color: '000599'
9
+ id: 340
10
+ - name: Task
11
+ position: 4
12
+ color: 'ffe066'
13
+ id: 341
14
+ - name: Sprint
15
+ position: 2
16
+ color: 'ff0db6'
17
+ id: 342
18
+ - name: Release
19
+ position: 1
20
+ color: '990024'
21
+ id: 343
22
+ - name: Feature
23
+ position: 6
24
+ color: '296600'
25
+ id: 344
26
+ - name: Epic Story
27
+ position: 7
28
+ color: '00d91d'
29
+ id: 345
@@ -0,0 +1,81 @@
1
+ ---
2
+ - card_type_id: 342
3
+ position: 3
4
+ property_definition_id: 1145
5
+ id: 2015
6
+ - card_type_id: 341
7
+ position: 1
8
+ property_definition_id: 1145
9
+ id: 2016
10
+ - card_type_id: 341
11
+ position: 2
12
+ property_definition_id: 1146
13
+ id: 2017
14
+ - card_type_id: 341
15
+ position: 3
16
+ property_definition_id: 1144
17
+ id: 2018
18
+ - card_type_id: 341
19
+ position: 4
20
+ property_definition_id: 1141
21
+ id: 2019
22
+ - card_type_id: 341
23
+ position: 6
24
+ property_definition_id: 1149
25
+ id: 2021
26
+ - card_type_id: 341
27
+ position: 7
28
+ property_definition_id: 1147
29
+ id: 2022
30
+ - card_type_id: 340
31
+ position: 1
32
+ property_definition_id: 1145
33
+ id: 2023
34
+ - card_type_id: 340
35
+ position: 2
36
+ property_definition_id: 1146
37
+ id: 2024
38
+ - card_type_id: 340
39
+ position: 3
40
+ property_definition_id: 1144
41
+ id: 2025
42
+ - card_type_id: 340
43
+ position: 5
44
+ property_definition_id: 1141
45
+ id: 2027
46
+ - card_type_id: 340
47
+ position: 7
48
+ property_definition_id: 1149
49
+ id: 2029
50
+ - card_type_id: 339
51
+ position: 1
52
+ property_definition_id: 1148
53
+ id: 2033
54
+ - card_type_id: 339
55
+ position: 4
56
+ property_definition_id: 1145
57
+ id: 2036
58
+ - card_type_id: 339
59
+ position: 5
60
+ property_definition_id: 1146
61
+ id: 2037
62
+ - card_type_id: 339
63
+ position: 6
64
+ property_definition_id: 1141
65
+ id: 2038
66
+ - card_type_id: 339
67
+ position: 7
68
+ property_definition_id: 1140
69
+ id: 2039
70
+ - card_type_id: 339
71
+ position: 8
72
+ property_definition_id: 1149
73
+ id: 2040
74
+ - card_type_id: 339
75
+ position: 12
76
+ property_definition_id: 1142
77
+ id: 2044
78
+ - card_type_id: 339
79
+ position: 13
80
+ property_definition_id: 1143
81
+ id: 2045
@@ -0,0 +1,5 @@
1
+ ---
2
+ - name: Current Release
3
+ value: 'Release 3'
4
+ - name: Current Sprint
5
+ value: 'Iteration 7'
@@ -0,0 +1,4 @@
1
+ ---
2
+ - identifier: scrum_template_2_1
3
+ id: 78
4
+ name: scrum_template(2.1)
@@ -0,0 +1,41 @@
1
+ ---
2
+ - id: 1140
3
+ name: Status
4
+ description: ""
5
+ type_description: Managed text list
6
+ - id: 1141
7
+ name: Priority
8
+ description: ""
9
+ type_description: Managed text list
10
+ - id: 1142
11
+ name: Development Started On
12
+ description: ""
13
+ type_description: Managed text list
14
+ - id: 1143
15
+ description: ""
16
+ name: Development Completed On
17
+ type_description: Date
18
+ - id: 1144
19
+ description: ""
20
+ name: Added On
21
+ type_description: Date
22
+ - id: 1145
23
+ description: ""
24
+ name: Estimate - Planning
25
+ type_description: Formula
26
+ - id: 1146
27
+ description: ""
28
+ name: Risk
29
+ type_description: Managed text list
30
+ - id: 1147
31
+ description: ""
32
+ name: Task Estimate
33
+ type_description: Managed text list
34
+ - id: 1148
35
+ description:
36
+ name: Estimate - Task
37
+ type_description: Managed text list
38
+ - id: 1149
39
+ description: Individual assigned to a work item
40
+ name: Owner
41
+ type_description: User
@@ -0,0 +1,16 @@
1
+ ---
2
+ - project_id: 78
3
+ login: groucho
4
+ name: Groucho Marx
5
+ version_control_user_name: gmarx
6
+ email: gmarx@example.com
7
+ - project_id: 78
8
+ login: harpo
9
+ name: Harpo Marx
10
+ version_control_user_name:
11
+ email: kmarx@example.com
12
+ - project_id: 78
13
+ login: karl
14
+ name: Karl Marx
15
+ version_control_user_name: kmarx
16
+ email: kmarx@example.com
@@ -0,0 +1,20 @@
1
+ #Copyright 2008 ThoughtWorks, Inc. All rights reserved.
2
+
3
+ require 'test/unit'
4
+ require 'macro_development_toolkit'
5
+ require File.dirname(__FILE__) + '/rest_loader'
6
+
7
+ class Test::Unit::TestCase
8
+
9
+ def project(name)
10
+ @project ||= RESTfulLoaders::ProjectLoader.new(name, nil, self).project
11
+ end
12
+
13
+ def errors
14
+ @errors ||= []
15
+ end
16
+
17
+ def alert(message)
18
+ errors << message
19
+ end
20
+ end
@@ -0,0 +1,302 @@
1
+ #Copyright 2008 ThoughtWorks, Inc. All rights reserved.
2
+
3
+ module RESTfulLoaders
4
+ class RemoteError < StandardError
5
+ def self.parse(response_body)
6
+ Hash.from_xml(response_body)['hash'].delete("message")
7
+ end
8
+ end
9
+
10
+ class Base
11
+ def initialize(project_name)
12
+ @xml_model = if (String === project_name)
13
+ @project_name = project_name
14
+ OpenStruct.new(Hash.from_xml(get(project_name)))
15
+ else
16
+ project_name
17
+ end
18
+ raise "No such project resource available! #{@project_name}" unless (@xml_model && @xml_model.to_s != '')
19
+ end
20
+
21
+ def get(url)
22
+ url = URI.parse(url) unless url.respond_to?(:path)
23
+ get_request = Net::HTTP::Get.new(url.request_uri)
24
+ get_request.basic_auth(url.user, url.password)
25
+ response = Net::HTTP.start(url.host, url.port) { |http| http.request(get_request) }
26
+ if response.code.to_s != "200"
27
+ raise RemoteError, RemoteError.parse(response.body)
28
+ end
29
+ response.body
30
+ end
31
+
32
+ def load_all_card_types_from_xml
33
+ extract_array_of 'card_types', :from => @xml_model.project
34
+ end
35
+
36
+ def load_all_property_definitions_from_xml
37
+ extract_array_of 'property_definitions', :from => @xml_model.project
38
+ end
39
+
40
+ def load_all_card_types_property_definitions_from_xml
41
+ load_all_card_types_from_xml.collect do |card_type_xml|
42
+ card_types_property_definitions = OpenStruct.new(card_type_xml).card_types_property_definitions
43
+ [card_types_property_definitions['card_type_property_definition']].flatten if card_types_property_definitions
44
+ end.compact.flatten
45
+ end
46
+
47
+ def card_types_property_definitions_by_card_type_id_loader(card_type_id)
48
+ LoadCardTypesPropertyDefinitionsByCardTypeId.new(card_type_id, @xml_model)
49
+ end
50
+
51
+ def card_types_property_definitions_by_property_definition_id_loader(property_definition_id)
52
+ LoadCardTypesPropertyDefinitionsByPropertyDefinitionId.new(property_definition_id, @xml_model)
53
+ end
54
+
55
+ def values_by_property_definition_id_loader(property_definition_id)
56
+ LoadValuesByPropertyDefinitionId.new(property_definition_id, @xml_model)
57
+ end
58
+
59
+ def card_type_by_id_loader(card_type_id)
60
+ LoadCardTypeById.new(card_type_id, @xml_model)
61
+ end
62
+
63
+ def property_definition_by_id_loader(property_definition_id)
64
+ LoadPropertyDefinitionById.new(property_definition_id, @xml_model)
65
+ end
66
+
67
+ def card_types_by_project_id_loader
68
+ LoadCardTypesByProjectId.new(@xml_model)
69
+ end
70
+
71
+ def property_definitions_by_project_id_loader
72
+ LoadPropertyDefinitionsByProjectId.new(@xml_model)
73
+ end
74
+
75
+ def team_by_project_id_loader
76
+ LoadTeamByProjectId.new(@xml_model)
77
+ end
78
+
79
+ def project_variables_by_project_id_loader
80
+ LoadProjectVariablesByProjectId.new(@xml_model)
81
+ end
82
+
83
+ def extract_array_of(container_key, options)
84
+ contents_hash = options[:from]
85
+ container = contents_hash[container_key]
86
+ container ? [container[container_key.singularize]].flatten : []
87
+ end
88
+ end
89
+
90
+ class ProjectLoader < Base
91
+ class MqlExecutionDelegate < SimpleDelegator
92
+ def initialize(delegate, mql_executor)
93
+ @mql_executor = mql_executor
94
+ __setobj__(delegate)
95
+ end
96
+
97
+ def execute_mql(mql)
98
+ begin
99
+ @mql_executor.execute_mql(mql, self)
100
+ rescue => e
101
+ __getobj__.send(:add_alert, e.message)
102
+ []
103
+ end
104
+ end
105
+
106
+ def format_number_with_project_precision(number)
107
+ begin
108
+ @mql_executor.format_number_with_project_precision(number, self)
109
+ rescue => e
110
+ __getobj__.send(:add_alert, e.message)
111
+ end
112
+ end
113
+
114
+ def format_date_with_project_date_format(date)
115
+ begin
116
+ @mql_executor.format_date_with_project_date_format(date, self)
117
+ rescue => e
118
+ __getobj__.send(:add_alert, e.message)
119
+ end
120
+ end
121
+ end
122
+
123
+ def initialize(project_name, macro_context=nil, alert_receiver=nil)
124
+ super(project_name)
125
+ @macro_context = macro_context
126
+ @alert_receiver = alert_receiver
127
+ end
128
+
129
+ def load_project_from_xml
130
+ @xml_model.project
131
+ end
132
+
133
+ def project
134
+ project = MqlExecutionDelegate.new(Mingle::Project.new(OpenStruct.new(load_project_from_xml), :alert_receiver => @alert_receiver), self)
135
+ project.card_types_loader = card_types_by_project_id_loader
136
+ project.property_definitions_loader = property_definitions_by_project_id_loader
137
+ project.team_loader = team_by_project_id_loader
138
+ project.project_variables_loader = project_variables_by_project_id_loader
139
+ project
140
+ end
141
+
142
+ def execute_mql(mql, project)
143
+ from_xml_data(
144
+ Hash.from_xml(
145
+ get(
146
+ build_request_url(:path => "/projects/#{project.identifier}/cards/execute_mql.xml", :query => "mql=#{mql}"))))
147
+ end
148
+
149
+ def format_number_with_project_precision(number, project)
150
+ from_xml_data(
151
+ Hash.from_xml(
152
+ get(
153
+ build_request_url(:path => "/projects/#{project.identifier}/cards/format_number_to_project_precision.xml", :query => "number=#{number}"))))
154
+ end
155
+
156
+ def format_date_with_project_date_format(date, project)
157
+ from_xml_data(
158
+ Hash.from_xml(
159
+ get(
160
+ build_request_url(:path => "/projects/#{project.identifier}/cards/format_string_to_date_format.xml", :query => "date=#{date}"))))
161
+ end
162
+
163
+ def build_request_url(params)
164
+ url = URI.parse(@project_name)
165
+ request_class = if url.scheme == 'http'
166
+ URI::HTTP
167
+ elsif
168
+ URI::HTTPS
169
+ else
170
+ raise "Unknown protocol used to access project resource #{@project_name}. Supported protocols are HTTP & HTTPS"
171
+ end
172
+ default_params = {:userinfo => "#{url.user}:#{url.password}", :host => url.host, :port => url.port.to_s}
173
+ request_class.build2(default_params.merge(params))
174
+ end
175
+
176
+ def from_xml_data(data)
177
+ if data.is_a?(Hash) && data.keys.size == 1
178
+ from_xml_data(data.values.first)
179
+ else
180
+ data
181
+ end
182
+ end
183
+
184
+ end
185
+
186
+ class LoadCardTypesByProjectId < Base
187
+ def load
188
+ load_all_card_types_from_xml.collect do |ct|
189
+ card_type = Mingle::CardType.new(OpenStruct.new(ct))
190
+ card_type.card_types_property_definitions_loader = card_types_property_definitions_by_card_type_id_loader(ct['id'])
191
+ card_type
192
+ end.sort_by(&:position)
193
+ end
194
+ end
195
+
196
+ class LoadPropertyDefinitionsByProjectId < Base
197
+ def load
198
+ load_all_property_definitions_from_xml.collect do |pd|
199
+ property_definition = Mingle::PropertyDefinition.new(OpenStruct.new(pd))
200
+ property_definition.card_types_property_definitions_loader = card_types_property_definitions_by_property_definition_id_loader(pd['id'])
201
+ property_definition.values_loader = values_by_property_definition_id_loader(pd['id'])
202
+ property_definition
203
+ end
204
+ end
205
+ end
206
+
207
+ class LoadCardTypesPropertyDefinitionsByCardTypeId < Base
208
+ def initialize(card_type_id, fixture_file_name)
209
+ super(fixture_file_name)
210
+ @card_type_id = card_type_id
211
+ end
212
+
213
+ def load
214
+ load_all_card_types_property_definitions_from_xml.collect do |ctpd|
215
+ next unless ctpd['card_type_id'] == @card_type_id
216
+
217
+ card_type_property_definition = Mingle::CardTypePropertyDefinition.new(OpenStruct.new(ctpd))
218
+ card_type_property_definition.card_type_loader = card_type_by_id_loader(ctpd['card_type_id'])
219
+ card_type_property_definition.property_definition_loader = property_definition_by_id_loader(ctpd['property_definition_id'])
220
+ card_type_property_definition
221
+ end.compact.sort_by(&:position).compact
222
+ end
223
+
224
+ end
225
+
226
+ class LoadCardTypesPropertyDefinitionsByPropertyDefinitionId < Base
227
+ def initialize(property_definition_id, fixture_file_name)
228
+ super(fixture_file_name)
229
+ @property_definition_id = property_definition_id
230
+ end
231
+
232
+ def load
233
+ load_all_card_types_property_definitions_from_xml.collect do |ctpd|
234
+ next unless ctpd['property_definition_id'] == @property_definition_id
235
+
236
+ card_type_property_definition = Mingle::CardTypePropertyDefinition.new(OpenStruct.new(ctpd))
237
+ card_type_property_definition.card_type_loader = card_type_by_id_loader(ctpd['card_type_id'])
238
+ card_type_property_definition.property_definition_loader = property_definition_by_id_loader(ctpd['property_definition_id'])
239
+ card_type_property_definition
240
+ end.compact.sort_by(&:position).compact
241
+ end
242
+
243
+ end
244
+
245
+ class LoadCardTypeById < Base
246
+ def initialize(card_type_id, fixture_file_name)
247
+ super(fixture_file_name)
248
+ @card_type_id = card_type_id
249
+ end
250
+
251
+ def load
252
+ yaml = load_all_card_types_from_xml.detect { |ct| ct['id'] == @card_type_id }
253
+ ct = Mingle::CardType.new(OpenStruct.new(yaml))
254
+ ct.card_types_property_definitions_loader = card_types_property_definitions_by_card_type_id_loader(yaml['id'])
255
+ ct
256
+ end
257
+ end
258
+
259
+ class LoadPropertyDefinitionById < Base
260
+ def initialize(property_definition_id, fixture_file_name)
261
+ super(fixture_file_name)
262
+ @property_definition_id = property_definition_id
263
+ end
264
+
265
+ def load
266
+ yaml = load_all_property_definitions_from_xml.detect { |pd| pd['id'] == @property_definition_id }
267
+ pd = Mingle::PropertyDefinition.new(OpenStruct.new(yaml))
268
+ pd.card_types_property_definitions_loader = card_types_property_definitions_by_property_definition_id_loader(yaml['id'])
269
+ pd.values_loader = values_by_property_definition_id_loader(yaml['id'])
270
+ pd
271
+ end
272
+ end
273
+
274
+ class LoadValuesByPropertyDefinitionId < Base
275
+ def initialize(property_definition_id, fixture_file_name)
276
+ super(fixture_file_name)
277
+ @property_definition_id = property_definition_id
278
+ end
279
+
280
+ def load
281
+ property_definition = load_all_property_definitions_from_xml.detect { |property_definition_xml| property_definition_xml['id'] == @property_definition_id }
282
+ return unless property_definition
283
+ extract_array_of('values', :from => property_definition).collect do |property_value_xml|
284
+ property_value = Mingle::PropertyValue.new(OpenStruct.new(property_value_xml))
285
+ property_value.property_definition_loader = property_definition_by_id_loader(property_value_xml['property_definition_id'])
286
+ property_value
287
+ end.compact
288
+ end
289
+ end
290
+
291
+ class LoadTeamByProjectId < Base
292
+ def load
293
+ extract_array_of('users', :from => @xml_model.project).collect { |user| Mingle::User.new(OpenStruct.new(user)) }
294
+ end
295
+ end
296
+
297
+ class LoadProjectVariablesByProjectId < Base
298
+ def load
299
+ extract_array_of('project_variables', :from => @xml_model.project).collect { |pv| Mingle::ProjectVariable.new(OpenStruct.new(pv)) }
300
+ end
301
+ end
302
+ end