mingle-macro-development-toolkit 1.3.3 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/{History.txt → History} +11 -0
- data/{LICENSE.txt → LICENSE} +0 -0
- data/{README.txt → README} +4 -2
- data/Rakefile +15 -40
- data/bin/new_mingle_macro +25 -137
- data/lib/mingle_macro_development_toolkit.rb +4 -0
- data/lib/mingle_macro_development_toolkit/loaders.rb +5 -0
- data/lib/mingle_macro_development_toolkit/loaders/fixture_loaders/base.rb +19 -0
- data/lib/mingle_macro_development_toolkit/loaders/fixture_loaders/card_type_loader.rb +15 -0
- data/lib/mingle_macro_development_toolkit/loaders/fixture_loaders/card_types_loader.rb +10 -0
- data/lib/mingle_macro_development_toolkit/loaders/fixture_loaders/card_types_property_definitions_loader.rb +12 -0
- data/lib/mingle_macro_development_toolkit/loaders/fixture_loaders/project_loader.rb +16 -0
- data/lib/mingle_macro_development_toolkit/loaders/fixture_loaders/project_variables_loader.rb +10 -0
- data/lib/mingle_macro_development_toolkit/loaders/fixture_loaders/property_definition_loader.rb +16 -0
- data/lib/mingle_macro_development_toolkit/loaders/fixture_loaders/property_definitions_loader.rb +10 -0
- data/lib/mingle_macro_development_toolkit/loaders/fixture_loaders/property_values_loader.rb +10 -0
- data/lib/mingle_macro_development_toolkit/loaders/fixture_loaders/team_loader.rb +10 -0
- data/lib/mingle_macro_development_toolkit/loaders/restful_loaders/base.rb +85 -0
- data/lib/mingle_macro_development_toolkit/loaders/restful_loaders/card_type_loader.rb +19 -0
- data/lib/mingle_macro_development_toolkit/loaders/restful_loaders/card_types_loader.rb +14 -0
- data/lib/mingle_macro_development_toolkit/loaders/restful_loaders/card_types_property_definitions_loader.rb +32 -0
- data/lib/mingle_macro_development_toolkit/loaders/restful_loaders/project_loader.rb +26 -0
- data/lib/mingle_macro_development_toolkit/loaders/restful_loaders/project_variables_loader.rb +14 -0
- data/lib/mingle_macro_development_toolkit/loaders/restful_loaders/property_definition_loader.rb +20 -0
- data/lib/mingle_macro_development_toolkit/loaders/restful_loaders/property_definitions_loader.rb +14 -0
- data/lib/mingle_macro_development_toolkit/loaders/restful_loaders/property_values_loader.rb +14 -0
- data/lib/mingle_macro_development_toolkit/loaders/restful_loaders/team_loader.rb +14 -0
- data/templates/Rakefile +23 -0
- data/{getting_started.txt → templates/getting_started.txt} +93 -93
- data/templates/init.rb +1 -0
- data/{example → templates/lib}/macro.rb +0 -0
- data/{test/project_data → templates/test/data/exports}/macro_toolkit_test_template.mingle +0 -0
- data/{test/fixtures/sample → templates/test/data/fixtures}/card_types.yml +0 -0
- data/{test/fixtures/sample → templates/test/data/fixtures}/project_variables.yml +0 -0
- data/{test/fixtures/sample → templates/test/data/fixtures}/projects.yml +0 -0
- data/{test/fixtures/sample → templates/test/data/fixtures}/property_definitions.yml +0 -0
- data/{test/fixtures/sample → templates/test/data/fixtures}/property_type_mappings.yml +0 -0
- data/{test/fixtures/sample → templates/test/data/fixtures}/property_values.yml +0 -0
- data/{test/fixtures/sample → templates/test/data/fixtures}/users.yml +0 -0
- data/{example → templates/test/integration}/integration_test.rb +4 -5
- data/templates/test/integration/integration_test_helper.rb +23 -0
- data/templates/test/test_helper.rb +4 -0
- data/{example → templates/test/unit}/unit_test.rb +7 -7
- data/templates/test/unit/unit_test_helper.rb +13 -0
- data/test/data/exports/macro_toolkit_test_template.mingle +0 -0
- data/test/data/fixtures/card_types.yml +37 -0
- data/test/data/fixtures/project_variables.yml +7 -0
- data/test/data/fixtures/projects.yml +4 -0
- data/test/data/fixtures/property_definitions.yml +51 -0
- data/test/data/fixtures/property_type_mappings.yml +81 -0
- data/test/data/fixtures/property_values.yml +19 -0
- data/test/data/fixtures/users.yml +16 -0
- data/test/integration/integration_test_helper.rb +3 -13
- data/test/integration/{rest_loader_test.rb → restful_loaders_test.rb} +4 -4
- data/test/test_helper.rb +2 -0
- data/test/unit/{fixture_loader_test.rb → fixture_loaders_test.rb} +2 -2
- data/test/unit/unit_test_helper.rb +7 -6
- metadata +101 -100
- data/example/Rakefile +0 -6
- data/example/deploy.rake +0 -10
- data/example/init.rb +0 -10
- data/example/integration_test_helper.rb +0 -31
- data/example/unit_test_helper.rb +0 -13
- data/lib/macro_development_toolkit.rb +0 -29
- data/lib/macro_development_toolkit/mingle/card_type.rb +0 -41
- data/lib/macro_development_toolkit/mingle/card_type_property_definition.rb +0 -26
- data/lib/macro_development_toolkit/mingle/project.rb +0 -168
- data/lib/macro_development_toolkit/mingle/project_variable.rb +0 -24
- data/lib/macro_development_toolkit/mingle/property_definition.rb +0 -105
- data/lib/macro_development_toolkit/mingle/property_value.rb +0 -67
- data/lib/macro_development_toolkit/mingle/user.rb +0 -33
- data/lib/macro_development_toolkit/mingle_model_loader.rb +0 -151
- data/tasks/test.rake +0 -16
- data/test/integration/rest_loader.rb +0 -233
- data/test/unit/fixture_loader.rb +0 -109
@@ -0,0 +1,19 @@
|
|
1
|
+
#Copyright 2012 ThoughtWorks, Inc. All rights reserved.
|
2
|
+
module RESTfulLoaders
|
3
|
+
class CardTypeLoader
|
4
|
+
def initialize(project, ct)
|
5
|
+
@project = project
|
6
|
+
@ct = ct
|
7
|
+
end
|
8
|
+
|
9
|
+
def card_type
|
10
|
+
@card_type ||= load
|
11
|
+
end
|
12
|
+
|
13
|
+
def load
|
14
|
+
card_type = Mingle::CardType.new(OpenStruct.new(@ct))
|
15
|
+
card_type.card_types_property_definitions_loader = CardTypesPropertyDefinitionsLoader.new(@project, 'card_type_id' => @ct['id'])
|
16
|
+
card_type
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
#Copyright 2012 ThoughtWorks, Inc. All rights reserved.
|
2
|
+
module RESTfulLoaders
|
3
|
+
class CardTypesLoader
|
4
|
+
include LoaderHelper
|
5
|
+
|
6
|
+
def initialize(project)
|
7
|
+
@project = project
|
8
|
+
end
|
9
|
+
|
10
|
+
def load
|
11
|
+
extract('card_types', @project).collect { |ct| CardTypeLoader.new(@project, ct) }.sort_by { |loader| loader.card_type.position }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
#Copyright 2012 ThoughtWorks, Inc. All rights reserved.
|
2
|
+
module RESTfulLoaders
|
3
|
+
class CardTypesPropertyDefinitionsLoader
|
4
|
+
include LoaderHelper
|
5
|
+
|
6
|
+
def initialize(project, params)
|
7
|
+
@project = project
|
8
|
+
@params = params
|
9
|
+
end
|
10
|
+
|
11
|
+
def load
|
12
|
+
mappings = extract('card_types', @project).collect do |card_type|
|
13
|
+
mapping = card_type['card_types_property_definitions'].values
|
14
|
+
end.flatten
|
15
|
+
|
16
|
+
pds = extract('property_definitions', @project)
|
17
|
+
cts = extract('card_types', @project)
|
18
|
+
mappings.collect do |mapping|
|
19
|
+
if (match?(mapping))
|
20
|
+
pd = pds.find { |pd| pd['id'] && pd['id'] == mapping['property_definition_id'] }
|
21
|
+
ct = cts.find { |ct| ct['id'] == mapping['card_type_id'] }
|
22
|
+
OpenStruct.new(:card_type => CardTypeLoader.new(@project, ct).load, :property_definition => PropertyDefinitionLoader.new(@project, pd).load)
|
23
|
+
end
|
24
|
+
end.compact
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
def match?(mapping)
|
29
|
+
@params.all? { |key, value| value == mapping[key] }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
#Copyright 2012 ThoughtWorks, Inc. All rights reserved.
|
2
|
+
module RESTfulLoaders
|
3
|
+
class ProjectLoader
|
4
|
+
include LoaderHelper
|
5
|
+
|
6
|
+
def initialize(resource, error_handler)
|
7
|
+
@resource = resource
|
8
|
+
@error_handler = error_handler
|
9
|
+
end
|
10
|
+
|
11
|
+
def project
|
12
|
+
@project ||= load
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
def load
|
17
|
+
proj = OpenStruct.new(get(@resource)).project
|
18
|
+
project = MqlExecutor.new(@resource, @error_handler, Mingle::Project.new(OpenStruct.new(proj), nil))
|
19
|
+
project.card_types_loader = CardTypesLoader.new(proj)
|
20
|
+
project.property_definitions_loader = PropertyDefinitionsLoader.new(proj)
|
21
|
+
project.team_loader = TeamLoader.new(proj)
|
22
|
+
project.project_variables_loader = ProjectVariablesLoader.new(proj)
|
23
|
+
project
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
#Copyright 2012 ThoughtWorks, Inc. All rights reserved.
|
2
|
+
module RESTfulLoaders
|
3
|
+
class ProjectVariablesLoader
|
4
|
+
include LoaderHelper
|
5
|
+
|
6
|
+
def initialize(project)
|
7
|
+
@project = project
|
8
|
+
end
|
9
|
+
|
10
|
+
def load
|
11
|
+
extract('project_variables', @project).collect {|pv| Mingle::ProjectVariable.new(OpenStruct.new(pv))}
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/lib/mingle_macro_development_toolkit/loaders/restful_loaders/property_definition_loader.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
#Copyright 2012 ThoughtWorks, Inc. All rights reserved.
|
2
|
+
module RESTfulLoaders
|
3
|
+
class PropertyDefinitionLoader
|
4
|
+
def initialize(project, pd)
|
5
|
+
@project = project
|
6
|
+
@pd = pd
|
7
|
+
end
|
8
|
+
|
9
|
+
def property_definition
|
10
|
+
@property_definition ||= load
|
11
|
+
end
|
12
|
+
|
13
|
+
def load
|
14
|
+
@property_definition = Mingle::PropertyDefinition.new(OpenStruct.new(@pd))
|
15
|
+
@property_definition.card_types_property_definitions_loader = CardTypesPropertyDefinitionsLoader.new(@project, 'property_definition_id' => @pd['id'])
|
16
|
+
@property_definition.values_loader = PropertyValuesLoader.new(@pd)
|
17
|
+
@property_definition
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/mingle_macro_development_toolkit/loaders/restful_loaders/property_definitions_loader.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#Copyright 2012 ThoughtWorks, Inc. All rights reserved.
|
2
|
+
module RESTfulLoaders
|
3
|
+
class PropertyDefinitionsLoader
|
4
|
+
include LoaderHelper
|
5
|
+
|
6
|
+
def initialize(project)
|
7
|
+
@project = project
|
8
|
+
end
|
9
|
+
|
10
|
+
def load
|
11
|
+
extract('property_definitions', @project).collect { |pd| PropertyDefinitionLoader.new(@project, pd) }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
#Copyright 2012 ThoughtWorks, Inc. All rights reserved.
|
2
|
+
module RESTfulLoaders
|
3
|
+
class PropertyValuesLoader
|
4
|
+
include LoaderHelper
|
5
|
+
|
6
|
+
def initialize(property_definition)
|
7
|
+
@property_definition = property_definition
|
8
|
+
end
|
9
|
+
|
10
|
+
def load
|
11
|
+
extract('values', @property_definition).collect {|value| Mingle::PropertyValue.new(OpenStruct.new(value))}
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
#Copyright 2012 ThoughtWorks, Inc. All rights reserved.
|
2
|
+
module RESTfulLoaders
|
3
|
+
class TeamLoader
|
4
|
+
include LoaderHelper
|
5
|
+
|
6
|
+
def initialize(project)
|
7
|
+
@project = project
|
8
|
+
end
|
9
|
+
|
10
|
+
def load
|
11
|
+
(extract('users', @project) || []).collect{ |user| Mingle::User(OpenStruct.new(user))}
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/templates/Rakefile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
namespace :test do
|
2
|
+
Rake::TestTask.new(:units) do |t|
|
3
|
+
t.libs << "test/unit"
|
4
|
+
t.pattern = 'test/unit/*_test.rb'
|
5
|
+
t.verbose = true
|
6
|
+
end
|
7
|
+
|
8
|
+
Rake::TestTask.new(:integration) do |t|
|
9
|
+
t.libs << "test/integration"
|
10
|
+
t.pattern = 'test/integration/*_test.rb'
|
11
|
+
t.verbose = true
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
namespace :macro do |ns|
|
16
|
+
task :deploy, :mingle_location do |task|
|
17
|
+
macro_folder = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
18
|
+
mingle_location = task[:mingle_location] || ENV["MINGLE_LOCATION"]
|
19
|
+
mingle_plugins_folder = File.join(mingle_location, 'vendor', 'plugins')
|
20
|
+
FileUtils.cp_r(macro_folder, mingle_plugins_folder)
|
21
|
+
puts "#{macro_folder} successfully copied over to #{mingle_plugins_folder}. Restart the Mingle server to start using the macro."
|
22
|
+
end
|
23
|
+
end
|
@@ -1,73 +1,73 @@
|
|
1
1
|
A background on macros in Mingle
|
2
2
|
---------------------------------
|
3
|
-
|
3
|
+
|
4
4
|
Macros are a special kind of markup in the Mingle wiki. A macro is identified by the following markup generic syntax
|
5
|
-
|
5
|
+
|
6
6
|
{{
|
7
7
|
macro_name
|
8
8
|
parameter1: value1
|
9
9
|
parameter2: value2
|
10
10
|
...
|
11
11
|
}}
|
12
|
-
|
12
|
+
|
13
13
|
The markup has to be valid YAML syntax. Specifically, this means that the markup is sensitive to spacing and indentation. For more help around YAML and what constitutes valid YAML markup, you can refer to http://yaml.org/spec/current.html
|
14
|
-
|
14
|
+
|
15
15
|
Specific examples of this include the pre-written macros, such as the value, average & table macros and the macros for all the charts.
|
16
|
-
|
16
|
+
|
17
17
|
When Mingle encounters a macro while rendering the markup, it delegates handling of the macro to a custom class behind the scenes that is registered to handle it. e.g., if Mingle encountered the following markup,
|
18
|
-
|
18
|
+
|
19
19
|
{{
|
20
20
|
average
|
21
21
|
query: SELECT 'Pre-release Estimate' WHERE Release = (current release)
|
22
22
|
}}
|
23
|
-
|
23
|
+
|
24
24
|
it would parse the content between the opening and closing double braces, and identify the following.
|
25
|
-
|
25
|
+
|
26
26
|
Macro Name: average
|
27
27
|
Macro Parameters: {query => "SELECT 'Pre-release Estimate' WHERE Release = (current release)"}
|
28
|
-
|
28
|
+
|
29
29
|
It then scans a registry of known macros, for a class that is configured to handle a macro with name average. This is the AverageMacro class. You can find this class under the vendor/plugins/average_macro directory of your installation of Mingle.
|
30
|
-
|
30
|
+
|
31
31
|
class AverageMacro
|
32
|
-
|
32
|
+
|
33
33
|
def initialize(parameters, project, current_user)
|
34
34
|
@parameters = parameters
|
35
35
|
@project = project
|
36
36
|
raise "Parameter <b>query</b> is required" unless query
|
37
37
|
end
|
38
|
-
|
38
|
+
|
39
39
|
def execute
|
40
40
|
first_values = @project.execute_mql(query).collect { |record| record.values.first }
|
41
41
|
data = first_values.reject(&:blank?).collect(&:to_f)
|
42
42
|
data.empty? ? 'no values found' : @project.format_number_with_project_precision(data.sum.to_f/data.size.to_f)
|
43
43
|
end
|
44
|
-
|
44
|
+
|
45
45
|
private
|
46
|
-
|
46
|
+
|
47
47
|
def query
|
48
48
|
@parameters['query']
|
49
49
|
end
|
50
50
|
end
|
51
|
-
|
51
|
+
|
52
52
|
All data that is required to execute the macro is injected into the macro through the constructor. The parameters that are interpreted from the markup are passed in as a hash.
|
53
|
-
|
53
|
+
|
54
54
|
The second parameter is special and could be either a single project or multiple projects, based on the parameter name used in the macro. In versions 1.3 and above of the gem, a comma separated list of project identifiers when supplied as the value of a macro parameter called project-group will be interpretted as multiple projects. This functionality can be used to build macros that report across multiple projects. The project or projects that are passed in are a lightweight representation of the project in the Mingle model which is documented at http://mingle-macros.rubyforge.org/rdoc
|
55
|
-
|
55
|
+
|
56
56
|
The execute method uses the MQL execution facility that the project class provides, to execute the MQL string that is passed into through the parameters hash. It then formats the results to be a number, and provides that result of the execute command.
|
57
|
-
|
57
|
+
|
58
58
|
For more help on what constitutes valid MQL, you can refer to our help documentation at http://studios.thoughtworks.com/mingle-agile-project-management/3.1/help/mql_reference.html
|
59
|
-
|
59
|
+
|
60
60
|
To see an example of how to use the gem to report across multiple projects, refer to the cross_project_rollup macro which ships with versions of Mingle 3.0 and higher.
|
61
|
-
|
61
|
+
|
62
62
|
Writing your own macro
|
63
63
|
----------------------
|
64
|
-
|
64
|
+
|
65
65
|
To write your own macro, you can start with the generated skeleton for the macro. To generate your own macro skeleton, use the new_mingle_macro script that installed with your gem.
|
66
|
-
|
66
|
+
|
67
67
|
% new_mingle_macro your_new_macro
|
68
|
-
|
68
|
+
|
69
69
|
This should generate a folder structure as follows
|
70
|
-
|
70
|
+
|
71
71
|
your_new_macro
|
72
72
|
|
|
73
73
|
|----lib
|
@@ -90,67 +90,67 @@ your_new_macro
|
|
90
90
|
|---- integration
|
91
91
|
|
|
92
92
|
your_new_macro_integration_test.rb, ...
|
93
|
-
|
94
|
-
|
93
|
+
|
94
|
+
|
95
95
|
The lib directory contains the actual macro, and the test folders give you the option to run the tests either against local YAML based fixtures, or using REST to test against a deployed mingle instance.
|
96
|
-
|
96
|
+
|
97
97
|
When this macro is deployed to Mingle, all wiki markup of the form
|
98
|
-
|
98
|
+
|
99
99
|
{{
|
100
100
|
your_new_macro
|
101
101
|
parameter1: value1
|
102
102
|
parameter2: <some_mql_statement>
|
103
103
|
...
|
104
104
|
}}
|
105
|
-
|
105
|
+
|
106
106
|
will be parsed as YAML and handling will be delegated to an instance of your macro class, YourNewMacro. The parameters will be parsed into a Ruby hash, of the following structure:
|
107
|
-
|
107
|
+
|
108
108
|
{'parameter1' => 'value1', 'parameter2' => '<some_mql_snippet>'}
|
109
|
-
|
109
|
+
|
110
110
|
and will be passed into the constructor of the class. Additionally, by default, an instance of Mingle::Project that represents the project that this macro is being rendered on will also be provided. You can specify a different project to be passed in by setting a project parameter in your macro. Also you can specify a group of projects to be provided to your macro by setting a project-group parameter. In this case the projects will be an array of Mingle::Project. You will also need to implement a class method called supports_project_group? which returns true if you want your macro to be used with project-group, for example:
|
111
|
-
|
111
|
+
|
112
112
|
def self.supports_project_group?
|
113
113
|
true
|
114
114
|
end
|
115
|
-
|
115
|
+
|
116
116
|
As an example of what you can do with this toolkit is the following macro, which uses the Google Charting API to render a Google-o-meter style chart to represent work completed in a fuel gauge style meter.
|
117
|
-
|
117
|
+
|
118
118
|
class WorkGauge
|
119
|
-
|
119
|
+
|
120
120
|
def initialize(parameters, project, current_user)
|
121
121
|
@parameters = parameters
|
122
122
|
@project = project
|
123
123
|
@current_user = current_user
|
124
124
|
end
|
125
|
-
|
125
|
+
|
126
126
|
def execute
|
127
127
|
completed_work = @project.execute_mql(@parameters['completed_work']).first.values.sum
|
128
128
|
total_work = @project.execute_mql(@parameters['total_work']).first.values.sum
|
129
129
|
completion_percentage = (completed_work.to_f / total_work.to_f) * 100
|
130
|
-
|
130
|
+
|
131
131
|
%Q{ <img src='http://chart.apis.google.com/chart?cht=gom&chs=350&chd=t:#{completion_percentage}&chds=0,100' /> }
|
132
132
|
end
|
133
|
-
|
133
|
+
|
134
134
|
end
|
135
|
-
|
135
|
+
|
136
136
|
The execute method can return any valid HTML or Javascript content. The ability to emit Javascript content, specifically, can only be used on Mingle version 2.3 and above.
|
137
|
-
|
137
|
+
|
138
138
|
The following example demonstrates how to use the Javascript emission functionality to embed a Google Maps map.
|
139
|
-
|
139
|
+
|
140
140
|
Note: For this example to work, you will have to supply your own Google Maps API key as a part of the script tag that loads the Google API.
|
141
|
-
|
141
|
+
|
142
142
|
class GoogleMap
|
143
|
-
|
143
|
+
|
144
144
|
def initialize(parameters, project, current_user)
|
145
145
|
@latitude = parameters['latitude'] || 39.55
|
146
146
|
@longitude = parameters['longitude'] || 116.25
|
147
147
|
@zoom_level = parameters['zoom_level'] || 8
|
148
148
|
end
|
149
|
-
|
149
|
+
|
150
150
|
def execute
|
151
151
|
<<-HTML
|
152
152
|
h2. Google Maps JavaScript API Example: Simple Map
|
153
|
-
|
153
|
+
|
154
154
|
<div id="map_canvas" style="width: 600px; height: 400px"></div>
|
155
155
|
<script src="http://maps.google.com/maps?file=api" type="text/javascript"></script>
|
156
156
|
<script type="text/javascript">
|
@@ -164,99 +164,99 @@ class GoogleMap
|
|
164
164
|
</script>
|
165
165
|
HTML
|
166
166
|
end
|
167
|
-
|
167
|
+
|
168
168
|
def can_be_cached?
|
169
169
|
false # if appropriate, switch to true once you move your macro to production
|
170
170
|
end
|
171
|
-
|
171
|
+
|
172
172
|
end
|
173
|
-
|
173
|
+
|
174
174
|
Long running or integration macros which run on server will result in long page render times. We recommend these sort
|
175
175
|
of macros use Javascript (or JSONP) to offload work to the browser.
|
176
|
-
|
176
|
+
|
177
177
|
You can find both simpler and more complex examples in the vendor/plugins/sample_macros directory.
|
178
|
-
|
178
|
+
|
179
179
|
Tip: If you wish to use JQuery, you will have to add a call to JQuery.noConflict() to prevent conflicts with the Prototype library that ships with Mingle. You can refer to the JQuery documentation here (http://docs.jquery.com/Core/jQuery.noConflict) to see the correct order in which to load the libraries and other conditions to be followed in order to make this work properly. If you do not do this, you can still use JQuery without using the $ shorthand for the jQuery function.
|
180
|
-
|
181
|
-
|
180
|
+
|
181
|
+
|
182
182
|
Unit testing your macro
|
183
183
|
------------------------
|
184
|
-
|
184
|
+
|
185
185
|
The macro development toolkit comes with a built in unit testing framework, that borrows the familiar idea of YAML based fixtures. The one small difference we have made to it is that each project that you are providing fixtures for gets its own subfolder within the fixtures directory. We hope that this makes it easier to identify relationships between the objects set up in the YAML files.
|
186
|
-
|
186
|
+
|
187
187
|
If you are using the skeleton project set up by the new_mingle_macro script, the fixtures directory provides you with a sample project fixture. The data in that should give you a sense of the relationships between the various objects.
|
188
|
-
|
188
|
+
|
189
189
|
The skeleton project also has a sample unit test set up for you, which uses the sample fixture data. Note the helper method project(...) which takes the name of a sample project to load information from. This method loads a web of objects from the directory named the same as the argument, in the fixtures folder.
|
190
|
-
|
190
|
+
|
191
191
|
class YourNewMacroTest < Test::Unit::TestCase
|
192
|
-
|
192
|
+
|
193
193
|
FIXTURE = 'sample'
|
194
|
-
|
194
|
+
|
195
195
|
def test_macro_contents
|
196
196
|
macro = YourNewMacro.new(nil, project(FIXTURE), nil)
|
197
197
|
result = macro.execute
|
198
198
|
assert result
|
199
199
|
end
|
200
|
-
|
200
|
+
|
201
201
|
end
|
202
|
-
|
202
|
+
|
203
203
|
Once loaded, you can test things like parameter checking and validations using this style of test. While you cannot execute MQL in this style of test, you can use your favorite mocking library to test how results get handled.
|
204
|
-
|
204
|
+
|
205
205
|
You can see examples of unit tests in the average macro that is packaged with Mingle in the vendor/plugins/average_macro directory.
|
206
|
-
|
206
|
+
|
207
207
|
To run your unit tests, run
|
208
|
-
|
208
|
+
|
209
209
|
% rake test:units
|
210
|
-
|
210
|
+
|
211
211
|
from the root of your custom macro
|
212
|
-
|
212
|
+
|
213
213
|
Integration testing your macro
|
214
214
|
-------------------------------
|
215
|
-
|
215
|
+
|
216
216
|
########################################################NOTE###############################################################
|
217
217
|
# #
|
218
218
|
# IN ORDER TO RUN THE INTEGRATION TESTS, YOU WILL NEED TO TURN ON BASIC AUTHENTICATION FOR THE MINGLE 3.1 SERVER THAT YOU #
|
219
219
|
# ARE GOING TO BE TESTING AGAINST. #
|
220
220
|
# #
|
221
221
|
#########################################################NOTE##############################################################
|
222
|
-
|
222
|
+
|
223
223
|
The integration tests look very similar to the unit tests, the primary difference being that they actually communicate with a deployed Mingle instance over REST. The helper methods populate a web of objects representing a project, that look and behave in a manner identical to how they will in production.
|
224
|
-
|
224
|
+
|
225
225
|
The one significant difference about these style of tests is that you can actually execute MQL remotely on the Mingle instance instead of mocking out the MQL execution. This will give you a good idea of what results and errors you may expect to see in production, without having to deploy the macro every time.
|
226
|
-
|
226
|
+
|
227
227
|
There are tradeoffs, of course. Some of these are as follows
|
228
|
-
|
228
|
+
|
229
229
|
* Should you decide to add these tests to a Continuous Integration build, like Cruise, you will hit the production Mingle sever with every test run. Not hot.
|
230
|
-
|
230
|
+
|
231
231
|
* Given that each test makes a call to a production server, there is no guarantee (unless you set it up in such a way) - that multiple calls to fetch the same resource will give the same result.
|
232
|
-
|
232
|
+
|
233
233
|
* Also, while not slow, these tests are definitely much slower than the unit tests. So while it is certainly possible to write only integration tests, we would encourage as a judicious mix of both styles.
|
234
|
-
|
234
|
+
|
235
235
|
class YourNewMacroIntegrationTest < Test::Unit::TestCase
|
236
|
-
|
236
|
+
|
237
237
|
PROJECT_RESOURCE = 'http://yourname:password@your.mingle.server:port/lightweight_project/project_identifier.xml'
|
238
|
-
|
238
|
+
|
239
239
|
def test_macro_contents
|
240
240
|
macro = YourNewMacro.new(nil, project(PROJECT_RESOURCE), nil)
|
241
241
|
result = macro.execute
|
242
242
|
assert result
|
243
243
|
end
|
244
|
-
|
244
|
+
|
245
245
|
end
|
246
|
-
|
246
|
+
|
247
247
|
The skeleton project also has a sample integration test set up for you, which points to a bogus Mingle server, and uses bad credentials. Replace this resource URL with the URL for a deployed instance within your organization. The helper method project(...) which takes the resource URL, loads the data from the XML data obtained from the live instance of Mingle.
|
248
|
-
|
248
|
+
|
249
249
|
You can see examples of integration tests in the average macro that is packaged with Mingle in the vendor/plugins/average_macro directory. These tests run against a standard template that ships with Mingle 3.1, and so you should be able to run them within your organization too, without a problem.
|
250
|
-
|
250
|
+
|
251
251
|
To run your integration tests, run
|
252
|
-
|
252
|
+
|
253
253
|
% rake test:integration
|
254
|
-
|
254
|
+
|
255
255
|
from the root of your custom macro
|
256
|
-
|
256
|
+
|
257
257
|
Deploying your macro
|
258
258
|
---------------------
|
259
|
-
|
259
|
+
|
260
260
|
####################################################### CAUTION ###########################################################
|
261
261
|
# #
|
262
262
|
# BEFORE YOU DEPLOY ANYTHING TO YOUR MINGLE INSTANCE, PLEASE MAKE SURE THAT IT IS COMPLETELY SAFE. THIS IS ESPECIALLY #
|
@@ -272,31 +272,31 @@ Deploying your macro
|
|
272
272
|
# * OTHER DATABASE ACTIVITY, SUCH AS TRANSACTION COMMITS, SHOULD BE MONITORED AND AVOIDED #
|
273
273
|
# #
|
274
274
|
######################################################## CAUTION ##########################################################
|
275
|
-
|
276
|
-
|
275
|
+
|
276
|
+
|
277
277
|
To deploy your macro to a locally deployed instance of Mingle, which is running at mingle_root
|
278
|
-
|
278
|
+
|
279
279
|
% rake macro:deploy MINGLE_LOCATION=/path/to/mingle_root
|
280
|
-
|
280
|
+
|
281
281
|
where /path/to/mingle_root is the location where Mingle 3.1 is installed.
|
282
|
-
|
282
|
+
|
283
283
|
* On Windows, this is the location that the installer installed Mingle at
|
284
284
|
* On OSX, this will be within the app bundle, at <mingle_application_bundle>/Contents/Resources/app
|
285
285
|
* On *NIX, this is the expanded archive
|
286
|
-
|
286
|
+
|
287
287
|
The entire macro folder and its contents will be copied over into the vendor/plugins directory of that Mingle installation. Once deployed, the server will need to be restarted in order for the macro to become available for use.
|
288
|
-
|
288
|
+
|
289
289
|
Alternatively, you could also copy the folder by hand into the same location.
|
290
|
-
|
290
|
+
|
291
291
|
########################################################NOTE################################################################
|
292
292
|
# #
|
293
293
|
# LEGAL NOTICES AND INFORMATION #
|
294
294
|
# #
|
295
295
|
#########################################################NOTE###############################################################
|
296
|
-
|
296
|
+
|
297
297
|
This Getting Started file and the mingle-macro-development-toolkit-1.3.1.gem are owned exclusively by ThoughtWorks, Inc.,
|
298
298
|
and ThoughtWorks reserves all rights therein.
|
299
|
-
|
299
|
+
|
300
300
|
We believe that it is a sound practice from legal, business and software development perspectives to always provide copyright
|
301
301
|
information and license information with any software that you make available to others. We have provided this information
|
302
302
|
for the Mingle Macro Development Toolkit in the LICENSE.txt file distributed with the Toolkit. We encourage you to use that
|