mingle-macro-development-toolkit 1.0

Sign up to get free protection for your applications and to get access to all the features.
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