saxon-rb 0.4.0-java → 0.5.0-java
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.circleci/config.yml +429 -42
- data/Gemfile +2 -2
- data/README.md +317 -10
- data/Rakefile +237 -7
- data/lib/net/sf/saxon/Saxon-HE/{9.9.1-5/Saxon-HE-9.9.1-5.jar → 9.9.1-6/Saxon-HE-9.9.1-6.jar} +0 -0
- data/lib/saxon-rb.rb +1 -0
- data/lib/{saxon_jars.rb → saxon-rb_jars.rb} +2 -2
- data/lib/saxon.rb +13 -0
- data/lib/saxon/axis_iterator.rb +8 -1
- data/lib/saxon/configuration.rb +1 -0
- data/lib/saxon/item_type.rb +12 -17
- data/lib/saxon/item_type/lexical_string_conversion.rb +136 -58
- data/lib/saxon/item_type/value_to_ruby.rb +13 -0
- data/lib/saxon/loader.rb +4 -1
- data/lib/saxon/nokogiri.rb +78 -0
- data/lib/saxon/occurrence_indicator.rb +32 -3
- data/lib/saxon/processor.rb +32 -1
- data/lib/saxon/qname.rb +37 -2
- data/lib/saxon/s9api.rb +5 -0
- data/lib/saxon/sequence_type.rb +131 -0
- data/lib/saxon/source.rb +207 -71
- data/lib/saxon/version.rb +1 -1
- data/lib/saxon/xdm.rb +7 -0
- data/lib/saxon/xdm/array.rb +16 -0
- data/lib/saxon/xdm/atomic_value.rb +7 -1
- data/lib/saxon/xdm/empty_sequence.rb +13 -0
- data/lib/saxon/xdm/external_object.rb +1 -0
- data/lib/saxon/xdm/function_item.rb +1 -0
- data/lib/saxon/xdm/item.rb +7 -0
- data/lib/saxon/xdm/map.rb +38 -0
- data/lib/saxon/xdm/node.rb +19 -1
- data/lib/saxon/xdm/sequence_like.rb +15 -0
- data/lib/saxon/xdm/value.rb +21 -5
- data/lib/saxon/xpath.rb +9 -0
- data/lib/saxon/xpath/compiler.rb +36 -1
- data/lib/saxon/xpath/executable.rb +53 -28
- data/lib/saxon/xpath/static_context.rb +19 -39
- data/lib/saxon/xpath/variable_declaration.rb +16 -49
- data/lib/saxon/xslt.rb +12 -0
- data/lib/saxon/xslt/compiler.rb +75 -6
- data/lib/saxon/xslt/evaluation_context.rb +19 -3
- data/lib/saxon/xslt/executable.rb +204 -14
- data/saxon-rb.gemspec +1 -1
- metadata +9 -7
- data/saxon.gemspec +0 -30
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,15 +1,37 @@
|
|
1
|
-
# Saxon
|
1
|
+
# saxon-rb – An idiomatic Ruby wrapper for Saxon
|
2
2
|
|
3
|
-
|
3
|
+
`saxon-rb` aims to be a fully-featured idiomatic wrapper for the Saxon XML
|
4
|
+
processing and transformation library. Built after several years experience with
|
5
|
+
writing, using, and maintaining the `saxon-xslt` XSLT-focussed wrapper,
|
6
|
+
`saxon-rb` aims to keep that ease-of-use for those most common cases, while
|
7
|
+
making the less-common cases easy as well.
|
4
8
|
|
5
|
-
|
9
|
+
Saxon provides a massive amount of valuable functionality beyond simple XSLT
|
10
|
+
compilation and invocation, and a lot of that is very hard to use from Ruby. The
|
11
|
+
facilities provided by it, and by the XSLT 2 and 3 specs, rely heavily on the
|
12
|
+
XDM values and types system, which `saxon-rb` makes easy to work with.
|
13
|
+
|
14
|
+
Parameter creation and passing, are richer and more expressive; results from
|
15
|
+
XSLT, or XPath, that aren't just result trees can be worked with directly in
|
16
|
+
Ruby.
|
17
|
+
|
18
|
+
[![Gem Version](https://badge.fury.io/rb/saxon.svg)](http://badge.fury.io/rb/saxon)
|
19
|
+
[![Code Climate](https://codeclimate.com/github/fidothe/saxon-rb/badges/gpa.svg)](https://codeclimate.com/github/fidothe/saxon-rb)
|
20
|
+
[![Test Coverage](https://codeclimate.com/github/fidothe/saxon-rb/badges/coverage.svg)](https://codeclimate.com/github/fidothe/saxon-rb/coverage)
|
21
|
+
[![CircleCI](https://circleci.com/gh/fidothe/saxon-rb.svg?style=svg)](https://circleci.com/gh/fidothe/saxon-rb)
|
22
|
+
|
23
|
+
You can find Saxon HE at http://saxon.sourceforge.net/ and Saxonica at
|
24
|
+
http://www.saxonica.com/
|
25
|
+
|
26
|
+
Saxon HE is (c) Michael H. Kay and released under the Mozilla MPL 1.0
|
27
|
+
(http://www.mozilla.org/MPL/1.0/)
|
6
28
|
|
7
29
|
## Installation
|
8
30
|
|
9
31
|
Add this line to your application's Gemfile:
|
10
32
|
|
11
33
|
```ruby
|
12
|
-
gem 'saxon'
|
34
|
+
gem 'saxon-rb'
|
13
35
|
```
|
14
36
|
|
15
37
|
And then execute:
|
@@ -18,21 +40,306 @@ And then execute:
|
|
18
40
|
|
19
41
|
Or install it yourself as:
|
20
42
|
|
21
|
-
$ gem install saxon
|
43
|
+
$ gem install saxon-rb
|
44
|
+
|
45
|
+
## Simple usage
|
46
|
+
|
47
|
+
### Parse an XML document
|
48
|
+
|
49
|
+
Using a default document builder from the default processor:
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
document_node = Saxon::Processor.create.document_builder.build(Saxon::Source.from_path('/path/to/your.xml'))
|
53
|
+
```
|
54
|
+
|
55
|
+
Or
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
document_node = Saxon.XML('/path/to/your.xml')
|
59
|
+
```
|
60
|
+
|
61
|
+
### Transform an XML document with XSLT
|
62
|
+
|
63
|
+
```ruby
|
64
|
+
transformer = Saxon::Processor.create.xslt_compiler.compile(Saxon::Source.from_path('/path/to/your.xsl'))
|
65
|
+
# Or
|
66
|
+
transformer = Saxon.XSLT('/path/to/your.xsl')
|
67
|
+
|
68
|
+
# Apply templates against a document
|
69
|
+
result_1 = transformer.apply_templates(document_node)
|
70
|
+
|
71
|
+
# Call a template without a context item to process
|
72
|
+
result_2 = transformer.call_template('main-template')
|
73
|
+
```
|
74
|
+
|
75
|
+
### Run XPath queries against an XML document
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
processor = Saxon::Processor.create
|
79
|
+
xpath = processor.xpath_compiler.compile('//element[@attr = $a:var]')
|
80
|
+
|
81
|
+
matches = xpath.run(document_node)
|
82
|
+
```
|
83
|
+
|
84
|
+
## Migrating from `saxon-xslt` (or Nokogiri)
|
85
|
+
|
86
|
+
`saxon-xslt` wrapped Saxon and provided a Nokogiri-esque API. Nokogiri is built on XSLT 1 processors, and the APIs support XSLT 1 features, but won't allow XSLT 2/3 features (like setting initial tunnel parameters, starting processing by calling a named template, or a function). The main API for invoking XSLT in `saxon-rb` needs to be different from Nokogiri's so that full use of XSLT 2/3 features is possible.
|
87
|
+
|
88
|
+
By default, the original `saxon-xslt` API (on `Saxon::XSLT::Stylesheet`) is not available. If you need those methods, then you can load the legacy API by requiring `saxon/nokogiri`.
|
89
|
+
|
90
|
+
That gives you back the `#transform`, `#apply_to`, and `#serialize` methods on the object you get back after compiling an XSLT: `Saxon::XSLT::Executable` in `saxon-rb`. They work the same way, and you should be able to drop in `saxon-rb` as a replacement for XSLT processing.
|
91
|
+
|
92
|
+
```ruby
|
93
|
+
require 'saxon-rb'
|
94
|
+
require 'saxon/nokogiri'
|
95
|
+
|
96
|
+
xslt = Saxon.XSLT('/path/to/my.xsl')
|
97
|
+
xslt.apply_to(Saxon.XML('/path/to/my.xml')) #=> "<result-xml/>"
|
98
|
+
```
|
22
99
|
|
23
100
|
## Usage
|
24
101
|
|
25
|
-
|
102
|
+
### XSLT
|
103
|
+
|
104
|
+
Using XSLT involves creating a compiler, compiling an XSLT document, and then
|
105
|
+
using that compiled document to transform something.
|
106
|
+
|
107
|
+
#### Constructing a Compiler
|
108
|
+
|
109
|
+
The simplest way is to call `#xslt_compiler` on a `Saxon::Processor` instance.
|
110
|
+
|
111
|
+
```ruby
|
112
|
+
processor = Saxon::Processor.create
|
113
|
+
|
114
|
+
# Simplest, default options
|
115
|
+
compiler = processor.xslt_compiler
|
116
|
+
```
|
117
|
+
|
118
|
+
In order to set compile-time options, declare static compile-time parameters
|
119
|
+
then pass a block to the method using the DSL syntax (see [the DSL
|
120
|
+
RDoc](https://rubydoc.info/gems/saxon-rb/Saxon/XSLT/EvaluationContext/DSL) for
|
121
|
+
complete details):
|
122
|
+
|
123
|
+
```ruby
|
124
|
+
compiler = processor.xslt_compiler {
|
125
|
+
static_parameters 'param' => 'value'
|
126
|
+
default_collation 'https://www.w3.org/2005/xpath-functions/collation/html-ascii-case-insensitive/'
|
127
|
+
}
|
128
|
+
```
|
129
|
+
|
130
|
+
The static context for a Compiler cannot be changed, you must create a new one
|
131
|
+
with the context you want. We make it very simple to create a new Compiler based
|
132
|
+
on an existing one. Declaring a parameter again overwrites the value.
|
133
|
+
|
134
|
+
```ruby
|
135
|
+
new_compiler = compiler.create {
|
136
|
+
static_parameters 'param' => 'new value'
|
137
|
+
}
|
138
|
+
|
139
|
+
new_compiler.default_collation #=> "https://www.w3.org/2005/xpath-functions/collation/html-ascii-case-insensitive/"
|
140
|
+
```
|
141
|
+
|
142
|
+
If you wanted to remove a value, you need to start from scratch. You can, of
|
143
|
+
course, extract any data you want from a compiler instance separately and use
|
144
|
+
that to create a new one.
|
145
|
+
|
146
|
+
```ruby
|
147
|
+
params = compiler.static_parameters
|
148
|
+
new_compiler = processor.xslt_compiler {
|
149
|
+
static_parameters params
|
150
|
+
}
|
151
|
+
new_compiler.default_collation #=> nil
|
152
|
+
```
|
153
|
+
|
154
|
+
#### Compiling an XSLT stylesheet
|
155
|
+
|
156
|
+
Once you have a compiler, call `#compile` and pass in a {Saxon::Source} or an
|
157
|
+
existing {Saxon::XDM::Node}. Parameters and other run-time configuration options
|
158
|
+
can be set using a block in the same way as creating a compiler. You'll be returned a {Saxon::XSLT::Executable}.
|
159
|
+
|
160
|
+
```ruby
|
161
|
+
source = Saxon::Source.create('my.xsl')
|
162
|
+
xslt = compiler.compile(source) {
|
163
|
+
initial_template_parameters 'param' => 'other value'
|
164
|
+
}
|
165
|
+
```
|
166
|
+
|
167
|
+
You can also pass in (or override) parameters at stylesheet execution time, but if you'll be executing the same stylesheet against many documents with the same initial parameters then setting them at compile time is simpler.
|
168
|
+
|
169
|
+
#### Executing an XSLT stylesheet
|
170
|
+
|
171
|
+
Once you have a compiled stylesheet, then it can be executed against a source document in a variety of ways.
|
172
|
+
|
173
|
+
First, you can use the traditional *apply templates* ,method, which was the only way in XSLT 1.
|
174
|
+
|
175
|
+
```ruby
|
176
|
+
input = Saxon::Source.create('input.xml')
|
177
|
+
result = xslt.apply_templates(input)
|
178
|
+
```
|
179
|
+
|
180
|
+
Next, you can call a specific named template (new in XSLT 2).
|
181
|
+
|
182
|
+
```ruby
|
183
|
+
result = xslt.call_template('template-name')
|
184
|
+
```
|
185
|
+
|
186
|
+
Note that there's no input document here. If your XSLT needs a global context item set when you invoke it via a named template, then you can do that, too:
|
187
|
+
|
188
|
+
```ruby
|
189
|
+
input = processor.XML('input.xml')
|
190
|
+
result = xslt.call_template('template-name', {
|
191
|
+
global_context_item: input
|
192
|
+
})
|
193
|
+
```
|
194
|
+
|
195
|
+
Global and initial template parameters can be set at compiler creation time, compile time, or execution time. See [Setting parameters](#label-Setting+parameters) for details.
|
196
|
+
|
197
|
+
#### Setting parameters
|
198
|
+
|
199
|
+
There are four kinds of parameters you can set: *Static parameters*, which are
|
200
|
+
set at stylesheet compile time and cannot be changed after compilation. *Global
|
201
|
+
parameters* defined by top-level `<xsl:parameter/>` are available throughout an
|
202
|
+
XSLT, and they can be set when the compiled XSLT is run. The other two kinds
|
203
|
+
of parameters relate to parameters passed to the *first* template run (either
|
204
|
+
the first template matched when called with `#apply_templates`, or the named
|
205
|
+
template called with `#call_template`). *Initial template parameters* are
|
206
|
+
essentially implied `<xsl:with-parameter tunnel="no">` elements. *Initial
|
207
|
+
template tunnel parameters* are implied `<xsl:with-parameter tunnel="yes">`
|
208
|
+
elements.
|
209
|
+
|
210
|
+
```ruby
|
211
|
+
# At compile time
|
212
|
+
xslt = compiler.compile(source) {
|
213
|
+
static_parameters 'static-param' => 'static value'
|
214
|
+
global_parameters 'param' => 'global value'
|
215
|
+
initial_template_parameters 'param' => 'other value'
|
216
|
+
initial_template_tunnel_parameters 'param' => 'tunnel value'
|
217
|
+
}
|
218
|
+
|
219
|
+
# At execution time
|
220
|
+
xslt.apply_templates(input, {
|
221
|
+
global_parameters: {'param' => 'global value'},
|
222
|
+
initial_template_parameters: {'param' => 'other value'},
|
223
|
+
initial_template_tunnel_parameters: {'param' => 'tunnel value'}
|
224
|
+
})
|
225
|
+
```
|
226
|
+
|
227
|
+
Multiple parameters can be set:
|
228
|
+
|
229
|
+
```ruby
|
230
|
+
# At compile time
|
231
|
+
xslt = compiler.compile(source) {
|
232
|
+
global_parameters 'param-1' => 'a', 'param-2' => 'b'
|
233
|
+
}
|
234
|
+
|
235
|
+
# At execution time
|
236
|
+
xslt.apply_templates(input, {
|
237
|
+
global_parameters: {'param-1' => 'a', 'param-2' => 'b'}
|
238
|
+
})
|
239
|
+
```
|
240
|
+
|
241
|
+
Parameter names in XSLT are QNames, and values are an XDM Value. `saxon-rb` will
|
242
|
+
convert Ruby values (see {Saxon::QName.resolve} and {Saxon::XDM.Value}). You can
|
243
|
+
also use explicit {Saxon::QName} or XDM values:
|
244
|
+
|
245
|
+
```ruby
|
246
|
+
compiler.compile(source) {
|
247
|
+
global_parameters Saxon::QName.clark('{http://example.org/#ns}name') => Saxon::XDM.Value(1)
|
248
|
+
}
|
249
|
+
```
|
250
|
+
|
251
|
+
If you need to use parameter names which use a namespace prefix, you must use an
|
252
|
+
explicit {Saxon::QName} to refer to it.
|
253
|
+
|
254
|
+
### XPath
|
255
|
+
|
256
|
+
Using an XPath involves creating a compiler, compiling an XPath into an
|
257
|
+
executable, and then running that XPath executable against an XDM node.
|
258
|
+
|
259
|
+
In order to use prefixed QNames in your XPaths, like +/ns:name/+, then you need
|
260
|
+
to declare prefix/namespace URI bindings when you create a compiler.
|
261
|
+
|
262
|
+
It's also possible to make use of variables in your XPaths by declaring them at
|
263
|
+
the compiler creation stage, and then passing in values for them as XPath run
|
264
|
+
time.
|
265
|
+
|
266
|
+
```ruby
|
267
|
+
processor = Saxon::Processor.create
|
268
|
+
xpath = processor.xpath_compiler {
|
269
|
+
namespace a: 'http://example.org/a'
|
270
|
+
variable 'a:var', 'xs:string'
|
271
|
+
}.compile('//a:element[@attr = $a:var]')
|
272
|
+
|
273
|
+
matches = xpath.evaluate(document_node, {
|
274
|
+
'a:var' => 'the value'
|
275
|
+
}) #=> Saxon::XDM::Value
|
276
|
+
```
|
277
|
+
|
278
|
+
The {XPath::Executable#evaluate} method returns an XDM Value containing the
|
279
|
+
result sequence. For a result sequence with multiple items then it'll be a
|
280
|
+
{Saxon::XDM::Value}. A single-item sequence will return an appropriate item
|
281
|
+
instance - a {Saxon::XDM::Node} or a {Saxon::XDM::AtomicValue}.
|
282
|
+
|
283
|
+
You can also use the {XPath::Executable#as_enum} to return a lazy enumerator
|
284
|
+
over the result.
|
285
|
+
|
286
|
+
## Using your Saxon PE license and `.jar`s instead of the bundled Saxon HE
|
287
|
+
|
288
|
+
Saxon 9.9 HE is bundled with the gem. To use Saxon PE or EE (the commercial
|
289
|
+
versions) you need to make the `.jar`s available, and then create a licensed
|
290
|
+
`Saxon::Configuration` object. To make the `.jar`s available is simply a matter
|
291
|
+
of adding them to the `CLASS_PATH`. The version of Saxon downloaded directly
|
292
|
+
provides several `.jar` files. We provide a `Saxon::Loader` method for adding
|
293
|
+
the `.jar`s within the directory correctly. Saxon is distributed through Maven
|
294
|
+
as a single `.jar`, which you can just add to the `LOAD_PATH`/`CLASS_PATH`. If
|
295
|
+
you're adding to the `CLASS_PATH` directly, or calling `Saxon::Loader.load!`,
|
296
|
+
then you need to do it before you try and use the library.
|
297
|
+
|
298
|
+
### Loading a Saxon PE you downloaded directly from Saxonica
|
299
|
+
|
300
|
+
```ruby
|
301
|
+
require 'saxon-rb'
|
302
|
+
|
303
|
+
Saxon::Loader.load!('/path/to/SaxonPE9-9-1-2J') # The folder that contains the .jars, like $SAXON_HOME
|
304
|
+
config = Saxon::Configuration.create_licensed('/path/to/saxon.lic')
|
305
|
+
processor = Saxon::Processor.create(config)
|
306
|
+
|
307
|
+
processor.xslt_compiler...
|
308
|
+
```
|
309
|
+
|
310
|
+
### Loading a Saxon PE installed via Maven (e.g. with JBundler)
|
311
|
+
|
312
|
+
```ruby
|
313
|
+
require 'jbundler'
|
314
|
+
require 'saxon-rb'
|
315
|
+
|
316
|
+
config = Saxon::Configuration.create_licensed('/path/to/saxon.lic')
|
317
|
+
processor = Saxon::Processor.create(config)
|
318
|
+
|
319
|
+
...
|
320
|
+
```
|
321
|
+
|
322
|
+
See https://github.com/mkristian/jbundler for more on loading Java deps from
|
323
|
+
Maven.
|
26
324
|
|
27
325
|
## Development
|
28
326
|
|
29
|
-
After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
327
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
328
|
+
`rake spec` to run the tests. You can also run `bin/console` for an interactive
|
329
|
+
prompt that will allow you to experiment.
|
30
330
|
|
31
|
-
To install this gem onto your local machine, run `bundle exec rake install`. To
|
331
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To
|
332
|
+
release a new version, update the version number in `version.rb`, and then run
|
333
|
+
`bundle exec rake release`, which will create a git tag for the version, push
|
334
|
+
git commits and tags, and push the `.gem` file to
|
335
|
+
[rubygems.org](https://rubygems.org).
|
32
336
|
|
33
337
|
## Contributing
|
34
338
|
|
35
|
-
Bug reports and pull requests are welcome on GitHub at
|
339
|
+
Bug reports and pull requests are welcome on GitHub at
|
340
|
+
https://github.com/fidothe/saxon-rb. This project is intended to be a safe,
|
341
|
+
welcoming space for collaboration, and contributors are expected to adhere to
|
342
|
+
the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
36
343
|
|
37
344
|
## License
|
38
345
|
|
@@ -40,4 +347,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
40
347
|
|
41
348
|
## Code of Conduct
|
42
349
|
|
43
|
-
Everyone interacting in the Saxon project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/
|
350
|
+
Everyone interacting in the Saxon-rb project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/fidothe/saxon-rb/blob/master/CODE_OF_CONDUCT.md).
|
data/Rakefile
CHANGED
@@ -1,5 +1,4 @@
|
|
1
|
-
|
2
|
-
require 'bundler/gem_helper'
|
1
|
+
require 'bundler/gem_tasks'
|
3
2
|
require 'rspec/core/rake_task'
|
4
3
|
require 'jars/installer'
|
5
4
|
|
@@ -12,9 +11,240 @@ task :install_jars do
|
|
12
11
|
Jars::Installer.vendor_jars!
|
13
12
|
end
|
14
13
|
|
15
|
-
|
16
|
-
|
14
|
+
desc "Generate travis-esque build matrix config"
|
15
|
+
task :circleci do
|
16
|
+
require 'yaml'
|
17
|
+
class CircleCIConfig
|
18
|
+
def self.generate(path)
|
19
|
+
new.generate(path)
|
20
|
+
end
|
21
|
+
|
22
|
+
def generate(path)
|
23
|
+
File.open(path, 'w:utf-8') do |f|
|
24
|
+
f.write(YAML.dump(config))
|
25
|
+
end
|
26
|
+
end
|
27
|
+
def jruby_image_tags
|
28
|
+
%w{9.2.9.0 9.1.17.0 9.2.10.0-SNAPSHOT-latest}
|
29
|
+
end
|
30
|
+
|
31
|
+
def jdk_image_tags
|
32
|
+
{
|
33
|
+
"8-jdk-slim" => "JDK 8",
|
34
|
+
"11-jdk-slim" => "JDK 11",
|
35
|
+
"13-jdk-slim" => "JDK 13"
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
def alt_saxon_urls
|
40
|
+
{
|
41
|
+
"https://sourceforge.net/projects/saxon/files/Saxon-HE/9.8/SaxonHE9-8-0-15J.zip" => "Saxon HE 9.8"
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
def skip_jdk_versions
|
46
|
+
[
|
47
|
+
%w{9.1.17.0 11-jdk-slim},
|
48
|
+
%w{9.1.17.0 13-jdk-slim},
|
49
|
+
]
|
50
|
+
end
|
51
|
+
|
52
|
+
def codeclimate_jobs
|
53
|
+
(alt_saxon_urls.keys << nil).map { |alt_saxon_url|
|
54
|
+
["9.2.9.0", "8-jdk-slim", alt_saxon_url]
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
def all_job_variants
|
59
|
+
jruby_image_tags.product(jdk_image_tags.keys, alt_saxon_urls.keys << nil).reject { |jruby, jdk, _|
|
60
|
+
skip_jdk_versions.include?([jruby, jdk])
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
64
|
+
def job_name(jruby_image_tag, jdk_image_tag, alt_saxon_url)
|
65
|
+
[
|
66
|
+
"JRuby #{jruby_image_tag}, #{jdk_image_tags[jdk_image_tag]}",
|
67
|
+
alt_saxon_urls[alt_saxon_url]
|
68
|
+
].compact.join(' ')
|
69
|
+
end
|
70
|
+
|
71
|
+
def all_job_names
|
72
|
+
all_job_variants.map { |jruby_image_tag, jdk_image_tag, alt_saxon_url|
|
73
|
+
job_name(jruby_image_tag, jdk_image_tag, alt_saxon_url)
|
74
|
+
}
|
75
|
+
end
|
76
|
+
|
77
|
+
def jobs
|
78
|
+
all_job_variants.map { |jruby_image_tag, jdk_image_tag, alt_saxon_url|
|
79
|
+
run_codeclimate = codeclimate_jobs.include?([jruby_image_tag, jdk_image_tag, alt_saxon_url])
|
80
|
+
[
|
81
|
+
job_name(jruby_image_tag, jdk_image_tag, alt_saxon_url),
|
82
|
+
job_config({
|
83
|
+
run_codeclimate: run_codeclimate, alt_saxon_url: alt_saxon_url,
|
84
|
+
docker_image: docker_image(jruby_image_tag, jdk_image_tag)
|
85
|
+
})
|
86
|
+
]
|
87
|
+
}.to_h.merge(report_test_coverage_job({
|
88
|
+
docker_image: "circleci/ruby:latest",
|
89
|
+
run_codeclimate: true
|
90
|
+
}))
|
91
|
+
end
|
92
|
+
|
93
|
+
def docker_image(jruby_image_tag, jdk_image_tag)
|
94
|
+
"fidothe/circleci:jruby-#{jruby_image_tag}-#{jdk_image_tag}"
|
95
|
+
end
|
96
|
+
|
97
|
+
def job_config(opts = {})
|
98
|
+
job = {
|
99
|
+
"docker" => [
|
100
|
+
{"image" => opts.fetch(:docker_image)}
|
101
|
+
],
|
102
|
+
"environment" => environment(opts),
|
103
|
+
"steps" => steps(opts)
|
104
|
+
}
|
105
|
+
end
|
106
|
+
|
107
|
+
def environment(opts = {})
|
108
|
+
env = {
|
109
|
+
"BUNDLE_JOBS" => 3,
|
110
|
+
"BUNDLE_RETRY" => 3,
|
111
|
+
"BUNDLE_PATH" => "vendor/bundle",
|
112
|
+
"JRUBY_OPTS" => "--dev --debug"
|
113
|
+
}
|
114
|
+
env["ALTERNATE_SAXON_HOME"] = "/tmp/saxon" if opts.fetch(:alt_saxon_url)
|
115
|
+
env
|
116
|
+
end
|
117
|
+
|
118
|
+
def steps(opts = {})
|
119
|
+
[
|
120
|
+
"checkout",
|
121
|
+
alt_saxon_install(opts),
|
122
|
+
{
|
123
|
+
"run" => {
|
124
|
+
"name" => "Bundle Install",
|
125
|
+
"command" => "bundle check || bundle install"
|
126
|
+
}
|
127
|
+
},
|
128
|
+
install_codeclimate_reporter_step(opts),
|
129
|
+
run_tests_step(opts),
|
130
|
+
persist_test_coverage_to_workspace_step(opts),
|
131
|
+
{
|
132
|
+
"store_test_results" => {"path" => "/tmp/test-results"}
|
133
|
+
},
|
134
|
+
{
|
135
|
+
"store_artifacts" => {"path" => "/tmp/test-results", "destination" => "test-results"}
|
136
|
+
}
|
137
|
+
].compact
|
138
|
+
end
|
139
|
+
|
140
|
+
def alt_saxon_install(opts)
|
141
|
+
return nil unless opts.fetch(:alt_saxon_url)
|
142
|
+
saxon_file = File.basename(opts[:alt_saxon_url])
|
143
|
+
{
|
144
|
+
"run" => {
|
145
|
+
"name" => "Download #{saxon_file}",
|
146
|
+
"command" => [
|
147
|
+
"mkdir -p /tmp/saxon",
|
148
|
+
"cd /tmp/saxon",
|
149
|
+
"curl -L -O #{opts[:alt_saxon_url]}",
|
150
|
+
"unzip #{saxon_file}",
|
151
|
+
"rm -f #{saxon_file}"
|
152
|
+
].join("\n")
|
153
|
+
}
|
154
|
+
}
|
155
|
+
end
|
156
|
+
|
157
|
+
def attach_workspace_step(opts)
|
158
|
+
return nil unless opts.fetch(:run_codeclimate)
|
159
|
+
{
|
160
|
+
"attach_workspace" => {
|
161
|
+
"at" => "/tmp/workspace"
|
162
|
+
}
|
163
|
+
}
|
164
|
+
end
|
165
|
+
|
166
|
+
def install_codeclimate_reporter_step(opts)
|
167
|
+
return nil unless opts.fetch(:run_codeclimate)
|
168
|
+
{
|
169
|
+
"run" => {
|
170
|
+
"name" => "Install Code Climate Test Reporter",
|
171
|
+
"command" =>
|
172
|
+
"curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter\n" +
|
173
|
+
"chmod +x ./cc-test-reporter\n"
|
174
|
+
}
|
175
|
+
}
|
176
|
+
end
|
177
|
+
|
178
|
+
def persist_test_coverage_to_workspace_step(opts)
|
179
|
+
return nil unless opts.fetch(:run_codeclimate)
|
180
|
+
{
|
181
|
+
"persist_to_workspace" => {
|
182
|
+
"root" => "~/project",
|
183
|
+
"paths" => [
|
184
|
+
"cc-coverage*"
|
185
|
+
]
|
186
|
+
}
|
187
|
+
}
|
188
|
+
end
|
189
|
+
|
190
|
+
def run_tests_step(opts)
|
191
|
+
command = [
|
192
|
+
"mkdir -p /tmp/test-results",
|
193
|
+
"bundle exec rspec spec --profile 10 --format RspecJunitFormatter --out /tmp/test-results/rspec.xml --format progress"
|
194
|
+
]
|
195
|
+
if opts.fetch(:run_codeclimate)
|
196
|
+
command.prepend("./cc-test-reporter before-build")
|
197
|
+
command.append("if [ $? -eq 0 ]; then ./cc-test-reporter format-coverage -t simplecov -o \"cc-coverage#{"-alt-saxon" if opts.fetch(:alt_saxon_url)}.json\"; fi")
|
198
|
+
end
|
199
|
+
{
|
200
|
+
"run" => {
|
201
|
+
"name" => "Run the tests" + (opts.fetch(:run_codeclimate) ? ", and upload coverage data to Code Climate" : ""),
|
202
|
+
"command" => command.join("\n")
|
203
|
+
}
|
204
|
+
}
|
205
|
+
end
|
206
|
+
|
207
|
+
def report_test_coverage_job(opts)
|
208
|
+
{
|
209
|
+
"Report test coverage to Code Climate" => {
|
210
|
+
"docker" => [
|
211
|
+
{"image" => opts.fetch(:docker_image)}
|
212
|
+
],
|
213
|
+
"steps" => [
|
214
|
+
{
|
215
|
+
"attach_workspace" => {
|
216
|
+
"at" => "/tmp/workspace"
|
217
|
+
}
|
218
|
+
},
|
219
|
+
install_codeclimate_reporter_step(opts),
|
220
|
+
{
|
221
|
+
"run" => {
|
222
|
+
"name" => "Upload test coverage to Code Climate",
|
223
|
+
"command" => "find /tmp/workspace -name 'cc-coverage*.json' &&\\\n ./cc-test-reporter sum-coverage /tmp/workspace/cc-coverage*.json &&\\\n ./cc-test-reporter upload-coverage"
|
224
|
+
}
|
225
|
+
}
|
226
|
+
]
|
227
|
+
}
|
228
|
+
}
|
229
|
+
end
|
230
|
+
|
231
|
+
def config
|
232
|
+
{
|
233
|
+
"version" => 2,
|
234
|
+
"jobs" => jobs,
|
235
|
+
"workflows" => {
|
236
|
+
"version" => 2,
|
237
|
+
"build_and_test" => {
|
238
|
+
"jobs" => all_job_names << {
|
239
|
+
"Report test coverage to Code Climate" => {
|
240
|
+
"requires" => codeclimate_jobs.map { |args| job_name(*args) }
|
241
|
+
}
|
242
|
+
}
|
243
|
+
}
|
244
|
+
}
|
245
|
+
}
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
CircleCIConfig.generate('.circleci/config.yml')
|
17
250
|
end
|
18
|
-
namespace :old_saxon do
|
19
|
-
Bundler::GemHelper.install_tasks(name: 'saxon')
|
20
|
-
end
|