saxon-rb 0.4.0-java → 0.7.2-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +429 -42
  3. data/.ruby-version +1 -1
  4. data/.yardopts +1 -0
  5. data/Gemfile +2 -2
  6. data/README.md +358 -10
  7. data/Rakefile +237 -7
  8. data/docs/templates/plugin.rb +73 -0
  9. 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
  10. data/lib/saxon-rb.rb +0 -0
  11. data/lib/{saxon_jars.rb → saxon-rb_jars.rb} +2 -2
  12. data/lib/saxon.rb +13 -0
  13. data/lib/saxon/axis_iterator.rb +8 -1
  14. data/lib/saxon/configuration.rb +16 -13
  15. data/lib/saxon/document_builder.rb +216 -5
  16. data/lib/saxon/feature_flags.rb +11 -0
  17. data/lib/saxon/feature_flags/errors.rb +8 -0
  18. data/lib/saxon/feature_flags/helpers.rb +15 -0
  19. data/lib/saxon/feature_flags/version.rb +100 -0
  20. data/lib/saxon/item_type.rb +129 -89
  21. data/lib/saxon/item_type/lexical_string_conversion.rb +214 -59
  22. data/lib/saxon/item_type/value_to_ruby.rb +25 -0
  23. data/lib/saxon/loader.rb +6 -1
  24. data/lib/saxon/nokogiri.rb +78 -0
  25. data/lib/saxon/occurrence_indicator.rb +32 -3
  26. data/lib/saxon/processor.rb +50 -5
  27. data/lib/saxon/qname.rb +37 -2
  28. data/lib/saxon/s9api.rb +5 -0
  29. data/lib/saxon/sequence_type.rb +131 -0
  30. data/lib/saxon/serializer.rb +3 -137
  31. data/lib/saxon/serializer/destination.rb +80 -0
  32. data/lib/saxon/serializer/object.rb +93 -0
  33. data/lib/saxon/serializer/output_properties.rb +83 -0
  34. data/lib/saxon/source.rb +207 -71
  35. data/lib/saxon/version.rb +7 -1
  36. data/lib/saxon/version/library.rb +89 -0
  37. data/lib/saxon/xdm.rb +7 -0
  38. data/lib/saxon/xdm/array.rb +16 -0
  39. data/lib/saxon/xdm/atomic_value.rb +10 -2
  40. data/lib/saxon/xdm/empty_sequence.rb +13 -0
  41. data/lib/saxon/xdm/external_object.rb +1 -0
  42. data/lib/saxon/xdm/function_item.rb +1 -0
  43. data/lib/saxon/xdm/item.rb +7 -0
  44. data/lib/saxon/xdm/map.rb +38 -0
  45. data/lib/saxon/xdm/node.rb +50 -1
  46. data/lib/saxon/xdm/sequence_like.rb +15 -0
  47. data/lib/saxon/xdm/value.rb +21 -5
  48. data/lib/saxon/xpath.rb +9 -0
  49. data/lib/saxon/xpath/compiler.rb +37 -2
  50. data/lib/saxon/xpath/executable.rb +53 -28
  51. data/lib/saxon/xpath/static_context.rb +25 -40
  52. data/lib/saxon/xpath/variable_declaration.rb +16 -49
  53. data/lib/saxon/xslt.rb +12 -0
  54. data/lib/saxon/xslt/compiler.rb +75 -6
  55. data/lib/saxon/xslt/evaluation_context.rb +30 -4
  56. data/lib/saxon/xslt/executable.rb +206 -29
  57. data/lib/saxon/xslt/invocation.rb +97 -0
  58. data/saxon-rb.gemspec +3 -3
  59. metadata +22 -10
  60. data/saxon.gemspec +0 -30
@@ -1 +1 @@
1
- jruby-9.2.6.0
1
+ jruby-9.2.9.0
@@ -0,0 +1 @@
1
+ -e docs/templates/plugin.rb
data/Gemfile CHANGED
@@ -2,5 +2,5 @@ source "https://rubygems.org"
2
2
 
3
3
  git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
4
 
