semantic_puppet 0.1.3 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.gitignore +1 -0
- data/.rubocop.yml +672 -0
- data/.travis.yml +28 -0
- data/CHANGELOG.md +21 -0
- data/CODEOWNERS +1 -0
- data/README.md +17 -15
- data/appveyor.yml +19 -0
- data/lib/semantic_puppet.rb +0 -2
- data/lib/semantic_puppet/dependency.rb +18 -1
- data/lib/semantic_puppet/dependency/unsatisfiable_graph.rb +9 -3
- data/lib/semantic_puppet/gem_version.rb +3 -0
- data/lib/semantic_puppet/version.rb +122 -114
- data/lib/semantic_puppet/version_range.rb +654 -343
- data/semantic_puppet.gemspec +10 -6
- data/spec/spec_helper.rb +0 -1
- data/spec/unit/semantic_puppet/dependency/unsatisfiable_graph_spec.rb +1 -1
- data/spec/unit/semantic_puppet/dependency_spec.rb +25 -25
- data/spec/unit/semantic_puppet/version_range_spec.rb +468 -79
- data/spec/unit/semantic_puppet/version_spec.rb +218 -80
- metadata +9 -6
data/.travis.yml
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
---
|
2
|
+
language: ruby
|
3
|
+
|
4
|
+
cache: bundler
|
5
|
+
|
6
|
+
before_install:
|
7
|
+
- bundle -v
|
8
|
+
- rm Gemfile.lock || true
|
9
|
+
- gem update --system
|
10
|
+
- gem update bundler
|
11
|
+
- gem --version
|
12
|
+
- bundle -v
|
13
|
+
|
14
|
+
script: "bundle exec rspec --color --format documentation spec/unit"
|
15
|
+
|
16
|
+
notifications:
|
17
|
+
email: false
|
18
|
+
|
19
|
+
sudo: false
|
20
|
+
|
21
|
+
rvm:
|
22
|
+
- "2.5.8"
|
23
|
+
- "2.6.6"
|
24
|
+
- "2.7.2"
|
25
|
+
- "jruby-19mode"
|
26
|
+
|
27
|
+
jdk:
|
28
|
+
- openjdk8
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,27 @@
|
|
2
2
|
All notable changes to this project will be documented in this file.
|
3
3
|
This project adheres to [Semantic Versioning](http://semver.org/).
|
4
4
|
|
5
|
+
## 1.0.3 - 2021-01-12
|
6
|
+
- List failed module install dependencies
|
7
|
+
- Add Ruby 2.7 to Travis and AppVeyor
|
8
|
+
|
9
|
+
## 1.0.2 - 2018-03-13
|
10
|
+
- Removed i18n/gettext configuration and string externalization. After further consideration we have decided that
|
11
|
+
as a library, semantic_puppet should not be attempting to configure global localization state and the localization
|
12
|
+
of error messages etc. is the responsibility of the consuming application.
|
13
|
+
- Added Appveyor CI configuration
|
14
|
+
|
15
|
+
## 1.0.1 - 2017-07-01
|
16
|
+
- Fix bug causing pre-release identifiers being considered invalid when they contained letters but started with a zero
|
17
|
+
|
18
|
+
## 1.0.0 - 2017-04-05
|
19
|
+
- Complete rewrite of the VersionRange to make it compatible with Node Semver
|
20
|
+
- General speedup of Version (hash, <=>, and to_s in particular)
|
21
|
+
|
22
|
+
## 0.1.4 - 2016-07-06
|
23
|
+
### Changed
|
24
|
+
- Externalized all user-facing strings using gettext libraries to support future localization.
|
25
|
+
|
5
26
|
## 0.1.3 - 2016-05-24
|
6
27
|
### Added
|
7
28
|
- Typesafe implementation of ModuleRelease#eql? (and ModuleRelease#==). (PUP-6341)
|
data/CODEOWNERS
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
* @puppetlabs/forge-team @puppetlabs/platform-core
|
data/README.md
CHANGED
@@ -12,10 +12,6 @@ Versions and Version Ranges and to query and resolve module dependencies.
|
|
12
12
|
|
13
13
|
For sparse, but accurate documentation, please see the docs directory.
|
14
14
|
|
15
|
-
Note that this is a 0 release version, and things can change. Expect that the
|
16
|
-
version and version range code to stay relatively stable, but the module
|
17
|
-
dependency code is expected to change.
|
18
|
-
|
19
15
|
This library is used by a number of Puppet Labs projects, including
|
20
16
|
[Puppet](https://github.com/puppetlabs/puppet) and
|
21
17
|
[r10k](https://github.com/puppetlabs/r10k).
|
@@ -23,9 +19,8 @@ This library is used by a number of Puppet Labs projects, including
|
|
23
19
|
Requirements
|
24
20
|
------------
|
25
21
|
|
26
|
-
Semantic_puppet will work on several ruby versions, including 1.9.3,
|
27
|
-
2.
|
28
|
-
[r10k](https://github.com/puppetlabs/r10k).
|
22
|
+
Semantic_puppet will work on several ruby versions, including 1.9.3,
|
23
|
+
2.0.0, 2.1.9 and 2.4.1. Please see the exact matrix in `.travis.yml`.
|
29
24
|
|
30
25
|
No gem/library requirements.
|
31
26
|
|
@@ -48,17 +43,24 @@ install it out of a git repository:
|
|
48
43
|
Usage
|
49
44
|
-----
|
50
45
|
|
51
|
-
SemanticPuppet is intended to be used as a library.
|
46
|
+
SemanticPuppet is intended to be used as a library.
|
52
47
|
|
53
|
-
###
|
48
|
+
### Version Range Operator Support
|
54
49
|
|
55
|
-
SemanticPuppet will support the same version range operators as those
|
56
|
-
publishing modules to [Puppet
|
57
|
-
documented at
|
58
|
-
|
50
|
+
SemanticPuppet will support the same version range operators as those
|
51
|
+
used when publishing modules to [Puppet
|
52
|
+
Forge](https://forge.puppetlabs.com) which is documented at [Publishing
|
53
|
+
Modules on the Puppet
|
54
|
+
Forge](https://docs.puppetlabs.com/puppet/latest/reference/modules_publishing.html#dependencies-in-metadatajson).
|
59
55
|
|
60
56
|
Contributors
|
61
57
|
------------
|
62
58
|
|
63
|
-
Pieter van de Bruggen wrote the library originally
|
64
|
-
|
59
|
+
Pieter van de Bruggen wrote the library originally. See
|
60
|
+
[https://github.com/puppetlabs/semantic_puppet/graphs/contributors](https://github.com/puppetlabs/semantic_puppet/graphs/contributors)
|
61
|
+
for a list of contributors.
|
62
|
+
|
63
|
+
## Maintenance
|
64
|
+
|
65
|
+
Tickets: File at
|
66
|
+
[https://tickets.puppet.com/browse/FORGE](https://tickets.puppet.com/browse/FORGE)
|
data/appveyor.yml
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
version: 1.0.1.{build}
|
2
|
+
clone_depth: 10
|
3
|
+
image: Visual Studio 2019
|
4
|
+
environment:
|
5
|
+
matrix:
|
6
|
+
- RUBY_VERSION: 25-x64
|
7
|
+
- RUBY_VERSION: 26-x64
|
8
|
+
- RUBY_VERSION: 27-x64
|
9
|
+
matrix:
|
10
|
+
fast_finish: true
|
11
|
+
install:
|
12
|
+
- set PATH=C:\Ruby%RUBY_VERSION%\bin;%PATH%
|
13
|
+
- bundle install --jobs 4 --retry 2
|
14
|
+
build: off
|
15
|
+
test_script:
|
16
|
+
- ruby -v
|
17
|
+
- gem -v
|
18
|
+
- bundle -v
|
19
|
+
- bundle exec rspec --format documentation --color spec/unit
|
data/lib/semantic_puppet.rb
CHANGED
@@ -37,6 +37,15 @@ module SemanticPuppet
|
|
37
37
|
|
38
38
|
# @!endgroup
|
39
39
|
|
40
|
+
# Returns the unsatisfiable dependency, if any.
|
41
|
+
# @return String
|
42
|
+
def unsatisfiable
|
43
|
+
@module_dependencies ||= []
|
44
|
+
@satisfieds ||= []
|
45
|
+
|
46
|
+
(@module_dependencies - @satisfieds).first
|
47
|
+
end
|
48
|
+
|
40
49
|
# Fetches a graph of modules and their dependencies from the currently
|
41
50
|
# configured list of {Source}s.
|
42
51
|
#
|
@@ -64,10 +73,12 @@ module SemanticPuppet
|
|
64
73
|
# @param graph [Graph] the root of a dependency graph
|
65
74
|
# @return [Array<ModuleRelease>] the list of releases to act on
|
66
75
|
def resolve(graph)
|
76
|
+
@module_dependencies, @satisfieds = nil
|
77
|
+
|
67
78
|
catch :next do
|
68
79
|
return walk(graph, graph.dependencies.dup)
|
69
80
|
end
|
70
|
-
raise UnsatisfiableGraph.new(graph)
|
81
|
+
raise UnsatisfiableGraph.new(graph, unsatisfiable)
|
71
82
|
end
|
72
83
|
|
73
84
|
# Fetches all available releases for the given module name.
|
@@ -100,6 +111,9 @@ module SemanticPuppet
|
|
100
111
|
# @param considering [Array<GraphNode>] the set of releases being tested
|
101
112
|
# @return [Array<GraphNode>] the list of releases to use, if successful
|
102
113
|
def walk(graph, dependencies, *considering)
|
114
|
+
@satisfieds ||= []
|
115
|
+
@module_dependencies ||= []
|
116
|
+
|
103
117
|
return considering if dependencies.empty?
|
104
118
|
|
105
119
|
# Selecting a dependency from the collection...
|
@@ -113,6 +127,7 @@ module SemanticPuppet
|
|
113
127
|
|
114
128
|
# ... we'll iterate through the list of possible versions in order.
|
115
129
|
preferred_releases(deps).reverse_each do |dep|
|
130
|
+
@module_dependencies |= [name]
|
116
131
|
|
117
132
|
# We should skip any releases that violate any module's constraints.
|
118
133
|
unless [graph, *considering].all? { |x| x.satisfies_constraints?(dep) }
|
@@ -125,6 +140,8 @@ module SemanticPuppet
|
|
125
140
|
next
|
126
141
|
end
|
127
142
|
|
143
|
+
@satisfieds |= [name]
|
144
|
+
|
128
145
|
catch :next do
|
129
146
|
# After adding any new dependencies and imposing our own constraints
|
130
147
|
# on existing dependencies, we'll mark ourselves as "under
|
@@ -3,13 +3,19 @@ require 'semantic_puppet/dependency'
|
|
3
3
|
module SemanticPuppet
|
4
4
|
module Dependency
|
5
5
|
class UnsatisfiableGraph < StandardError
|
6
|
-
attr_reader :graph
|
6
|
+
attr_reader :graph, :unsatisfied
|
7
7
|
|
8
|
-
def initialize(graph)
|
8
|
+
def initialize(graph, unsatisfied = nil)
|
9
9
|
@graph = graph
|
10
10
|
|
11
11
|
deps = sentence_from_list(graph.modules)
|
12
|
-
|
12
|
+
|
13
|
+
if unsatisfied
|
14
|
+
@unsatisfied = unsatisfied
|
15
|
+
super "Could not find satisfying releases of #{unsatisfied} for #{deps}"
|
16
|
+
else
|
17
|
+
super "Could not find satisfying releases for #{deps}"
|
18
|
+
end
|
13
19
|
end
|
14
20
|
|
15
21
|
private
|
@@ -1,7 +1,6 @@
|
|
1
1
|
require 'semantic_puppet'
|
2
2
|
|
3
3
|
module SemanticPuppet
|
4
|
-
|
5
4
|
# @note SemanticPuppet::Version subclasses Numeric so that it has sane Range
|
6
5
|
# semantics in Ruby 1.9+.
|
7
6
|
class Version < Numeric
|
@@ -9,69 +8,46 @@ module SemanticPuppet
|
|
9
8
|
|
10
9
|
class ValidationFailure < ArgumentError; end
|
11
10
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
match, major, minor, patch, prerelease, build = *ver.match(/\A#{REGEX_FULL}\Z/)
|
19
|
-
|
20
|
-
if match.nil?
|
21
|
-
raise "Unable to parse '#{ver}' as a semantic version identifier"
|
22
|
-
end
|
23
|
-
|
24
|
-
prerelease = parse_prerelease(prerelease) if prerelease
|
25
|
-
# Build metadata is not yet supported in semantic_puppet, but we hope to.
|
26
|
-
# The following code prevents build metadata for now.
|
27
|
-
#build = parse_build_metadata(build) if build
|
28
|
-
if !build.nil?
|
29
|
-
raise "'#{ver}' MUST NOT include build identifiers"
|
30
|
-
end
|
31
|
-
|
32
|
-
self.new(major.to_i, minor.to_i, patch.to_i, prerelease, build)
|
33
|
-
end
|
11
|
+
# Parse a Semantic Version string.
|
12
|
+
#
|
13
|
+
# @param ver [String] the version string to parse
|
14
|
+
# @return [Version] a comparable {Version} object
|
15
|
+
def self.parse(ver)
|
16
|
+
match, major, minor, patch, prerelease, build = *ver.match(REGEX_FULL_RX)
|
34
17
|
|
35
|
-
#
|
36
|
-
#
|
37
|
-
# @param ver [String] the version string to validate
|
38
|
-
# @return [bool] whether or not the string represents a valid Semantic Version
|
39
|
-
def valid?(ver)
|
40
|
-
!!(ver =~ /\A#{REGEX_FULL}\Z/)
|
41
|
-
end
|
18
|
+
raise ValidationFailure, "Unable to parse '#{ver}' as a semantic version identifier" unless match
|
42
19
|
|
43
|
-
|
44
|
-
|
45
|
-
subject = 'Prerelease identifiers'
|
46
|
-
prerelease = prerelease.split('.', -1)
|
47
|
-
|
48
|
-
if prerelease.empty? or prerelease.any? { |x| x.empty? }
|
49
|
-
raise "#{subject} MUST NOT be empty"
|
50
|
-
elsif prerelease.any? { |x| x =~ /[^0-9a-zA-Z-]/ }
|
51
|
-
raise "#{subject} MUST use only ASCII alphanumerics and hyphens"
|
52
|
-
elsif prerelease.any? { |x| x =~ /^0\d+$/ }
|
53
|
-
raise "#{subject} MUST NOT contain leading zeroes"
|
54
|
-
end
|
20
|
+
new(major.to_i, minor.to_i, patch.to_i, parse_prerelease(prerelease), parse_build(build)).freeze
|
21
|
+
end
|
55
22
|
|
56
|
-
|
23
|
+
# Validate a Semantic Version string.
|
24
|
+
#
|
25
|
+
# @param ver [String] the version string to validate
|
26
|
+
# @return [bool] whether or not the string represents a valid Semantic Version
|
27
|
+
def self.valid?(ver)
|
28
|
+
match = ver.match(REGEX_FULL_RX)
|
29
|
+
if match.nil?
|
30
|
+
false
|
31
|
+
else
|
32
|
+
prerelease = match[4]
|
33
|
+
prerelease.nil? || prerelease.split('.').all? { |x| !(x =~ /^0\d+$/) }
|
57
34
|
end
|
35
|
+
end
|
58
36
|
|
59
|
-
|
60
|
-
|
61
|
-
|
37
|
+
def self.parse_build(build)
|
38
|
+
build.nil? ? nil : build.split('.').freeze
|
39
|
+
end
|
62
40
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
41
|
+
def self.parse_prerelease(prerelease)
|
42
|
+
return nil unless prerelease
|
43
|
+
prerelease.split('.').map do |x|
|
44
|
+
if x =~ /^\d+$/
|
45
|
+
raise ValidationFailure, 'Numeric pre-release identifiers MUST NOT contain leading zeroes' if x.length > 1 && x.start_with?('0')
|
46
|
+
x.to_i
|
47
|
+
else
|
48
|
+
x
|
67
49
|
end
|
68
|
-
|
69
|
-
return build
|
70
|
-
end
|
71
|
-
|
72
|
-
def raise(msg)
|
73
|
-
super ValidationFailure, msg, caller.drop_while { |x| x !~ /\bparse\b/ }
|
74
|
-
end
|
50
|
+
end.freeze
|
75
51
|
end
|
76
52
|
|
77
53
|
attr_reader :major, :minor, :patch
|
@@ -95,24 +71,39 @@ module SemanticPuppet
|
|
95
71
|
end
|
96
72
|
end
|
97
73
|
|
74
|
+
# @return [String] the `prerelease` identifier as a string without the leading '-'
|
98
75
|
def prerelease
|
99
|
-
@prerelease
|
76
|
+
(@prerelease.nil? || @prerelease.empty?) ? nil : @prerelease.join('.')
|
100
77
|
end
|
101
78
|
|
102
79
|
# @return [Boolean] true if this is a stable release
|
103
80
|
def stable?
|
104
|
-
@prerelease.nil?
|
81
|
+
@prerelease.nil? || @prerelease.empty?
|
82
|
+
end
|
83
|
+
|
84
|
+
# @return [Version] this version stripped from any prerelease identifier.
|
85
|
+
def to_stable
|
86
|
+
@prerelease.nil? ? self : Version.new(@major, @minor, @patch, nil, @build)
|
105
87
|
end
|
106
88
|
|
89
|
+
# @return [String] the `build` identifier as a string without the leading '+'
|
107
90
|
def build
|
108
|
-
@build
|
91
|
+
(@build.nil? || @build.empty?) ? nil : @build.join('.')
|
109
92
|
end
|
110
93
|
|
111
94
|
def <=>(other)
|
112
|
-
return
|
113
|
-
|
114
|
-
|
115
|
-
|
95
|
+
return nil unless other.is_a?(Version)
|
96
|
+
cmp = @major <=> other.major
|
97
|
+
if cmp == 0
|
98
|
+
cmp = @minor <=> other.minor
|
99
|
+
if cmp == 0
|
100
|
+
cmp = @patch <=> other.patch
|
101
|
+
if cmp == 0
|
102
|
+
cmp = compare_prerelease(other)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
cmp
|
116
107
|
end
|
117
108
|
|
118
109
|
def eql?(other)
|
@@ -126,70 +117,87 @@ module SemanticPuppet
|
|
126
117
|
alias == eql?
|
127
118
|
|
128
119
|
def to_s
|
129
|
-
"#{major}.#{minor}.#{patch}"
|
130
|
-
|
131
|
-
|
120
|
+
s = "#{@major}.#{@minor}.#{@patch}"
|
121
|
+
|
122
|
+
# Appending the @prerelease and @build in a thoroughly tested and optimized
|
123
|
+
# way. Using interpolations and/or array joins may look simpler but will slows
|
124
|
+
# things down. Don't change this code without measuring performance of new
|
125
|
+
# solution.
|
126
|
+
unless @prerelease.nil?
|
127
|
+
s << '-' << @prerelease[0].to_s
|
128
|
+
i = 0
|
129
|
+
l = @prerelease.length
|
130
|
+
while (i += 1) < l
|
131
|
+
s << '.' << @prerelease[i].to_s
|
132
|
+
end
|
133
|
+
end
|
134
|
+
unless @build.nil?
|
135
|
+
s << '+' << @build[0].to_s
|
136
|
+
i = 0
|
137
|
+
l = @build.length
|
138
|
+
while (i += 1) < l
|
139
|
+
s << '.' << @build[i].to_s
|
140
|
+
end
|
141
|
+
end
|
142
|
+
s
|
132
143
|
end
|
144
|
+
alias inspect to_s
|
133
145
|
|
134
146
|
def hash
|
135
|
-
|
147
|
+
(((((@major * 0x100) ^ @minor) * 0x100) ^ @patch) * 0x100) ^ @prerelease.hash
|
136
148
|
end
|
137
149
|
|
138
|
-
private
|
139
|
-
# This is a hack; tildes sort later than any valid identifier. The
|
140
|
-
# advantage is that we don't need to handle stable vs. prerelease
|
141
|
-
# comparisons separately.
|
142
|
-
@@STABLE_RELEASE = [ '~' ].freeze
|
143
|
-
|
144
150
|
def compare_prerelease(other)
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
#
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
return
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
151
|
+
mine = @prerelease
|
152
|
+
|
153
|
+
# Need to use the instance variable here to avoid getting a string
|
154
|
+
yours = other.instance_variable_get(:@prerelease)
|
155
|
+
|
156
|
+
# A version that has a prerelease is always less than a version that doesn't
|
157
|
+
if mine.nil?
|
158
|
+
yours.nil? ? 0 : 1
|
159
|
+
elsif yours.nil?
|
160
|
+
-1
|
161
|
+
else
|
162
|
+
# Compare all prerelease identifier segments that can be compared. Should
|
163
|
+
# all segments compare equal up to the point where one of the prereleases
|
164
|
+
# have no more segments, then the one with more segements is greater.
|
165
|
+
your_max = yours.size
|
166
|
+
mine.each_with_index do |x, idx|
|
167
|
+
# 'mine' win if 'your' list of segments is exhausted
|
168
|
+
return 1 if idx >= your_max
|
169
|
+
y = yours[idx]
|
170
|
+
|
171
|
+
# Integer always wins over String
|
172
|
+
cmp = if x.is_a?(Integer)
|
173
|
+
y.is_a?(Integer) ? x <=> y : -1
|
174
|
+
elsif y.is_a?(Integer)
|
175
|
+
1
|
176
|
+
else
|
177
|
+
x <=> y
|
178
|
+
end
|
179
|
+
|
180
|
+
# No need to continue if a diff is found
|
181
|
+
return cmp unless cmp == 0
|
171
182
|
end
|
172
|
-
end
|
173
183
|
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
self.class.new(@major, @minor, @patch, [])
|
184
|
+
# All segments, up to the point where at least one list of segement is exhausted,
|
185
|
+
# compared equal. The one with the highest segment count wins.
|
186
|
+
mine.size <=> your_max
|
187
|
+
end
|
179
188
|
end
|
180
189
|
|
181
|
-
public
|
182
|
-
|
183
190
|
# Version string matching regexes
|
184
|
-
REGEX_NUMERIC =
|
185
|
-
REGEX_PRE =
|
186
|
-
REGEX_BUILD =
|
187
|
-
REGEX_FULL = REGEX_NUMERIC + REGEX_PRE + REGEX_BUILD
|
191
|
+
REGEX_NUMERIC = '(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)'.freeze # Major . Minor . Patch
|
192
|
+
REGEX_PRE = '(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?'.freeze # Prerelease
|
193
|
+
REGEX_BUILD = '(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?'.freeze # Build
|
194
|
+
REGEX_FULL = REGEX_NUMERIC + REGEX_PRE + REGEX_BUILD.freeze
|
195
|
+
REGEX_FULL_RX = /\A#{REGEX_FULL}\Z/.freeze
|
188
196
|
|
189
197
|
# The lowest precedence Version possible
|
190
|
-
MIN = self.new(0, 0, 0, []).freeze
|
198
|
+
MIN = self.new(0, 0, 0, [].freeze).freeze
|
191
199
|
|
192
200
|
# The highest precedence Version possible
|
193
|
-
MAX = self.new(
|
201
|
+
MAX = self.new(Float::INFINITY, 0, 0).freeze
|
194
202
|
end
|
195
203
|
end
|