1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
|
# -----------------------------------------------------------------------------
#
# Versionomy format namespace
#
# -----------------------------------------------------------------------------
# Copyright 2008 Daniel Azuma
#
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of the copyright holder, nor the names of any other
# contributors to this software, may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# -----------------------------------------------------------------------------
;
require 'thread'
require 'monitor'
module Versionomy
# === Version number format.
#
# A format controls the parsing and unparsing of a version number.
# Any time a version number is parsed from a string, a format is provided
# to parse it. Similarly, every version number value references a format
# that is used to unparse it back into a string.
#
# A format is always tied to a particular schema and knows how to parse
# only that schema's version numbers.
#
# Under many circumstances, you should use the standard format, which
# can be retrieved by calling Versionomy::Format#standard. This format
# understands most common version numbers, including prerelease
# (e.g. alpha, beta, release candidate, etc.) forms and patchlevels.
#
# You may also create your own formats, either by implementing the
# format contract (see Versionomy::Format::Base), or by using the
# Versionomy::Format::Delimiter tool, which can be used to construct
# parsers for many version number formats.
#
# === Format registry
#
# Formats may be registered with Versionomy and given a name using the
# methods of this module. This allows version numbers to be serialized
# with their format. When a version number is serialized, its format
# name is written to the stream, along with the version number's string
# representation. When the version number is reconstructed, its format
# is looked up by name so versionomy can determine how to parse the
# string.
#
# Format names are strings that may include letters, numerals, dashes,
# underscores, and periods. By convention, periods are used as namespace
# delimiters. Format names without a namespace (that is, with no periods)
# are considered reserved for standard versionomy formats. If you define
# your own format, you should use a name that includes a namespace (e.g.
# "mycompany.LibraryVersion") to reduce the chance of name collisions.
#
# You may register formats directly using the register method, or set it
# up to be autoloaded on demand. When a format is requested, if it has
# not been registered explicitly, Versionomy looks for a format definition
# file for that format. Such a file has the name of the format, with the
# ".rb" extension for ruby (e.g. "mycompany.LibraryVersion.rb") and must
# be located in a directory in versionomy's format lookup path. By
# default, the directory containing versionomy's predefined formats
# (such as "standard") is in this path. However, you may add your own
# directories using the add_directory method. This lets you autoload your
# own formats. A format definition file itself must contain ruby code
# that defines the format and registers it using the correct name. See
# the files in the "lib/versionomy/format_definitions/" directory for
# examples.
module Format
@mutex = ::Mutex.new
@load_mutex = ::Monitor.new
@directories = [::File.expand_path(::File.dirname(__FILE__)+'/format_definitions')]
@names_to_formats = {}
@formats_to_names = {}
class << self
# Add a directory to the format path.
#
# The format path is an array of directory paths. These directories
# are searched for format definitions if a format name that has not
# been registered is requested.
#
# If high_priority_ is set to true, the directory is added to the
# front of the lookup path; otherwise it is added to the back.
def add_directory(path_, high_priority_=false)
path_ = ::File.expand_path(path_)
@mutex.synchronize do
unless @directories.include?(path_)
if high_priority_
@directories.unshift(path_)
else
@directories.push(path_)
end
end
end
end
# Get the format with the given name.
#
# If the given name has not been defined, attempts to autoload it from
# a format definition file. See the description of the Format module
# for details on this procedure.
#
# If the given name still cannot be resolved, and strict is set to
# true, raises Versionomy::Errors::UnknownFormatError. If strict is
# set to false, returns nil if the given name cannot be resolved.
def get(name_, strict_=false)
name_ = _check_name(name_)
format_ = @mutex.synchronize{ @names_to_formats[name_] }
if format_.nil?
# Attempt to find the format in the directory path
dirs_ = @mutex.synchronize{ @directories.dup }
dirs_.each do |dir_|
path_ = "#{dir_}/#{name_}.rb"
if ::File.readable?(path_)
@load_mutex.synchronize{ ::Kernel.load(path_) }
end
format_ = @mutex.synchronize{ @names_to_formats[name_] }
break unless format_.nil?
end
end
if format_.nil? && strict_
raise Errors::UnknownFormatError, name_
end
format_
end
# Determines whether a format with the given name has been registered
# explicitly. Does not attempt to autoload the format.
def registered?(name_)
name_ = _check_name(name_)
@mutex.synchronize{ @names_to_formats.include?(name_) }
end
# Register the given format under the given name.
#
# Valid names may contain only letters, digits, underscores, dashes,
# and periods.
#
# Raises Versionomy::Errors::FormatRedefinedError if the name has
# already been defined.
def register(name_, format_, silent_=false)
name_ = _check_name(name_)
@mutex.synchronize do
if @names_to_formats.include?(name_)
unless silent_
raise Errors::FormatRedefinedError, name_
end
else
@names_to_formats[name_] = format_
@formats_to_names[format_.object_id] = name_
end
end
end
# Get the canonical name for the given format, as a string.
# This is the first name the format was registered under.
#
# If the given format was never registered, and strict is set to true,
# raises Versionomy::Errors::UnknownFormatError. If strict is set to
# false, returns nil if the given format was never registered.
def canonical_name_for(format_, strict_=false)
name_ = @mutex.synchronize{ @formats_to_names[format_.object_id] }
if name_.nil? && strict_
raise Errors::UnknownFormatError
end
name_
end
private
def _check_name(name_) # :nodoc:
name_ = name_.to_s
unless name_ =~ /\A[\w.-]+\z/
raise ::ArgumentError, "Illegal name: #{name_.inspect}"
end
name_
end
end
end
# Versionomy::Formats is an alias for Versionomy::Format, for backward
# compatibility with version 0.1.0 code. It is deprecated; use
# Versionomy::Format instead.
Formats = Format
end
|