diff options
-rw-r--r-- | Rakefile | 1 | ||||
-rw-r--r-- | chef-config/lib/chef-config/package_task.rb | 9 | ||||
-rw-r--r-- | lib/chef/version.rb | 4 | ||||
-rw-r--r-- | lib/chef/version_string.rb | 143 | ||||
-rw-r--r-- | spec/unit/version_string_spec.rb | 79 |
5 files changed, 232 insertions, 4 deletions
@@ -34,6 +34,7 @@ require_relative "tasks/version" ChefConfig::PackageTask.new(File.expand_path("..", __FILE__), "Chef", "chef") do |package| package.component_paths = ["chef-config"] package.generate_version_class = true + package.use_versionstring = true end task :pedant, :chef_zero_spec diff --git a/chef-config/lib/chef-config/package_task.rb b/chef-config/lib/chef-config/package_task.rb index 6c4ca4f435..33c28b23fa 100644 --- a/chef-config/lib/chef-config/package_task.rb +++ b/chef-config/lib/chef-config/package_task.rb @@ -59,6 +59,9 @@ module ChefConfig # Name of git remote used to push tags during a release. Default is origin. attr_accessor :git_remote + # True if should use Chef::VersionString. + attr_accessor :use_versionstring + def initialize(root_path = nil, module_name = nil, gem_name = nil) init(root_path, module_name, gem_name) yield self if block_given? @@ -162,7 +165,7 @@ module ChefConfig end namespace :version do - desc 'Regenerate lib/#{@module_path}/version.rb from VERSION file' + desc "Regenerate lib/#{module_path}/version.rb from VERSION file" task :update => :update_components_versions do update_version_rb update_gemfile_lock @@ -219,10 +222,10 @@ module ChefConfig # this repo. Do not edit this manually. Edit the VERSION file and run the rake # task instead. #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - +#{"\nrequire 'chef/version_string'\n" if use_versionstring} #{class_or_module} #{module_name} #{module_name.upcase}_ROOT = File.expand_path("../..", __FILE__) - VERSION = "#{version}" + VERSION = #{use_versionstring ? "Chef::VersionString.new(\"#{version}\")" : "\"#{version}\""} end # diff --git a/lib/chef/version.rb b/lib/chef/version.rb index ca2996d505..44596e20da 100644 --- a/lib/chef/version.rb +++ b/lib/chef/version.rb @@ -19,9 +19,11 @@ # task instead. #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +require "chef/version_string" + class Chef CHEF_ROOT = File.expand_path("../..", __FILE__) - VERSION = "13.1.32" + VERSION = Chef::VersionString.new("13.1.32") end # diff --git a/lib/chef/version_string.rb b/lib/chef/version_string.rb new file mode 100644 index 0000000000..c307509425 --- /dev/null +++ b/lib/chef/version_string.rb @@ -0,0 +1,143 @@ +# Copyright:: Copyright 2017, Noah Kantrowitz +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +class Chef + # String-like object for version strings. + # + # @since 13.2 + # @api internal + class VersionString < String + # Parsed version object for the string. + # @return [Gem::Version] + attr_reader :parsed_version + + # Create a new VersionString from an input String. + # + # @param val [String] Version string to parse. + def initialize(val) + super + @parsed_version = ::Gem::Version.create(self) + end + + # @!group Compat wrappers for String + + # Compat wrapper for + to behave like a normal String. + # + # @param other [String] + # @return [String] + def +(other) + to_s + other + end + + # Compat wrapper for * to behave like a normal String. + # + # @param other [Integer] + # @return [String] + def *(other) + to_s * other + end + + # @!group Comparison operators + + # Compare a VersionString to an object. If compared to another VersionString + # then sort like `Gem::Version`, otherwise try to treat the other object as + # a version but fall back to normal string comparison. + # + # @param other [Object] + # @return [Integer] + def <=>(other) + other_ver = case other + when VersionString + other.parsed_version + else + begin + Gem::Version.create(other.to_s) + rescue ArgumentError + # Comparing to a string that isn't a version. + return super + end + end + parsed_version <=> other_ver + end + + # Compat wrapper for == based on <=>. + # + # @param other [Object] + # @return [Boolean] + def ==(other) + (self <=> other) == 0 + end + + # Compat wrapper for != based on <=>. + # + # @param other [Object] + # @return [Boolean] + def !=(other) + (self <=> other) != 0 + end + + # Compat wrapper for < based on <=>. + # + # @param other [Object] + # @return [Boolean] + def <(other) + (self <=> other) < 0 + end + + # Compat wrapper for <= based on <=>. + # + # @param other [Object] + # @return [Boolean] + def <=(other) + (self <=> other) < 1 + end + + # Compat wrapper for > based on <=>. + # + # @param other [Object] + # @return [Boolean] + def >(other) + (self <=> other) > 0 + end + + # Compat wrapper for >= based on <=>. + # + # @param other [Object] + # @return [Boolean] + def >=(other) + (self <=> other) > -1 + end + + # @!group Matching operators + + # Matching operator to support checking against a requirement string. + # + # @param other [Regexp, String] + # @return [Boolean] + # @example Match against a Regexp + # Chef::VersionString.new('1.0.0') =~ /^1/ + # @example Match against a requirement + # Chef::VersionString.new('1.0.0') =~ '~> 1.0' + def =~(other) + case other + when Regexp + super + else + Gem::Requirement.create(other) =~ parsed_version + end + end + + end +end diff --git a/spec/unit/version_string_spec.rb b/spec/unit/version_string_spec.rb new file mode 100644 index 0000000000..14fc786d1b --- /dev/null +++ b/spec/unit/version_string_spec.rb @@ -0,0 +1,79 @@ +# Copyright:: Copyright 2017, Noah Kantrowitz +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require "spec_helper" +require "chef/version_string" + +describe Chef::VersionString do + let(:input) { "1.2.3" } + subject(:described_object) { described_class.new(input) } + + it { is_expected.to eq "1.2.3" } + it { is_expected.to eql "1.2.3" } + it { is_expected.to be == "1.2.3" } + it { is_expected.to be < "abc" } + it { is_expected.to be > "0" } + it { is_expected.to eq described_class.new("1.2.3") } + it { is_expected.to be == described_class.new("1.2.3") } + + context "with !=" do + subject { described_object != "1.2.4" } + it { is_expected.to be true } + end + + context "with +" do + subject { described_object + "asdf" } + it { is_expected.to eq "1.2.3asdf" } + end + + context "with *" do + subject { described_object * 3 } + it { is_expected.to eq "1.2.31.2.31.2.3" } + end + + context "with version-like comparisons" do + subject { described_class.new("1.02.3") } + + it { is_expected.to eq "1.2.3" } + it { is_expected.to be > "1.2.2" } + it { is_expected.to be > "1.2.3a" } + it { is_expected.to be < "1.2.4" } + end + + context "with =~ Regexp" do + subject { described_object =~ /^1/ } + it { is_expected.to eq 0 } + end + + context "with =~ Requirement" do + subject { described_object =~ Gem::Requirement.create("~> 1.0") } + it { is_expected.to be true } + end + + context "with =~ String" do + subject { described_object =~ "~> 1.0" } + it { is_expected.to be true } + end + + context "with Regexp =~" do + subject { /^2/ =~ described_object } + it { is_expected.to be nil } + end + + context "with String =~" do + subject { "~> 1.0" =~ described_object } + it { expect { subject }.to raise_error TypeError } + end +end |