├── Version
├── .gitignore
├── .travis.yml
├── Gemfile
├── lib
├── versionomy
│ ├── version.rb
│ ├── conversion
│ │ ├── base.rb
│ │ └── parsing.rb
│ ├── schema.rb
│ ├── errors.rb
│ ├── format
│ │ └── base.rb
│ ├── conversion.rb
│ ├── interface.rb
│ ├── format.rb
│ ├── format_definitions
│ │ ├── semver.rb
│ │ └── rubygems.rb
│ └── schema
│ │ └── wrapper.rb
└── versionomy.rb
├── test
├── tc_custom_format.rb
├── tc_version_of.rb
├── tc_standard_misc.rb
├── tc_standard_change.rb
├── tc_standard_reset.rb
├── tc_readme_examples.rb
├── tc_standard_comparison.rb
├── tc_standard_bump.rb
├── tc_standard_basic.rb
├── tc_semver_basic.rb
├── tc_semver_conversions.rb
├── tc_rubygems_conversions.rb
├── tc_rubygems_basic.rb
└── tc_standard_parse.rb
├── versionomy.gemspec
├── History.rdoc
├── README.rdoc
├── Rakefile
└── Versionomy.rdoc
/Version:
--------------------------------------------------------------------------------
1 | 0.5.0
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /doc/
2 | /pkg/
3 | .rbx/
4 | .DS_Store
5 | *.rbc
6 | /Gemfile.lock
7 | .ruby-version
8 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: ruby
2 | rvm:
3 | - 1.9.3
4 | - 2.0.0
5 | - 2.1
6 | - 2.2
7 | - 2.3.0
8 | - jruby
9 | - rbx
10 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | # -----------------------------------------------------------------------------
2 | #
3 | # Versionomy Gemfile
4 | #
5 | # This file indicates gems needed for testing.
6 | #
7 | # -----------------------------------------------------------------------------
8 | # Copyright 2012 Daniel Azuma
9 | #
10 | # All rights reserved.
11 | #
12 | # Redistribution and use in source and binary forms, with or without
13 | # modification, are permitted provided that the following conditions are met:
14 | #
15 | # * Redistributions of source code must retain the above copyright notice,
16 | # this list of conditions and the following disclaimer.
17 | # * Redistributions in binary form must reproduce the above copyright notice,
18 | # this list of conditions and the following disclaimer in the documentation
19 | # and/or other materials provided with the distribution.
20 | # * Neither the name of the copyright holder, nor the names of any other
21 | # contributors to this software, may be used to endorse or promote products
22 | # derived from this software without specific prior written permission.
23 | #
24 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
28 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 | # POSSIBILITY OF SUCH DAMAGE.
35 | # -----------------------------------------------------------------------------
36 | ;
37 |
38 |
39 | source "http://rubygems.org/"
40 |
41 | gem('blockenspiel', '>= 0.5.0')
42 |
43 | group(:test) do
44 | gem('minitest', '>= 5.8')
45 | gem('rake', '>= 10.0')
46 | gem('rdoc', '>= 4.2')
47 | end
48 |
--------------------------------------------------------------------------------
/lib/versionomy/version.rb:
--------------------------------------------------------------------------------
1 | # -----------------------------------------------------------------------------
2 | #
3 | # Versionomy version
4 | #
5 | # -----------------------------------------------------------------------------
6 | # Copyright 2008 Daniel Azuma
7 | #
8 | # All rights reserved.
9 | #
10 | # Redistribution and use in source and binary forms, with or without
11 | # modification, are permitted provided that the following conditions are met:
12 | #
13 | # * Redistributions of source code must retain the above copyright notice,
14 | # this list of conditions and the following disclaimer.
15 | # * Redistributions in binary form must reproduce the above copyright notice,
16 | # this list of conditions and the following disclaimer in the documentation
17 | # and/or other materials provided with the distribution.
18 | # * Neither the name of the copyright holder, nor the names of any other
19 | # contributors to this software, may be used to endorse or promote products
20 | # derived from this software without specific prior written permission.
21 | #
22 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 | # POSSIBILITY OF SUCH DAMAGE.
33 | # -----------------------------------------------------------------------------
34 | ;
35 |
36 |
37 | module Versionomy
38 |
39 | # Current gem version, as a frozen string.
40 | VERSION_STRING = ::File.read(::File.dirname(__FILE__)+'/../../Version').strip.freeze
41 |
42 | # Current gem version, as a Versionomy::Value.
43 | VERSION = ::Versionomy.parse(VERSION_STRING, :standard)
44 |
45 | end
46 |
--------------------------------------------------------------------------------
/lib/versionomy.rb:
--------------------------------------------------------------------------------
1 | # -----------------------------------------------------------------------------
2 | #
3 | # Versionomy entry point
4 | #
5 | # -----------------------------------------------------------------------------
6 | # Copyright 2008 Daniel Azuma
7 | #
8 | # All rights reserved.
9 | #
10 | # Redistribution and use in source and binary forms, with or without
11 | # modification, are permitted provided that the following conditions are met:
12 | #
13 | # * Redistributions of source code must retain the above copyright notice,
14 | # this list of conditions and the following disclaimer.
15 | # * Redistributions in binary form must reproduce the above copyright notice,
16 | # this list of conditions and the following disclaimer in the documentation
17 | # and/or other materials provided with the distribution.
18 | # * Neither the name of the copyright holder, nor the names of any other
19 | # contributors to this software, may be used to endorse or promote products
20 | # derived from this software without specific prior written permission.
21 | #
22 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 | # POSSIBILITY OF SUCH DAMAGE.
33 | # -----------------------------------------------------------------------------
34 | ;
35 |
36 |
37 | require 'blockenspiel'
38 |
39 | require 'versionomy/errors'
40 | require 'versionomy/schema'
41 | require 'versionomy/schema/field'
42 | require 'versionomy/schema/wrapper'
43 | require 'versionomy/format'
44 | require 'versionomy/format/base'
45 | require 'versionomy/format/delimiter'
46 | require 'versionomy/value'
47 | require 'versionomy/conversion'
48 | require 'versionomy/conversion/base'
49 | require 'versionomy/conversion/parsing'
50 | require 'versionomy/interface'
51 | require 'versionomy/version'
52 |
--------------------------------------------------------------------------------
/test/tc_custom_format.rb:
--------------------------------------------------------------------------------
1 | # -----------------------------------------------------------------------------
2 | #
3 | # Versionomy parsing tests on standard schema
4 | #
5 | # This file contains tests for parsing on the standard schema
6 | #
7 | # -----------------------------------------------------------------------------
8 | # Copyright 2008 Daniel Azuma
9 | #
10 | # All rights reserved.
11 | #
12 | # Redistribution and use in source and binary forms, with or without
13 | # modification, are permitted provided that the following conditions are met:
14 | #
15 | # * Redistributions of source code must retain the above copyright notice,
16 | # this list of conditions and the following disclaimer.
17 | # * Redistributions in binary form must reproduce the above copyright notice,
18 | # this list of conditions and the following disclaimer in the documentation
19 | # and/or other materials provided with the distribution.
20 | # * Neither the name of the copyright holder, nor the names of any other
21 | # contributors to this software, may be used to endorse or promote products
22 | # derived from this software without specific prior written permission.
23 | #
24 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
28 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 | # POSSIBILITY OF SUCH DAMAGE.
35 | # -----------------------------------------------------------------------------
36 |
37 |
38 | require 'minitest/autorun'
39 | require 'versionomy'
40 |
41 |
42 | module Versionomy
43 | module Tests # :nodoc:
44 |
45 | class TestCustomFormat < ::Minitest::Test # :nodoc:
46 |
47 |
48 | # Test parsing with custom format for patchlevel
49 |
50 | def test_parsing_custom_patchlevel_format
51 | format_ = ::Versionomy.default_format.modified_copy do
52 | field(:patchlevel, :requires_previous_field => false) do
53 | recognize_number(:delimiter_regexp => '\s?sp', :default_delimiter => ' SP')
54 | end
55 | end
56 | value1_ = ::Versionomy.parse('2008 SP2', format_)
57 | assert_equal(2, value1_.patchlevel)
58 | value2_ = value1_.format.parse('2008 sp3')
59 | assert_equal(3, value2_.patchlevel)
60 | end
61 |
62 |
63 | end
64 |
65 | end
66 | end
67 |
--------------------------------------------------------------------------------
/versionomy.gemspec:
--------------------------------------------------------------------------------
1 | # -----------------------------------------------------------------------------
2 | #
3 | # Versionomy gemspec
4 | #
5 | # This file contains the gemspec for Versionomy.
6 | #
7 | # -----------------------------------------------------------------------------
8 | # Copyright 2011 Daniel Azuma
9 | #
10 | # All rights reserved.
11 | #
12 | # Redistribution and use in source and binary forms, with or without
13 | # modification, are permitted provided that the following conditions are met:
14 | #
15 | # * Redistributions of source code must retain the above copyright notice,
16 | # this list of conditions and the following disclaimer.
17 | # * Redistributions in binary form must reproduce the above copyright notice,
18 | # this list of conditions and the following disclaimer in the documentation
19 | # and/or other materials provided with the distribution.
20 | # * Neither the name of the copyright holder, nor the names of any other
21 | # contributors to this software, may be used to endorse or promote products
22 | # derived from this software without specific prior written permission.
23 | #
24 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
28 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 | # POSSIBILITY OF SUCH DAMAGE.
35 | # -----------------------------------------------------------------------------
36 | ;
37 |
38 |
39 | ::Gem::Specification.new do |s_|
40 | s_.name = 'versionomy'
41 | s_.summary = 'Versionomy is a generalized version number library.'
42 | s_.description = 'Versionomy is a generalized version number library. It provides tools to represent, manipulate, parse, and compare version numbers in the wide variety of versioning schemes in use.'
43 | s_.version = "#{::File.read('Version').strip}.nonrelease"
44 | s_.licenses = ['BSD-3-Clause']
45 | s_.author = 'Daniel Azuma'
46 | s_.email = 'dazuma@gmail.com'
47 | s_.homepage = 'http://dazuma.github.com/versionomy'
48 | s_.rubyforge_project = 'virtuoso'
49 | s_.required_ruby_version = '>= 1.9.3'
50 | s_.files = ::Dir.glob("lib/**/*.rb") +
51 | ::Dir.glob("test/**/*.rb") +
52 | ::Dir.glob("*.rdoc") +
53 | ['Version']
54 | s_.extra_rdoc_files = ::Dir.glob("*.rdoc")
55 | s_.test_files = ::Dir.glob("test/**/tc_*.rb")
56 | s_.platform = ::Gem::Platform::RUBY
57 | s_.add_dependency('blockenspiel', '~> 0.5')
58 | end
59 |
--------------------------------------------------------------------------------
/test/tc_version_of.rb:
--------------------------------------------------------------------------------
1 | # -----------------------------------------------------------------------------
2 | #
3 | # Versionomy basic tests on standard schema
4 | #
5 | # This file contains tests for the basic use cases on the standard schema
6 | #
7 | # -----------------------------------------------------------------------------
8 | # Copyright 2008 Daniel Azuma
9 | #
10 | # All rights reserved.
11 | #
12 | # Redistribution and use in source and binary forms, with or without
13 | # modification, are permitted provided that the following conditions are met:
14 | #
15 | # * Redistributions of source code must retain the above copyright notice,
16 | # this list of conditions and the following disclaimer.
17 | # * Redistributions in binary form must reproduce the above copyright notice,
18 | # this list of conditions and the following disclaimer in the documentation
19 | # and/or other materials provided with the distribution.
20 | # * Neither the name of the copyright holder, nor the names of any other
21 | # contributors to this software, may be used to endorse or promote products
22 | # derived from this software without specific prior written permission.
23 | #
24 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
28 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 | # POSSIBILITY OF SUCH DAMAGE.
35 | # -----------------------------------------------------------------------------
36 |
37 |
38 | require 'minitest/autorun'
39 | require 'versionomy'
40 |
41 |
42 | module Versionomy
43 | module Tests # :nodoc:
44 |
45 | class TestVersionOf < ::Minitest::Test # :nodoc:
46 |
47 |
48 | # Gems to test if we can
49 | GEM_LIST = {
50 | 'activerecord' => {:require => 'active_record', :module_name => 'ActiveRecord'},
51 | 'blockenspiel' => {:module_name => 'Blockenspiel'},
52 | 'bundler' => {:module_name => 'Bundler'},
53 | 'erubis' => {:module_name => 'Erubis'},
54 | }
55 |
56 |
57 | # Engine that tests each gem if it's installed
58 | zero_ = ::Versionomy.create(:major => 0)
59 | GEM_LIST.each do |name_, data_|
60 | unless data_.kind_of?(::Hash)
61 | data_ = {:module_name => data_}
62 | end
63 | begin
64 | gem name_
65 | require data_[:require] || name_
66 | rescue ::LoadError
67 | next
68 | end
69 | define_method("test_gem_#{name_}") do
70 | mod_ = eval(data_[:module_name])
71 | value_ = ::Versionomy.version_of(mod_)
72 | refute_nil(value_)
73 | refute_equal(zero_, value_)
74 | end
75 | end
76 |
77 |
78 | end
79 |
80 | end
81 | end
82 |
--------------------------------------------------------------------------------
/lib/versionomy/conversion/base.rb:
--------------------------------------------------------------------------------
1 | # -----------------------------------------------------------------------------
2 | #
3 | # Versionomy conversion base class
4 | #
5 | # -----------------------------------------------------------------------------
6 | # Copyright 2008 Daniel Azuma
7 | #
8 | # All rights reserved.
9 | #
10 | # Redistribution and use in source and binary forms, with or without
11 | # modification, are permitted provided that the following conditions are met:
12 | #
13 | # * Redistributions of source code must retain the above copyright notice,
14 | # this list of conditions and the following disclaimer.
15 | # * Redistributions in binary form must reproduce the above copyright notice,
16 | # this list of conditions and the following disclaimer in the documentation
17 | # and/or other materials provided with the distribution.
18 | # * Neither the name of the copyright holder, nor the names of any other
19 | # contributors to this software, may be used to endorse or promote products
20 | # derived from this software without specific prior written permission.
21 | #
22 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 | # POSSIBILITY OF SUCH DAMAGE.
33 | # -----------------------------------------------------------------------------
34 | ;
35 |
36 |
37 | module Versionomy
38 |
39 | module Conversion
40 |
41 |
42 | # The base conversion class.
43 | #
44 | # This base class defines the API for a conversion. All conversions must
45 | # define the method convert_value documented here. Conversions
46 | # need not actually extend this base class, as long as they duck-type
47 | # this method. However, this base class does provide a few convenience
48 | # methods such as a sane implementation of inspect.
49 |
50 | class Base
51 |
52 |
53 | # Create a conversion using a simple DSL.
54 | # You can pass a block to the initializer that takes the same
55 | # parameters as convert_value, and the conversion will use that block
56 | # to perform the conversion.
57 |
58 | def initialize(&block_)
59 | @_converter = block_
60 | end
61 |
62 |
63 | # Convert the given value to the given format and return the converted
64 | # value.
65 | #
66 | # The convert_params may be interpreted however the particular
67 | # conversion wishes.
68 | #
69 | # Raises Versionomy::Errors::ConversionError if the conversion failed.
70 |
71 | def convert_value(value_, format_, convert_params_=nil)
72 | if @_converter
73 | @_converter.call(value_, format_, convert_params_)
74 | else
75 | raise Errors::ConversionError, "Conversion not implemented"
76 | end
77 | end
78 |
79 |
80 | # Inspect this conversion.
81 |
82 | def inspect
83 | "#<#{self.class}:0x#{object_id.to_s(16)}>"
84 | end
85 |
86 |
87 | # The default to_s implementation just calls inspect.
88 |
89 | def to_s
90 | inspect
91 | end
92 |
93 |
94 | end
95 |
96 |
97 | end
98 |
99 | end
100 |
--------------------------------------------------------------------------------
/test/tc_standard_misc.rb:
--------------------------------------------------------------------------------
1 | # -----------------------------------------------------------------------------
2 | #
3 | # Versionomy basic tests on standard schema
4 | #
5 | # This file contains tests for the basic use cases on the standard schema
6 | #
7 | # -----------------------------------------------------------------------------
8 | # Copyright 2008 Daniel Azuma
9 | #
10 | # All rights reserved.
11 | #
12 | # Redistribution and use in source and binary forms, with or without
13 | # modification, are permitted provided that the following conditions are met:
14 | #
15 | # * Redistributions of source code must retain the above copyright notice,
16 | # this list of conditions and the following disclaimer.
17 | # * Redistributions in binary form must reproduce the above copyright notice,
18 | # this list of conditions and the following disclaimer in the documentation
19 | # and/or other materials provided with the distribution.
20 | # * Neither the name of the copyright holder, nor the names of any other
21 | # contributors to this software, may be used to endorse or promote products
22 | # derived from this software without specific prior written permission.
23 | #
24 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
28 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 | # POSSIBILITY OF SUCH DAMAGE.
35 | # -----------------------------------------------------------------------------
36 |
37 |
38 | require 'minitest/autorun'
39 | require 'versionomy'
40 | require 'yaml'
41 |
42 | module Versionomy
43 | module Tests # :nodoc:
44 |
45 | class TestStandardMisc < ::Minitest::Test # :nodoc:
46 |
47 |
48 | # Test "prerelase?" custom method
49 |
50 | def test_method_prereleasep
51 | value_ = ::Versionomy.create(:major => 2, :tiny => 1, :release_type => :beta, :beta_version => 3)
52 | assert_equal(true, value_.prerelease?)
53 | value_ = ::Versionomy.create(:major => 2, :tiny => 1, :release_type => :final, :patchlevel => 1)
54 | assert_equal(false, value_.prerelease?)
55 | value_ = ::Versionomy.create(:major => 2, :tiny => 1)
56 | assert_equal(false, value_.prerelease?)
57 | end
58 |
59 |
60 | # Test "relase" custom method
61 |
62 | def test_method_release
63 | value_ = ::Versionomy.create(:major => 1, :minor => 9, :tiny => 2, :release_type => :alpha, :alpha_version => 4)
64 | value2_ = value_.release
65 | assert_equal([1, 9, 2, 0, :final, 0, 0], value2_.values_array)
66 | value_ = ::Versionomy.create(:major => 1, :minor => 9, :tiny => 2)
67 | value2_ = value_.release
68 | assert_equal(value_, value2_)
69 | end
70 |
71 |
72 | # Test marshalling
73 |
74 | def test_marshal
75 | value_ = ::Versionomy.create(:major => 1, :minor => 9, :tiny => 2, :release_type => :alpha, :alpha_version => 4)
76 | str_ = ::Marshal.dump(value_)
77 | value2_ = ::Marshal.load(str_)
78 | assert_equal(value_, value2_)
79 | end
80 |
81 |
82 | # Test YAML
83 |
84 | def test_yaml
85 | value_ = ::Versionomy.create(:major => 1, :minor => 9, :tiny => 2, :release_type => :alpha, :alpha_version => 4)
86 | str_ = ::YAML.dump(value_)
87 | value2_ = ::YAML.load(str_)
88 | assert_equal(value_, value2_)
89 | end
90 |
91 |
92 | end
93 |
94 | end
95 | end
96 |
--------------------------------------------------------------------------------
/test/tc_standard_change.rb:
--------------------------------------------------------------------------------
1 | # -----------------------------------------------------------------------------
2 | #
3 | # Versionomy change tests on standard schema
4 | #
5 | # This file contains tests for the change function on the standard schema
6 | #
7 | # -----------------------------------------------------------------------------
8 | # Copyright 2008 Daniel Azuma
9 | #
10 | # All rights reserved.
11 | #
12 | # Redistribution and use in source and binary forms, with or without
13 | # modification, are permitted provided that the following conditions are met:
14 | #
15 | # * Redistributions of source code must retain the above copyright notice,
16 | # this list of conditions and the following disclaimer.
17 | # * Redistributions in binary form must reproduce the above copyright notice,
18 | # this list of conditions and the following disclaimer in the documentation
19 | # and/or other materials provided with the distribution.
20 | # * Neither the name of the copyright holder, nor the names of any other
21 | # contributors to this software, may be used to endorse or promote products
22 | # derived from this software without specific prior written permission.
23 | #
24 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
28 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 | # POSSIBILITY OF SUCH DAMAGE.
35 | # -----------------------------------------------------------------------------
36 |
37 |
38 | require 'minitest/autorun'
39 | require 'versionomy'
40 |
41 |
42 | module Versionomy
43 | module Tests # :nodoc:
44 |
45 | class TestStandardChange < ::Minitest::Test # :nodoc:
46 |
47 |
48 | # Test with a changed tiny
49 |
50 | def test_change_tiny
51 | value_ = ::Versionomy.create(:major => 2, :tiny => 1, :tiny2 => 3, :release_type => :release_candidate, :release_candidate_version => 2)
52 | value_ = value_.change(:tiny => 4)
53 | assert_equal(2, value_.major)
54 | assert_equal(0, value_.minor)
55 | assert_equal(4, value_.tiny)
56 | assert_equal(3, value_.tiny2)
57 | assert_equal(:release_candidate, value_.release_type)
58 | assert_equal(2, value_.release_candidate_version)
59 | assert_equal(0, value_.release_candidate_minor)
60 | end
61 |
62 |
63 | # Test with several changed fields
64 |
65 | def test_change_several
66 | value_ = ::Versionomy.create(:major => 2, :tiny => 1, :tiny2 => 3, :release_type => :release_candidate, :release_candidate_version => 2)
67 | value_ = value_.change(:tiny => 4, :release_candidate_version => 5)
68 | assert_equal(2, value_.major)
69 | assert_equal(0, value_.minor)
70 | assert_equal(4, value_.tiny)
71 | assert_equal(3, value_.tiny2)
72 | assert_equal(:release_candidate, value_.release_type)
73 | assert_equal(5, value_.release_candidate_version)
74 | assert_equal(0, value_.release_candidate_minor)
75 | end
76 |
77 |
78 | # Test with a changed release type
79 |
80 | def test_change_release_type
81 | value_ = ::Versionomy.create(:major => 2, :tiny => 1, :tiny2 => 3, :release_type => :release_candidate, :release_candidate_version => 2)
82 | value_ = value_.change(:release_type => :beta)
83 | assert_equal(2, value_.major)
84 | assert_equal(0, value_.minor)
85 | assert_equal(1, value_.tiny)
86 | assert_equal(3, value_.tiny2)
87 | assert_equal(:beta, value_.release_type)
88 | assert_equal(1, value_.beta_version)
89 | assert_equal(0, value_.beta_minor)
90 | assert_equal(false, value_.has_field?(:release_candidate_version))
91 | assert_equal(false, value_.has_field?(:release_candidate_minor))
92 | end
93 |
94 |
95 | end
96 |
97 | end
98 | end
99 |
--------------------------------------------------------------------------------
/test/tc_standard_reset.rb:
--------------------------------------------------------------------------------
1 | # -----------------------------------------------------------------------------
2 | #
3 | # Versionomy bump tests on standard schema
4 | #
5 | # This file contains tests for the bump function on the standard schema
6 | #
7 | # -----------------------------------------------------------------------------
8 | # Copyright 2008 Daniel Azuma
9 | #
10 | # All rights reserved.
11 | #
12 | # Redistribution and use in source and binary forms, with or without
13 | # modification, are permitted provided that the following conditions are met:
14 | #
15 | # * Redistributions of source code must retain the above copyright notice,
16 | # this list of conditions and the following disclaimer.
17 | # * Redistributions in binary form must reproduce the above copyright notice,
18 | # this list of conditions and the following disclaimer in the documentation
19 | # and/or other materials provided with the distribution.
20 | # * Neither the name of the copyright holder, nor the names of any other
21 | # contributors to this software, may be used to endorse or promote products
22 | # derived from this software without specific prior written permission.
23 | #
24 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
28 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 | # POSSIBILITY OF SUCH DAMAGE.
35 | # -----------------------------------------------------------------------------
36 |
37 |
38 | require 'minitest/autorun'
39 | require 'versionomy'
40 |
41 |
42 | module Versionomy
43 | module Tests # :nodoc:
44 |
45 | class TestStandardReset < ::Minitest::Test # :nodoc:
46 |
47 |
48 | # Test resetting a minor patchlevel.
49 |
50 | def test_reset_patchlevel_minor
51 | value_ = ::Versionomy.create(:major => 2, :tiny => 1, :patchlevel => 3, :patchlevel_minor => 1)
52 | value_ = value_.reset(:patchlevel_minor)
53 | assert_equal([2,0,1,0,:final,3,0], value_.values_array)
54 | end
55 |
56 |
57 | # Test resetting a major patchlevel.
58 |
59 | def test_reset_patchlevel
60 | value_ = ::Versionomy.create(:major => 2, :tiny => 1, :patchlevel => 3, :patchlevel_minor => 1)
61 | value_ = value_.reset(:patchlevel)
62 | assert_equal([2,0,1,0,:final,0,0], value_.values_array)
63 | end
64 |
65 |
66 | # Test resetting a beta release type.
67 |
68 | def test_reset_beta_release_type
69 | value_ = ::Versionomy.create(:major => 2, :tiny => 1, :release_type => :beta, :beta_version => 2)
70 | value_ = value_.reset(:release_type)
71 | assert_equal([2,0,1,0,:final,0,0], value_.values_array)
72 | end
73 |
74 |
75 | # Test resetting a final release type.
76 |
77 | def test_reset_final_release_type
78 | value_ = ::Versionomy.create(:major => 2, :tiny => 1, :patchlevel => 2)
79 | value_ = value_.reset(:release_type)
80 | assert_equal([2,0,1,0,:final,0,0], value_.values_array)
81 | end
82 |
83 |
84 | # Test resetting tiny.
85 |
86 | def test_reset_tiny
87 | value_ = ::Versionomy.create(:major => 2, :tiny => 1, :tiny2 => 3, :release_type => :release_candidate, :release_candidate_version => 2)
88 | value_ = value_.reset(:tiny)
89 | assert_equal([2,0,0,0,:final,0,0], value_.values_array)
90 | end
91 |
92 |
93 | # Test resetting major.
94 |
95 | def test_reset_major
96 | value_ = ::Versionomy.create(:major => 2, :tiny => 1, :tiny2 => 3, :release_type => :release_candidate, :release_candidate_version => 2)
97 | value_ = value_.reset(:major)
98 | assert_equal([1,0,0,0,:final,0,0], value_.values_array)
99 | end
100 |
101 |
102 | end
103 |
104 | end
105 | end
106 |
--------------------------------------------------------------------------------
/lib/versionomy/schema.rb:
--------------------------------------------------------------------------------
1 | # -----------------------------------------------------------------------------
2 | #
3 | # Versionomy schema namespace
4 | #
5 | # -----------------------------------------------------------------------------
6 | # Copyright 2008 Daniel Azuma
7 | #
8 | # All rights reserved.
9 | #
10 | # Redistribution and use in source and binary forms, with or without
11 | # modification, are permitted provided that the following conditions are met:
12 | #
13 | # * Redistributions of source code must retain the above copyright notice,
14 | # this list of conditions and the following disclaimer.
15 | # * Redistributions in binary form must reproduce the above copyright notice,
16 | # this list of conditions and the following disclaimer in the documentation
17 | # and/or other materials provided with the distribution.
18 | # * Neither the name of the copyright holder, nor the names of any other
19 | # contributors to this software, may be used to endorse or promote products
20 | # derived from this software without specific prior written permission.
21 | #
22 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 | # POSSIBILITY OF SUCH DAMAGE.
33 | # -----------------------------------------------------------------------------
34 | ;
35 |
36 |
37 | module Versionomy
38 |
39 |
40 | # === Version number schema.
41 | #
42 | # A schema defines the structure and semantics of a version number.
43 | # The schema controls what fields are present in the version, how
44 | # version numbers are compared, what the default values are, and how
45 | # values can change. Version numbers with the same schema can be
46 | # compared with one another, and version numbers can be converted
47 | # trivially to formats that share the same schema, without requiring a
48 | # Conversion implementation.
49 | #
50 | # At its simplest, a version number is defined as a sequence of fields,
51 | # each with a name and data type. These fields may be integer-valued,
52 | # string-valued, or symbolic, though most will probably be integers.
53 | # Symbolic fields are enumerated types that are useful, for example, if
54 | # you want a field to specify the type of prerelease (e.g. "alpha",
55 | # "beta", or "release candidate").
56 | #
57 | # As a simple conceptual example, you could construct a schema for
58 | # version numbers of the form "major.minor.tiny" like this. (This is a
59 | # conceptual diagram, not actual syntax.)
60 | #
61 | # ("major": integer), ("minor": integer), ("tiny": integer)
62 | #
63 | # More generally, fields are actually organized into a DAG (directed
64 | # acyclic graph) in which the "most significant" field is the root, the
65 | # next most significant is a child of that root, and so forth down the
66 | # line. The simple schema above, then, is actually represented as a
67 | # linked list (a graph with one path), like this:
68 | #
69 | # ("major": integer) ->
70 | # ("minor": integer) ->
71 | # ("tiny": integer) ->
72 | # nil
73 | #
74 | # It is, however, possible for the form of a field to depend on the value
75 | # of the previous field. For example, suppose we wanted a schema in which
76 | # if the value of the "minor" field is 0, then the "tiny" field doesn't
77 | # exist. e.g.
78 | #
79 | # ("major": integer) ->
80 | # ("minor": integer) ->
81 | # [value == 0] : nil
82 | # [otherwise] : ("tiny": integer) ->
83 | # nil
84 | #
85 | # The Versionomy::Schema::Field class represents a field in this graph.
86 | # The Versionomy::Schema::Wrapper class represents a full schema object.
87 | #
88 | # Generally, you should create schemas using Versionomy::Schema#create.
89 | # That method provides a DSL that lets you quickly create the fields.
90 |
91 | module Schema
92 | end
93 |
94 |
95 | end
96 |
--------------------------------------------------------------------------------
/test/tc_readme_examples.rb:
--------------------------------------------------------------------------------
1 | # -----------------------------------------------------------------------------
2 | #
3 | # Versionomy tests of the README examples
4 | #
5 | # This file contains tests to ensure the README is valid
6 | #
7 | # -----------------------------------------------------------------------------
8 | # Copyright 2008 Daniel Azuma
9 | #
10 | # All rights reserved.
11 | #
12 | # Redistribution and use in source and binary forms, with or without
13 | # modification, are permitted provided that the following conditions are met:
14 | #
15 | # * Redistributions of source code must retain the above copyright notice,
16 | # this list of conditions and the following disclaimer.
17 | # * Redistributions in binary form must reproduce the above copyright notice,
18 | # this list of conditions and the following disclaimer in the documentation
19 | # and/or other materials provided with the distribution.
20 | # * Neither the name of the copyright holder, nor the names of any other
21 | # contributors to this software, may be used to endorse or promote products
22 | # derived from this software without specific prior written permission.
23 | #
24 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
28 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 | # POSSIBILITY OF SUCH DAMAGE.
35 | # -----------------------------------------------------------------------------
36 |
37 |
38 | require 'minitest/autorun'
39 | require 'versionomy'
40 |
41 |
42 | module Versionomy
43 | module Tests # :nodoc:
44 |
45 | class TestReadmeExamples < ::Minitest::Test # :nodoc:
46 |
47 |
48 | # Test the README file.
49 | # This actually reads the README file and does some eval magic
50 | # to run it and ensure it works the way it claims.
51 |
52 | def test_readme_file
53 | binding_ = _get_binding
54 | ::File.open("#{::File.dirname(__FILE__)}/../README.rdoc") do |io_|
55 | running_ = false
56 | buffer_ = ''
57 | buffer_start_line_ = nil
58 | io_.each_line do |line_|
59 |
60 | # Run code in the "Some examples" section.
61 | running_ = false if line_ =~ /^===/
62 | running_ = true if line_ =~ /^=== Some examples/
63 | next unless running_ && line_[0,1] == ' '
64 | # Skip the require line
65 | next if line_ =~ /^\s+require/
66 |
67 | # If there isn't an expects clause, then collect the code into
68 | # a buffer to run all at once, because it might be code that
69 | # gets spread over multiple lines.
70 | delim_index_ = line_.index(' # ')
71 | if !delim_index_ || line_[0, delim_index_].strip.length == 0
72 | buffer_start_line_ ||= io_.lineno
73 | buffer_ << line_
74 | next
75 | end
76 |
77 | # At this point, we have an expects clause. First run any buffer
78 | # accumulated up to now.
79 | if buffer_.length > 0
80 | ::Kernel.eval(buffer_, binding_, 'README.rdoc', buffer_start_line_)
81 | buffer_ = ''
82 | buffer_start_line_ = nil
83 | end
84 |
85 | # Parse the line into an expression and an expectation
86 | expr_ = line_[0,delim_index_]
87 | expect_ = line_[delim_index_+3..-1]
88 |
89 | if expect_ =~ /^=> (.*)$/
90 | # Expect a value
91 | expect_value_ = ::Kernel.eval($1, binding_, 'README.rdoc', io_.lineno)
92 | actual_value_ = ::Kernel.eval(expr_, binding_, 'README.rdoc', io_.lineno)
93 | assert_equal(expect_value_, actual_value_,
94 | "Values did not match on line #{io_.lineno} of README.rdoc")
95 |
96 | elsif expect_ =~ /^raises (.*)$/
97 | # Expect an exception to be raised
98 | expect_error_ = ::Kernel.eval($1, binding_, 'README.rdoc', io_.lineno)
99 | assert_raises(expect_error_) do
100 | ::Kernel.eval(expr_, binding_, 'README.rdoc', io_.lineno)
101 | end
102 |
103 | else
104 | raise "Unknown expect syntax: #{expect_.inspect}"
105 | end
106 |
107 | end
108 | end
109 | end
110 |
111 |
112 | def _get_binding
113 | binding
114 | end
115 |
116 |
117 | end
118 |
119 | end
120 | end
121 |
--------------------------------------------------------------------------------
/lib/versionomy/errors.rb:
--------------------------------------------------------------------------------
1 | # -----------------------------------------------------------------------------
2 | #
3 | # Versionomy exceptions
4 | #
5 | # -----------------------------------------------------------------------------
6 | # Copyright 2008 Daniel Azuma
7 | #
8 | # All rights reserved.
9 | #
10 | # Redistribution and use in source and binary forms, with or without
11 | # modification, are permitted provided that the following conditions are met:
12 | #
13 | # * Redistributions of source code must retain the above copyright notice,
14 | # this list of conditions and the following disclaimer.
15 | # * Redistributions in binary form must reproduce the above copyright notice,
16 | # this list of conditions and the following disclaimer in the documentation
17 | # and/or other materials provided with the distribution.
18 | # * Neither the name of the copyright holder, nor the names of any other
19 | # contributors to this software, may be used to endorse or promote products
20 | # derived from this software without specific prior written permission.
21 | #
22 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 | # POSSIBILITY OF SUCH DAMAGE.
33 | # -----------------------------------------------------------------------------
34 | ;
35 |
36 |
37 | module Versionomy
38 |
39 |
40 | # This is a namespace for errors that can be thrown by Versionomy.
41 |
42 | module Errors
43 |
44 |
45 | # Base class for all Versionomy exceptions
46 |
47 | class VersionomyError < ::RuntimeError
48 | end
49 |
50 |
51 | # This exception is raised if parsing failed.
52 |
53 | class ParseError < VersionomyError
54 | end
55 |
56 |
57 | # This exception is raised if unparsing failed.
58 |
59 | class UnparseError < VersionomyError
60 | end
61 |
62 |
63 | # This exception is raised if you try to set a value that is not
64 | # allowed by the schema.
65 |
66 | class IllegalValueError < VersionomyError
67 | end
68 |
69 |
70 | # This exception is raised if you try to perform a comparison
71 | # between incompatible schemas.
72 |
73 | class SchemaMismatchError < VersionomyError
74 | end
75 |
76 |
77 | # Base class for all Versionomy schema creation exceptions
78 |
79 | class SchemaCreationError < VersionomyError
80 | end
81 |
82 |
83 | # This exception is raised during schema creation if you try to add
84 | # the same symbol twice to the same symbolic field.
85 |
86 | class SymbolRedefinedError < SchemaCreationError
87 | end
88 |
89 |
90 | # This exception is raised during schema creation if you try to
91 | # create two fields covering overlapping ranges.
92 |
93 | class RangeOverlapError < SchemaCreationError
94 | end
95 |
96 |
97 | # This exception is raised during schema creation if the range
98 | # specification cannot be interpreted.
99 |
100 | class RangeSpecificationError < SchemaCreationError
101 | end
102 |
103 |
104 | # This exception is raised during schema creation if you try to
105 | # add a symbol to a non-symbolic schema.
106 |
107 | class TypeMismatchError < SchemaCreationError
108 | end
109 |
110 |
111 | # This exception is raised during schema creation if you try to
112 | # create a circular graph.
113 |
114 | class CircularDescendantError < SchemaCreationError
115 | end
116 |
117 |
118 | # Base class for all Versionomy format creation exceptions.
119 |
120 | class FormatCreationError < VersionomyError
121 | end
122 |
123 |
124 | # This exception is raised if you try to register a format
125 | # with a name that has already been used.
126 |
127 | class FormatRedefinedError < VersionomyError
128 | end
129 |
130 |
131 | # Raised by the Format registry if you try to retrieve a format with
132 | # an unrecognized name in strict mode.
133 |
134 | class UnknownFormatError < VersionomyError
135 | end
136 |
137 |
138 | # Raised when a conversion fails.
139 |
140 | class ConversionError < VersionomyError
141 | end
142 |
143 |
144 | # Raised when a conversion fails because no conversion implementation
145 | # was found.
146 |
147 | class UnknownConversionError < ConversionError
148 | end
149 |
150 |
151 | # Raised when you try to register a conversion when one already
152 | # exists for its schemas.
153 |
154 | class ConversionRedefinedError < VersionomyError
155 | end
156 |
157 |
158 | end
159 |
160 | end
161 |
--------------------------------------------------------------------------------
/lib/versionomy/format/base.rb:
--------------------------------------------------------------------------------
1 | # -----------------------------------------------------------------------------
2 | #
3 | # Versionomy format base class
4 | #
5 | # -----------------------------------------------------------------------------
6 | # Copyright 2008 Daniel Azuma
7 | #
8 | # All rights reserved.
9 | #
10 | # Redistribution and use in source and binary forms, with or without
11 | # modification, are permitted provided that the following conditions are met:
12 | #
13 | # * Redistributions of source code must retain the above copyright notice,
14 | # this list of conditions and the following disclaimer.
15 | # * Redistributions in binary form must reproduce the above copyright notice,
16 | # this list of conditions and the following disclaimer in the documentation
17 | # and/or other materials provided with the distribution.
18 | # * Neither the name of the copyright holder, nor the names of any other
19 | # contributors to this software, may be used to endorse or promote products
20 | # derived from this software without specific prior written permission.
21 | #
22 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 | # POSSIBILITY OF SUCH DAMAGE.
33 | # -----------------------------------------------------------------------------
34 | ;
35 |
36 |
37 | module Versionomy
38 |
39 | module Format
40 |
41 |
42 | # The base format.
43 | #
44 | # This format doesn't actually do anything useful. It causes all strings
45 | # to parse to the schema's default value, and unparses all values to the
46 | # empty string. Instead, the purpose here is to define the API for a
47 | # format.
48 | #
49 | # All formats must define the methods +schema+, +parse+, and +unparse+.
50 | # It is also recommended that formats define the === method,
51 | # though this is not strictly required. Finally, formats may optionally
52 | # implement uparse_for_serialize.
53 | #
54 | # Formats need not extend this base class, as long as they duck-type
55 | # these methods.
56 |
57 | class Base
58 |
59 |
60 | # Create an instance of this base format, with the given schema.
61 |
62 | def initialize(schema_)
63 | @_schema = schema_
64 | end
65 |
66 |
67 | def inspect # :nodoc:
68 | "#<#{self.class}:0x#{object_id.to_s(16)} schema=#{@_schema.inspect}>"
69 | end
70 |
71 | def to_s # :nodoc:
72 | inspect
73 | end
74 |
75 |
76 | # Returns the schema understood by this format.
77 |
78 | def schema
79 | @_schema
80 | end
81 |
82 |
83 | # Parse the given string and return a value.
84 | #
85 | # The optional parameter hash can be used to pass parameters to the
86 | # parser to affect its behavior. The exact parameters supported are
87 | # defined by the format.
88 |
89 | def parse(string_, params_=nil)
90 | Value.new([], self)
91 | end
92 |
93 |
94 | # Unparse the given value and return a string.
95 | #
96 | # The optional parameter hash can be used to pass parameters to the
97 | # unparser to affect its behavior. The exact parameters supported
98 | # are defined by the format.
99 |
100 | def unparse(value_, params_=nil)
101 | ''
102 | end
103 |
104 |
105 | # An optional method that does unparsing especially for serialization.
106 | # Implement this if normal unparsing is "lossy" and doesn't guarantee
107 | # reconstruction of the version number. This method should attempt to
108 | # unparse in such a way that the entire version value can be
109 | # reconstructed from the unparsed string. Serialization routines will
110 | # first attempt to call this method to unparse for serialization. If
111 | # this method is not present, the normal unparse method will be used.
112 | #
113 | # Return either the unparsed string, or an array consisting of the
114 | # unparsed string and a hash of parse params to pass to the parser
115 | # when the string is to be reconstructed. You may also either return
116 | # nil or raise Versionomy::Errors::UnparseError if the unparsing
117 | # cannot be done satisfactorily for serialization. In this case,
118 | # serialization will be done using the raw value data rather than an
119 | # unparsed string.
120 | #
121 | # This default implementation just turns around and calls unparse.
122 | # Thus it is equivalent to the method not being present at all.
123 |
124 | def unparse_for_serialization(value_)
125 | unparse(value_)
126 | end
127 |
128 |
129 | # Determine whether the given value uses this format.
130 |
131 | def ===(obj_)
132 | if obj_.kind_of?(Value)
133 | obj_.format == self
134 | else
135 | obj_ == self
136 | end
137 | end
138 |
139 |
140 | end
141 |
142 |
143 | end
144 |
145 | end
146 |
--------------------------------------------------------------------------------
/test/tc_standard_comparison.rb:
--------------------------------------------------------------------------------
1 | # -----------------------------------------------------------------------------
2 | #
3 | # Versionomy comparison tests on standard schema
4 | #
5 | # This file contains tests for comparisons on the standard schema
6 | #
7 | # -----------------------------------------------------------------------------
8 | # Copyright 2008 Daniel Azuma
9 | #
10 | # All rights reserved.
11 | #
12 | # Redistribution and use in source and binary forms, with or without
13 | # modification, are permitted provided that the following conditions are met:
14 | #
15 | # * Redistributions of source code must retain the above copyright notice,
16 | # this list of conditions and the following disclaimer.
17 | # * Redistributions in binary form must reproduce the above copyright notice,
18 | # this list of conditions and the following disclaimer in the documentation
19 | # and/or other materials provided with the distribution.
20 | # * Neither the name of the copyright holder, nor the names of any other
21 | # contributors to this software, may be used to endorse or promote products
22 | # derived from this software without specific prior written permission.
23 | #
24 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
28 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 | # POSSIBILITY OF SUCH DAMAGE.
35 | # -----------------------------------------------------------------------------
36 |
37 |
38 | require 'minitest/autorun'
39 | require 'versionomy'
40 |
41 |
42 | module Versionomy
43 | module Tests # :nodoc:
44 |
45 | class TestStandardComparison < ::Minitest::Test # :nodoc:
46 |
47 |
48 | # Test comparisons with difference in major.
49 |
50 | def test_comparison_major
51 | value1_ = ::Versionomy.create(:major => 2, :tiny => 1, :tiny2 => 3, :release_type => :release_candidate, :release_candidate_version => 2)
52 | value2_ = ::Versionomy.create(:major => 3, :release_type => :alpha)
53 | assert(value2_ > value1_)
54 | end
55 |
56 |
57 | # Test comparisons with difference in minor.
58 |
59 | def test_comparison_minor
60 | value1_ = ::Versionomy.create(:major => 2, :tiny => 1, :tiny2 => 3, :release_type => :release_candidate, :release_candidate_version => 2)
61 | value2_ = ::Versionomy.create(:major => 2, :minor => 1, :release_type => :alpha)
62 | assert(value2_ > value1_)
63 | end
64 |
65 |
66 | # Test comparisons with difference in release type.
67 |
68 | def test_comparison_release_type
69 | value1_ = ::Versionomy.create(:major => 2, :release_type => :alpha, :alpha_version => 5)
70 | value2_ = ::Versionomy.create(:major => 2, :release_type => :release_candidate, :release_candidate_version => 2)
71 | assert(value2_ > value1_)
72 | end
73 |
74 |
75 | # Test equality for a simple case.
76 |
77 | def test_equality_simple
78 | value1_ = ::Versionomy.create(:major => 2, :minor => 0, :release_type => :alpha, :alpha_version => 5)
79 | value2_ = ::Versionomy.create(:major => 2, :release_type => :alpha, :alpha_version => 5)
80 | assert_equal(value2_, value1_)
81 | assert_equal(value2_.hash, value1_.hash)
82 | end
83 |
84 |
85 | # Test equality from parsed values.
86 |
87 | def test_equality_parsed
88 | value1_ = ::Versionomy.parse("1.8.7p72")
89 | value2_ = ::Versionomy.parse("1.8.7.0-72.0")
90 | assert_equal(value2_, value1_)
91 | assert_equal(value2_.hash, value1_.hash)
92 | end
93 |
94 |
95 | # Test non-equality from parsed values.
96 |
97 | def test_nonequality_parsed
98 | value1_ = ::Versionomy.parse("1.8.7b7")
99 | value2_ = ::Versionomy.parse("1.8.7a7")
100 | refute_equal(value2_, value1_)
101 | refute_equal(value2_.hash, value1_.hash)
102 | end
103 |
104 |
105 | # Test equality with string.
106 |
107 | def test_equality_string
108 | value1_ = ::Versionomy.parse("1.8.7p72")
109 | assert_operator(value1_, :==, "1.8.7p72")
110 | assert_operator(value1_, :==, "1.8.7.0-72.0")
111 | end
112 |
113 |
114 | # Test comparison with string.
115 |
116 | def test_comparison_string
117 | value1_ = ::Versionomy.parse("1.8.7p72")
118 | assert_operator(value1_, :<, "1.8.7p73")
119 | assert_operator(value1_, :<, "1.8.8pre1")
120 | assert_operator(value1_, :>, "1.8.7p71")
121 | assert_operator(value1_, :>, "1.8.7rc2")
122 | assert_operator(value1_, :>, "1.8.7.0")
123 | end
124 |
125 |
126 | # Test sorting.
127 |
128 | def test_sort
129 | value1_ = ::Versionomy.parse("1.8.7p73")
130 | value2_ = ::Versionomy.parse("1.8.7p72")
131 | value3_ = ::Versionomy.parse("1.8.8pre1")
132 | value4_ = ::Versionomy.parse("1.8.7.0")
133 | value5_ = ::Versionomy.parse("1.8.7rc2")
134 | assert_equal([value5_, value4_, value2_, value1_, value3_],
135 | [value1_, value2_, value3_, value4_, value5_].sort)
136 | end
137 |
138 |
139 | end
140 |
141 | end
142 | end
143 |
--------------------------------------------------------------------------------
/History.rdoc:
--------------------------------------------------------------------------------
1 | === 0.5.0 / 2016-01-07
2 |
3 | * The gemspec no longer includes the timestamp in the version, so that bundler can pull from github. (Reported by corneverbruggen)
4 | * Updated the Rakefile, tests, and general infrastructure to play better with modern Rubies.
5 | * Dropped support for Ruby 1.8, because who still uses 1.8???
6 |
7 | === 0.4.4 / 2012-06-27
8 |
9 | * Tried to be a little more robust against incomplete psych installations.
10 | * Travis CI integration.
11 |
12 | === 0.4.3 / 2012-03-27
13 |
14 | * Fixed a few warnings.
15 |
16 | === 0.4.2 / 2012-02-17
17 |
18 | * Support psych interface for YAML serialization.
19 |
20 | === 0.4.1 / 2011-04-26
21 |
22 | * Support underscore "_" as a delimiter.
23 | * A .gemspec file is now available for gem building and bundler git integration.
24 | * Some cleanup of the Rakefile and tests.
25 |
26 | === 0.4.0 / 2010-05-24
27 |
28 | * Included Semantic Version (http://semver.org/) format called "semver".
29 | * Parsing conversions can now prescreen or premodify the original format value.
30 | * Conversion algorithm tries to convert through the standard format if a direct conversion isn't found. For example, there is currently no direct conversion between semver and rubygems formats, but the converter can nevertheless convert the two because both can convert to and from standard.
31 | * Expanded the module version detection to include VERSION submodules.
32 | * Some Rakefile fixes to match RDoc and Ruby 1.9 changes.
33 |
34 | === 0.3.0 / 2009-11-30
35 |
36 | * Alpha release, opened for public feedback
37 | * Autoloading of format definitions using a load path.
38 | * Format and conversion registry/lookup is now thread-safe.
39 | * Implemented resetting a particular field in the value.
40 | * Implemented aliases for field names.
41 | * Changed the canonical YAML tag to its permanent value. The old one will continue to be recognized, but only the permanent one will be written from now on.
42 | * Documentation updates
43 |
44 | === 0.2.5 / 2009-11-24
45 |
46 | * Preserve minimum width of a field, if the field has leading zeros. For example, "2.01".
47 |
48 | === 0.2.4 / 2009-11-19
49 |
50 | * Fixed a regression introduced in 0.2.2 where "1.0a" was being recognized as an alpha version rather than a patchlevel of 1, and similar for "1.0b" and "1.0d".
51 |
52 | === 0.2.3 / 2009-11-19
53 |
54 | * Recognize "_" and "u" as patchlevel delimiters, to support Sun's Java version numbers (e.g. "1.6.0_17", "6u17").
55 | * Recognize "v" prefix found on some version numbers (e.g. "v1.2")
56 |
57 | === 0.2.2 / 2009-11-18
58 |
59 | * Standard format now supports certain kinds of prereleases without a prerelease number. e.g. "1.9.2dev" is interpreted as the same number as "1.9.2dev0".
60 | * Added Versionomy#ruby_version.
61 | * A field can specify a default_value for parsing, distinct from the one specified by the schema.
62 | * A field can specify requires_next_field to control whether the following field is required or optional.
63 |
64 | === 0.2.1 / 2009-11-08
65 |
66 | * Added Versionomy#version_of.
67 | * Now lets Blockenspiel set its own VERSION instead of reaching into Blockenspiel's namespace.
68 |
69 | === 0.2.0 / 2009-11-05
70 |
71 | * API CHANGE: Slight change to value comparison semantics. Value#eql? returns true only if the schemas are the same, whereas Value#== and the greater than and less than comparisons attempt to compare the semantic value, and thus may perform automatic schema conversion on the RHS.
72 | * API CHANGE: Merged Formats namespace into Format. Versionomy::Formats is now a (deprecated) alias for Versionomy::Format.
73 | * API CHANGE: The delimiter parsing algorithm now defaults :extra_characters to :error instead of :ignore.
74 | * Added a mechanism for converting from one format/schema to another.
75 | * Added Rubygems format, along with conversions to and from the standard format.
76 | * Values now include Comparable.
77 | * Values can now be serialized using Marshal and YAML.
78 | * Schemas can now add custom methods to value objects. Added "prerelease?" and "release" methods to rubygems and standard format values.
79 | * Added default field settings to schema DSL.
80 | * Implemented #=== for schemas and formats.
81 | * Many minor bug fixes and documentation updates.
82 |
83 | === 0.1.3 / 2009-10-29
84 |
85 | * Fixed an issue with parsing the "-p" patchlevel delimiter, e.g. "1.9.1-p243". (Reported by Luis Lavena.)
86 |
87 | === 0.1.2 / 2009-10-28
88 |
89 | * You can now specify fields by index in methods of Versionomy::Value.
90 | * Minor rakefile and documentation updates.
91 |
92 | === 0.1.1 / 2009-10-19
93 |
94 | * Formats can now be specified by name in the convenience interface.
95 | * FormatRedefinedError no longer subclasses FormatCreationError.
96 | * Some documentation updates.
97 | * Rakefile updates for publishing to rubyforge and gemcutter.
98 |
99 | === 0.1.0 / 2009-10-14
100 |
101 | * General rearchitecture. Better distinction between format and schema. Schema split into schema and field objects so the API makes more sense. Values are tighter and easier to use. Formats can now be built using a DSL. A bunch of API changes and bug fixes accompanied this-- too many to list.
102 | * In the standard schema, renamed release type "release" to "final". Also renamed release type "prerelease" to "preview", now sorted between "release candidate" and "final".
103 | * Documentation is much more complete.
104 | * Now tested and confirmed compatible with Matz Ruby 1.9.1 and JRuby 1.4.
105 | * Now uses blockenspiel 0.2; thus longer requires the mixology gem.
106 | * Building no longer requires hoe.
107 |
108 | === 0.0.4 / 2008-10-24
109 |
110 | * Fixed incompatibility with Blockenspiel 0.0.4
111 | * Fixed a number of issues with remembering value parse settings
112 | * Parser recognizes additional release type formats
113 | * Values have a parse method to parse another string in the same form
114 | * Implemented comparison between value and string
115 | * Exceptions correctly raised on comparison between incompatible types
116 |
117 | === 0.0.3 / 2008-10-21
118 |
119 | * Fixed string representations (inspect method)
120 | * Fixed up equality and hash computation for version values
121 |
122 | === 0.0.2 / 2008-10-20
123 |
124 | * Fixed manifest
125 |
126 | === 0.0.1 / 2008-10-20
127 |
128 | * Initial test release
129 |
--------------------------------------------------------------------------------
/lib/versionomy/conversion.rb:
--------------------------------------------------------------------------------
1 | # -----------------------------------------------------------------------------
2 | #
3 | # Versionomy conversion interface and registry
4 | #
5 | # -----------------------------------------------------------------------------
6 | # Copyright 2008 Daniel Azuma
7 | #
8 | # All rights reserved.
9 | #
10 | # Redistribution and use in source and binary forms, with or without
11 | # modification, are permitted provided that the following conditions are met:
12 | #
13 | # * Redistributions of source code must retain the above copyright notice,
14 | # this list of conditions and the following disclaimer.
15 | # * Redistributions in binary form must reproduce the above copyright notice,
16 | # this list of conditions and the following disclaimer in the documentation
17 | # and/or other materials provided with the distribution.
18 | # * Neither the name of the copyright holder, nor the names of any other
19 | # contributors to this software, may be used to endorse or promote products
20 | # derived from this software without specific prior written permission.
21 | #
22 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 | # POSSIBILITY OF SUCH DAMAGE.
33 | # -----------------------------------------------------------------------------
34 | ;
35 |
36 |
37 | require 'thread'
38 |
39 |
40 | module Versionomy
41 |
42 |
43 | # === Conversion between version schemas.
44 | #
45 | # Conversions are algorithms for converting from one schema to another.
46 | # This is useful for performing conversions as well as comparing version
47 | # numbers that use different schemas.
48 | #
49 | # To implement a conversion algorithm, implement the API defined by
50 | # Versionomy::Conversion::Base. Then, register your conversion by calling
51 | # Versionomy::Conversion#register. You will need to specify which schemas
52 | # (from and to) that your conversion should handle. From that point on,
53 | # whenever Versionomy needs to convert a value between those two schemas,
54 | # it will use your conversion. You can register the same conversion object
55 | # for multiple pairs of schemas, but you can register only one conversion
56 | # object for any pair.
57 | #
58 | # A common technique for doing conversions is to unparse the version to a
59 | # string, and then parse it in the new format. Versionomy provides a tool,
60 | # Versionomy::Conversion::Parsing, for performing such conversions. The
61 | # conversions between the standard and rubygems formats uses this tool.
62 | # See Versionomy::Conversion::Rubygems for annotated examples.
63 |
64 | module Conversion
65 |
66 | @registry = {}
67 | @mutex = ::Mutex.new
68 |
69 | class << self
70 |
71 |
72 | # Convert the given value to the given format. This is identical to
73 | # calling value_.convert(format_, convert_params_).
74 | #
75 | # The format may be specified as a format object or as the name of a
76 | # format in the Format registry.
77 | #
78 | # Raises Versionomy::Errors::ConversionError if the value could not
79 | # be converted.
80 |
81 | def convert(value_, format_, convert_params_=nil)
82 | value_.convert(format_, convert_params_)
83 | end
84 |
85 |
86 | # Get a conversion capable of converting between the given schemas.
87 | #
88 | # The schemas may be specified as format names, Format objects,
89 | # schema wrapper objects, or the root field of the schema.
90 | #
91 | # If strict is set to false, returns nil if no such conversion could
92 | # be found. If strict is set to true, may raise one of these errors:
93 | #
94 | # Raises Versionomy::Errors::UnknownFormatError if a format was
95 | # specified by name but the name is not known.
96 | #
97 | # Raises Versionomy::Errors::UnknownConversionError if the formats
98 | # were recognized but no conversion was found to handle them.
99 |
100 | def get(from_schema_, to_schema_, strict_=false)
101 | key_ = _get_key(from_schema_, to_schema_)
102 | conversion_ = @mutex.synchronize{ @registry[key_] }
103 | if strict_ && conversion_.nil?
104 | raise Errors::UnknownConversionError
105 | end
106 | conversion_
107 | end
108 |
109 |
110 | # Register the given conversion as the handler for the given schemas.
111 | #
112 | # The schemas may be specified as format names, Format objects,
113 | # schema wrapper objects, or the root field of the schema.
114 | #
115 | # Raises Versionomy::Errors::ConversionRedefinedError if a conversion
116 | # has already been registered for the given schemas.
117 | #
118 | # Raises Versionomy::Errors::UnknownFormatError if a format was
119 | # specified by name but the name is not known.
120 |
121 | def register(from_schema_, to_schema_, conversion_, silent_=false)
122 | key_ = _get_key(from_schema_, to_schema_)
123 | @mutex.synchronize do
124 | if @registry.include?(key_)
125 | unless silent_
126 | raise Errors::ConversionRedefinedError
127 | end
128 | else
129 | @registry[key_] = conversion_
130 | end
131 | end
132 | end
133 |
134 |
135 | private
136 |
137 | def _get_key(from_schema_, to_schema_) # :nodoc:
138 | [_get_schema(from_schema_), _get_schema(to_schema_)]
139 | end
140 |
141 | def _get_schema(schema_) # :nodoc:
142 | schema_ = Format.get(schema_, true) if schema_.kind_of?(::String) || schema_.kind_of?(::Symbol)
143 | schema_ = schema_.schema if schema_.respond_to?(:schema)
144 | schema_ = schema_.root_field if schema_.respond_to?(:root_field)
145 | schema_
146 | end
147 |
148 |
149 | end
150 |
151 | end
152 |
153 |
154 | end
155 |
--------------------------------------------------------------------------------
/test/tc_standard_bump.rb:
--------------------------------------------------------------------------------
1 | # -----------------------------------------------------------------------------
2 | #
3 | # Versionomy bump tests on standard schema
4 | #
5 | # This file contains tests for the bump function on the standard schema
6 | #
7 | # -----------------------------------------------------------------------------
8 | # Copyright 2008 Daniel Azuma
9 | #
10 | # All rights reserved.
11 | #
12 | # Redistribution and use in source and binary forms, with or without
13 | # modification, are permitted provided that the following conditions are met:
14 | #
15 | # * Redistributions of source code must retain the above copyright notice,
16 | # this list of conditions and the following disclaimer.
17 | # * Redistributions in binary form must reproduce the above copyright notice,
18 | # this list of conditions and the following disclaimer in the documentation
19 | # and/or other materials provided with the distribution.
20 | # * Neither the name of the copyright holder, nor the names of any other
21 | # contributors to this software, may be used to endorse or promote products
22 | # derived from this software without specific prior written permission.
23 | #
24 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
28 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 | # POSSIBILITY OF SUCH DAMAGE.
35 | # -----------------------------------------------------------------------------
36 |
37 |
38 | require 'minitest/autorun'
39 | require 'versionomy'
40 |
41 |
42 | module Versionomy
43 | module Tests # :nodoc:
44 |
45 | class TestStandardBump < ::Minitest::Test # :nodoc:
46 |
47 |
48 | # Test bumping a minor patchlevel.
49 |
50 | def test_bump_patchlevel_minor
51 | value_ = ::Versionomy.create(:major => 2, :tiny => 1, :patchlevel => 3, :patchlevel_minor => 0)
52 | value_ = value_.bump(:patchlevel_minor)
53 | assert_equal(2, value_.major)
54 | assert_equal(0, value_.minor)
55 | assert_equal(1, value_.tiny)
56 | assert_equal(0, value_.tiny2)
57 | assert_equal(:final, value_.release_type)
58 | assert_equal(3, value_.patchlevel)
59 | assert_equal(1, value_.patchlevel_minor)
60 | end
61 |
62 |
63 | # Test bumping a major patchlevel.
64 |
65 | def test_bump_patchlevel
66 | value_ = ::Versionomy.create(:major => 2, :tiny => 1, :patchlevel => 3, :patchlevel_minor => 1)
67 | value_ = value_.bump(:patchlevel)
68 | assert_equal(2, value_.major)
69 | assert_equal(0, value_.minor)
70 | assert_equal(1, value_.tiny)
71 | assert_equal(0, value_.tiny2)
72 | assert_equal(:final, value_.release_type)
73 | assert_equal(4, value_.patchlevel)
74 | assert_equal(0, value_.patchlevel_minor)
75 | end
76 |
77 |
78 | # Test bumping release type preview.
79 |
80 | def test_bump_preview_to_release
81 | value_ = ::Versionomy.create(:major => 2, :tiny => 1, :release_type => :preview)
82 | value_ = value_.bump(:release_type)
83 | assert_equal(2, value_.major)
84 | assert_equal(0, value_.minor)
85 | assert_equal(1, value_.tiny)
86 | assert_equal(0, value_.tiny2)
87 | assert_equal(:final, value_.release_type)
88 | assert_equal(0, value_.patchlevel)
89 | assert_equal(0, value_.patchlevel_minor)
90 | end
91 |
92 |
93 | # Test bumping release type development.
94 |
95 | def test_bump_development_to_alpha
96 | value_ = ::Versionomy.create(:major => 2, :tiny => 1, :release_type => :development, :development_version => 7)
97 | value_ = value_.bump(:release_type)
98 | assert_equal(2, value_.major)
99 | assert_equal(0, value_.minor)
100 | assert_equal(1, value_.tiny)
101 | assert_equal(0, value_.tiny2)
102 | assert_equal(:alpha, value_.release_type)
103 | assert_equal(1, value_.alpha_version)
104 | assert_equal(0, value_.alpha_minor)
105 | end
106 |
107 |
108 | # Test bumping release type alpha.
109 |
110 | def test_bump_alpha_to_beta
111 | value_ = ::Versionomy.create(:major => 2, :tiny => 1, :release_type => :alpha)
112 | value_ = value_.bump(:release_type)
113 | assert_equal(2, value_.major)
114 | assert_equal(0, value_.minor)
115 | assert_equal(1, value_.tiny)
116 | assert_equal(0, value_.tiny2)
117 | assert_equal(:beta, value_.release_type)
118 | assert_equal(1, value_.beta_version)
119 | assert_equal(0, value_.beta_minor)
120 | end
121 |
122 |
123 | # Test bumping release type release_candidate.
124 |
125 | def test_bump_release_candidate_to_release
126 | value_ = ::Versionomy.create(:major => 2, :tiny => 1, :release_type => :release_candidate, :release_candidate_version => 2)
127 | value_ = value_.bump(:release_type)
128 | assert_equal(2, value_.major)
129 | assert_equal(0, value_.minor)
130 | assert_equal(1, value_.tiny)
131 | assert_equal(0, value_.tiny2)
132 | assert_equal(:final, value_.release_type)
133 | assert_equal(0, value_.patchlevel)
134 | assert_equal(0, value_.patchlevel_minor)
135 | end
136 |
137 |
138 | # Test bumping tiny.
139 |
140 | def test_bump_tiny
141 | value_ = ::Versionomy.create(:major => 2, :tiny => 1, :tiny2 => 3, :release_type => :release_candidate, :release_candidate_version => 2)
142 | value_ = value_.bump(:tiny)
143 | assert_equal(2, value_.major)
144 | assert_equal(0, value_.minor)
145 | assert_equal(2, value_.tiny)
146 | assert_equal(0, value_.tiny2)
147 | assert_equal(:final, value_.release_type)
148 | assert_equal(0, value_.patchlevel)
149 | assert_equal(0, value_.patchlevel_minor)
150 | end
151 |
152 |
153 | # Test bumping major.
154 |
155 | def test_bump_major
156 | value_ = ::Versionomy.create(:major => 2, :tiny => 1, :tiny2 => 3, :release_type => :release_candidate, :release_candidate_version => 2)
157 | value_ = value_.bump(:major)
158 | assert_equal(3, value_.major)
159 | assert_equal(0, value_.minor)
160 | assert_equal(0, value_.tiny)
161 | assert_equal(0, value_.tiny2)
162 | assert_equal(:final, value_.release_type)
163 | assert_equal(0, value_.patchlevel)
164 | assert_equal(0, value_.patchlevel_minor)
165 | end
166 |
167 |
168 | end
169 |
170 | end
171 | end
172 |
--------------------------------------------------------------------------------
/test/tc_standard_basic.rb:
--------------------------------------------------------------------------------
1 | # -----------------------------------------------------------------------------
2 | #
3 | # Versionomy basic tests on standard schema
4 | #
5 | # This file contains tests for the basic use cases on the standard schema
6 | #
7 | # -----------------------------------------------------------------------------
8 | # Copyright 2008 Daniel Azuma
9 | #
10 | # All rights reserved.
11 | #
12 | # Redistribution and use in source and binary forms, with or without
13 | # modification, are permitted provided that the following conditions are met:
14 | #
15 | # * Redistributions of source code must retain the above copyright notice,
16 | # this list of conditions and the following disclaimer.
17 | # * Redistributions in binary form must reproduce the above copyright notice,
18 | # this list of conditions and the following disclaimer in the documentation
19 | # and/or other materials provided with the distribution.
20 | # * Neither the name of the copyright holder, nor the names of any other
21 | # contributors to this software, may be used to endorse or promote products
22 | # derived from this software without specific prior written permission.
23 | #
24 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
28 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 | # POSSIBILITY OF SUCH DAMAGE.
35 | # -----------------------------------------------------------------------------
36 |
37 |
38 | require 'minitest/autorun'
39 | require 'versionomy'
40 |
41 |
42 | module Versionomy
43 | module Tests # :nodoc:
44 |
45 | class TestStandardBasic < ::Minitest::Test # :nodoc:
46 |
47 |
48 | # Test the default version value.
49 |
50 | def test_default_value
51 | value_ = ::Versionomy.create
52 | assert_equal(1, value_.major)
53 | assert_equal(0, value_.minor)
54 | assert_equal(0, value_.tiny)
55 | assert_equal(0, value_.tiny2)
56 | assert_equal(:final, value_.release_type)
57 | assert_equal(0, value_.patchlevel)
58 | assert_equal(0, value_.patchlevel_minor)
59 | end
60 |
61 |
62 | # Test an arbitrary release value.
63 |
64 | def test_release_value_1
65 | value_ = ::Versionomy.create(:major => 1, :tiny => 4, :tiny2 => 2, :patchlevel => 5)
66 | assert_equal(1, value_.major)
67 | assert_equal(0, value_.minor)
68 | assert_equal(4, value_.tiny)
69 | assert_equal(2, value_.tiny2)
70 | assert_equal(:final, value_.release_type)
71 | assert_equal(5, value_.patchlevel)
72 | assert_equal(0, value_.patchlevel_minor)
73 | assert_equal(false, value_.has_field?(:prerelase_version))
74 | assert_equal(false, value_.has_field?(:prerelase_minor))
75 | end
76 |
77 |
78 | # Test an arbitrary release value.
79 |
80 | def test_release_value_2
81 | value_ = ::Versionomy.create(:major => 0, :minor => 3)
82 | assert_equal(0, value_.major)
83 | assert_equal(3, value_.minor)
84 | assert_equal(0, value_.tiny)
85 | assert_equal(0, value_.tiny2)
86 | assert_equal(:final, value_.release_type)
87 | assert_equal(0, value_.patchlevel)
88 | assert_equal(0, value_.patchlevel_minor)
89 | assert_equal(false, value_.has_field?(:prerelase_version))
90 | assert_equal(false, value_.has_field?(:prerelase_minor))
91 | end
92 |
93 |
94 | # Test an arbitrary preview value.
95 |
96 | def test_preview_value_1
97 | value_ = ::Versionomy.create(:major => 2, :minor => 3, :release_type => :preview, :preview_version => 3)
98 | assert_equal(2, value_.major)
99 | assert_equal(3, value_.minor)
100 | assert_equal(0, value_.tiny)
101 | assert_equal(0, value_.tiny2)
102 | assert_equal(:preview, value_.release_type)
103 | assert_equal(3, value_.preview_version)
104 | assert_equal(0, value_.preview_minor)
105 | assert_equal(false, value_.has_field?(:patchlevel))
106 | assert_equal(false, value_.has_field?(:patchlevel_minor))
107 | end
108 |
109 |
110 | # Test an arbitrary preview value.
111 |
112 | def test_preview_value_2
113 | value_ = ::Versionomy.create(:major => 2, :minor => 3, :release_type => :preview)
114 | assert_equal(2, value_.major)
115 | assert_equal(3, value_.minor)
116 | assert_equal(0, value_.tiny)
117 | assert_equal(0, value_.tiny2)
118 | assert_equal(:preview, value_.release_type)
119 | assert_equal(1, value_.preview_version)
120 | assert_equal(0, value_.preview_minor)
121 | assert_equal(false, value_.has_field?(:patchlevel))
122 | assert_equal(false, value_.has_field?(:patchlevel_minor))
123 | end
124 |
125 |
126 | # Test an arbitrary beta value.
127 |
128 | def test_beta_value
129 | value_ = ::Versionomy.create(:major => 2, :tiny => 1, :release_type => :beta, :beta_version => 3)
130 | assert_equal(2, value_.major)
131 | assert_equal(0, value_.minor)
132 | assert_equal(1, value_.tiny)
133 | assert_equal(0, value_.tiny2)
134 | assert_equal(:beta, value_.release_type)
135 | assert_equal(3, value_.beta_version)
136 | assert_equal(0, value_.beta_minor)
137 | assert_equal(false, value_.has_field?(:prerelase_version))
138 | assert_equal(false, value_.has_field?(:prerelase_minor))
139 | assert_equal(false, value_.has_field?(:patchlevel))
140 | assert_equal(false, value_.has_field?(:patchlevel_minor))
141 | end
142 |
143 |
144 | # Test specifying fields by index.
145 |
146 | def test_field_get_index
147 | value_ = ::Versionomy.create(:major => 2, :tiny => 1, :release_type => :beta, :beta_version => 3)
148 | assert_equal(2, value_[0])
149 | assert_equal(0, value_[1])
150 | assert_equal(1, value_[2])
151 | assert_equal(0, value_[3])
152 | assert_equal(:beta, value_[4])
153 | assert_equal(3, value_[5])
154 | assert_equal(0, value_[6])
155 | end
156 |
157 |
158 | # Test specifying fields by name.
159 |
160 | def test_field_get_name
161 | value_ = ::Versionomy.create(:major => 2, :tiny => 1, :release_type => :beta, :beta_version => 3)
162 | assert_equal(2, value_[:major])
163 | assert_equal(0, value_[:minor])
164 | assert_equal(1, value_[:tiny])
165 | assert_equal(0, value_[:tiny2])
166 | assert_equal(:beta, value_[:release_type])
167 | assert_equal(3, value_[:beta_version])
168 | assert_equal(0, value_[:beta_minor])
169 | end
170 |
171 |
172 | end
173 |
174 | end
175 | end
176 |
--------------------------------------------------------------------------------
/test/tc_semver_basic.rb:
--------------------------------------------------------------------------------
1 | # -----------------------------------------------------------------------------
2 | #
3 | # Versionomy basic tests on rubygems schema
4 | #
5 | # This file contains tests for the basic use cases on the rubygems schema
6 | #
7 | # -----------------------------------------------------------------------------
8 | # Copyright 2008 Daniel Azuma
9 | #
10 | # All rights reserved.
11 | #
12 | # Redistribution and use in source and binary forms, with or without
13 | # modification, are permitted provided that the following conditions are met:
14 | #
15 | # * Redistributions of source code must retain the above copyright notice,
16 | # this list of conditions and the following disclaimer.
17 | # * Redistributions in binary form must reproduce the above copyright notice,
18 | # this list of conditions and the following disclaimer in the documentation
19 | # and/or other materials provided with the distribution.
20 | # * Neither the name of the copyright holder, nor the names of any other
21 | # contributors to this software, may be used to endorse or promote products
22 | # derived from this software without specific prior written permission.
23 | #
24 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
28 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 | # POSSIBILITY OF SUCH DAMAGE.
35 | # -----------------------------------------------------------------------------
36 |
37 |
38 | require 'minitest/autorun'
39 | require 'versionomy'
40 | require 'yaml'
41 |
42 | module Versionomy
43 | module Tests # :nodoc:
44 |
45 | class TestSemverBasic < ::Minitest::Test # :nodoc:
46 |
47 |
48 | # Test the default version value.
49 |
50 | def test_default_value
51 | value_ = ::Versionomy.create(nil, :semver)
52 | assert_equal(1, value_.major)
53 | assert_equal(0, value_.minor)
54 | assert_equal(0, value_.patch)
55 | assert_equal('', value_.prerelease_suffix)
56 | end
57 |
58 |
59 | # Test an arbitrary value.
60 |
61 | def test_arbitrary_value
62 | value_ = ::Versionomy.create([1, 9, 2, 'pre2'], :semver)
63 | assert_equal(1, value_.major)
64 | assert_equal(9, value_.minor)
65 | assert_equal(2, value_.patch)
66 | assert_equal('pre2', value_.prerelease_suffix)
67 | end
68 |
69 |
70 | # Test aliases
71 |
72 | def test_alias_fields
73 | value_ = ::Versionomy.create([1, 9, 2, 'pre2'], :semver)
74 | assert_equal('pre2', value_.special_suffix)
75 | end
76 |
77 |
78 | # Test construction using aliases
79 |
80 | def test_alias_field_construction
81 | value_ = ::Versionomy.create({:major => 1, :minor => 9, :special_suffix => 'pre2'}, :semver)
82 | assert_equal([1, 9, 0, 'pre2'], value_.values_array)
83 | end
84 |
85 |
86 | # Test comparison of numeric values.
87 |
88 | def test_numeric_comparison
89 | value1_ = ::Versionomy.create([1, 9, 2], :semver)
90 | value2_ = ::Versionomy.create([1, 9], :semver)
91 | assert(value2_ < value1_)
92 | value1_ = ::Versionomy.create([1, 9, 0], :semver)
93 | value2_ = ::Versionomy.create([1, 9], :semver)
94 | assert(value2_ == value1_)
95 | end
96 |
97 |
98 | # Test comparison of string values.
99 |
100 | def test_string_comparison
101 | value1_ = ::Versionomy.create([1, 9, 2, 'a2'], :semver)
102 | value2_ = ::Versionomy.create([1, 9, 2, 'b1'], :semver)
103 | assert(value2_ > value1_)
104 | end
105 |
106 |
107 | # Test comparison of numeric and string values.
108 |
109 | def test_numeric_and_string_comparison
110 | value1_ = ::Versionomy.create([1, 9, 2, 'a2'], :semver)
111 | value2_ = ::Versionomy.create([1, 9, 2], :semver)
112 | assert(value2_ > value1_)
113 | end
114 |
115 |
116 | # Test parsing numeric.
117 |
118 | def test_parsing_numeric
119 | value_ = ::Versionomy.parse('2.0.1', :semver)
120 | assert_equal([2, 0, 1, ''], value_.values_array)
121 | assert_equal('2.0.1', value_.unparse)
122 | end
123 |
124 |
125 | # Test parsing with a string.
126 |
127 | def test_parsing_with_string
128 | value_ = ::Versionomy.parse('1.9.2pre2', :semver)
129 | assert_equal([1, 9, 2, 'pre2'], value_.values_array)
130 | assert_equal('1.9.2pre2', value_.unparse)
131 | end
132 |
133 |
134 | # Test making sure unparsing requires all three fields.
135 |
136 | def test_parsing_require_all_fields
137 | value_ = ::Versionomy.parse('2.0', :semver)
138 | assert_equal([2, 0, 0, ''], value_.values_array)
139 | assert_equal('2.0.0', value_.unparse)
140 | assert_raises(::Versionomy::Errors::ParseError) do
141 | value_ = ::Versionomy.parse('2.0b1', :semver)
142 | end
143 | end
144 |
145 |
146 | # Test convenience parsing
147 |
148 | def test_convenience_parse
149 | value_ = ::Versionomy.semver('2.0.1')
150 | assert_equal([2, 0, 1, ''], value_.values_array)
151 | end
152 |
153 |
154 | # Test convenience creation from hash
155 |
156 | def test_convenience_create
157 | value_ = ::Versionomy.semver(:major => 2, :patch => 1, :prerelease_suffix => 'b2')
158 | assert_equal([2, 0, 1, 'b2'], value_.values_array)
159 | end
160 |
161 |
162 | # Test bumping a numeric field.
163 |
164 | def test_bump_numeric
165 | value_ = ::Versionomy.create([1, 9, 2, 'a2'], :semver)
166 | value_ = value_.bump(:patch)
167 | assert_equal([1, 9, 3, ''], value_.values_array)
168 | end
169 |
170 |
171 | # Test bumping a string field.
172 |
173 | def test_bump_string
174 | value_ = ::Versionomy.create([1, 9, 2, 'a2'], :semver)
175 | value_ = value_.bump(:prerelease_suffix)
176 | assert_equal([1, 9, 2, 'a3'], value_.values_array)
177 | end
178 |
179 |
180 | # Test "prerelase?" custom method
181 |
182 | def test_method_prereleasep
183 | value_ = ::Versionomy.create([1, 9, 2, 'a2'], :semver)
184 | assert_equal(true, value_.prerelease?)
185 | value_ = ::Versionomy.create([1, 9, 2], :semver)
186 | assert_equal(false, value_.prerelease?)
187 | end
188 |
189 |
190 | # Test marshalling
191 |
192 | def test_marshal
193 | value_ = ::Versionomy.create([1, 9, 2, 'pre2'], :semver)
194 | str_ = ::Marshal.dump(value_)
195 | value2_ = ::Marshal.load(str_)
196 | assert_equal(value_, value2_)
197 | end
198 |
199 |
200 | # Test YAML
201 |
202 | def test_yaml
203 | value_ = ::Versionomy.create([1, 9, 2, 'pre2'], :semver)
204 | str_ = ::YAML.dump(value_)
205 | value2_ = ::YAML.load(str_)
206 | assert_equal(value_, value2_)
207 | end
208 |
209 |
210 | end
211 |
212 | end
213 | end
214 |
--------------------------------------------------------------------------------
/README.rdoc:
--------------------------------------------------------------------------------
1 | == Versionomy
2 |
3 | Versionomy is a generalized version number library.
4 | It provides tools to represent, manipulate, parse, and compare version
5 | numbers in the wide variety of versioning schemes in use.
6 |
7 | This document summarizes the features of Versionomy with a quick synopsis
8 | and feature list. For more detailed usage information and examples, see
9 | {Versionomy.rdoc}[link:Versionomy\_rdoc.html].
10 |
11 | === Some examples
12 |
13 | require 'versionomy'
14 |
15 | # Create version numbers that understand their own semantics
16 | v1 = Versionomy.create(:major => 1, :minor => 3, :tiny => 2)
17 | v1.major # => 1
18 | v1.minor # => 3
19 | v1.tiny # => 2
20 | v1.release_type # => :final
21 | v1.patchlevel # => 0
22 |
23 | # Parse version numbers, including common prerelease syntax
24 | v2 = Versionomy.parse('1.4a3')
25 | v2.major # => 1
26 | v2.minor # => 4
27 | v2.tiny # => 0
28 | v2.release_type # => :alpha
29 | v2.alpha_version # => 3
30 | v2 > v1 # => true
31 | v2.to_s # => '1.4a3'
32 |
33 | # Version numbers are semantically self-adjusting.
34 | v3 = Versionomy.parse('1.4.0b2')
35 | v3.major # => 1
36 | v3.minor # => 4
37 | v3.tiny # => 0
38 | v3.release_type # => :beta
39 | v3.alpha_version # raises NoMethodError
40 | v3.beta_version # => 2
41 | v3 > v2 # => true
42 | v3.to_s # => '1.4.0b2'
43 |
44 | # You can bump any field
45 | v4 = Versionomy.parse('1.4.0b2').bump(:beta_version)
46 | v4.to_s # => '1.4.0b3'
47 | v5 = v4.bump(:tiny)
48 | v5.to_s # => '1.4.1'
49 |
50 | # Bumping the release type works as you would expect
51 | v6 = Versionomy.parse('1.4.0b2').bump(:release_type)
52 | v6.release_type # => :release_candidate
53 | v6.to_s # => '1.4.0rc1'
54 | v7 = v6.bump(:release_type)
55 | v7.release_type # => :final
56 | v7.to_s # => '1.4.0'
57 |
58 | # If a version has trailing zeros, it remembers how many fields to
59 | # unparse; however, you can also change this.
60 | v8 = Versionomy.parse('1.4.0b2').bump(:major)
61 | v8.to_s # => '2.0.0'
62 | v8.unparse(:optional_fields => [:tiny]) # => '2.0'
63 | v8.unparse(:required_fields => [:tiny2]) # => '2.0.0.0'
64 |
65 | # Comparisons are semantic, so will behave as expected even if the
66 | # formatting is set up differently.
67 | v9 = Versionomy.parse('2.0.0.0')
68 | v9.to_s # => '2.0.0.0'
69 | v9 == Versionomy.parse('2') # => true
70 |
71 | # Patchlevels are supported when the release type is :final
72 | v10 = Versionomy.parse('2.0.0').bump(:patchlevel)
73 | v10.patchlevel # => 1
74 | v10.to_s # => '2.0.0-1'
75 | v11 = Versionomy.parse('2.0p1')
76 | v11.patchlevel # => 1
77 | v11.to_s # => '2.0p1'
78 | v11 == v10 # => true
79 |
80 | # You can create your own format from scratch or by modifying an
81 | # existing format
82 | microsoft_format = Versionomy.default_format.modified_copy do
83 | field(:minor) do
84 | recognize_number(:default_value_optional => true,
85 | :delimiter_regexp => '\s?sp',
86 | :default_delimiter => ' SP')
87 | end
88 | end
89 | v12 = microsoft_format.parse('2008 SP2')
90 | v12.major # => 2008
91 | v12.minor # => 2
92 | v12.tiny # => 0
93 | v12.to_s # => '2008 SP2'
94 | v12 == Versionomy.parse('2008.2') # => true
95 |
96 | === Feature list
97 |
98 | Versionomy's default versioning scheme handles four primary fields (labeled
99 | +major+, +minor+, +tiny+, and +tiny2+). It also supports prerelease versions
100 | such as preview, development, alpha, beta, and release candidate. Finally,
101 | it supports patchlevel numbers for released versions.
102 |
103 | Versionomy can compare any two version numbers with compatible structure,
104 | and "bump" versions at any level. It supports parsing and unparsing in most
105 | commonly-used formats, and allows you to extend the parsing to include
106 | custom formats.
107 |
108 | Finally, Versionomy also lets you to create alternate versioning "schemas".
109 | You can define any number of version number fields, and provide your own
110 | semantics for comparing, parsing, and modifying version numbers. You can
111 | provide conversions from one schema to another. As an example, Versionomy
112 | provides a schema and formatter/parser matching Gem::Version.
113 |
114 | === Requirements
115 |
116 | * Ruby 1.9.3 or later, JRuby 1.5 or later, or Rubinius 1.0 or later.
117 | * blockenspiel 0.5.0 or later.
118 |
119 | === Installation
120 |
121 | gem install versionomy
122 |
123 | === Known issues and limitations
124 |
125 | * Test coverage is still a little skimpy. It is focused on the "standard"
126 | version number format and schema, but doesn't fully exercise all the
127 | capabilities of custom formats.
128 |
129 | === Development and support
130 |
131 | Documentation is available at http://dazuma.github.com/versionomy/rdoc
132 |
133 | Source code is hosted on Github at http://github.com/dazuma/versionomy
134 |
135 | Contributions are welcome. Fork the project on Github.
136 |
137 | Build status: {
}[http://travis-ci.org/dazuma/versionomy]
138 |
139 | Report bugs on Github issues at http://github.org/dazuma/versionomy/issues
140 |
141 | Contact the author at dazuma at gmail dot com.
142 |
143 | === Author / Credits
144 |
145 | Versionomy is written by Daniel Azuma (http://www.daniel-azuma.com/).
146 |
147 | == LICENSE:
148 |
149 | Copyright 2008 Daniel Azuma.
150 |
151 | All rights reserved.
152 |
153 | Redistribution and use in source and binary forms, with or without
154 | modification, are permitted provided that the following conditions are met:
155 |
156 | * Redistributions of source code must retain the above copyright notice,
157 | this list of conditions and the following disclaimer.
158 | * Redistributions in binary form must reproduce the above copyright notice,
159 | this list of conditions and the following disclaimer in the documentation
160 | and/or other materials provided with the distribution.
161 | * Neither the name of the copyright holder, nor the names of any other
162 | contributors to this software, may be used to endorse or promote products
163 | derived from this software without specific prior written permission.
164 |
165 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
166 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
167 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
168 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
169 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
170 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
171 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
172 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
173 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
174 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
175 | POSSIBILITY OF SUCH DAMAGE.
176 |
--------------------------------------------------------------------------------
/lib/versionomy/conversion/parsing.rb:
--------------------------------------------------------------------------------
1 | # -----------------------------------------------------------------------------
2 | #
3 | # Versionomy conversion base class
4 | #
5 | # -----------------------------------------------------------------------------
6 | # Copyright 2008 Daniel Azuma
7 | #
8 | # All rights reserved.
9 | #
10 | # Redistribution and use in source and binary forms, with or without
11 | # modification, are permitted provided that the following conditions are met:
12 | #
13 | # * Redistributions of source code must retain the above copyright notice,
14 | # this list of conditions and the following disclaimer.
15 | # * Redistributions in binary form must reproduce the above copyright notice,
16 | # this list of conditions and the following disclaimer in the documentation
17 | # and/or other materials provided with the distribution.
18 | # * Neither the name of the copyright holder, nor the names of any other
19 | # contributors to this software, may be used to endorse or promote products
20 | # derived from this software without specific prior written permission.
21 | #
22 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 | # POSSIBILITY OF SUCH DAMAGE.
33 | # -----------------------------------------------------------------------------
34 | ;
35 |
36 |
37 | module Versionomy
38 |
39 | module Conversion
40 |
41 |
42 | # A conversion strategy that relies on parsing.
43 | # Essentially, it unparses the value and then attempts to parse it with
44 | # the new format.
45 |
46 | class Parsing < Base
47 |
48 |
49 | # Create a parsing conversion.
50 | #
51 | # By default, this just unparses and reparses using the default
52 | # parse settings. In some cases, this may be enough, but you may
53 | # wish to improve the reliability of the conversion by tweaking the
54 | # parsing settings. To do so, pass a block to the new method, and
55 | # call methods of Versionomy::Conversion::Parsing::Builder in that
56 | # block.
57 |
58 | def initialize(&block_)
59 | if block_
60 | builder_ = Builder.new
61 | ::Blockenspiel.invoke(block_, builder_)
62 | @original_value_modifier = builder_._get_original_value_modifier
63 | @string_modifier = builder_._get_string_modifier
64 | @unparse_params_modifier = builder_._get_unparse_params_modifier
65 | @parse_params_generator ||= builder_._get_parse_params_generator
66 | end
67 | end
68 |
69 |
70 | # Returns a value equivalent to the given value in the given format.
71 | #
72 | # The convert_params are passed to this conversion's customization
73 | # blocks (if any).
74 | #
75 | # Raises Versionomy::Errors::ConversionError if the conversion failed.
76 | # Typically, this is due to a failure of the parsing or unparsing.
77 |
78 | def convert_value(value_, format_, convert_params_=nil)
79 | begin
80 | convert_params_ ||= {}
81 | if @original_value_modifier
82 | value_ = @original_value_modifier.call(value_, convert_params_)
83 | end
84 | unparse_params_ = value_.unparse_params || {}
85 | if @unparse_params_modifier
86 | unparse_params_ = @unparse_params_modifier.call(unparse_params_, convert_params_)
87 | end
88 | string_ = value_.unparse(unparse_params_)
89 | if @string_modifier
90 | string_ = @string_modifier.call(string_, convert_params_)
91 | end
92 | if @parse_params_generator
93 | parse_params_ = @parse_params_generator.call(convert_params_)
94 | else
95 | parse_params_ = nil
96 | end
97 | new_value_ = format_.parse(string_, parse_params_)
98 | return new_value_
99 | rescue Errors::UnparseError => ex_
100 | raise Errors::ConversionError, "Unparsing failed: #{ex_.inspect}"
101 | rescue Errors::ParseError => ex_
102 | raise Errors::ConversionError, "Parsing failed: #{ex_.inspect}"
103 | end
104 | end
105 |
106 |
107 | # Call methods of this class in the block passed to
108 | # Versionomy::Conversion::Parsing#new to fine-tune the behavior of
109 | # the converter.
110 |
111 | class Builder
112 |
113 | include ::Blockenspiel::DSL
114 |
115 |
116 | def initialize # :nodoc:
117 | @original_value_modifier = nil
118 | @string_modifier = nil
119 | @parse_params_generator = nil
120 | @unparse_params_modifier = nil
121 | end
122 |
123 |
124 | # Provide a block that generates the params used to parse the new
125 | # value. The block should take one parameter, the convert_params
126 | # passed to convert_value (which may be nil). It should return the
127 | # parse params that should be used.
128 |
129 | def to_generate_parse_params(&block_)
130 | @parse_params_generator = block_
131 | end
132 |
133 |
134 | # Provide a block that can modify the params used to unparse the
135 | # old value. The block should take two parameters: first, the
136 | # original unparse params from the old value (which may be nil),
137 | # and second, the convert_params passed to convert_value (which
138 | # may also be nil). It should return the unparse params that
139 | # should actually be used.
140 |
141 | def to_modify_unparse_params(&block_)
142 | @unparse_params_modifier = block_
143 | end
144 |
145 |
146 | # Provide a block that can modify the original value prior to it
147 | # being unparsed. The block should take two parameters: first, the
148 | # original value to be converted, and second, the convert_params
149 | # passed to convert_value (which may be nil). It should return the
150 | # value to be unparsed, which may be the same as the value
151 | # originally passed in. This method may fail-fast by raising a
152 | # Versionomy::Errors::ConversionError if it determines that the
153 | # value passed in cannot be converted as is.
154 |
155 | def to_modify_original_value(&block_)
156 | @original_value_modifier = block_
157 | end
158 |
159 |
160 | # Provide a block that can modify the unparsed string prior to
161 | # it being passed to the parser. The block should take two
162 | # parameters: first, the string resulting from unparsing the old
163 | # value, and second, the convert_params passed to convert_value
164 | # (which may be nil). It should return the string to be parsed to
165 | # get the new value.
166 |
167 | def to_modify_string(&block_)
168 | @string_modifier = block_
169 | end
170 |
171 |
172 | def _get_original_value_modifier # :nodoc:
173 | @original_value_modifier
174 | end
175 |
176 | def _get_string_modifier # :nodoc:
177 | @string_modifier
178 | end
179 |
180 | def _get_unparse_params_modifier # :nodoc:
181 | @unparse_params_modifier
182 | end
183 |
184 | def _get_parse_params_generator # :nodoc:
185 | @parse_params_generator
186 | end
187 |
188 | end
189 |
190 |
191 | end
192 |
193 |
194 | end
195 |
196 | end
197 |
--------------------------------------------------------------------------------
/test/tc_semver_conversions.rb:
--------------------------------------------------------------------------------
1 | # -----------------------------------------------------------------------------
2 | #
3 | # Versionomy tests on rubygems schema conversions
4 | #
5 | # This file contains tests converting to and from the rubygems schema
6 | #
7 | # -----------------------------------------------------------------------------
8 | # Copyright 2008 Daniel Azuma
9 | #
10 | # All rights reserved.
11 | #
12 | # Redistribution and use in source and binary forms, with or without
13 | # modification, are permitted provided that the following conditions are met:
14 | #
15 | # * Redistributions of source code must retain the above copyright notice,
16 | # this list of conditions and the following disclaimer.
17 | # * Redistributions in binary form must reproduce the above copyright notice,
18 | # this list of conditions and the following disclaimer in the documentation
19 | # and/or other materials provided with the distribution.
20 | # * Neither the name of the copyright holder, nor the names of any other
21 | # contributors to this software, may be used to endorse or promote products
22 | # derived from this software without specific prior written permission.
23 | #
24 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
28 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 | # POSSIBILITY OF SUCH DAMAGE.
35 | # -----------------------------------------------------------------------------
36 |
37 |
38 | require 'minitest/autorun'
39 | require 'versionomy'
40 |
41 |
42 | module Versionomy
43 | module Tests # :nodoc:
44 |
45 | class TestSemverConversions < ::Minitest::Test # :nodoc:
46 |
47 |
48 | def setup
49 | @standard_format = Format.get(:standard)
50 | @semver_format = Format.get(:semver)
51 | end
52 |
53 |
54 | # Test simple conversion from standard to semver.
55 |
56 | def test_standard_to_semver_simple
57 | value_ = ::Versionomy.parse('1.2')
58 | value2_ = value_.convert(:semver)
59 | assert_equal(@semver_format, value2_.format)
60 | assert_equal([1, 2, 0, ''], value2_.values_array)
61 | value_ = ::Versionomy.parse('1.2.4')
62 | value2_ = value_.convert(:semver)
63 | assert_equal(@semver_format, value2_.format)
64 | assert_equal([1, 2, 4, ''], value2_.values_array)
65 | end
66 |
67 |
68 | # Test conversion from standard to semver with a beta version
69 |
70 | def test_standard_to_semver_beta
71 | value_ = ::Versionomy.parse('1.2b3')
72 | value2_ = value_.convert(:semver)
73 | assert_equal([1, 2, 0, 'b3'], value2_.values_array)
74 | value_ = ::Versionomy.parse('1.2 beta 3')
75 | value2_ = value_.convert(:semver)
76 | assert_equal([1, 2, 0, 'beta3'], value2_.values_array)
77 | end
78 |
79 |
80 | # Test conversion from standard to semver with a "v" prefix
81 |
82 | def test_standard_to_semver_with_v
83 | value_ = ::Versionomy.parse('v1.2.3')
84 | value2_ = value_.convert(:semver)
85 | assert_equal([1, 2, 3, ''], value2_.values_array)
86 | value_ = ::Versionomy.parse('V 1.2.3')
87 | value2_ = value_.convert(:semver)
88 | assert_equal([1, 2, 3, ''], value2_.values_array)
89 | end
90 |
91 |
92 | # Test conversion from standard to semver with an expectation of failure
93 |
94 | def test_standard_to_semver_fail
95 | value_ = ::Versionomy.parse('1.2.3.4', :standard)
96 | assert_raises(::Versionomy::Errors::ConversionError) do
97 | value_.convert(:semver)
98 | end
99 | end
100 |
101 |
102 | # Test simple conversion from semver to standard.
103 |
104 | def test_semver_to_standard_simple
105 | value_ = ::Versionomy.parse('1.2', :semver)
106 | value2_ = value_.convert(:standard)
107 | assert_equal(@standard_format, value2_.format)
108 | assert_equal([1, 2, 0, 0, :final, 0, 0], value2_.values_array)
109 | value_ = ::Versionomy.parse('1.2.4', :semver)
110 | value2_ = value_.convert(:standard)
111 | assert_equal(@standard_format, value2_.format)
112 | assert_equal([1, 2, 4, 0, :final, 0, 0], value2_.values_array)
113 | end
114 |
115 |
116 | # Test conversion from semver to standard with a beta version
117 |
118 | def test_semver_to_standard_beta
119 | value_ = ::Versionomy.parse('1.2.0b3', :semver)
120 | value2_ = value_.convert(:standard)
121 | assert_equal([1, 2, 0, 0, :beta, 3, 0], value2_.values_array)
122 | value_ = ::Versionomy.parse('1.2.4beta3', :semver)
123 | value2_ = value_.convert(:standard)
124 | assert_equal([1, 2, 4, 0, :beta, 3, 0], value2_.values_array)
125 | end
126 |
127 |
128 | # Test conversion from rubygems to standard with an expectation of failure
129 |
130 | def test_semver_to_standard_fail
131 | value_ = ::Versionomy.parse('1.2.3c4', :semver)
132 | assert_raises(::Versionomy::Errors::ConversionError) do
133 | value_.convert(:standard)
134 | end
135 | end
136 |
137 |
138 | # Test conversion when there aren't unparse_params
139 |
140 | def test_standard_to_semver_without_unparse_params
141 | value_ = ::Versionomy.create([1,2,3], :standard)
142 | value2_ = value_.convert(:semver)
143 | assert_equal([1, 2, 3, ''], value2_.values_array)
144 | end
145 |
146 |
147 | # Test conversion between semver and rubygems
148 |
149 | def test_semver_and_rubygems
150 | value_ = ::Versionomy.create([1,2,3], :semver)
151 | value2_ = value_.convert(:rubygems)
152 | assert_equal([1, 2, 3, 0, 0, 0, 0, 0], value2_.values_array)
153 | value_ = ::Versionomy.create([1,2,3], :rubygems)
154 | value2_ = value_.convert(:semver)
155 | assert_equal([1, 2, 3, ''], value2_.values_array)
156 | end
157 |
158 |
159 | # Test equality comparisons between semver and standard
160 |
161 | def test_semver_to_standard_equality_comparison
162 | assert_operator(::Versionomy.parse('1.2.0', :semver), :==, ::Versionomy.parse('1.2'))
163 | assert_operator(::Versionomy.parse('1.2.0b3', :semver), :==, ::Versionomy.parse('1.2b3'))
164 | end
165 |
166 |
167 | # Test inequality comparisons between semver and standard
168 |
169 | def test_semver_to_standard_inequality_comparison
170 | assert_operator(::Versionomy.parse('1.2.3', :semver), :<, ::Versionomy.parse('1.2.4'))
171 | assert_operator(::Versionomy.parse('1.2.0b3', :semver), :>, ::Versionomy.parse('1.2b2'))
172 | assert_operator(::Versionomy.parse('1.2.0', :semver), :>, ::Versionomy.parse('1.2b1'))
173 | end
174 |
175 |
176 | # Test equality comparisons between standard and rubygems
177 |
178 | def test_standard_to_semver_equality_comparison
179 | assert_operator(::Versionomy.parse('1.2.0'), :==, ::Versionomy.parse('1.2.0', :semver))
180 | assert_operator(::Versionomy.parse('1.2b3'), :==, ::Versionomy.parse('1.2.0beta3', :semver))
181 | end
182 |
183 |
184 | # Test inequality comparisons between standard and rubygems
185 |
186 | def test_standard_to_semver_inequality_comparison
187 | assert_operator(::Versionomy.parse('1.2.4'), :>, ::Versionomy.parse('1.2.3', :semver))
188 | assert_operator(::Versionomy.parse('1.2b2'), :<, ::Versionomy.parse('1.2.0beta3', :semver))
189 | assert_operator(::Versionomy.parse('1.2b2'), :<, ::Versionomy.parse('1.2.0', :semver))
190 | end
191 |
192 |
193 | end
194 |
195 | end
196 | end
197 |
--------------------------------------------------------------------------------
/test/tc_rubygems_conversions.rb:
--------------------------------------------------------------------------------
1 | # -----------------------------------------------------------------------------
2 | #
3 | # Versionomy tests on rubygems schema conversions
4 | #
5 | # This file contains tests converting to and from the rubygems schema
6 | #
7 | # -----------------------------------------------------------------------------
8 | # Copyright 2008 Daniel Azuma
9 | #
10 | # All rights reserved.
11 | #
12 | # Redistribution and use in source and binary forms, with or without
13 | # modification, are permitted provided that the following conditions are met:
14 | #
15 | # * Redistributions of source code must retain the above copyright notice,
16 | # this list of conditions and the following disclaimer.
17 | # * Redistributions in binary form must reproduce the above copyright notice,
18 | # this list of conditions and the following disclaimer in the documentation
19 | # and/or other materials provided with the distribution.
20 | # * Neither the name of the copyright holder, nor the names of any other
21 | # contributors to this software, may be used to endorse or promote products
22 | # derived from this software without specific prior written permission.
23 | #
24 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
28 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 | # POSSIBILITY OF SUCH DAMAGE.
35 | # -----------------------------------------------------------------------------
36 |
37 |
38 | require 'minitest/autorun'
39 | require 'versionomy'
40 |
41 |
42 | module Versionomy
43 | module Tests # :nodoc:
44 |
45 | class TestRubygemsConversions < ::Minitest::Test # :nodoc:
46 |
47 |
48 | def setup
49 | @standard_format = Format.get(:standard)
50 | @rubygems_format = Format.get(:rubygems)
51 | end
52 |
53 |
54 | # Test simple conversion from standard to rubygems.
55 |
56 | def test_standard_to_rubygems_simple
57 | value_ = ::Versionomy.parse('1.2')
58 | value2_ = value_.convert(:rubygems)
59 | assert_equal(@rubygems_format, value2_.format)
60 | assert_equal([1, 2, 0, 0, 0, 0, 0, 0], value2_.values_array)
61 | value_ = ::Versionomy.parse('1.2.4.1')
62 | value2_ = value_.convert(:rubygems)
63 | assert_equal(@rubygems_format, value2_.format)
64 | assert_equal([1, 2, 4, 1, 0, 0, 0, 0], value2_.values_array)
65 | end
66 |
67 |
68 | # Test conversion from standard to rubygems including a patchlevel
69 |
70 | def test_standard_to_rubygems_with_patchlevel
71 | value_ = ::Versionomy.parse('1.2-3')
72 | value2_ = value_.convert(:rubygems)
73 | assert_equal([1, 2, 3, 0, 0, 0, 0, 0], value2_.values_array)
74 | value_ = ::Versionomy.parse('1.2p3')
75 | value2_ = value_.convert(:rubygems)
76 | assert_equal([1, 2, 3, 0, 0, 0, 0, 0], value2_.values_array)
77 | value_ = ::Versionomy.parse('1.2c')
78 | value2_ = value_.convert(:rubygems)
79 | assert_equal([1, 2, 3, 0, 0, 0, 0, 0], value2_.values_array)
80 | end
81 |
82 |
83 | # Test conversion from standard to rubygems with a beta version
84 |
85 | def test_standard_to_rubygems_beta
86 | value_ = ::Versionomy.parse('1.2b3')
87 | value2_ = value_.convert(:rubygems)
88 | assert_equal([1, 2, 'b', 3, 0, 0, 0, 0], value2_.values_array)
89 | value_ = ::Versionomy.parse('1.2 beta 3.4')
90 | value2_ = value_.convert(:rubygems)
91 | assert_equal([1, 2, 'beta', 3, 4, 0, 0, 0], value2_.values_array)
92 | end
93 |
94 |
95 | # Test conversion from standard to rubygems with a "v" prefix
96 |
97 | def test_standard_to_rubygems_with_v
98 | value_ = ::Versionomy.parse('v1.2b3')
99 | value2_ = value_.convert(:rubygems)
100 | assert_equal([1, 2, 'b', 3, 0, 0, 0, 0], value2_.values_array)
101 | value_ = ::Versionomy.parse('V 1.2b3')
102 | value2_ = value_.convert(:rubygems)
103 | assert_equal([1, 2, 'b', 3, 0, 0, 0, 0], value2_.values_array)
104 | end
105 |
106 |
107 | # Test simple conversion from rubygems to standard.
108 |
109 | def test_rubygems_to_standard_simple
110 | value_ = ::Versionomy.parse('1.2', :rubygems)
111 | value2_ = value_.convert(:standard)
112 | assert_equal(@standard_format, value2_.format)
113 | assert_equal([1, 2, 0, 0, :final, 0, 0], value2_.values_array)
114 | value_ = ::Versionomy.parse('1.2.4.1', :rubygems)
115 | value2_ = value_.convert(:standard)
116 | assert_equal(@standard_format, value2_.format)
117 | assert_equal([1, 2, 4, 1, :final, 0, 0], value2_.values_array)
118 | end
119 |
120 |
121 | # Test conversion from rubygems to standard with a beta version
122 |
123 | def test_rubygems_to_standard_beta
124 | value_ = ::Versionomy.parse('1.2.b.3', :rubygems)
125 | value2_ = value_.convert(:standard)
126 | assert_equal([1, 2, 0, 0, :beta, 3, 0], value2_.values_array)
127 | value_ = ::Versionomy.parse('1.2.b3', :rubygems)
128 | value2_ = value_.convert(:standard)
129 | assert_equal([1, 2, 0, 0, :beta, 3, 0], value2_.values_array)
130 | value_ = ::Versionomy.parse('1.2.beta3', :rubygems)
131 | value2_ = value_.convert(:standard)
132 | assert_equal([1, 2, 0, 0, :beta, 3, 0], value2_.values_array)
133 | value_ = ::Versionomy.parse('1.2.b', :rubygems)
134 | value2_ = value_.convert(:standard)
135 | assert_equal([1, 2, 0, 0, :beta, 0, 0], value2_.values_array)
136 | end
137 |
138 |
139 | # Test conversion from rubygems to standard with an expectation of failure
140 |
141 | def test_rubygems_to_standard_fail
142 | value_ = ::Versionomy.parse('1.2.b.3.4.5', :rubygems)
143 | assert_raises(::Versionomy::Errors::ConversionError) do
144 | value_.convert(:standard)
145 | end
146 | value_ = ::Versionomy.parse('1.2.c.3', :rubygems)
147 | assert_raises(::Versionomy::Errors::ConversionError) do
148 | value_.convert(:standard)
149 | end
150 | end
151 |
152 |
153 | # Test equality comparisons between rubygems and standard
154 |
155 | def test_rubygems_to_standard_equality_comparison
156 | assert_operator(::Versionomy.parse('1.2.0', :rubygems), :==, ::Versionomy.parse('1.2'))
157 | assert_operator(::Versionomy.parse('1.2.b.3', :rubygems), :==, ::Versionomy.parse('1.2b3'))
158 | end
159 |
160 |
161 | # Test inequality comparisons between rubygems and standard
162 |
163 | def test_rubygems_to_standard_inequality_comparison
164 | assert_operator(::Versionomy.parse('1.2.3', :rubygems), :<, ::Versionomy.parse('1.2.4'))
165 | assert_operator(::Versionomy.parse('1.2.b.3', :rubygems), :>, ::Versionomy.parse('1.2b2'))
166 | assert_operator(::Versionomy.parse('1.2', :rubygems), :>, ::Versionomy.parse('1.2b1'))
167 | end
168 |
169 |
170 | # Test equality comparisons between standard and rubygems
171 |
172 | def test_standard_to_rubygems_equality_comparison
173 | assert_operator(::Versionomy.parse('1.2.0'), :==, ::Versionomy.parse('1.2', :rubygems))
174 | assert_operator(::Versionomy.parse('1.2b3'), :==, ::Versionomy.parse('1.2.beta.3', :rubygems))
175 | end
176 |
177 |
178 | # Test inequality comparisons between standard and rubygems
179 |
180 | def test_standard_to_rubygems_inequality_comparison
181 | assert_operator(::Versionomy.parse('1.2.4'), :>, ::Versionomy.parse('1.2.3', :rubygems))
182 | assert_operator(::Versionomy.parse('1.2b2'), :<, ::Versionomy.parse('1.2.beta.3', :rubygems))
183 | assert_operator(::Versionomy.parse('1.2b2'), :<, ::Versionomy.parse('1.2', :rubygems))
184 | end
185 |
186 |
187 | end
188 |
189 | end
190 | end
191 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | # -----------------------------------------------------------------------------
2 | #
3 | # Generic Gem Rakefile
4 | #
5 | # -----------------------------------------------------------------------------
6 | # Copyright 2010 Daniel Azuma
7 | #
8 | # All rights reserved.
9 | #
10 | # Redistribution and use in source and binary forms, with or without
11 | # modification, are permitted provided that the following conditions are met:
12 | #
13 | # * Redistributions of source code must retain the above copyright notice,
14 | # this list of conditions and the following disclaimer.
15 | # * Redistributions in binary form must reproduce the above copyright notice,
16 | # this list of conditions and the following disclaimer in the documentation
17 | # and/or other materials provided with the distribution.
18 | # * Neither the name of the copyright holder, nor the names of any other
19 | # contributors to this software, may be used to endorse or promote products
20 | # derived from this software without specific prior written permission.
21 | #
22 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 | # POSSIBILITY OF SUCH DAMAGE.
33 | # -----------------------------------------------------------------------------
34 | ;
35 |
36 |
37 | # Load config if present
38 |
39 | config_path_ = ::File.expand_path('rakefile_config.rb', ::File.dirname(__FILE__))
40 | load(config_path_) if ::File.exists?(config_path_)
41 | RAKEFILE_CONFIG = {} unless defined?(::RAKEFILE_CONFIG)
42 |
43 |
44 | # Gemspec
45 |
46 | require 'rubygems'
47 | require 'rubygems/package'
48 | gemspec_ = eval(::File.read(::Dir.glob('*.gemspec').first))
49 | release_gemspec_ = eval(::File.read(::Dir.glob('*.gemspec').first))
50 | release_gemspec_.version = gemspec_.version.to_s.sub(/\.nonrelease$/, '')
51 |
52 |
53 | # Platform info
54 |
55 | dlext_ = ::RbConfig::CONFIG['DLEXT']
56 |
57 | platform_ =
58 | case ::RUBY_DESCRIPTION
59 | when /^jruby\s/ then :jruby
60 | when /^ruby\s/ then :mri
61 | when /^rubinius\s/ then :rubinius
62 | else :unknown
63 | end
64 |
65 | platform_suffix_ =
66 | case platform_
67 | when :mri
68 | if ::RUBY_VERSION =~ /^1\.8\..*$/
69 | 'mri18'
70 | elsif ::RUBY_VERSION =~ /^1\.9\..*$/
71 | 'mri19'
72 | elsif ::RUBY_VERSION =~ /^2\..*$/
73 | 'mri2'
74 | else
75 | raise "Unknown version of Matz Ruby Interpreter (#{::RUBY_VERSION})"
76 | end
77 | when :rubinius then 'rbx'
78 | when :jruby then 'jruby'
79 | else 'unknown'
80 | end
81 |
82 |
83 | # Directories
84 |
85 | doc_directory_ = ::RAKEFILE_CONFIG[:doc_directory] || 'doc'
86 | pkg_directory_ = ::RAKEFILE_CONFIG[:pkg_directory] || 'pkg'
87 | tmp_directory_ = ::RAKEFILE_CONFIG[:tmp_directory] || 'tmp'
88 |
89 |
90 | # Build tasks
91 |
92 | internal_ext_info_ = gemspec_.extensions.map do |extconf_path_|
93 | source_dir_ = ::File.dirname(extconf_path_)
94 | name_ = ::File.basename(source_dir_)
95 | {
96 | :name => name_,
97 | :source_dir => source_dir_,
98 | :extconf_path => extconf_path_,
99 | :source_glob => "#{source_dir_}/*.{c,h}",
100 | :obj_glob => "#{source_dir_}/*.{o,dSYM}",
101 | :suffix_makefile_path => "#{source_dir_}/Makefile_#{platform_suffix_}",
102 | :built_lib_path => "#{source_dir_}/#{name_}.#{dlext_}",
103 | :staged_lib_path => "#{source_dir_}/#{name_}_#{platform_suffix_}.#{dlext_}",
104 | }
105 | end
106 | internal_ext_info_ = [] if platform_ == :jruby
107 |
108 | internal_ext_info_.each do |info_|
109 | file info_[:staged_lib_path] => [info_[:suffix_makefile_path]] + ::Dir.glob(info_[:source_glob]) do
110 | ::Dir.chdir(info_[:source_dir]) do
111 | cp "Makefile_#{platform_suffix_}", 'Makefile'
112 | sh 'make'
113 | rm 'Makefile'
114 | end
115 | mv info_[:built_lib_path], info_[:staged_lib_path]
116 | rm_r ::Dir.glob(info_[:obj_glob])
117 | end
118 | file info_[:suffix_makefile_path] => info_[:extconf_path] do
119 | ::Dir.chdir(info_[:source_dir]) do
120 | ruby 'extconf.rb'
121 | mv 'Makefile', "Makefile_#{platform_suffix_}"
122 | end
123 | end
124 | end
125 |
126 | task :build_ext => internal_ext_info_.map{ |info_| info_[:staged_lib_path] } do
127 | internal_ext_info_.each do |info_|
128 | target_prefix_ = target_name_ = nil
129 | ::Dir.chdir(info_[:source_dir]) do
130 | ruby 'extconf.rb'
131 | ::File.open('Makefile') do |file_|
132 | file_.each do |line_|
133 | if line_ =~ /^target_prefix\s*=\s*(\S+)\s/
134 | target_prefix_ = $1
135 | elsif line_ =~ /^TARGET\s*=\s*(\S+)\s/
136 | target_name_ = $1
137 | end
138 | end
139 | end
140 | rm 'Makefile'
141 | end
142 | raise "Could not find target_prefix in makefile for #{info_[:name]}" unless target_prefix_
143 | raise "Could not find TARGET in makefile for #{info_[:name]}" unless target_name_
144 | cp info_[:staged_lib_path], "lib#{target_prefix_}/#{target_name_}.#{dlext_}"
145 | end
146 | end
147 |
148 |
149 | # Clean task
150 |
151 | clean_files_ = [doc_directory_, pkg_directory_, tmp_directory_] +
152 | ::Dir.glob('ext/**/Makefile*') +
153 | ::Dir.glob('ext/**/*.{o,class,log,dSYM}') +
154 | ::Dir.glob("**/*.{bundle,so,dll,rbc,jar}") +
155 | ::Dir.glob('**/.rbx') +
156 | (::RAKEFILE_CONFIG[:extra_clean_files] || [])
157 | task :clean do
158 | clean_files_.each{ |path_| rm_rf path_ }
159 | end
160 |
161 |
162 | # RDoc tasks
163 |
164 | task :build_rdoc => "#{doc_directory_}/index.html"
165 | all_rdoc_files_ = ::Dir.glob("lib/**/*.rb") + gemspec_.extra_rdoc_files
166 | main_rdoc_file_ = ::RAKEFILE_CONFIG[:main_rdoc_file]
167 | main_rdoc_file_ = 'README.rdoc' if !main_rdoc_file_ && ::File.readable?('README.rdoc')
168 | main_rdoc_file_ = ::Dir.glob("*.rdoc").first unless main_rdoc_file_
169 | file "#{doc_directory_}/index.html" => all_rdoc_files_ do
170 | rm_r doc_directory_ rescue nil
171 | args_ = []
172 | args_ << '-o' << doc_directory_
173 | args_ << '--main' << main_rdoc_file_ if main_rdoc_file_
174 | args_ << '--title' << "#{::RAKEFILE_CONFIG[:product_visible_name] || gemspec_.name.capitalize} #{release_gemspec_.version} Documentation"
175 | args_ << '-f' << 'darkfish'
176 | args_ << '--verbose' if ::ENV['VERBOSE']
177 | gem 'rdoc'
178 | require 'rdoc/rdoc'
179 | ::RDoc::RDoc.new.document(args_ + all_rdoc_files_)
180 | end
181 |
182 |
183 | # Gem release tasks
184 |
185 | task :build_other
186 |
187 | task :build_gem => :build_other do
188 | if defined?(::Gem::Builder)
189 | ::Gem::Builder.new(gemspec_).build
190 | else
191 | ::Gem::Package.build(gemspec_)
192 | end
193 | mkdir_p(pkg_directory_)
194 | mv "#{gemspec_.name}-#{gemspec_.version}.gem", "#{pkg_directory_}/"
195 | end
196 |
197 | task :build_release => :build_other do
198 | if defined?(::Gem::Builder)
199 | ::Gem::Builder.new(release_gemspec_).build
200 | else
201 | ::Gem::Package.build(release_gemspec_)
202 | end
203 | mkdir_p(pkg_directory_)
204 | mv "#{release_gemspec_.name}-#{release_gemspec_.version}.gem", "#{pkg_directory_}/"
205 | end
206 |
207 | task :release_gem => :build_release do
208 | ::Dir.chdir(pkg_directory_) do
209 | sh "#{::RbConfig::TOPDIR}/bin/gem push #{release_gemspec_.name}-#{release_gemspec_.version}.gem"
210 | end
211 | end
212 |
213 |
214 | # Unit test task
215 |
216 | task :test => [:build_ext, :build_other] do
217 | $:.unshift(::File.expand_path('lib', ::File.dirname(__FILE__)))
218 | if (cases_ = ::ENV['TESTCASE'])
219 | test_files_ = cases_.split(',').map{ |c_| ::Dir.glob("test/#{c_}.rb") }.flatten
220 | else
221 | test_files_ = ::Dir.glob("test/**/tc_*.rb")
222 | end
223 | $VERBOSE = true
224 | test_files_.each do |path_|
225 | load path_
226 | puts "Loaded testcase #{path_}"
227 | end
228 | end
229 |
230 |
231 | # Default task
232 |
233 | task :default => [:clean, :test]
234 |
--------------------------------------------------------------------------------
/lib/versionomy/interface.rb:
--------------------------------------------------------------------------------
1 | # -----------------------------------------------------------------------------
2 | #
3 | # Versionomy convenience interface
4 | #
5 | # -----------------------------------------------------------------------------
6 | # Copyright 2008 Daniel Azuma
7 | #
8 | # All rights reserved.
9 | #
10 | # Redistribution and use in source and binary forms, with or without
11 | # modification, are permitted provided that the following conditions are met:
12 | #
13 | # * Redistributions of source code must retain the above copyright notice,
14 | # this list of conditions and the following disclaimer.
15 | # * Redistributions in binary form must reproduce the above copyright notice,
16 | # this list of conditions and the following disclaimer in the documentation
17 | # and/or other materials provided with the distribution.
18 | # * Neither the name of the copyright holder, nor the names of any other
19 | # contributors to this software, may be used to endorse or promote products
20 | # derived from this software without specific prior written permission.
21 | #
22 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 | # POSSIBILITY OF SUCH DAMAGE.
33 | # -----------------------------------------------------------------------------
34 | ;
35 |
36 |
37 | # == Versionomy
38 | #
39 | # The Versionomy module contains some convenience methods for creating and
40 | # parsing version numbers.
41 |
42 | module Versionomy
43 |
44 | @default_format = nil
45 |
46 |
47 | class << self
48 |
49 |
50 | # Gets the current default format. Usually this is the "standard"
51 | # format returned by Versionomy::Format.standard.
52 |
53 | def default_format
54 | @default_format ||= Format.standard
55 | end
56 |
57 |
58 | # Sets the default format used by other methods of this convenience
59 | # interface. Usually, this is set to the "standard" format returned by
60 | # Versionomy::Format.standard and should not be changed.
61 | #
62 | # The format can be specified as a format object or the name of a format
63 | # registered with Versionomy::Format. If the format is set to nil, the
64 | # default_format will be reset to the "standard" format.
65 | #
66 | # Raises Versionomy::Errors::UnknownFormatError if a name is given that
67 | # is not registered.
68 |
69 | def default_format=(format_)
70 | if format_.kind_of?(::String) || format_.kind_of?(::Symbol)
71 | format_ = Format.get(format_, true)
72 | end
73 | @default_format = format_
74 | end
75 |
76 |
77 | # Create a new version number given a hash or array of values, and an
78 | # optional format.
79 | #
80 | # The values should either be a hash of field names and values, or an
81 | # array of values that will be interpreted in field order.
82 | #
83 | # The format can be specified as a format object or the name of a format
84 | # registered with Versionomy::Format. If the format is omitted or set
85 | # to nil, the default_format will be used.
86 | #
87 | # You can also optionally provide default parameters to be used when
88 | # unparsing this value or any derived from it.
89 | #
90 | # Raises Versionomy::Errors::UnknownFormatError if a name is given that
91 | # is not registered.
92 |
93 | def create(values_=nil, format_=nil, unparse_params_=nil)
94 | if format_.kind_of?(::Hash) && unparse_params_.nil?
95 | unparse_params_ = format_
96 | format_ = nil
97 | end
98 | if format_.kind_of?(::String) || format_.kind_of?(::Symbol)
99 | format_ = Format.get(format_, true)
100 | end
101 | format_ ||= default_format
102 | Value.new(values_ || [], format_, unparse_params_)
103 | end
104 |
105 |
106 | # Create a new version number given a string to parse, and an optional
107 | # format.
108 | #
109 | # The format can be specified as a format object or the name of a format
110 | # registered with Versionomy::Format. If the format is omitted or set
111 | # to nil, the default_format will be used.
112 | #
113 | # The parameter hash, if present, will be passed as parsing parameters
114 | # to the format.
115 | #
116 | # Raises Versionomy::Errors::UnknownFormatError if a name is given that
117 | # is not registered.
118 | #
119 | # May raise Versionomy::Errors::ParseError if parsing failed.
120 |
121 | def parse(str_, format_=nil, parse_params_=nil)
122 | if format_.kind_of?(::Hash) && parse_params_.nil?
123 | parse_params_ = format_
124 | format_ = nil
125 | end
126 | if format_.kind_of?(::String) || format_.kind_of?(::Symbol)
127 | format_ = Format.get(format_, true)
128 | end
129 | format_ ||= default_format
130 | format_.parse(str_, parse_params_)
131 | end
132 |
133 |
134 | # Convenience method for creating a version number using the Semantic
135 | # Versioning format (see http://semver.org/).
136 | #
137 | # You may pass a string to parse, or a hash with the following keys, all
138 | # of which are optional:
139 | # :major::
140 | # Major version number
141 | # :minor::
142 | # Minor version number
143 | # :patch::
144 | # Patch version number
145 | # :prerelease_suffix::
146 | # A prerelease suffix (e.g. "b2")
147 | #
148 | # May raise Versionomy::Errors::ParseError if parsing failed.
149 |
150 | def semver(input_)
151 | if input_.kind_of?(::Hash)
152 | create(input_, :semver)
153 | else
154 | parse(input_.to_s, :semver)
155 | end
156 | end
157 |
158 |
159 | # Get the version of the given module as a Versionomy::Value.
160 | # Tries a number of common approaches to embedding version numbers into
161 | # modules, such as string or array constants, submodules containing
162 | # constants, or module method calls.
163 | # Returns the version number, or nil if it wasn't found or couldn't
164 | # be interpreted.
165 |
166 | def version_of(mod_)
167 | version_ = nil
168 | [:VERSION, :VERSION_STRING, :GemVersion].each do |sym_|
169 | if mod_.const_defined?(sym_)
170 | version_ = mod_.const_get(sym_)
171 | break
172 | end
173 | end
174 | if version_.kind_of?(::Module)
175 | if version_.const_defined?(:STRING)
176 | version_ = version_.const_get(:STRING)
177 | elsif version_.const_defined?(:VERSION)
178 | version_ = version_.const_get(:VERSION)
179 | elsif version_.const_defined?(:MAJOR) && version_.const_defined?(:MINOR) && version_.const_defined?(:TINY)
180 | version_ = Value.new([version_.const_get(:MAJOR), version_.const_get(:MINOR), version_.const_get(:TINY)], :standard)
181 | end
182 | end
183 | unless version_.kind_of?(::String) || version_.kind_of?(::Array) || version_.kind_of?(Value)
184 | [:version, :release].each do |sym_|
185 | if mod_.respond_to?(sym_)
186 | version_ = mod_.send(sym_)
187 | break
188 | end
189 | end
190 | end
191 | if version_.kind_of?(::String)
192 | version_ = parse(version_, :standard) rescue nil
193 | elsif version_.kind_of?(::Array)
194 | version_ = create(version_, :standard) rescue nil
195 | elsif !version_.kind_of?(Value)
196 | version_ = nil
197 | end
198 | version_
199 | end
200 |
201 |
202 | # Get the ruby version as a Versionomy::Value, using the builtin
203 | # constants RUBY_VERSION and RUBY_PATCHLEVEL.
204 |
205 | def ruby_version
206 | @ruby_version ||= begin
207 | version_ = parse(::RUBY_VERSION, :standard)
208 | if version_.release_type == :final
209 | version_ = version_.change({:patchlevel => ::RUBY_PATCHLEVEL},
210 | :patchlevel_required => true, :patchlevel_delim => '-p')
211 | end
212 | version_
213 | end
214 | end
215 |
216 |
217 | end
218 |
219 | end
220 |
--------------------------------------------------------------------------------
/test/tc_rubygems_basic.rb:
--------------------------------------------------------------------------------
1 | # -----------------------------------------------------------------------------
2 | #
3 | # Versionomy basic tests on rubygems schema
4 | #
5 | # This file contains tests for the basic use cases on the rubygems schema
6 | #
7 | # -----------------------------------------------------------------------------
8 | # Copyright 2008 Daniel Azuma
9 | #
10 | # All rights reserved.
11 | #
12 | # Redistribution and use in source and binary forms, with or without
13 | # modification, are permitted provided that the following conditions are met:
14 | #
15 | # * Redistributions of source code must retain the above copyright notice,
16 | # this list of conditions and the following disclaimer.
17 | # * Redistributions in binary form must reproduce the above copyright notice,
18 | # this list of conditions and the following disclaimer in the documentation
19 | # and/or other materials provided with the distribution.
20 | # * Neither the name of the copyright holder, nor the names of any other
21 | # contributors to this software, may be used to endorse or promote products
22 | # derived from this software without specific prior written permission.
23 | #
24 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
28 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 | # POSSIBILITY OF SUCH DAMAGE.
35 | # -----------------------------------------------------------------------------
36 |
37 |
38 | require 'minitest/autorun'
39 | require 'versionomy'
40 | require 'yaml'
41 |
42 | module Versionomy
43 | module Tests # :nodoc:
44 |
45 | class TestRubygemsBasic < ::Minitest::Test # :nodoc:
46 |
47 |
48 | # Test the default version value.
49 |
50 | def test_default_value
51 | value_ = ::Versionomy.create(nil, :rubygems)
52 | assert_equal(1, value_.field0)
53 | assert_equal(0, value_.field1)
54 | assert_equal(0, value_.field2)
55 | assert_equal(0, value_.field3)
56 | assert_equal(0, value_.field4)
57 | assert_equal(0, value_.field5)
58 | assert_equal(0, value_.field6)
59 | assert_equal(0, value_.field7)
60 | end
61 |
62 |
63 | # Test an arbitrary value.
64 |
65 | def test_arbitrary_value
66 | value_ = ::Versionomy.create([1, 9, 2, 'pre', 2], :rubygems)
67 | assert_equal(1, value_.field0)
68 | assert_equal(9, value_.field1)
69 | assert_equal(2, value_.field2)
70 | assert_equal('pre', value_.field3)
71 | assert_equal(2, value_.field4)
72 | assert_equal(0, value_.field5)
73 | assert_equal(0, value_.field6)
74 | assert_equal(0, value_.field7)
75 | end
76 |
77 |
78 | # Test aliases
79 |
80 | def test_alias_fields
81 | value_ = ::Versionomy.create([1, 9, 2, 'a', 2], :rubygems)
82 | assert_equal(1, value_.major)
83 | assert_equal(9, value_.minor)
84 | end
85 |
86 |
87 | # Test construction using aliases
88 |
89 | def test_alias_field_construction
90 | value_ = ::Versionomy.create({:major => 1, :minor => 9, :field2 => 2}, :rubygems)
91 | assert_equal([1, 9, 2, 0, 0, 0, 0, 0], value_.values_array)
92 | end
93 |
94 |
95 | # Test comparison of numeric values.
96 |
97 | def test_numeric_comparison
98 | value1_ = ::Versionomy.create([1, 9, 2], :rubygems)
99 | value2_ = ::Versionomy.create([1, 9], :rubygems)
100 | assert(value2_ < value1_)
101 | value1_ = ::Versionomy.create([1, 9, 0], :rubygems)
102 | value2_ = ::Versionomy.create([1, 9], :rubygems)
103 | assert(value2_ == value1_)
104 | end
105 |
106 |
107 | # Test comparison of string values.
108 |
109 | def test_string_comparison
110 | value1_ = ::Versionomy.create([1, 9, 2, 'a', 2], :rubygems)
111 | value2_ = ::Versionomy.create([1, 9, 2, 'b', 1], :rubygems)
112 | assert(value2_ > value1_)
113 | end
114 |
115 |
116 | # Test comparison of numeric and string values.
117 |
118 | def test_numeric_and_string_comparison
119 | value1_ = ::Versionomy.create([1, 9, 2, 'a', 2], :rubygems)
120 | value2_ = ::Versionomy.create([1, 9, 2, 1], :rubygems)
121 | assert(value2_ > value1_)
122 | end
123 |
124 |
125 | # Test parsing numeric.
126 |
127 | def test_parsing_numeric
128 | value_ = ::Versionomy.parse('2.0.1.1.4.6', :rubygems)
129 | assert_equal([2, 0, 1, 1, 4, 6, 0, 0], value_.values_array)
130 | assert_equal('2.0.1.1.4.6', value_.unparse)
131 | end
132 |
133 |
134 | # Test parsing with a string.
135 |
136 | def test_parsing_with_string
137 | value_ = ::Versionomy.parse('1.9.2.pre.2', :rubygems)
138 | assert_equal([1, 9, 2, 'pre', 2, 0, 0, 0], value_.values_array)
139 | assert_equal('1.9.2.pre.2', value_.unparse)
140 | end
141 |
142 |
143 | # Test parsing with trailing zeros.
144 |
145 | def test_parsing_trailing_zeros
146 | value_ = ::Versionomy.parse('2.0.0', :rubygems)
147 | assert_equal([2, 0, 0, 0, 0, 0, 0, 0], value_.values_array)
148 | assert_equal('2.0.0', value_.unparse)
149 | assert_equal('2.0.0.0.0', value_.unparse(:required_fields => [:field4]))
150 | assert_equal('2.0', value_.unparse(:optional_fields => [:field2]))
151 | end
152 |
153 |
154 | # Test bumping a numeric field.
155 |
156 | def test_bump_numeric
157 | value_ = ::Versionomy.create([1, 9, 2, 'a', 2], :rubygems)
158 | value_ = value_.bump(:field2)
159 | assert_equal([1, 9, 3, 0, 0, 0, 0, 0], value_.values_array)
160 | end
161 |
162 |
163 | # Test bumping a string field.
164 |
165 | def test_bump_string
166 | value_ = ::Versionomy.create([1, 9, 2, 'a', 2], :rubygems)
167 | value_ = value_.bump(:field3)
168 | assert_equal([1, 9, 2, 'b', 0, 0, 0, 0], value_.values_array)
169 | end
170 |
171 |
172 | # Test bumping an alias field.
173 |
174 | def test_bump_alias
175 | value_ = ::Versionomy.create([1, 9, 2, 'a', 2], :rubygems)
176 | value_ = value_.bump(:minor)
177 | assert_equal([1, 10, 0, 0, 0, 0, 0, 0], value_.values_array)
178 | end
179 |
180 |
181 | # Test changing an alias field.
182 |
183 | def test_change_alias
184 | value_ = ::Versionomy.create([1, 8, 7, 'a', 2], :rubygems)
185 | value_ = value_.change(:minor => 9)
186 | assert_equal([1, 9, 7, 'a', 2, 0, 0, 0], value_.values_array)
187 | end
188 |
189 |
190 | # Test "prerelase?" custom method
191 |
192 | def test_method_prereleasep
193 | value_ = ::Versionomy.create([1, 9, 2, 'a', 2], :rubygems)
194 | assert_equal(true, value_.prerelease?)
195 | value_ = ::Versionomy.create([1, 9, 2, 2], :rubygems)
196 | assert_equal(false, value_.prerelease?)
197 | end
198 |
199 |
200 | # Test "relase" custom method
201 |
202 | def test_method_release
203 | value_ = ::Versionomy.create([1, 9, 2, 'a', 2], :rubygems)
204 | value2_ = value_.release
205 | assert_equal([1, 9, 2, 0, 0, 0, 0, 0], value2_.values_array)
206 | value_ = ::Versionomy.create([1, 9, 2, 5, 2], :rubygems)
207 | value2_ = value_.release
208 | assert_equal(value_, value2_)
209 | end
210 |
211 |
212 | # Test "parts" custom method
213 |
214 | def test_method_parts
215 | value_ = ::Versionomy.create([1, 9, 2, 'a', 2], :rubygems)
216 | assert_equal([1, 9, 2, 'a', 2], value_.parts)
217 | value_ = ::Versionomy.create([1, 9, 0], :rubygems)
218 | assert_equal([1, 9], value_.parts)
219 | end
220 |
221 |
222 | # Test marshalling
223 |
224 | def test_marshal
225 | value_ = ::Versionomy.create([1, 9, 2, 'a', 2], :rubygems)
226 | str_ = ::Marshal.dump(value_)
227 | value2_ = ::Marshal.load(str_)
228 | assert_equal(value_, value2_)
229 | end
230 |
231 |
232 | # Test YAML
233 |
234 | def test_yaml
235 | value_ = ::Versionomy.create([1, 9, 2, 'a', 2], :rubygems)
236 | str_ = ::YAML.dump(value_)
237 | value2_ = ::YAML.load(str_)
238 | assert_equal(value_, value2_)
239 | end
240 |
241 |
242 | end
243 |
244 | end
245 | end
246 |
--------------------------------------------------------------------------------
/lib/versionomy/format.rb:
--------------------------------------------------------------------------------
1 | # -----------------------------------------------------------------------------
2 | #
3 | # Versionomy format namespace
4 | #
5 | # -----------------------------------------------------------------------------
6 | # Copyright 2008 Daniel Azuma
7 | #
8 | # All rights reserved.
9 | #
10 | # Redistribution and use in source and binary forms, with or without
11 | # modification, are permitted provided that the following conditions are met:
12 | #
13 | # * Redistributions of source code must retain the above copyright notice,
14 | # this list of conditions and the following disclaimer.
15 | # * Redistributions in binary form must reproduce the above copyright notice,
16 | # this list of conditions and the following disclaimer in the documentation
17 | # and/or other materials provided with the distribution.
18 | # * Neither the name of the copyright holder, nor the names of any other
19 | # contributors to this software, may be used to endorse or promote products
20 | # derived from this software without specific prior written permission.
21 | #
22 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 | # POSSIBILITY OF SUCH DAMAGE.
33 | # -----------------------------------------------------------------------------
34 | ;
35 |
36 |
37 | require 'thread'
38 | require 'monitor'
39 |
40 |
41 | module Versionomy
42 |
43 |
44 | # === Version number format.
45 | #
46 | # A format controls the parsing and unparsing of a version number.
47 | # Any time a version number is parsed from a string, a format is provided
48 | # to parse it. Similarly, every version number value references a format
49 | # that is used to unparse it back into a string.
50 | #
51 | # A format is always tied to a particular schema and knows how to parse
52 | # only that schema's version numbers.
53 | #
54 | # Under many circumstances, you should use the standard format, which
55 | # can be retrieved by calling Versionomy::Format#standard. This format
56 | # understands most common version numbers, including prerelease
57 | # (e.g. alpha, beta, release candidate, etc.) forms and patchlevels.
58 | #
59 | # You may also create your own formats, either by implementing the
60 | # format contract (see Versionomy::Format::Base), or by using the
61 | # Versionomy::Format::Delimiter tool, which can be used to construct
62 | # parsers for many version number formats.
63 | #
64 | # === Format registry
65 | #
66 | # Formats may be registered with Versionomy and given a name using the
67 | # methods of this module. This allows version numbers to be serialized
68 | # with their format. When a version number is serialized, its format
69 | # name is written to the stream, along with the version number's string
70 | # representation. When the version number is reconstructed, its format
71 | # is looked up by name so versionomy can determine how to parse the
72 | # string.
73 | #
74 | # Format names are strings that may include letters, numerals, dashes,
75 | # underscores, and periods. By convention, periods are used as namespace
76 | # delimiters. Format names without a namespace (that is, with no periods)
77 | # are considered reserved for standard versionomy formats. If you define
78 | # your own format, you should use a name that includes a namespace (e.g.
79 | # "mycompany.LibraryVersion") to reduce the chance of name collisions.
80 | #
81 | # You may register formats directly using the register method, or set it
82 | # up to be autoloaded on demand. When a format is requested, if it has
83 | # not been registered explicitly, Versionomy looks for a format definition
84 | # file for that format. Such a file has the name of the format, with the
85 | # ".rb" extension for ruby (e.g. "mycompany.LibraryVersion.rb") and must
86 | # be located in a directory in versionomy's format lookup path. By
87 | # default, the directory containing versionomy's predefined formats
88 | # (such as "standard") is in this path. However, you may add your own
89 | # directories using the add_directory method. This lets you autoload your
90 | # own formats. A format definition file itself must contain ruby code
91 | # that defines the format and registers it using the correct name. See
92 | # the files in the "lib/versionomy/format_definitions/" directory for
93 | # examples.
94 |
95 | module Format
96 |
97 | @mutex = ::Mutex.new
98 | @load_mutex = ::Monitor.new
99 | @directories = [::File.expand_path(::File.dirname(__FILE__)+'/format_definitions')]
100 | @names_to_formats = {}
101 | @formats_to_names = {}
102 |
103 | class << self
104 |
105 |
106 | # Add a directory to the format path.
107 | #
108 | # The format path is an array of directory paths. These directories
109 | # are searched for format definitions if a format name that has not
110 | # been registered is requested.
111 | #
112 | # If high_priority_ is set to true, the directory is added to the
113 | # front of the lookup path; otherwise it is added to the back.
114 |
115 | def add_directory(path_, high_priority_=false)
116 | path_ = ::File.expand_path(path_)
117 | @mutex.synchronize do
118 | unless @directories.include?(path_)
119 | if high_priority_
120 | @directories.unshift(path_)
121 | else
122 | @directories.push(path_)
123 | end
124 | end
125 | end
126 | end
127 |
128 |
129 | # Get the format with the given name.
130 | #
131 | # If the given name has not been defined, attempts to autoload it from
132 | # a format definition file. See the description of the Format module
133 | # for details on this procedure.
134 | #
135 | # If the given name still cannot be resolved, and strict is set to
136 | # true, raises Versionomy::Errors::UnknownFormatError. If strict is
137 | # set to false, returns nil if the given name cannot be resolved.
138 |
139 | def get(name_, strict_=false)
140 | name_ = _check_name(name_)
141 | format_ = @mutex.synchronize{ @names_to_formats[name_] }
142 | if format_.nil?
143 | # Attempt to find the format in the directory path
144 | dirs_ = @mutex.synchronize{ @directories.dup }
145 | dirs_.each do |dir_|
146 | path_ = "#{dir_}/#{name_}.rb"
147 | if ::File.readable?(path_)
148 | @load_mutex.synchronize{ ::Kernel.load(path_) }
149 | end
150 | format_ = @mutex.synchronize{ @names_to_formats[name_] }
151 | break unless format_.nil?
152 | end
153 | end
154 | if format_.nil? && strict_
155 | raise Errors::UnknownFormatError, name_
156 | end
157 | format_
158 | end
159 |
160 |
161 | # Determines whether a format with the given name has been registered
162 | # explicitly. Does not attempt to autoload the format.
163 |
164 | def registered?(name_)
165 | name_ = _check_name(name_)
166 | @mutex.synchronize{ @names_to_formats.include?(name_) }
167 | end
168 |
169 |
170 | # Register the given format under the given name.
171 | #
172 | # Valid names may contain only letters, digits, underscores, dashes,
173 | # and periods.
174 | #
175 | # Raises Versionomy::Errors::FormatRedefinedError if the name has
176 | # already been defined.
177 |
178 | def register(name_, format_, silent_=false)
179 | name_ = _check_name(name_)
180 | @mutex.synchronize do
181 | if @names_to_formats.include?(name_)
182 | unless silent_
183 | raise Errors::FormatRedefinedError, name_
184 | end
185 | else
186 | @names_to_formats[name_] = format_
187 | @formats_to_names[format_.object_id] = name_
188 | end
189 | end
190 | end
191 |
192 |
193 | # Get the canonical name for the given format, as a string.
194 | # This is the first name the format was registered under.
195 | #
196 | # If the given format was never registered, and strict is set to true,
197 | # raises Versionomy::Errors::UnknownFormatError. If strict is set to
198 | # false, returns nil if the given format was never registered.
199 |
200 | def canonical_name_for(format_, strict_=false)
201 | name_ = @mutex.synchronize{ @formats_to_names[format_.object_id] }
202 | if name_.nil? && strict_
203 | raise Errors::UnknownFormatError
204 | end
205 | name_
206 | end
207 |
208 |
209 | private
210 |
211 | def _check_name(name_) # :nodoc:
212 | name_ = name_.to_s
213 | unless name_ =~ /\A[\w.-]+\z/
214 | raise ::ArgumentError, "Illegal name: #{name_.inspect}"
215 | end
216 | name_
217 | end
218 |
219 |
220 | end
221 |
222 | end
223 |
224 |
225 | # Versionomy::Formats is an alias for Versionomy::Format, for backward
226 | # compatibility with version 0.1.0 code. It is deprecated; use
227 | # Versionomy::Format instead.
228 | Formats = Format
229 |
230 |
231 | end
232 |
--------------------------------------------------------------------------------
/lib/versionomy/format_definitions/semver.rb:
--------------------------------------------------------------------------------
1 | # -----------------------------------------------------------------------------
2 | #
3 | # Versionomy semver format implementation
4 | #
5 | # -----------------------------------------------------------------------------
6 | # Copyright 2010 Daniel Azuma
7 | #
8 | # All rights reserved.
9 | #
10 | # Redistribution and use in source and binary forms, with or without
11 | # modification, are permitted provided that the following conditions are met:
12 | #
13 | # * Redistributions of source code must retain the above copyright notice,
14 | # this list of conditions and the following disclaimer.
15 | # * Redistributions in binary form must reproduce the above copyright notice,
16 | # this list of conditions and the following disclaimer in the documentation
17 | # and/or other materials provided with the distribution.
18 | # * Neither the name of the copyright holder, nor the names of any other
19 | # contributors to this software, may be used to endorse or promote products
20 | # derived from this software without specific prior written permission.
21 | #
22 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 | # POSSIBILITY OF SUCH DAMAGE.
33 | # -----------------------------------------------------------------------------
34 | ;
35 |
36 |
37 | module Versionomy
38 |
39 | module Format
40 |
41 |
42 | # Get the semver format.
43 | # This is identical to calling get('semver').
44 | #
45 | # The semver format is designed to conform to the Semantic Versioning
46 | # spec by Tom Preston-Warner. See http://semver.org/
47 | #
48 | # For the exact annotated definition of the semver schema and format,
49 | # see the source code for Versionomy::Format::Semver#create.
50 |
51 | def self.semver
52 | get('semver')
53 | end
54 |
55 |
56 | # This is a namespace for the implementation of the semver schema
57 | # and format.
58 |
59 | module Semver
60 |
61 |
62 | # Extra methods added to version values that use the semver schema.
63 |
64 | module ExtraMethods
65 |
66 |
67 | # Returns true if the version is a prerelease version-- that is,
68 | # if the prerelease_suffix is nonempty.
69 |
70 | def prerelease?
71 | prerelease_suffix.length > 0
72 | end
73 |
74 |
75 | # Returns the release for this version.
76 | # For example, converts "1.2.0a1" to "1.2.0".
77 | # Non-prerelease versions return themselves unchanged.
78 |
79 | def release
80 | prerelease? ? self.change(:prerelease_suffix => '') : self
81 | end
82 |
83 |
84 | # Returns true if this version is compatible with the given version,
85 | # according to the Semantic Versioning specification.
86 | # For example, 1.1.0 is compatible with 1.0.0 but not vice versa,
87 | # 1.1.1 and 1.1.0 are compatible with each other, while 1.0.0 and
88 | # 2.0.0 are mutually incompatible.
89 |
90 | def compatible_with?(version_)
91 | self.major == version_.major ? self.minor >= version_.minor : false
92 | end
93 |
94 |
95 | end
96 |
97 |
98 | # Create the semver format.
99 | # This method is called internally when Versionomy loads the semver
100 | # format, and you should not need to call it again. It is documented
101 | # so that you can inspect its source code from RDoc, since the source
102 | # contains useful examples of how to use the schema and format
103 | # definition DSLs.
104 |
105 | def self.create
106 |
107 | # The following is the definition of the semver schema
108 | schema_ = Schema.create do
109 |
110 | # The first field has the default value of 1. All other fields
111 | # have a default value of 0. Thus, the default version number
112 | # overall is "1.0".
113 | field(:major, :type => :integer, :default_value => 1) do
114 | field(:minor, :type => :integer) do
115 | field(:patch, :type => :integer) do
116 | field(:prerelease_suffix, :type => :string) do
117 | to_compare do |a_, b_|
118 | a_.length == 0 ? (b_.length == 0 ? 0 : 1) : (b_.length == 0 ? -1 : a_ <=> b_)
119 | end
120 | end
121 | end
122 | end
123 | end
124 |
125 | # An alias
126 | alias_field(:special_suffix, :prerelease_suffix)
127 |
128 | # Add the methods in this module to each value
129 | add_module(Format::Semver::ExtraMethods)
130 | end
131 |
132 | # The following is the definition of the standard format. It
133 | # understands the standard schema defined above.
134 | Format::Delimiter.new(schema_) do
135 |
136 | # All version number strings must start with the major version.
137 | # Unlike other fields, it is not preceded by the usual "dot"
138 | # delimiter, but it can be preceded by a "v" indicator.
139 | field(:major) do
140 | recognize_number(:delimiter_regexp => 'v?', :default_delimiter => '')
141 | end
142 |
143 | # The remainder of the core version number are represented as
144 | # integers delimited by periods. These fields are required.
145 | field(:minor) do
146 | recognize_number
147 | end
148 | field(:patch) do
149 | recognize_number
150 | end
151 |
152 | # The optional prerelease field is represented as a string
153 | # beginning with an alphabetic character.
154 | field(:prerelease_suffix) do
155 | recognize_regexp('[a-zA-Z][0-9a-zA-Z-]*', :default_value_optional => true,
156 | :delimiter_regexp => '', :default_delimiter => '')
157 | end
158 | end
159 | end
160 |
161 |
162 | end
163 |
164 |
165 | register('semver', Format::Semver.create, true)
166 |
167 |
168 | end
169 |
170 |
171 | module Conversion
172 |
173 |
174 | # This is a namespace for the implementation of the conversion between
175 | # the semver and standard formats.
176 |
177 | module Semver
178 |
179 |
180 | # Create the conversion from standard to semver format.
181 | # This method is called internally when Versionomy loads the semver
182 | # format, and you should not need to call it again. It is documented
183 | # so that you can inspect its source code from RDoc, since the source
184 | # contains useful examples of how to use the conversion DSLs.
185 |
186 | def self.create_standard_to_semver
187 |
188 | # We'll use a parsing conversion.
189 | Conversion::Parsing.new do
190 |
191 | # Sanity check the original value and make sure it doesn't
192 | # include fields that we don't support.
193 | to_modify_original_value do |value_, convert_params_|
194 | if value_.has_field?(:patchlevel) && value_.patchlevel != 0
195 | raise Errors::ConversionError, 'Cannot convert a version with a patchlevel to semver'
196 | end
197 | if value_.tiny2 != 0
198 | raise Errors::ConversionError, 'Cannot convert a version more than three fields to semver'
199 | end
200 | value_
201 | end
202 |
203 | # We're going to modify how the standard format version is
204 | # unparsed, so the semver format will have a better chance
205 | # of parsing it.
206 | to_modify_unparse_params do |params_, convert_params_|
207 |
208 | # All three fields are required
209 | params_[:minor_required] = true
210 | params_[:tiny_required] = true
211 |
212 | # If the standard format version has a prerelease notation,
213 | # make sure it isn't set off using a delimiter.
214 | params_[:release_type_delim] = ''
215 | params_[:development_version_delim] = ''
216 | params_[:development_minor_delim] = '-'
217 | params_[:alpha_version_delim] = ''
218 | params_[:alpha_minor_delim] = '-'
219 | params_[:beta_version_delim] = ''
220 | params_[:beta_minor_delim] = '-'
221 | params_[:release_candidate_version_delim] = ''
222 | params_[:release_candidate_minor_delim] = '-'
223 | params_[:preview_version_delim] = ''
224 | params_[:preview_minor_delim] = '-'
225 |
226 | # If the standard format version includes a "v" prefix, strip it
227 | params_[:major_delim] = nil
228 |
229 | params_
230 | end
231 |
232 | end
233 |
234 | end
235 |
236 |
237 | # Create the conversion from semver to standard format.
238 | # This method is called internally when Versionomy loads the semver
239 | # format, and you should not need to call it again. It is documented
240 | # so that you can inspect its source code from RDoc, since the source
241 | # contains useful examples of how to use the conversion DSLs.
242 |
243 | def self.create_semver_to_standard
244 |
245 | # We'll use a parsing conversion.
246 | Conversion::Parsing.new do
247 |
248 | # Handle the case where the semver version ends with a string
249 | # field, e.g. "1.0b". We want to treat this like "1.0b0" rather
250 | # than "1.0-2" since the semver semantic states that this is a
251 | # prerelease version. So we add 0 to the end of the parsed string
252 | # if it ends in a letter.
253 | to_modify_string do |str_, convert_params_|
254 | str_.gsub(/([[:alpha:]])\z/, '\10')
255 | end
256 |
257 | end
258 |
259 | end
260 |
261 |
262 | end
263 |
264 |
265 | register(:standard, :semver, Conversion::Semver.create_standard_to_semver, true)
266 | register(:semver, :standard, Conversion::Semver.create_semver_to_standard, true)
267 |
268 |
269 | end
270 |
271 |
272 | end
273 |
--------------------------------------------------------------------------------
/lib/versionomy/schema/wrapper.rb:
--------------------------------------------------------------------------------
1 | # -----------------------------------------------------------------------------
2 | #
3 | # Versionomy schema wrapper class
4 | #
5 | # -----------------------------------------------------------------------------
6 | # Copyright 2008 Daniel Azuma
7 | #
8 | # All rights reserved.
9 | #
10 | # Redistribution and use in source and binary forms, with or without
11 | # modification, are permitted provided that the following conditions are met:
12 | #
13 | # * Redistributions of source code must retain the above copyright notice,
14 | # this list of conditions and the following disclaimer.
15 | # * Redistributions in binary form must reproduce the above copyright notice,
16 | # this list of conditions and the following disclaimer in the documentation
17 | # and/or other materials provided with the distribution.
18 | # * Neither the name of the copyright holder, nor the names of any other
19 | # contributors to this software, may be used to endorse or promote products
20 | # derived from this software without specific prior written permission.
21 | #
22 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 | # POSSIBILITY OF SUCH DAMAGE.
33 | # -----------------------------------------------------------------------------
34 | ;
35 |
36 |
37 | module Versionomy
38 |
39 | module Schema
40 |
41 |
42 | # Creates a schema.
43 | # Returns an object of type Versionomy::Schema::Wrapper.
44 | #
45 | # You may either pass a root field, or provide a block to use to build
46 | # fields. If you provide a block, you must use the methods in
47 | # Versionomy::Schema::Builder in the block to create the root field.
48 |
49 | def self.create(field_=nil, opts_={}, &block_)
50 | if field_ && block_
51 | raise ::ArgumentError, 'You may provide either a root field or block but not both'
52 | end
53 | if block_
54 | builder_ = Schema::Builder.new
55 | ::Blockenspiel.invoke(block_, builder_)
56 | field_ = builder_._get_field
57 | modules_ = builder_._get_modules
58 | aliases_ = builder_._get_aliases
59 | else
60 | modules_ = opts_[:modules] || []
61 | end
62 | Schema::Wrapper.new(field_, modules_, aliases_)
63 | end
64 |
65 |
66 | # Schemas are generally referenced through an object of this class.
67 |
68 | class Wrapper
69 |
70 |
71 | # Create a new schema wrapper object given a root field.
72 | # This is a low-level method. Usually you should call
73 | # Versionomy::Schema#create instead.
74 |
75 | def initialize(field_, modules_=[], aliases_={})
76 | @root_field = field_
77 | @names = @root_field._descendants_by_name
78 | @modules = modules_
79 | @aliases = {}
80 | aliases_.each do |k_,v_|
81 | k_ = k_.to_sym
82 | v_ = v_.to_sym
83 | if @names.include?(v_) && !@names.include?(k_)
84 | @aliases[k_] = v_
85 | end
86 | end
87 | end
88 |
89 |
90 | def inspect # :nodoc:
91 | "#<#{self.class}:0x#{object_id.to_s(16)} root=#{@root_field.inspect}>"
92 | end
93 |
94 | def to_s # :nodoc:
95 | inspect
96 | end
97 |
98 |
99 | # Returns true if this schema is equivalent to the other schema.
100 | # Two schemas are equivalent if their root fields are the same--
101 | # which means that the entire field tree is the same-- and they
102 | # include the same value modules.
103 | # Note that this is different from the definition of ==.
104 |
105 | def eql?(obj_)
106 | return false unless obj_.kind_of?(Schema::Wrapper)
107 | return @root_field == obj_.root_field && @modules == obj_.modules && @aliases == obj_.aliases
108 | end
109 |
110 |
111 | # Returns true if this schema is compatible with the other schema.
112 | # Two schemas are compatible if their root fields are the same--
113 | # which means that the entire field tree is the same. They may,
114 | # however, include different value modules.
115 | # Note that this is different from the definition of eql?.
116 |
117 | def ==(obj_)
118 | eql?(obj_)
119 | end
120 |
121 |
122 | # If the RHS is a schema, returns true if the schemas are equivalent.
123 | # If the RHS is a value, returns true if the value uses this schema.
124 |
125 | def ===(obj_)
126 | if obj_.kind_of?(Value)
127 | obj_.schema == self
128 | else
129 | obj_ == self
130 | end
131 | end
132 |
133 |
134 | def hash # :nodoc:
135 | @hash ||= @root_field.hash ^ @modules.hash
136 | end
137 |
138 |
139 | # Returns the root (most significant) field in this schema.
140 |
141 | def root_field
142 | @root_field
143 | end
144 |
145 |
146 | # Return the canonical field name given a name, or nil if the name
147 | # is not recognized.
148 |
149 | def canonical_name(name_)
150 | name_ = name_.to_sym
151 | name_ = @aliases[name_] || name_
152 | @names.include?(name_) ? name_ : nil
153 | end
154 |
155 |
156 | # Return the field with the given name, or nil if the given name
157 | # is not found in this schema. If include_aliases_ is set to true,
158 | # this also supports lookup by alias.
159 |
160 | def field_named(name_, include_aliases_=false)
161 | name_ = name_.to_sym
162 | name_ = @aliases[name_] || name_ if include_aliases_
163 | @names[name_]
164 | end
165 |
166 |
167 | # Returns an array of names present in this schema, in no particular
168 | # order. Does not include aliases.
169 |
170 | def names
171 | @names.keys
172 | end
173 |
174 |
175 | # Returns an array of modules that should be included in values that
176 | # use this schema.
177 |
178 | def modules
179 | @modules.dup
180 | end
181 |
182 |
183 | # Returns a hash of field name aliases.
184 |
185 | def aliases
186 | @aliases.dup
187 | end
188 |
189 |
190 | end
191 |
192 |
193 | # These methods are available in a schema definition block given to
194 | # Versionomy::Schema#create.
195 |
196 | class Builder
197 |
198 | include ::Blockenspiel::DSL
199 |
200 | def initialize() # :nodoc:
201 | @field = nil
202 | @modules = []
203 | @aliases = {}
204 | @defaults = { :integer => {}, :string => {}, :symbol => {} }
205 | end
206 |
207 |
208 | # Create the root field.
209 | #
210 | # Recognized options include:
211 | #
212 | # :type::
213 | # Type of field. This should be :integer, :string,
214 | # or :symbol. Default is :integer.
215 | # :default_value::
216 | # Default value for the field if no value is explicitly set. Default
217 | # is 0 for an integer field, the empty string for a string field, or
218 | # the first symbol added for a symbol field.
219 | #
220 | # You may provide an optional block. Within the block, you may call
221 | # methods of Versionomy::Schema::FieldBuilder to customize this field.
222 | #
223 | # Raises Versionomy::Errors::IllegalValueError if the given default
224 | # value is not legal.
225 | #
226 | # Raises Versionomy::Errors::RangeOverlapError if a root field has
227 | # already been created.
228 |
229 | def field(name_, opts_={}, &block_)
230 | if @field
231 | raise Errors::RangeOverlapError, "Root field already defined"
232 | end
233 | @field = Schema::Field.new(name_, opts_.merge(:master_builder => self), &block_)
234 | end
235 |
236 |
237 | # Create a field alias.
238 |
239 | def alias_field(alias_name_, field_name_)
240 | @aliases[alias_name_.to_sym] = field_name_.to_sym
241 | end
242 |
243 |
244 | # Add a module to the schema. All values that use this schema will
245 | # include this module. This provides a way to add schema-specific
246 | # capabilities to version numbers.
247 |
248 | def add_module(mod_)
249 | @modules << mod_
250 | end
251 |
252 |
253 | # Provide a default bump procedure for the given type.
254 | # The type should be :integer, :string, or
255 | # :symbol. You must provide a block that takes a field value
256 | # and returns the "bumped" value. This procedure will be used for
257 | # all fields of this type, unless explicitly overridden by the field.
258 |
259 | def to_bump_type(type_, &block_)
260 | @defaults[type_][:bump] = block_
261 | end
262 |
263 |
264 | # Provide a default compare procedure for the given type.
265 | # The type should be :integer, :string, or
266 | # :symbol. You must provide a block that takes two values
267 | # and returns a standard comparison result-- that is, a negative
268 | # integer if the first value is less, 0 if the values are equal, or a
269 | # positive integer if the first value is greater. This procedure will
270 | # be used for all fields of this type, unless explicitly overridden
271 | # by the field.
272 |
273 | def to_compare_type(type_, &block_)
274 | @defaults[type_][:compare] = block_
275 | end
276 |
277 |
278 | # Provide a default canonicalization procedure for the given type.
279 | # The type should be :integer, :string, or
280 | # :symbol. You must provide a block that takes a field value
281 | # and returns the canonical value. This procedure will be used for
282 | # all fields of this type, unless explicitly overridden by the field.
283 |
284 | def to_canonicalize_type(type_, &block_)
285 | @defaults[type_][:canonicalize] = block_
286 | end
287 |
288 |
289 | # Provide a default value for the given type.
290 | # The type should be :integer, :string, or
291 | # :symbol. You must provide a default value that will be
292 | # used for all fields of this type, unless explicitly overridden by
293 | # the field.
294 |
295 | def default_value_for_type(type_, value_)
296 | @defaults[type_][:value] = value_
297 | end
298 |
299 |
300 | def _get_field # :nodoc:
301 | @field
302 | end
303 |
304 | def _get_modules # :nodoc:
305 | @modules
306 | end
307 |
308 | def _get_aliases # :nodoc:
309 | @aliases
310 | end
311 |
312 | def _get_default_setting(type_, setting_) # :nodoc:
313 | @defaults[type_][setting_]
314 | end
315 |
316 | end
317 |
318 |
319 | end
320 |
321 | end
322 |
--------------------------------------------------------------------------------
/lib/versionomy/format_definitions/rubygems.rb:
--------------------------------------------------------------------------------
1 | # -----------------------------------------------------------------------------
2 | #
3 | # Versionomy standard format implementation
4 | #
5 | # -----------------------------------------------------------------------------
6 | # Copyright 2008 Daniel Azuma
7 | #
8 | # All rights reserved.
9 | #
10 | # Redistribution and use in source and binary forms, with or without
11 | # modification, are permitted provided that the following conditions are met:
12 | #
13 | # * Redistributions of source code must retain the above copyright notice,
14 | # this list of conditions and the following disclaimer.
15 | # * Redistributions in binary form must reproduce the above copyright notice,
16 | # this list of conditions and the following disclaimer in the documentation
17 | # and/or other materials provided with the distribution.
18 | # * Neither the name of the copyright holder, nor the names of any other
19 | # contributors to this software, may be used to endorse or promote products
20 | # derived from this software without specific prior written permission.
21 | #
22 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 | # POSSIBILITY OF SUCH DAMAGE.
33 | # -----------------------------------------------------------------------------
34 | ;
35 |
36 |
37 | module Versionomy
38 |
39 | module Format
40 |
41 |
42 | # Get the rubygems format.
43 | # This is identical to calling get('rubygems').
44 | #
45 | # The rubygems format is designed to be parse-compatible with the
46 | # Gem::Version class used in rubygems. The only caveat is, whereas
47 | # Gem::Version handles an arbitrary number of fields, this format is
48 | # limited to a maximum of 8.
49 | #
50 | # For the exact annotated definition of the rubygems schema and format,
51 | # see the source code for Versionomy::Format::Rubygems#create.
52 |
53 | def self.rubygems
54 | get('rubygems')
55 | end
56 |
57 |
58 | # This is a namespace for the implementation of the Rubygems schema
59 | # and format.
60 |
61 | module Rubygems
62 |
63 |
64 | # Extra methods added to version values that use the rubygems schema.
65 |
66 | module ExtraMethods
67 |
68 |
69 | # Returns true if the version is a prerelease version-- that is,
70 | # if any of the fields is non-numeric.
71 | #
72 | # This behaves the same as the Gem::Version#prerelease? method
73 | # in rubygems.
74 |
75 | def prerelease?
76 | values_array.any?{ |val_| val_.kind_of?(::String) }
77 | end
78 |
79 |
80 | # Returns the release for this version.
81 | # For example, converts "1.2.0.a.1" to "1.2.0".
82 | # Non-prerelease versions return themselves.
83 | #
84 | # This behaves the same as the Gem::Version#release method
85 | # in rubygems.
86 |
87 | def release
88 | values_ = []
89 | self.each_field_object do |field_, val_|
90 | break unless val_.kind_of?(::Integer)
91 | values_ << val_
92 | end
93 | Value.new(values_, self.format, self.unparse_params)
94 | end
95 |
96 |
97 | # Returns a list of the field values, in field order, with
98 | # trailing zeroes stripped off.
99 | #
100 | # This behaves the same as the Gem::Version#parts method
101 | # in rubygems.
102 |
103 | def parts
104 | unless defined?(@parts)
105 | @parts = values_array
106 | @parts.pop while @parts.size > 1 && @parts.last == 0
107 | end
108 | @parts
109 | end
110 |
111 |
112 | end
113 |
114 |
115 | # Create the rubygems format.
116 | # This method is called internally when Versionomy loads the rubygems
117 | # format, and you should not need to call it again. It is documented
118 | # so that you can inspect its source code from RDoc, since the source
119 | # contains useful examples of how to use the schema and format
120 | # definition DSLs.
121 |
122 | def self.create
123 |
124 | # The following is the definition of the rubygems schema
125 | schema_ = Schema.create do
126 |
127 | # Global comparison function
128 | to_compare_type(:string) do |a_, b_|
129 | if a_.kind_of?(::Integer)
130 | if b_.kind_of?(::Integer)
131 | a_ <=> b_
132 | else
133 | 1
134 | end
135 | else
136 | if b_.kind_of?(::Integer)
137 | -1
138 | else
139 | a_ <=> b_
140 | end
141 | end
142 | end
143 |
144 | # Global canonicalization function
145 | to_canonicalize_type(:string) do |val_|
146 | if val_.kind_of?(::Integer)
147 | val_
148 | else
149 | val_ = val_.to_s
150 | if val_ =~ /\A\d*\z/
151 | val_.to_i
152 | else
153 | val_
154 | end
155 | end
156 | end
157 |
158 | # The first field has the default value of 1. All other fields
159 | # have a default value of 0. Thus, the default version number
160 | # overall is "1.0".
161 | field(:field0, :type => :integer, :default_value => 1) do
162 | field(:field1, :type => :string) do
163 | field(:field2, :type => :string) do
164 | field(:field3, :type => :string) do
165 | field(:field4, :type => :string) do
166 | field(:field5, :type => :string) do
167 | field(:field6, :type => :string) do
168 | field(:field7, :type => :string)
169 | end
170 | end
171 | end
172 | end
173 | end
174 | end
175 | end
176 |
177 | # Some field aliases providing alternate names for major fields
178 | alias_field(:major, :field0)
179 | alias_field(:minor, :field1)
180 |
181 | # Add the methods in this module to each value
182 | add_module(Format::Rubygems::ExtraMethods)
183 | end
184 |
185 | # The following is the definition of the rubygems format. It
186 | # understands the rubygems schema defined above.
187 | Format::Delimiter.new(schema_) do
188 |
189 | # All version number strings must start with the major version.
190 | # Unlike other fields, it is not preceded by any delimiter.
191 | field(:field0) do
192 | recognize_number(:delimiter_regexp => '', :default_delimiter => '')
193 | end
194 |
195 | # The remainder of the version number are represented as strings
196 | # or integers delimited by periods by default. Each is also
197 | # dependent on the presence of the previous field, so
198 | # :requires_previous_field retains its default value of true.
199 | # Finally, they can be optional in an unparsed string if they are
200 | # set to the default value of 0.
201 | field(:field1) do
202 | recognize_regexp('[0-9a-zA-Z]+', :default_value_optional => true)
203 | end
204 | field(:field2) do
205 | recognize_regexp('[0-9a-zA-Z]+', :default_value_optional => true)
206 | end
207 | field(:field3) do
208 | recognize_regexp('[0-9a-zA-Z]+', :default_value_optional => true)
209 | end
210 | field(:field4) do
211 | recognize_regexp('[0-9a-zA-Z]+', :default_value_optional => true)
212 | end
213 | field(:field5) do
214 | recognize_regexp('[0-9a-zA-Z]+', :default_value_optional => true)
215 | end
216 | field(:field6) do
217 | recognize_regexp('[0-9a-zA-Z]+', :default_value_optional => true)
218 | end
219 | field(:field7) do
220 | recognize_regexp('[0-9a-zA-Z]+', :default_value_optional => true)
221 | end
222 |
223 | # By default, we require that at least the first two fields
224 | # appear in an unparsed version string.
225 | default_unparse_params(:required_fields => [:field1])
226 | end
227 | end
228 |
229 |
230 | end
231 |
232 |
233 | register('rubygems', Format::Rubygems.create, true)
234 |
235 |
236 | end
237 |
238 |
239 | module Conversion
240 |
241 |
242 | # This is a namespace for the implementation of the conversion between
243 | # the rubygems and standard formats.
244 |
245 | module Rubygems
246 |
247 |
248 | # Create the conversion from standard to rubygems format.
249 | # This method is called internally when Versionomy loads the rubygems
250 | # format, and you should not need to call it again. It is documented
251 | # so that you can inspect its source code from RDoc, since the source
252 | # contains useful examples of how to use the conversion DSLs.
253 |
254 | def self.create_standard_to_rubygems
255 |
256 | # We'll use a parsing conversion.
257 | Conversion::Parsing.new do
258 |
259 | # We're going to modify how the standard format version is
260 | # unparsed, so the rubygems format will have a better chance
261 | # of parsing it.
262 | to_modify_unparse_params do |params_, convert_params_|
263 |
264 | params_ ||= {}
265 |
266 | # If the standard format version has a prerelease notation,
267 | # make sure it is set off using a delimiter that the rubygems
268 | # format can recognize. So instead of "1.0b2", we force the
269 | # unparsing to generate "1.0.b.2".
270 | params_[:release_type_delim] = '.'
271 | params_[:development_version_delim] = '.'
272 | params_[:alpha_version_delim] = '.'
273 | params_[:beta_version_delim] = '.'
274 | params_[:release_candidate_version_delim] = '.'
275 | params_[:preview_version_delim] = '.'
276 |
277 | # If the standard format version has a patchlevel notation,
278 | # force it to use the default number rather than letter style.
279 | # So instead of "1.2c", we force the unparsing to generate
280 | # "1.2-3".
281 | params_[:patchlevel_style] = nil
282 |
283 | # If the standard format version has a patchlevel notation,
284 | # force it to use the default delimiter of "-" so the rubygems
285 | # format will recognize it. So instead of "1.9.1p243", we force
286 | # the unparsing to generate "1.9.1-243".
287 | params_[:patchlevel_delim] = nil
288 |
289 | # If the standard format version includes a "v" prefix, strip
290 | # it because rubygems doesn't like it.
291 | params_[:major_delim] = nil
292 |
293 | params_
294 | end
295 |
296 | # Standard formats sometimes allow hyphens and spaces in field
297 | # delimiters, but the rubygems format requires periods. So modify
298 | # the unparsed string to conform to rubygems's expectations.
299 | to_modify_string do |str_, convert_params_|
300 | str_.gsub(/[\.\s-]+/, '.')
301 | end
302 |
303 | end
304 |
305 | end
306 |
307 |
308 | # Create the conversion from rubygems to standard format.
309 | # This method is called internally when Versionomy loads the rubygems
310 | # format, and you should not need to call it again. It is documented
311 | # so that you can inspect its source code from RDoc, since the source
312 | # contains useful examples of how to use the conversion DSLs.
313 |
314 | def self.create_rubygems_to_standard
315 |
316 | # We'll use a parsing conversion.
317 | Conversion::Parsing.new do
318 |
319 | # Handle the case where the rubygems version ends with a string
320 | # field, e.g. "1.0.b". We want to treat this like "1.0b0" rather
321 | # than "1.0-2" since the rubygems semantic states that this is a
322 | # prerelease version. So we add 0 to the end of the parsed string
323 | # if it ends in a letter.
324 | to_modify_string do |str_, convert_params_|
325 | str_.gsub(/([[:alpha:]])\z/, '\10')
326 | end
327 |
328 | end
329 |
330 | end
331 |
332 |
333 | end
334 |
335 |
336 | register(:standard, :rubygems, Conversion::Rubygems.create_standard_to_rubygems, true)
337 | register(:rubygems, :standard, Conversion::Rubygems.create_rubygems_to_standard, true)
338 |
339 |
340 | end
341 |
342 |
343 | end
344 |
--------------------------------------------------------------------------------
/test/tc_standard_parse.rb:
--------------------------------------------------------------------------------
1 | # -----------------------------------------------------------------------------
2 | #
3 | # Versionomy parsing tests on standard schema
4 | #
5 | # This file contains tests for parsing on the standard schema
6 | #
7 | # -----------------------------------------------------------------------------
8 | # Copyright 2008 Daniel Azuma
9 | #
10 | # All rights reserved.
11 | #
12 | # Redistribution and use in source and binary forms, with or without
13 | # modification, are permitted provided that the following conditions are met:
14 | #
15 | # * Redistributions of source code must retain the above copyright notice,
16 | # this list of conditions and the following disclaimer.
17 | # * Redistributions in binary form must reproduce the above copyright notice,
18 | # this list of conditions and the following disclaimer in the documentation
19 | # and/or other materials provided with the distribution.
20 | # * Neither the name of the copyright holder, nor the names of any other
21 | # contributors to this software, may be used to endorse or promote products
22 | # derived from this software without specific prior written permission.
23 | #
24 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
28 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 | # POSSIBILITY OF SUCH DAMAGE.
35 | # -----------------------------------------------------------------------------
36 |
37 |
38 | require 'minitest/autorun'
39 | require 'versionomy'
40 |
41 |
42 | module Versionomy
43 | module Tests # :nodoc:
44 |
45 | class TestStandardParse < ::Minitest::Test # :nodoc:
46 |
47 |
48 | # Test parsing full.
49 |
50 | def test_parsing_full_release
51 | value_ = ::Versionomy.parse('2.0.1.1-4.6')
52 | assert_equal(2, value_.major)
53 | assert_equal(0, value_.minor)
54 | assert_equal(1, value_.tiny)
55 | assert_equal(1, value_.tiny2)
56 | assert_equal(:final, value_.release_type)
57 | assert_equal(4, value_.patchlevel)
58 | assert_equal(6, value_.patchlevel_minor)
59 | assert_equal('2.0.1.1-4.6', value_.unparse)
60 | end
61 |
62 |
63 | # Test parsing abbreviated.
64 |
65 | def test_parsing_abbrev_release
66 | value_ = ::Versionomy.parse('2.0.1')
67 | assert_equal(2, value_.major)
68 | assert_equal(0, value_.minor)
69 | assert_equal(1, value_.tiny)
70 | assert_equal(0, value_.tiny2)
71 | assert_equal(:final, value_.release_type)
72 | assert_equal(0, value_.patchlevel)
73 | assert_equal(0, value_.patchlevel_minor)
74 | assert_equal('2.0.1.0-0.0', value_.unparse(:required_fields => [:minor, :tiny, :tiny2, :patchlevel, :patchlevel_minor]))
75 | assert_equal('2.0.1-0', value_.unparse(:required_fields => [:minor, :patchlevel]))
76 | assert_equal('2.0.1', value_.unparse)
77 | end
78 |
79 |
80 | # Test parsing with trailing zeros.
81 |
82 | def test_parsing_trailing_zeros
83 | value_ = ::Versionomy.parse('2.0.0')
84 | assert_equal(2, value_.major)
85 | assert_equal(0, value_.minor)
86 | assert_equal(0, value_.tiny)
87 | assert_equal(0, value_.tiny2)
88 | assert_equal(:final, value_.release_type)
89 | assert_equal(0, value_.patchlevel)
90 | assert_equal(0, value_.patchlevel_minor)
91 | assert_equal('2.0.0', value_.unparse)
92 | end
93 |
94 |
95 | # Test parsing with leading zeros on a field.
96 |
97 | def test_parsing_field_leading_zeros
98 | value_ = ::Versionomy.parse('2.09')
99 | assert_equal(2, value_.major)
100 | assert_equal(9, value_.minor)
101 | assert_equal(0, value_.tiny)
102 | assert_equal('2.09', value_.unparse)
103 | value_ = value_.bump(:minor)
104 | assert_equal(10, value_.minor)
105 | assert_equal('2.10', value_.unparse)
106 | value_ = value_.change(:minor => 123)
107 | assert_equal(123, value_.minor)
108 | assert_equal('2.123', value_.unparse)
109 | value_ = value_.change(:minor => 4)
110 | assert_equal(4, value_.minor)
111 | assert_equal('2.04', value_.unparse)
112 | value_ = ::Versionomy.parse('2.00')
113 | assert_equal(0, value_.minor)
114 | assert_equal('2.00', value_.unparse)
115 | end
116 |
117 |
118 | # Test unparsing with a minimum width.
119 |
120 | def test_unparsing_minimum_width_field
121 | value_ = ::Versionomy.parse('2.0')
122 | assert_equal('2.0', value_.unparse)
123 | assert_equal('2.00', value_.unparse(:minor_width => 2))
124 | assert_equal('02.0', value_.unparse(:major_width => 2))
125 | value_ = value_.bump(:minor)
126 | assert_equal('2.1', value_.unparse)
127 | assert_equal('2.01', value_.unparse(:minor_width => 2))
128 | value_ = value_.change(:minor => 12)
129 | assert_equal('2.12', value_.unparse)
130 | assert_equal('2.12', value_.unparse(:minor_width => 1))
131 | end
132 |
133 |
134 | # Test parsing major version only.
135 |
136 | def test_parsing_major_only
137 | value_ = ::Versionomy.parse('2')
138 | assert_equal(2, value_.major)
139 | assert_equal(0, value_.minor)
140 | assert_equal(0, value_.tiny)
141 | assert_equal(0, value_.tiny2)
142 | assert_equal(:final, value_.release_type)
143 | assert_equal(0, value_.patchlevel)
144 | assert_equal(0, value_.patchlevel_minor)
145 | assert_equal('2', value_.unparse)
146 | end
147 |
148 |
149 | # Test parsing preview.
150 |
151 | def test_parsing_preview
152 | value_ = ::Versionomy.parse('2.0.1pre4')
153 | assert_equal(2, value_.major)
154 | assert_equal(0, value_.minor)
155 | assert_equal(1, value_.tiny)
156 | assert_equal(0, value_.tiny2)
157 | assert_equal(:preview, value_.release_type)
158 | assert_equal(4, value_.preview_version)
159 | assert_equal(0, value_.preview_minor)
160 | assert_equal('2.0.1pre4', value_.unparse)
161 | assert_equal('2.0.1pre4.0', value_.unparse(:required_fields => [:preview_minor]))
162 | end
163 |
164 |
165 | # Test parsing alpha.
166 |
167 | def test_parsing_alpha
168 | value_ = ::Versionomy.parse('2.0.1a4.1')
169 | assert_equal(2, value_.major)
170 | assert_equal(0, value_.minor)
171 | assert_equal(1, value_.tiny)
172 | assert_equal(0, value_.tiny2)
173 | assert_equal(:alpha, value_.release_type)
174 | assert_equal(4, value_.alpha_version)
175 | assert_equal(1, value_.alpha_minor)
176 | assert_equal('2.0.1a4.1', value_.unparse)
177 | assert_equal('2.0.1a4.1', value_.unparse(:optional_fields => [:alpha_minor]))
178 | end
179 |
180 |
181 | # Test parsing beta.
182 |
183 | def test_parsing_beta
184 | value_ = ::Versionomy.parse('2.52.1b4.0')
185 | assert_equal(2, value_.major)
186 | assert_equal(52, value_.minor)
187 | assert_equal(1, value_.tiny)
188 | assert_equal(0, value_.tiny2)
189 | assert_equal(:beta, value_.release_type)
190 | assert_equal(4, value_.beta_version)
191 | assert_equal(0, value_.beta_minor)
192 | assert_equal('2.52.1b4.0', value_.unparse)
193 | assert_equal('2.52.1b4', value_.unparse(:optional_fields => [:beta_minor]))
194 | end
195 |
196 |
197 | # Test parsing beta alternates
198 |
199 | def test_parsing_beta_alternates
200 | assert_equal(::Versionomy.parse('2.52.1 beta4'), '2.52.1b4')
201 | assert_equal(::Versionomy.parse('2.52.1-b4'), '2.52.1b4')
202 | assert_equal(::Versionomy.parse('2.52.1_b4'), '2.52.1b4')
203 | assert_equal(::Versionomy.parse('2.52.1.b4'), '2.52.1b4')
204 | assert_equal(::Versionomy.parse('2.52.1B4'), '2.52.1b4')
205 | assert_equal(::Versionomy.parse('2.52.1BETA4'), '2.52.1b4')
206 | assert_equal(::Versionomy.parse('2.52.1 Beta4'), '2.52.1b4')
207 | assert_equal(::Versionomy.parse('2.52.1 eta4', :extra_characters => :ignore), '2.52.1')
208 | assert_equal(::Versionomy.parse('2.52.1 Beta'), '2.52.1b0')
209 | end
210 |
211 |
212 | # Test parsing release candidate.
213 |
214 | def test_parsing_release_candidate
215 | value_ = ::Versionomy.parse('0.2rc0')
216 | assert_equal(0, value_.major)
217 | assert_equal(2, value_.minor)
218 | assert_equal(0, value_.tiny)
219 | assert_equal(0, value_.tiny2)
220 | assert_equal(:release_candidate, value_.release_type)
221 | assert_equal(0, value_.release_candidate_version)
222 | assert_equal(0, value_.release_candidate_minor)
223 | assert_equal('0.2rc0', value_.unparse)
224 | assert_equal('0.2rc0.0', value_.unparse(:required_fields => [:release_candidate_minor]))
225 | assert_equal('0.2rc', value_.unparse(:optional_fields => [:release_candidate_version]))
226 | end
227 |
228 |
229 | # Test parsing release candidate changing to other prerelease.
230 | # Ensures that :short style takes precedence over :long for parsing "rc".
231 |
232 | def test_parsing_release_candidate_type_change
233 | value_ = ::Versionomy.parse('0.2rc1')
234 | assert_equal(:release_candidate, value_.release_type)
235 | assert_equal(1, value_.release_candidate_version)
236 | assert_equal('0.2rc1', value_.unparse)
237 | value_ = value_.change(:release_type => :beta)
238 | assert_equal(:beta, value_.release_type)
239 | assert_equal(1, value_.beta_version)
240 | assert_equal('0.2b1', value_.unparse)
241 | value_ = value_.change({:release_type => :beta}, :release_type_style => :long)
242 | assert_equal(:beta, value_.release_type)
243 | assert_equal(1, value_.beta_version)
244 | assert_equal('0.2beta1', value_.unparse)
245 | end
246 |
247 |
248 | # Test parsing forms without a prerelease version
249 |
250 | def test_parsing_without_prerelease_version
251 | value_ = ::Versionomy.parse('1.9.2dev')
252 | assert_equal(value_.release_type, :development)
253 | assert_equal(value_.development_version, 0)
254 | assert_equal('1.9.2dev', value_.to_s)
255 | value_ = value_.bump(:development_version)
256 | assert_equal('1.9.2dev1', value_.to_s)
257 | end
258 |
259 |
260 | # Test parsing forms without a prerelease version.
261 | # Ensures that :development_version prefers to be required.
262 |
263 | def test_unparsing_prerelease_version_0
264 | value_ = ::Versionomy.parse('1.9.2dev1')
265 | assert_equal(value_.release_type, :development)
266 | assert_equal(value_.development_version, 1)
267 | assert_equal('1.9.2dev1', value_.to_s)
268 | value2_ = value_.change(:development_version => 0)
269 | assert_equal('1.9.2dev0', value2_.to_s)
270 | value2_ = value_.change({:development_version => 0}, :optional_fields => [:development_version])
271 | assert_equal('1.9.2dev', value2_.to_s)
272 | end
273 |
274 |
275 | # Test unparsing a value that requires lookback.
276 |
277 | def test_unparsing_with_lookback
278 | value_ = ::Versionomy.parse('2.0')
279 | value2_ = value_.change(:tiny2 => 1)
280 | assert_equal(1, value2_.tiny2)
281 | assert_equal('2.0.0.1', value2_.unparse)
282 | value3_ = value2_.change(:tiny2 => 0)
283 | assert_equal(0, value3_.tiny2)
284 | assert_equal('2.0', value3_.unparse)
285 | end
286 |
287 |
288 | # Test delimiter changes in a multi-form field.
289 |
290 | def test_multi_form_delimiter_changes
291 | value_ = ::Versionomy.parse('2.0 preview 1')
292 | assert_equal('2.0 preview 1', value_.to_s)
293 | value2_ = value_.change(:release_type => :final)
294 | assert_equal('2.0', value2_.to_s)
295 | value3_ = value2_.change(:release_type => :preview, :preview_version => 1)
296 | assert_equal('2.0 preview 1', value3_.to_s)
297 | end
298 |
299 |
300 | # Test different patchlevel separators.
301 |
302 | def test_patchlevel_separators
303 | expected_ = [1,9,1,0,:final,243,0]
304 | assert_equal(expected_, ::Versionomy.parse('1.9.1-p243').values_array)
305 | assert_equal(expected_, ::Versionomy.parse('1.9.1_p243').values_array)
306 | assert_equal(expected_, ::Versionomy.parse('1.9.1_u243').values_array)
307 | assert_equal(expected_, ::Versionomy.parse('1.9.1p243').values_array)
308 | assert_equal(expected_, ::Versionomy.parse('1.9.1.p243').values_array)
309 | assert_equal(expected_, ::Versionomy.parse('1.9.1 p243').values_array)
310 | assert_equal(expected_, ::Versionomy.parse('1.9.1-243').values_array)
311 | assert_equal(expected_, ::Versionomy.parse('1.9.1_243').values_array)
312 | end
313 |
314 |
315 | # Test alphabetic patchlevels.
316 | # In particular, make sure the parser can distinguish between these
317 | # and the markers for prereleases.
318 |
319 | def test_patchlevel_alphabetic
320 | value_ = ::Versionomy.parse('1.9a')
321 | assert_equal([1, 9, 0, 0, :final, 1, 0], value_.values_array)
322 | assert_equal('1.9a', value_.to_s)
323 | value_ = ::Versionomy.parse('1.9b')
324 | assert_equal([1, 9, 0, 0, :final, 2, 0], value_.values_array)
325 | assert_equal('1.9b', value_.to_s)
326 | value_ = ::Versionomy.parse('1.9d')
327 | assert_equal([1, 9, 0, 0, :final, 4, 0], value_.values_array)
328 | assert_equal('1.9d', value_.to_s)
329 | value_ = ::Versionomy.parse('1.9p')
330 | assert_equal([1, 9, 0, 0, :final, 16, 0], value_.values_array)
331 | assert_equal('1.9p', value_.to_s)
332 | value_ = ::Versionomy.parse('1.9r')
333 | assert_equal([1, 9, 0, 0, :final, 18, 0], value_.values_array)
334 | assert_equal('1.9r', value_.to_s)
335 | value_ = ::Versionomy.parse('1.9u')
336 | assert_equal([1, 9, 0, 0, :final, 21, 0], value_.values_array)
337 | assert_equal('1.9u', value_.to_s)
338 | end
339 |
340 |
341 | # Test setting delimiters on unparse, including testing for illegal delimiters
342 |
343 | def test_unparse_with_custom_delimiters
344 | value_ = ::Versionomy.parse('1.2b3')
345 | assert_equal('1.2.b.3', value_.unparse(:release_type_delim => '.', :beta_version_delim => '.'))
346 | assert_equal('1.2b3', value_.unparse(:release_type_delim => '=', :beta_version_delim => '*'))
347 | value_ = ::Versionomy.parse('1.2-4')
348 | assert_equal('1.2-4', value_.unparse(:release_type_delim => '.'))
349 | end
350 |
351 |
352 | # Test java version formats
353 |
354 | def test_java_formats
355 | value_ = ::Versionomy.parse('1.6.0_17')
356 | assert_equal([1, 6, 0, 0, :final, 17, 0], value_.values_array)
357 | assert_equal('1.6.0_17', value_.to_s)
358 | value_ = ::Versionomy.parse('6u17')
359 | assert_equal([6, 0, 0, 0, :final, 17, 0], value_.values_array)
360 | assert_equal('6u17', value_.to_s)
361 | end
362 |
363 |
364 | # Test formats prefixed with "v"
365 |
366 | def test_v_prefix
367 | value_ = ::Versionomy.parse('v1.2')
368 | assert_equal([1, 2, 0, 0, :final, 0, 0], value_.values_array)
369 | assert_equal('v1.2', value_.to_s)
370 | value_ = ::Versionomy.parse('V 2.3')
371 | assert_equal([2, 3, 0, 0, :final, 0, 0], value_.values_array)
372 | assert_equal('V 2.3', value_.to_s)
373 | end
374 |
375 |
376 | # Test parse errors
377 |
378 | def test_parsing_errors
379 | assert_raises(::Versionomy::Errors::ParseError) do
380 | ::Versionomy.parse('2.52.1 eta4')
381 | end
382 | end
383 |
384 |
385 | end
386 |
387 | end
388 | end
389 |
--------------------------------------------------------------------------------
/Versionomy.rdoc:
--------------------------------------------------------------------------------
1 | == Versionomy
2 |
3 | Versionomy is a generalized version number library.
4 | It provides tools to represent, manipulate, parse, and compare version
5 | numbers in the wide variety of versioning schemes in use.
6 |
7 | This document provides a step-by-step introduction to most of the features
8 | of Versionomy.
9 |
10 | === Version numbers done right?
11 |
12 | Let's be honest. Version numbers are not easy to deal with, and very
13 | seldom seem to be done right.
14 |
15 | Imagine the common case of testing the Ruby version. Most of us, if we
16 | need to worry about Ruby VM compatibility, will do something like:
17 |
18 | do_something if RUBY_VERSION >= "1.8.7"
19 |
20 | Treating the version number as a string works well enough, until it
21 | doesn't. The above code will do the right thing for Ruby 1.8.6, 1.8.7,
22 | 1.8.8, and 1.9.1. But it will fail if the version is "1.8.10" or "1.10".
23 | And properly interpreting "prerelease" version syntax such as
24 | "1.9.2-preview1"? Forget it.
25 |
26 | There are a few version number classes out there that do better than
27 | treating version numbers as plain strings. One example is Gem::Version,
28 | part of the RubyGems package. This class separates the version into fields
29 | and lets you manipulate and compare version numbers more robustly. It even
30 | provides limited support for "prerelease" versions through using string-
31 | valued fields-- although it's a hack, and a bit of a clumsy one at that. A
32 | prerelease version has to be represented like this: "1.9.2.b.1" or
33 | "1.9.2.preview.2". Wouldn't it be nice to be able to parse more typical
34 | version number formats such as "1.9.2b1" and "1.9.2-preview2"? Wouldn't
35 | it be nice for a version like "1.9.2b1" to _understand_ that it's a "beta"
36 | version and behave accordingly?
37 |
38 | With Versionomy, you can do all this and more. Here's how...
39 |
40 | === Creating version numbers
41 |
42 | Creating a version number object in Versionomy is as simple as passing a
43 | string to a factory. Versionomy understands a wide range of version number
44 | formats out of the box.
45 |
46 | v1 = Versionomy.parse('1.2') # Simple version numbers
47 | v2 = Versionomy.parse('2.1.5.0') # Up to four fields supported
48 | v3 = Versionomy.parse('1.9b3') # Alpha and beta versions
49 | v4 = Versionomy.parse('1.9rc2') # Release candidates too
50 | v5 = Versionomy.parse('1.9.2-preview2') # Preview releases
51 | v6 = Versionomy.parse('1.9.2-p6') # Patchlevels
52 | v7 = Versionomy.parse('v2.0 beta 6.1') # Many alternative syntaxes
53 |
54 | You can also construct version numbers manually by passing a hash of field
55 | values. See the next section for a discussion of fields.
56 |
57 | v1 = Versionomy.create(:major => 1, :minor => 2) # creates version "1.2"
58 | v2 = Versionomy.create(:major => 1, :minor => 9,
59 | :release_type => :beta, :beta_version => 3) # creates version "1.9b3"
60 |
61 | The current ruby virtual machine version can be obtained using:
62 |
63 | v1 = Versionomy.ruby_version
64 |
65 | Many other libraries include their version as a string constant in their
66 | main namespace module. Versionomy provides a quick facility to attempt to
67 | extract the version of a library:
68 |
69 | require 'nokogiri'
70 | v1 = Versionomy.version_of(Nokogiri)
71 |
72 | === Version number fields
73 |
74 | A version number is a collection of fields in a particular order. Standard
75 | version numbers have the following fields:
76 |
77 | * :major
78 | * :minor
79 | * :tiny
80 | * :tiny2
81 | * :release_type
82 |
83 | The first four fields correspond to the four numeric fields of the version
84 | number. E.g. version numbers have the form "major.minor.tiny.tiny2".
85 | Trailing fields that have a zero value may be omitted from a string
86 | representation, but are still present in the Versionomy::Value object.
87 |
88 | The fifth field is special. Its value is one of the following symbols:
89 |
90 | * :development
91 | * :alpha
92 | * :beta
93 | * :release_candidate
94 | * :preview
95 | * :final
96 |
97 | The value of the :release_type field determines which other fields are
98 | available in the version number. If the :release_type is :development, then
99 | the two fields :development_version and :development_minor are available.
100 | Similarly, if :release_type is :alpha, then the two fields :alpha_version
101 | and :alpha_minor are available, and so on. If :release_type is :final, that
102 | exposes the two fields :patchlevel and :patchlevel_minor.
103 |
104 | You can query a field value simply by calling a method on the value:
105 |
106 | v1 = Versionomy.parse('1.2b3')
107 | v1.major # => 1
108 | v1.minor # => 2
109 | v1.tiny # => 0
110 | v1.tiny2 # => 0
111 | v1.release_type # => :beta
112 | v1.beta_version # => 3
113 | v1.beta_minor # => 0
114 | v1.release_candidate_version # raises NoMethodError
115 |
116 | The above fields are merely the standard fields that Versionomy provides
117 | out of the box. Versionomy also provides advanced users the ability to
118 | define new version "schemas" with any number of different fields and
119 | different semantics. See the RDocs for Versionomy::Schema for more
120 | information.
121 |
122 | === Version number calculations
123 |
124 | Version numbers can be compared (and thus sorted). Versionomy knows how to
125 | handle prerelease versions and patchlevels correctly. It also compares the
126 | semantic value so even if versions use an alternate syntax, they will be
127 | compared correctly. Each of these expressions evaluates to true:
128 |
129 | Versionomy.parse('1.2') < Versionomy.parse('1.10')
130 | Versionomy.parse('1.2') > Versionomy.parse('1.2b3')
131 | Versionomy.parse('1.2b3') > Versionomy.parse('1.2a4')
132 | Versionomy.parse('1.2') < Versionomy.parse('1.2-p1')
133 | Versionomy.parse('1.2') == Versionomy.parse('1.2-p0')
134 | Versionomy.parse('1.2b3') == Versionomy.parse('1.2.0-beta3')
135 |
136 | Versionomy automatically converts (parses) strings when comparing with a
137 | version number, so you could even evaluate these:
138 |
139 | Versionomy.parse('1.2') < '1.10'
140 | Versionomy::VERSION > '0.2'
141 |
142 | The Versionomy API provides various methods for manipulating fields such as
143 | bumping, resetting to default, and changing to an arbitrary value. Version
144 | numbers are always immutable, so changing a version number always produces
145 | a copy. Below are a few examples. See the RDocs for the class
146 | Versionomy::Value for more details.
147 |
148 | v_orig = Versionomy.parse('1.2b3')
149 | v1 = v_orig.change(:beta_version => 4) # creates version "1.2b4"
150 | v2 = v_orig.change(:tiny => 4) # creates version "1.2.4b3"
151 | v3 = v_orig.bump(:minor) # creates version "1.3"
152 | v4 = v_orig.bump(:release_type) # creates version "1.2rc1"
153 | v5 = v_orig.reset(:minor) # creates version "1.0"
154 |
155 | A few more common calculations are also provided:
156 |
157 | v_orig = Versionomy.parse('1.2b3')
158 | v_orig.prerelease? # => true
159 | v6 = v_orig.release # creates version "1.2"
160 |
161 | === Parsing and unparsing
162 |
163 | Versionomy's parsing and unparsing services appear simple from the outside,
164 | but a closer look reveals some sophisticated features. Parsing is as simple
165 | as passing a string to Versionomy#parse, and unparsing is as simple as
166 | calling Versionomy::Value#unparse or Versionomy::Value#to_s.
167 |
168 | v = Versionomy.parse('1.2b3') # Create a Versionomy::Value
169 | v.unparse # => "1.2b3"
170 |
171 | Versionomy does its best to preserve the original syntax when parsing a
172 | version string, so that syntax can be used when unparsing.
173 |
174 | v1 = Versionomy.parse('1.2b3')
175 | v2 = Versionomy.parse('1.2.0-beta3')
176 | v1 == b2 # => true
177 | v1.unparse # => "1.2b3"
178 | v2.unparse # => "1.2.0-beta3"
179 |
180 | Versionomy even preserves the original syntax when changing a value:
181 |
182 | v1 = Versionomy.parse('1.2b3')
183 | v2 = Versionomy.parse('1.2.0.0b3')
184 | v1 == v2 # => true
185 | v1r = v1.release
186 | v2r = v2.release
187 | v1r == v2r # => true
188 | v1r.unparse # => "1.2"
189 | v2r.unparse # => "1.2.0.0"
190 |
191 | You can change the settings manually when unparsing a value.
192 |
193 | v1 = Versionomy.parse('1.2b3')
194 | v1.unparse # => "1.2b3"
195 | v1.unparse(:required_fields => :tiny) # => "1.2.0b3"
196 | v1.unparse(:release_type_delim => '-',
197 | :release_type_style => :long) # => "1.2-beta3"
198 |
199 | Versionomy also supports serialization using Marshal and YAML.
200 |
201 | require 'yaml'
202 | v1 = Versionomy.parse('1.2b3')
203 | v1.unparse # => "1.2b3"
204 | str = v1.to_yaml
205 | v2 = YAML.load(str)
206 | v2.unparse # => "1.2b3"
207 |
208 | === Customized formats
209 |
210 | Although the standard parser used by Versionomy is likely sufficient for
211 | most common syntaxes, Versionomy also lets you customize the parser for an
212 | unusual syntax. Here is an example of a customized formatter for version
213 | numbers used by a certain large software company:
214 |
215 | year_sp_format = Versionomy.default_format.modified_copy do
216 | field(:minor) do
217 | recognize_number(:default_value_optional => true,
218 | :delimiter_regexp => '\s?sp',
219 | :default_delimiter => ' SP')
220 | end
221 | end
222 | v1 = year_sp_format.parse('2008 SP2')
223 | v1.major # => 2008
224 | v1.minor # => 2
225 | v1.unparse # => "2008 SP2"
226 | v1 == "2008.2" # => true
227 | v2 = v1.bump(:minor)
228 | v2.unparse # => "2008 SP3"
229 |
230 | The above example uses a powerful DSL provided by Versionomy to create a
231 | specialized parser. In most cases, this DSL will be powerful enough to
232 | handle your parsing needs; in fact Versionomy's entire standard parser is
233 | written using the DSL. However, in case you need to parse very unusual
234 | syntax, you can also write an arbitrary parser. See the RDocs for the
235 | Versionomy::Format::Delimiter class for more information on the DSL. See
236 | the RDocs for the Versionomy::Format::Base class for information on the
237 | interface you need to implement to write an arbitrary parser.
238 |
239 | If you create a format, you can register it with Versionomy and provide a
240 | name for it. This will allow you to reference it easily, as well as allow
241 | Versionomy to serialize versions created with your custom format. See the
242 | RDocs for the Versionomy::Format module for more information.
243 |
244 | Versionomy::Format.register("bigcompany.versionformat", year_sp_format)
245 | v1 = Versionomy.parse("2009 SP1", "bigcompany.versionformat")
246 |
247 | Note that versions in the year_sp_format example can be compared with
248 | versions using the standard parser. This is because the versions actually
249 | share the same schema-- that is, they have the same fields. We have merely
250 | changed the parser.
251 |
252 | Recall that it is also possible to change the schema (the fields). This is
253 | also done via a DSL (see the Versionomy::Schema module and its contents).
254 | Version numbers with different schemas cannot normally be compared, because
255 | they have different fields and different semantics. You can, however,
256 | define ways to convert version numbers from one schema to another. See the
257 | Versionomy::Conversion module and its contents for details.
258 |
259 | Versionomy provides an example of a custom schema with its own custom
260 | format, designed to mimic the Rubygems version class. This can be accessed
261 | using the format registered under the name "rubygems". Conversion functions
262 | are also provided between the rubygems and standard schemas.
263 |
264 | v1 = Versionomy.parse("1.2b3") # Standard schema/format
265 | v2 = Versionomy.parse("1.2.b.4", :rubygems) # Rubygems schema/format
266 | v2.field0 # => 1
267 | # (Rubygems fields have different names)
268 | v1a = v1.convert(:rubygems) # creates rubygems version "1.2.b.3"
269 | v2a = v2.convert(:standard) # creates standard version "1.2b4"
270 | v1 < v2 # => true
271 | # (Schemas are different but Versionomy
272 | # autoconverts if possible)
273 | v2 < v1 # => false
274 | v3 = Versionomy.parse("1.2.foo", :rubygems) # rubygems schema/format
275 | v3a = v3.convert(:standard) # raises Versionomy::Errors::ConversionError
276 | # (Value not convertable to standard)
277 | v1 < v3 # raises Versionomy::Errors::SchemaMismatchError
278 | # (Autoconversion failed)
279 | v3 > v1 # => true
280 | # (Autoconversion is attempted only on the
281 | # the second value, and this one succeeds.)
282 |
283 | The APIs for defining schemas, formats, and conversions are rather complex.
284 | I recommend looking through the examples in the modules
285 | Versionomy::Format::Standard, Versionomy::Format::Rubygems, and
286 | Versionomy::Conversion::Rubygems for further information.
287 |
288 | === Requirements
289 |
290 | * Ruby 1.9.3 or later, JRuby 1.5 or later, or Rubinius 1.0 or later.
291 | * blockenspiel 0.5.0 or later.
292 |
293 | === Installation
294 |
295 | gem install versionomy
296 |
297 | === Known issues and limitations
298 |
299 | * Test coverage is still a little skimpy. It is focused on the "standard"
300 | version number format and schema, but doesn't fully exercise all the
301 | capabilities of custom formats.
302 |
303 | === Development and support
304 |
305 | Documentation is available at http://dazuma.github.com/versionomy/rdoc
306 |
307 | Source code is hosted on Github at http://github.com/dazuma/versionomy
308 |
309 | Contributions are welcome. Fork the project on Github.
310 |
311 | Build status: {
}[http://travis-ci.org/dazuma/versionomy]
312 |
313 | Report bugs on Github issues at http://github.org/dazuma/versionomy/issues
314 |
315 | Contact the author at dazuma at gmail dot com.
316 |
317 | === Author / Credits
318 |
319 | Versionomy is written by Daniel Azuma (http://www.daniel-azuma.com/).
320 |
321 | == LICENSE:
322 |
323 | Copyright 2008 Daniel Azuma.
324 |
325 | All rights reserved.
326 |
327 | Redistribution and use in source and binary forms, with or without
328 | modification, are permitted provided that the following conditions are met:
329 |
330 | * Redistributions of source code must retain the above copyright notice,
331 | this list of conditions and the following disclaimer.
332 | * Redistributions in binary form must reproduce the above copyright notice,
333 | this list of conditions and the following disclaimer in the documentation
334 | and/or other materials provided with the distribution.
335 | * Neither the name of the copyright holder, nor the names of any other
336 | contributors to this software, may be used to endorse or promote products
337 | derived from this software without specific prior written permission.
338 |
339 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
340 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
341 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
342 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
343 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
344 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
345 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
346 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
347 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
348 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
349 | POSSIBILITY OF SUCH DAMAGE.
350 |
--------------------------------------------------------------------------------