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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +429 -42
  3. data/Gemfile +2 -2
  4. data/README.md +317 -10
  5. data/Rakefile +237 -7
  6. 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
  7. data/lib/saxon-rb.rb +1 -0
  8. data/lib/{saxon_jars.rb → saxon-rb_jars.rb} +2 -2
  9. data/lib/saxon.rb +13 -0
  10. data/lib/saxon/axis_iterator.rb +8 -1
  11. data/lib/saxon/configuration.rb +1 -0
  12. data/lib/saxon/item_type.rb +12 -17
  13. data/lib/saxon/item_type/lexical_string_conversion.rb +136 -58
  14. data/lib/saxon/item_type/value_to_ruby.rb +13 -0
  15. data/lib/saxon/loader.rb +4 -1
  16. data/lib/saxon/nokogiri.rb +78 -0
  17. data/lib/saxon/occurrence_indicator.rb +32 -3
  18. data/lib/saxon/processor.rb +32 -1
  19. data/lib/saxon/qname.rb +37 -2
  20. data/lib/saxon/s9api.rb +5 -0
  21. data/lib/saxon/sequence_type.rb +131 -0
  22. data/lib/saxon/source.rb +207 -71
  23. data/lib/saxon/version.rb +1 -1
  24. data/lib/saxon/xdm.rb +7 -0
  25. data/lib/saxon/xdm/array.rb +16 -0
  26. data/lib/saxon/xdm/atomic_value.rb +7 -1
  27. data/lib/saxon/xdm/empty_sequence.rb +13 -0
  28. data/lib/saxon/xdm/external_object.rb +1 -0
  29. data/lib/saxon/xdm/function_item.rb +1 -0
  30. data/lib/saxon/xdm/item.rb +7 -0
  31. data/lib/saxon/xdm/map.rb +38 -0
  32. data/lib/saxon/xdm/node.rb +19 -1
  33. data/lib/saxon/xdm/sequence_like.rb +15 -0
  34. data/lib/saxon/xdm/value.rb +21 -5
  35. data/lib/saxon/xpath.rb +9 -0
  36. data/lib/saxon/xpath/compiler.rb +36 -1
  37. data/lib/saxon/xpath/executable.rb +53 -28
  38. data/lib/saxon/xpath/static_context.rb +19 -39
  39. data/lib/saxon/xpath/variable_declaration.rb +16 -49
  40. data/lib/saxon/xslt.rb +12 -0
  41. data/lib/saxon/xslt/compiler.rb +75 -6
  42. data/lib/saxon/xslt/evaluation_context.rb +19 -3
  43. data/lib/saxon/xslt/executable.rb +204 -14
  44. data/saxon-rb.gemspec +1 -1
  45. metadata +9 -7
  46. data/saxon.gemspec +0 -30
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,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
- 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
+ #### 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 `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
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 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).
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 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.
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/[USERNAME]/saxon/blob/master/CODE_OF_CONDUCT.md).
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
- #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