mingle-macro-development-toolkit 1.3.2 → 1.3.3
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +8 -0
- data/{README.rdoc → README.txt} +8 -6
- data/Rakefile +19 -27
- data/bin/new_mingle_macro +10 -4
- data/example/integration_test_helper.rb +5 -5
- data/example/unit_test_helper.rb +4 -13
- data/lib/macro_development_toolkit.rb +1 -1
- data/lib/macro_development_toolkit/mingle/project.rb +4 -4
- data/lib/macro_development_toolkit/mingle_model_loader.rb +100 -136
- data/test/fixtures/sample/card_types.yml +9 -1
- data/test/fixtures/sample/project_variables.yml +3 -1
- data/test/fixtures/sample/property_definitions.yml +13 -3
- data/test/integration/integration_test_helper.rb +20 -8
- data/test/integration/rest_loader.rb +172 -250
- data/test/unit/fixture_loader.rb +73 -145
- data/test/unit/fixture_loader_test.rb +11 -11
- data/test/unit/unit_test_helper.rb +6 -5
- metadata +29 -15
@@ -3,27 +3,35 @@
|
|
3
3
|
position: 3
|
4
4
|
color: 'ff3300'
|
5
5
|
id: 339
|
6
|
+
project_id: 78
|
6
7
|
- name: Defect
|
7
8
|
position: 5
|
8
9
|
color: '000599'
|
9
10
|
id: 340
|
11
|
+
project_id: 78
|
10
12
|
- name: Task
|
11
13
|
position: 4
|
12
14
|
color: 'ffe066'
|
13
15
|
id: 341
|
16
|
+
project_id: 78
|
14
17
|
- name: Sprint
|
15
18
|
position: 2
|
16
19
|
color: 'ff0db6'
|
17
20
|
id: 342
|
21
|
+
project_id: 78
|
18
22
|
- name: Release
|
19
23
|
position: 1
|
20
24
|
color: '990024'
|
21
25
|
id: 343
|
26
|
+
project_id: 78
|
22
27
|
- name: Feature
|
23
28
|
position: 6
|
24
29
|
color: '296600'
|
25
30
|
id: 344
|
31
|
+
project_id: 78
|
26
32
|
- name: Epic Story
|
27
33
|
position: 7
|
28
34
|
color: '00d91d'
|
29
|
-
id: 345
|
35
|
+
id: 345
|
36
|
+
project_id: 78
|
37
|
+
|
@@ -3,39 +3,49 @@
|
|
3
3
|
name: Status
|
4
4
|
description: ""
|
5
5
|
type_description: Managed text list
|
6
|
+
project_id: 78
|
6
7
|
- id: 1141
|
7
8
|
name: Priority
|
8
9
|
description: ""
|
9
10
|
type_description: Managed text list
|
11
|
+
project_id: 78
|
10
12
|
- id: 1142
|
11
13
|
name: Development Started On
|
12
14
|
description: ""
|
13
15
|
type_description: Managed text list
|
16
|
+
project_id: 78
|
14
17
|
- id: 1143
|
15
18
|
description: ""
|
16
19
|
name: Development Completed On
|
17
20
|
type_description: Date
|
21
|
+
project_id: 78
|
18
22
|
- id: 1144
|
19
23
|
description: ""
|
20
24
|
name: Added On
|
21
25
|
type_description: Date
|
26
|
+
project_id: 78
|
22
27
|
- id: 1145
|
23
28
|
description: ""
|
24
29
|
name: Estimate - Planning
|
25
|
-
type_description: Formula
|
30
|
+
type_description: Formula
|
31
|
+
project_id: 78
|
26
32
|
- id: 1146
|
27
33
|
description: ""
|
28
34
|
name: Risk
|
29
|
-
type_description: Managed text list
|
35
|
+
type_description: Managed text list
|
36
|
+
project_id: 78
|
30
37
|
- id: 1147
|
31
38
|
description: ""
|
32
39
|
name: Task Estimate
|
33
40
|
type_description: Managed text list
|
41
|
+
project_id: 78
|
34
42
|
- id: 1148
|
35
43
|
description:
|
36
44
|
name: Estimate - Task
|
37
45
|
type_description: Managed text list
|
46
|
+
project_id: 78
|
38
47
|
- id: 1149
|
39
48
|
description: Individual assigned to a work item
|
40
49
|
name: Owner
|
41
|
-
type_description: User
|
50
|
+
type_description: User
|
51
|
+
project_id: 78
|
@@ -2,20 +2,32 @@
|
|
2
2
|
|
3
3
|
require 'delegate'
|
4
4
|
require 'test/unit'
|
5
|
+
require 'net/http'
|
5
6
|
require 'macro_development_toolkit'
|
6
7
|
require File.dirname(__FILE__) + '/rest_loader'
|
7
8
|
|
8
9
|
class Test::Unit::TestCase
|
9
|
-
|
10
|
-
def project(
|
11
|
-
@
|
12
|
-
|
13
|
-
|
10
|
+
|
11
|
+
def project(resource)
|
12
|
+
@projects ||= {}
|
13
|
+
@projects[resource] ||= load_project_resource(resource)
|
14
|
+
end
|
15
|
+
|
16
|
+
def projects(*resources)
|
17
|
+
resources.collect {|resource| project(resource)}
|
18
|
+
end
|
19
|
+
|
14
20
|
def errors
|
15
21
|
@errors ||= []
|
16
|
-
end
|
22
|
+
end
|
17
23
|
|
18
24
|
def alert(message)
|
19
25
|
errors << message
|
20
|
-
end
|
21
|
-
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def load_project_resource(resource)
|
31
|
+
RESTfulLoaders::ProjectLoader.new(resource, self).project
|
32
|
+
end
|
33
|
+
end
|
@@ -1,311 +1,233 @@
|
|
1
1
|
#Copyright 2010 ThoughtWorks, Inc. All rights reserved.
|
2
2
|
|
3
3
|
module RESTfulLoaders
|
4
|
+
|
4
5
|
class RemoteError < StandardError
|
5
6
|
def self.parse(response_body)
|
6
7
|
Hash.from_xml(response_body)['errors'].delete("error")
|
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
8
|
end
|
9
|
+
end
|
20
10
|
|
21
|
-
|
22
|
-
|
11
|
+
module LoaderHelper
|
12
|
+
|
13
|
+
def extract(key, container)
|
14
|
+
container[key] ? container[key][key.singularize] : []
|
15
|
+
end
|
16
|
+
|
17
|
+
def get(resource)
|
18
|
+
url = URI.parse(resource)
|
23
19
|
get_request = Net::HTTP::Get.new(url.request_uri)
|
24
20
|
get_request.basic_auth(url.user, url.password)
|
25
21
|
response = Net::HTTP.start(url.host, url.port) { |http| http.request(get_request) }
|
26
|
-
if response.code.to_s != "200"
|
22
|
+
if response.code.to_s != "200"
|
27
23
|
raise RemoteError, RemoteError.parse(response.body)
|
28
24
|
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.compact
|
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)
|
25
|
+
Hash.from_xml(response.body)
|
53
26
|
end
|
27
|
+
end
|
54
28
|
|
55
|
-
|
56
|
-
|
57
|
-
end
|
29
|
+
class MqlExecutor < SimpleDelegator
|
30
|
+
include LoaderHelper
|
58
31
|
|
59
|
-
def
|
60
|
-
|
32
|
+
def initialize(resource, error_handler, delegator)
|
33
|
+
super(delegator)
|
34
|
+
@uri = URI.parse(resource)
|
35
|
+
@error_handler = error_handler
|
36
|
+
@version = /(\/api\/([^\/]*))\//.match(@uri.request_uri)[2]
|
61
37
|
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
38
|
|
71
|
-
def
|
72
|
-
|
73
|
-
|
39
|
+
def execute_mql(mql)
|
40
|
+
from_xml_data(get(url_for(:action => "execute_mql", :query => "mql=#{mql}")))
|
41
|
+
rescue => e
|
42
|
+
@error_handler.alert(e.message)
|
43
|
+
[]
|
44
|
+
end
|
74
45
|
|
75
|
-
def
|
76
|
-
|
46
|
+
def can_be_cached?(mql)
|
47
|
+
from_xml_data(get(url_for(:action => "can_be_cached", :query => "mql=#{mql}")))
|
48
|
+
rescue => e
|
49
|
+
@error_handler.alert(e.message)
|
50
|
+
[]
|
77
51
|
end
|
78
52
|
|
79
|
-
def
|
80
|
-
|
53
|
+
def format_number_with_project_precision(number)
|
54
|
+
from_xml_data(get(url_for(:action => "format_number_to_project_precision", :query => "number=#{number}")))
|
55
|
+
rescue => e
|
56
|
+
@error_handler.alert(e.message)
|
57
|
+
[]
|
81
58
|
end
|
82
59
|
|
83
|
-
def
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
60
|
+
def format_date_with_project_date_format(date)
|
61
|
+
from_xml_data(get(url_for(:action => "format_string_to_date_format", :query => "date=#{date}")))
|
62
|
+
rescue => e
|
63
|
+
@error_handler.alert(e.message)
|
64
|
+
[]
|
65
|
+
end
|
89
66
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
__getobj__.send(:add_alert, e.message)
|
101
|
-
[]
|
102
|
-
end
|
103
|
-
|
104
|
-
def can_be_cached?(mql)
|
105
|
-
@mql_executor.can_be_cached?(mql, self)
|
106
|
-
rescue => e
|
107
|
-
__getobj__.send(:add_alert, e.message)
|
108
|
-
[]
|
109
|
-
end
|
110
|
-
|
111
|
-
def format_number_with_project_precision(number)
|
112
|
-
@mql_executor.format_number_with_project_precision(number, self)
|
113
|
-
rescue => e
|
114
|
-
__getobj__.send(:add_alert, e.message)
|
115
|
-
end
|
116
|
-
|
117
|
-
def format_date_with_project_date_format(date)
|
118
|
-
@mql_executor.format_date_with_project_date_format(date, self)
|
119
|
-
rescue => e
|
120
|
-
__getobj__.send(:add_alert, e.message)
|
67
|
+
def url_for(params)
|
68
|
+
relative_path = URI.escape("/api/#{@version}/projects/#{identifier}/cards/#{params[:action]}.xml?#{params[:query]}")
|
69
|
+
@uri.merge(relative_path).to_s
|
70
|
+
end
|
71
|
+
|
72
|
+
def from_xml_data(data)
|
73
|
+
if data.is_a?(Hash) && data.keys.size == 1
|
74
|
+
from_xml_data(data.values.first)
|
75
|
+
else
|
76
|
+
data
|
121
77
|
end
|
122
|
-
end
|
78
|
+
end
|
79
|
+
end
|
123
80
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
@
|
129
|
-
@
|
81
|
+
class ProjectLoader
|
82
|
+
include LoaderHelper
|
83
|
+
|
84
|
+
def initialize(resource, error_handler)
|
85
|
+
@resource = resource
|
86
|
+
@error_handler = error_handler
|
130
87
|
end
|
131
|
-
|
132
|
-
def load_project_from_xml
|
133
|
-
@xml_model.project
|
134
|
-
end
|
135
88
|
|
136
89
|
def project
|
137
|
-
project
|
138
|
-
project.card_types_loader = card_types_by_project_id_loader
|
139
|
-
project.property_definitions_loader = property_definitions_by_project_id_loader
|
140
|
-
project.team_loader = team_by_project_id_loader
|
141
|
-
project.project_variables_loader = project_variables_by_project_id_loader
|
142
|
-
project
|
90
|
+
@project ||= load
|
143
91
|
end
|
144
92
|
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
93
|
+
private
|
94
|
+
|
95
|
+
def load
|
96
|
+
proj = OpenStruct.new(get(@resource)).project
|
97
|
+
project = MqlExecutor.new(@resource, @error_handler, Mingle::Project.new(OpenStruct.new(proj), nil))
|
98
|
+
project.card_types_loader = CardTypesLoader.new(proj)
|
99
|
+
project.property_definitions_loader = PropertyDefinitionsLoader.new(proj)
|
100
|
+
project.team_loader = TeamLoader.new(proj)
|
101
|
+
project.project_variables_loader = ProjectVariablesLoader.new(proj)
|
102
|
+
project
|
150
103
|
end
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
class CardTypesLoader
|
108
|
+
include LoaderHelper
|
151
109
|
|
152
|
-
def
|
153
|
-
|
154
|
-
Hash.from_xml(
|
155
|
-
get(
|
156
|
-
build_request_url(:path => url_for("can_be_cached"), :query => "mql=#{mql}"))))
|
110
|
+
def initialize(project)
|
111
|
+
@project = project
|
157
112
|
end
|
158
113
|
|
159
|
-
def
|
160
|
-
|
161
|
-
Hash.from_xml(
|
162
|
-
get(
|
163
|
-
build_request_url(:path => url_for("format_number_to_project_precision"), :query => "number=#{number}"))))
|
114
|
+
def load
|
115
|
+
extract('card_types', @project).collect { |ct| CardTypeLoader.new(@project, ct) }.sort_by { |loader| loader.card_type.position }
|
164
116
|
end
|
117
|
+
end
|
118
|
+
|
119
|
+
class CardTypeLoader
|
165
120
|
|
166
|
-
def
|
167
|
-
|
168
|
-
|
169
|
-
get(
|
170
|
-
build_request_url(:path => url_for("format_string_to_date_format"), :query => "date=#{date}"))))
|
121
|
+
def initialize(project, ct)
|
122
|
+
@project = project
|
123
|
+
@ct = ct
|
171
124
|
end
|
172
125
|
|
173
|
-
def
|
174
|
-
|
126
|
+
def card_type
|
127
|
+
@card_type ||= load
|
175
128
|
end
|
176
129
|
|
177
|
-
def build_request_url(params)
|
178
|
-
url = URI.parse(@project_name)
|
179
|
-
request_class = if url.scheme == 'http'
|
180
|
-
URI::HTTP
|
181
|
-
elsif
|
182
|
-
URI::HTTPS
|
183
|
-
else
|
184
|
-
raise "Unknown protocol used to access project resource #{@project_name}. Supported protocols are HTTP & HTTPS"
|
185
|
-
end
|
186
|
-
default_params = {:userinfo => "#{url.user}:#{url.password}", :host => url.host, :port => url.port.to_s}
|
187
|
-
request_class.build2(default_params.merge(params))
|
188
|
-
end
|
189
|
-
|
190
|
-
def from_xml_data(data)
|
191
|
-
if data.is_a?(Hash) && data.keys.size == 1
|
192
|
-
from_xml_data(data.values.first)
|
193
|
-
else
|
194
|
-
data
|
195
|
-
end
|
196
|
-
end
|
197
|
-
end
|
198
|
-
|
199
|
-
class LoadCardTypesByProjectId < Base
|
200
130
|
def load
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
end.sort_by(&:position)
|
206
|
-
end
|
131
|
+
card_type = Mingle::CardType.new(OpenStruct.new(@ct))
|
132
|
+
card_type.card_types_property_definitions_loader = CardTypesPropertyDefinitionsLoader.new(@project, 'card_type_id' => @ct['id'])
|
133
|
+
card_type
|
134
|
+
end
|
207
135
|
end
|
208
136
|
|
209
|
-
class
|
137
|
+
class PropertyDefinitionsLoader
|
138
|
+
include LoaderHelper
|
139
|
+
|
140
|
+
def initialize(project)
|
141
|
+
@project = project
|
142
|
+
end
|
143
|
+
|
210
144
|
def load
|
211
|
-
|
212
|
-
|
213
|
-
property_definition.card_types_property_definitions_loader = card_types_property_definitions_by_property_definition_id_loader(pd['id'])
|
214
|
-
property_definition.values_loader = values_by_property_definition_id_loader(pd['id'])
|
215
|
-
property_definition
|
216
|
-
end
|
217
|
-
end
|
145
|
+
extract('property_definitions', @project).collect { |pd| PropertyDefinitionLoader.new(@project, pd) }
|
146
|
+
end
|
218
147
|
end
|
219
|
-
|
220
|
-
class LoadCardTypesPropertyDefinitionsByCardTypeId < Base
|
221
|
-
def initialize(card_type_id, fixture_file_name)
|
222
|
-
super(fixture_file_name)
|
223
|
-
@card_type_id = card_type_id
|
224
|
-
end
|
225
148
|
|
149
|
+
class PropertyDefinitionLoader
|
150
|
+
def initialize(project, pd)
|
151
|
+
@project = project
|
152
|
+
@pd = pd
|
153
|
+
end
|
154
|
+
|
155
|
+
def property_definition
|
156
|
+
@property_definition ||= load
|
157
|
+
end
|
158
|
+
|
226
159
|
def load
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
card_type_property_definition
|
233
|
-
end.compact.sort_by(&:position).compact
|
234
|
-
end
|
235
|
-
end
|
236
|
-
|
237
|
-
class LoadCardTypesPropertyDefinitionsByPropertyDefinitionId < Base
|
238
|
-
def initialize(property_definition_id, fixture_file_name)
|
239
|
-
super(fixture_file_name)
|
240
|
-
@property_definition_id = property_definition_id
|
241
|
-
end
|
242
|
-
|
243
|
-
def load
|
244
|
-
load_all_card_types_property_definitions_from_xml.collect do |ctpd|
|
245
|
-
next unless ctpd['property_definition_id'] == @property_definition_id
|
246
|
-
card_type_property_definition = Mingle::CardTypePropertyDefinition.new(OpenStruct.new(ctpd))
|
247
|
-
card_type_property_definition.card_type_loader = card_type_by_id_loader(ctpd['card_type_id'])
|
248
|
-
card_type_property_definition.property_definition_loader = property_definition_by_id_loader(ctpd['property_definition_id'])
|
249
|
-
card_type_property_definition
|
250
|
-
end.compact.sort_by(&:position).compact
|
251
|
-
end
|
160
|
+
@property_definition = Mingle::PropertyDefinition.new(OpenStruct.new(@pd))
|
161
|
+
@property_definition.card_types_property_definitions_loader = CardTypesPropertyDefinitionsLoader.new(@project, 'property_definition_id' => @pd['id'])
|
162
|
+
@property_definition.values_loader = PropertyValuesLoader.new(@pd)
|
163
|
+
@property_definition
|
164
|
+
end
|
252
165
|
end
|
253
|
-
|
254
|
-
class
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
def load
|
261
|
-
yaml = load_all_card_types_from_xml.detect { |ct| ct['id'] == @card_type_id }
|
262
|
-
ct = Mingle::CardType.new(OpenStruct.new(yaml))
|
263
|
-
ct.card_types_property_definitions_loader = card_types_property_definitions_by_card_type_id_loader(yaml['id'])
|
264
|
-
ct
|
265
|
-
end
|
266
|
-
end
|
267
|
-
|
268
|
-
class LoadPropertyDefinitionById < Base
|
269
|
-
def initialize(property_definition_id, fixture_file_name)
|
270
|
-
super(fixture_file_name)
|
271
|
-
@property_definition_id = property_definition_id
|
166
|
+
|
167
|
+
class PropertyValuesLoader
|
168
|
+
include LoaderHelper
|
169
|
+
|
170
|
+
def initialize(property_definition)
|
171
|
+
@property_definition = property_definition
|
272
172
|
end
|
273
|
-
|
173
|
+
|
274
174
|
def load
|
275
|
-
|
276
|
-
pd = Mingle::PropertyDefinition.new(OpenStruct.new(yaml))
|
277
|
-
pd.card_types_property_definitions_loader = card_types_property_definitions_by_property_definition_id_loader(yaml['id'])
|
278
|
-
pd.values_loader = values_by_property_definition_id_loader(yaml['id'])
|
279
|
-
pd
|
280
|
-
end
|
281
|
-
end
|
282
|
-
|
283
|
-
class LoadValuesByPropertyDefinitionId < Base
|
284
|
-
def initialize(property_definition_id, fixture_file_name)
|
285
|
-
super(fixture_file_name)
|
286
|
-
@property_definition_id = property_definition_id
|
175
|
+
extract('values', @property_definition).collect {|value| Mingle::PropertyValue.new(OpenStruct.new(value))}
|
287
176
|
end
|
177
|
+
|
178
|
+
end
|
288
179
|
|
180
|
+
class CardTypesPropertyDefinitionsLoader
|
181
|
+
include LoaderHelper
|
182
|
+
|
183
|
+
def initialize(project, params)
|
184
|
+
@project = project
|
185
|
+
@params = params
|
186
|
+
end
|
187
|
+
|
289
188
|
def load
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
189
|
+
mappings = extract('card_types', @project).collect do |card_type|
|
190
|
+
mapping = card_type['card_types_property_definitions'].values
|
191
|
+
end.flatten
|
192
|
+
|
193
|
+
pds = extract('property_definitions', @project)
|
194
|
+
cts = extract('card_types', @project)
|
195
|
+
mappings.collect do |mapping|
|
196
|
+
if (match?(mapping))
|
197
|
+
pd = pds.find { |pd| pd['id'] && pd['id'] == mapping['property_definition_id'] }
|
198
|
+
ct = cts.find { |ct| ct['id'] == mapping['card_type_id'] }
|
199
|
+
OpenStruct.new(:card_type => CardTypeLoader.new(@project, ct).load, :property_definition => PropertyDefinitionLoader.new(@project, pd).load)
|
200
|
+
end
|
296
201
|
end.compact
|
297
|
-
end
|
202
|
+
end
|
203
|
+
|
204
|
+
private
|
205
|
+
def match?(mapping)
|
206
|
+
@params.all? { |key, value| value == mapping[key] }
|
207
|
+
end
|
298
208
|
end
|
299
209
|
|
300
|
-
class
|
210
|
+
class TeamLoader
|
211
|
+
include LoaderHelper
|
212
|
+
|
213
|
+
def initialize(project)
|
214
|
+
@project = project
|
215
|
+
end
|
216
|
+
|
301
217
|
def load
|
302
|
-
|
303
|
-
end
|
218
|
+
(extract('users', @project) || []).collect{ |user| Mingle::User(OpenStruct.new(user))}
|
219
|
+
end
|
304
220
|
end
|
305
|
-
|
306
|
-
class
|
221
|
+
|
222
|
+
class ProjectVariablesLoader
|
223
|
+
include LoaderHelper
|
224
|
+
|
225
|
+
def initialize(project)
|
226
|
+
@project = project
|
227
|
+
end
|
228
|
+
|
307
229
|
def load
|
308
|
-
|
309
|
-
end
|
230
|
+
extract('project_variables', @project).collect {|pv| Mingle::ProjectVariable.new(OpenStruct.new(pv))}
|
231
|
+
end
|
310
232
|
end
|
311
|
-
end
|
233
|
+
end
|