5
- # Specify your gem's dependencies in saxon.gemspec
6
- gemspec name: 'saxon-rb'
5
+ # Specify your gem's dependencies in saxon-rb.gemspec
6
+ gemspec
data/README.md CHANGED
@@ -1,15 +1,37 @@
1
- # Saxon
1
+ # saxon-rb – An idiomatic Ruby wrapper for Saxon
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/saxon`. To experiment with that code, run `bin/console` for an interactive prompt.
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
- TODO: Delete this and the text above, and describe your gem
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,347 @@ 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.evaluate(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
- TODO: Write usage instructions here
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
+ To serialize the document you can, of course, just call `#to_s` on the result:
198
+
199
+ ```ruby
200
+ result = xslt.apply_templates(input)
201
+ puts result.to_s #=> '<?xml version="1.0"...'
202
+ ```
203
+
204
+ You can also serialize directly to a file path or to any IO instance.
205
+
206
+ ```ruby
207
+ result = xslt.apply_templates(input)
208
+ result.serialize('/path/to/output.xml')
209
+
210
+ result_2 = xslt.apply_templates(input)
211
+ result_2.serialize($stderr)
212
+ ```
213
+
214
+ You can override serialization options that were set by `<xsl:output/>` in your XSLT:
215
+
216
+ ```ruby
217
+ result = xslt.apply_templates(input)
218
+ result.serialize('/path/to/output.xml') {
219
+ output_property[:indent] = 'yes'
220
+ }
221
+ ```
222
+
223
+ You can also obtain the result of the transform as an XDM Value:
224
+
225
+ ```ruby
226
+ result = xslt.apply_templates(input)
227
+ result.xdm_value #=> #<Saxon::XDM::Node...>
228
+ ```
229
+
230
+ You also have easy access to provide an instance of a class implementing Saxon's `net.sf.saxon.s9api.Destination` interface:
231
+
232
+ ```ruby
233
+ dom_document = javax.xml.parsers.DocumentBuilderFactory.newInstance.builder.newDocument
234
+ destination = Saxon::S9API::DOMDestination.new(dom_document)
235
+ result = xslt.apply_templates(input).to_destination(destination)
236
+ ```
237
+
238
+ #### Setting parameters
239
+
240
+ There are four kinds of parameters you can set: *Static parameters*, which are
241
+ set at stylesheet compile time and cannot be changed after compilation. *Global
242
+ parameters* defined by top-level `<xsl:parameter/>` are available throughout an
243
+ XSLT, and they can be set when the compiled XSLT is run. The other two kinds
244
+ of parameters relate to parameters passed to the *first* template run (either
245
+ the first template matched when called with `#apply_templates`, or the named
246
+ template called with `#call_template`). *Initial template parameters* are
247
+ essentially implied `<xsl:with-parameter tunnel="no">` elements. *Initial
248
+ template tunnel parameters* are implied `<xsl:with-parameter tunnel="yes">`
249
+ elements.
250
+
251
+ ```ruby
252
+ # At compile time
253
+ xslt = compiler.compile(source) {
254
+ static_parameters 'static-param' => 'static value'
255
+ global_parameters 'param' => 'global value'
256
+ initial_template_parameters 'param' => 'other value'
257
+ initial_template_tunnel_parameters 'param' => 'tunnel value'
258
+ }
259
+
260
+ # At execution time
261
+ xslt.apply_templates(input, {
262
+ global_parameters: {'param' => 'global value'},
263
+ initial_template_parameters: {'param' => 'other value'},
264
+ initial_template_tunnel_parameters: {'param' => 'tunnel value'}
265
+ })
266
+ ```
267
+
268
+ Multiple parameters can be set:
269
+
270
+ ```ruby
271
+ # At compile time
272
+ xslt = compiler.compile(source) {
273
+ global_parameters 'param-1' => 'a', 'param-2' => 'b'
274
+ }
275
+
276
+ # At execution time
277
+ xslt.apply_templates(input, {
278
+ global_parameters: {'param-1' => 'a', 'param-2' => 'b'}
279
+ })
280
+ ```
281
+
282
+ Parameter names in XSLT are QNames, and values are an XDM Value. `saxon-rb` will
283
+ convert Ruby values (see {Saxon::QName.resolve} and {Saxon::XDM.Value}). You can
284
+ also use explicit {Saxon::QName} or XDM values:
285
+
286
+ ```ruby
287
+ compiler.compile(source) {
288
+ global_parameters Saxon::QName.clark('{http://example.org/#ns}name') => Saxon::XDM.Value(1)
289
+ }
290
+ ```
291
+
292
+ If you need to use parameter names which use a namespace prefix, you must use an
293
+ explicit {Saxon::QName} to refer to it.
294
+
295
+ ### XPath
296
+
297
+ Using an XPath involves creating a compiler, compiling an XPath into an
298
+ executable, and then running that XPath executable against an XDM node.
299
+
300
+ In order to use prefixed QNames in your XPaths, like +/ns:name/+, then you need
301
+ to declare prefix/namespace URI bindings when you create a compiler.
302
+
303
+ It's also possible to make use of variables in your XPaths by declaring them at
304
+ the compiler creation stage, and then passing in values for them as XPath run
305
+ time.
306
+
307
+ ```ruby
308
+ processor = Saxon::Processor.create
309
+ xpath = processor.xpath_compiler {
310
+ namespace a: 'http://example.org/a'
311
+ variable 'a:var', 'xs:string'
312
+ }.compile('//a:element[@attr = $a:var]')
313
+
314
+ matches = xpath.evaluate(document_node, {
315
+ 'a:var' => 'the value'
316
+ }) #=> Saxon::XDM::Value
317
+ ```
318
+
319
+ The {XPath::Executable#evaluate} method returns an XDM Value containing the
320
+ result sequence. For a result sequence with multiple items then it'll be a
321
+ {Saxon::XDM::Value}. A single-item sequence will return an appropriate item
322
+ instance - a {Saxon::XDM::Node} or a {Saxon::XDM::AtomicValue}.
323
+
324
+ You can also use the {XPath::Executable#as_enum} to return a lazy enumerator
325
+ over the result.
326
+
327
+ ## Using your Saxon PE license and `.jar`s instead of the bundled Saxon HE
328
+
329
+ Saxon 9.9 HE is bundled with the gem. To use Saxon PE or EE (the commercial
330
+ versions) you need to make the `.jar`s available, and then create a licensed
331
+ `Saxon::Configuration` object. To make the `.jar`s available is simply a matter
332
+ of adding them to the `CLASS_PATH`. The version of Saxon downloaded directly
333
+ provides several `.jar` files. We provide a `Saxon::Loader` method for adding
334
+ the `.jar`s within the directory correctly. Saxon is distributed through Maven
335
+ as a single `.jar`, which you can just add to the `LOAD_PATH`/`CLASS_PATH`. If
336
+ you're adding to the `CLASS_PATH` directly, or calling `Saxon::Loader.load!`,
337
+ then you need to do it before you try and use the library.
338
+
339
+ ### Loading a Saxon PE you downloaded directly from Saxonica
340
+
341
+ ```ruby
342
+ require 'saxon-rb'
343
+
344
+ Saxon::Loader.load!('/path/to/SaxonPE9-9-1-2J') # The folder that contains the .jars, like $SAXON_HOME
345
+ config = Saxon::Configuration.create_licensed('/path/to/saxon.lic')
346
+ processor = Saxon::Processor.create(config)
347
+
348
+ processor.xslt_compiler...
349
+ ```
350
+
351
+ ### Loading a Saxon PE installed via Maven (e.g. with JBundler)
352
+
353
+ ```ruby
354
+ require 'jbundler'
355
+ require 'saxon-rb'
356
+
357
+ config = Saxon::Configuration.create_licensed('/path/to/saxon.lic')
358
+ processor = Saxon::Processor.create(config)
359
+
360
+ ...
361
+ ```
362
+
363
+ See https://github.com/mkristian/jbundler for more on loading Java deps from
364
+ Maven.
26
365
 
27
366
  ## Development
28
367
 
29
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
368
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run
369
+ `rake spec` to run the tests. You can also run `bin/console` for an interactive
370
+ prompt that will allow you to experiment.
30
371
 
31
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
372
+ To install this gem onto your local machine, run `bundle exec rake install`. To
373
+ release a new version, update the version number in `version.rb`, and then run
374
+ `bundle exec rake release`, which will create a git tag for the version, push
375
+ git commits and tags, and push the `.gem` file to
376
+ [rubygems.org](https://rubygems.org).
32
377
 
33
378
  ## Contributing
34
379
 
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/saxon. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
380
+ Bug reports and pull requests are welcome on GitHub at
381
+ https://github.com/fidothe/saxon-rb. This project is intended to be a safe,
382
+ welcoming space for collaboration, and contributors are expected to adhere to
383
+ the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
36
384
 
37
385
  ## License
38
386
 
@@ -40,4 +388,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
40
388
 
41
389
  ## Code of Conduct
42
390
 
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/[USERNAME]/saxon/blob/master/CODE_OF_CONDUCT.md).
391
+ 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
- #require 'bundler/gem_tasks'
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
- namespace :new_saxon do
16
- Bundler::GemHelper.install_tasks(name: 'saxon-rb')
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