Skip to content

Commit

Permalink
Merged Formats into Format and Conversions into Conversion; Marshal s…
Browse files Browse the repository at this point in the history
…upport; various fixes and fleshing out of documentation
  • Loading branch information
dazuma committed Nov 5, 2009
1 parent 1de0406 commit 5118922
Show file tree
Hide file tree
Showing 22 changed files with 561 additions and 399 deletions.
5 changes: 4 additions & 1 deletion History.rdoc
@@ -1,11 +1,14 @@
=== 0.2.0 / 2009-??-??
=== 0.2.0 / 2009-11-05

* Added a mechanism for converting from one format/schema to another.
* Added Rubygems format, along with conversions to and from the standard format.
* 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.
* Values now include Comparable.
* Values can now be marshalled and unmarshalled.
* Schemas can now add custom methods to value objects.
* Added default field settings to schema DSL.
* Implemented #=== for schemas and formats.
* Merged Formats namespace into Format. Versionomy::Formats is now a (deprecated) alias for Versionomy::Format.

=== 0.1.3 / 2009-10-29

Expand Down
8 changes: 3 additions & 5 deletions lib/versionomy.rb
Expand Up @@ -52,15 +52,13 @@
'format',
'format/base',
'format/delimiter',
'formats',
'formats/standard',
'formats/rubygems',
'format/standard',
'format/rubygems',
'value',
'conversion',
'conversion/base',
'conversion/parsing',
'conversions',
'conversions/rubygems',
'conversion/rubygems',
'interface',
'version',
]
Expand Down
77 changes: 76 additions & 1 deletion lib/versionomy/conversion.rb
Expand Up @@ -39,14 +39,31 @@ module Versionomy

# === Conversion between version schemas.
#
# Conversions are algorithms for converting from one schema to another.
# This is useful for performing conversions as well as comparing version
# numbers that use different schemas.
#
# To implement a conversion algorithm, implement the API defined by
# Versionomy::Conversion::Base. Then, register your conversion by calling
# Versionomy::Conversion#register. You will need to specify which schemas
# (from and to) that your conversion should handle. From that point on,
# whenever Versionomy needs to convert a value between those two schemas,
# it will use your conversion. You can register the same conversion object
# for multiple pairs of schemas, but you can register only one conversion
# object for any pair.

module Conversion

@registry = ::Hash.new

class << self


# Convert the given value to the given format.
# Convert the given value to the given format. This is identical to
# calling <tt>value_.convert(format_, convert_params_)</tt>.
#
# The format may be specified as a format object or as the name of a
# format in the Format registry.
#
# Raises Versionomy::Errors::ConversionError if the value could not
# be converted.
Expand All @@ -56,6 +73,64 @@ def convert(value_, format_, convert_params_=nil)
end


# Get a conversion capable of converting between the given schemas.
#
# The schemas may be specified as format names, Format objects,
# schema wrapper objects, or the root field of the schema.
#
# If strict is set to false, returns nil if no such conversion could
# be found. If strict is set to true, may raise one of these errors:
#
# Raises Versionomy::Errors::UnknownFormatError if a format was
# specified by name but the name is not known.
#
# Raises Versionomy::Errors::UnknownConversionError if the formats
# were recognized but no conversion was found to handle them.

def get(from_schema_, to_schema_, strict_=false)
key_ = _get_key(from_schema_, to_schema_)
conversion_ = @registry[key_]
if strict_ && conversion_.nil?
raise Errors::UnknownConversionError
end
conversion_
end


# Register the given conversion as the handler for the given schemas.
#
# The schemas may be specified as format names, Format objects,
# schema wrapper objects, or the root field of the schema.
#
# Raises Versionomy::Errors::ConversionRedefinedError if a conversion
# has already been registered for the given schemas.
#
# Raises Versionomy::Errors::UnknownFormatError if a format was
# specified by name but the name is not known.

def register(from_schema_, to_schema_, conversion_)
key_ = _get_key(from_schema_, to_schema_)
if @registry.include?(key_)
raise Errors::ConversionRedefinedError
end
@registry[key_] = conversion_
end


private

def _get_key(from_schema_, to_schema_) # :nodoc:
[_get_schema(from_schema_), _get_schema(to_schema_)]
end

def _get_schema(schema_) # :nodoc:
schema_ = Format.get(schema_, true) if schema_.kind_of?(::String) || schema_.kind_of?(::Symbol)
schema_ = schema_.schema if schema_.respond_to?(:schema)
schema_ = schema_.root_field if schema_.respond_to?(:root_field)
schema_
end


end

end
Expand Down
15 changes: 13 additions & 2 deletions lib/versionomy/conversion/base.rb
Expand Up @@ -49,8 +49,10 @@ module Conversion
class Base


# Convert the given value.
# Returns an equivalent value in the to_schema.
# Returns a value equivalent to the given value in the given format.
#
# The convert_params may be interpreted differently for different
# conversion implementations.
#
# Raises Versionomy::Errors::ConversionError if the conversion failed.

Expand All @@ -59,6 +61,15 @@ def convert_value(value_, format_, convert_params_=nil)
end


def inspect # :nodoc:
"#<#{self.class}:0x#{object_id.to_s(16)}>"
end

def to_s # :nodoc:
inspect
end


end


Expand Down
85 changes: 65 additions & 20 deletions lib/versionomy/conversion/parsing.rb
Expand Up @@ -47,37 +47,59 @@ module Conversion
class Parsing


# Create an instance of this base conversion, with the given from and
# to schemas.
# Create a parsing conversion.
#
# By default, this just unparses and reparses using the default
# parse settings. In some cases, this may be enough, but you may
# wish to improve the reliability of the conversion by tweaking the
# parsing settings. To do so, pass a block to the new method, and
# call methds of Versionomy::Conversion::Parsing::Builder in that
# block.

def initialize(opts_={}, &block_)
@parse_params = opts_[:parse_params]
def initialize(&block_)
if block_
builder_ = Builder.new
::Blockenspiel.invoke(block_, builder_)
@string_modifier = builder_._get_string_modifier
@unparse_params_modifier = builder_._get_unparse_params_modifier
@parse_params ||= builder_._get_parse_params
@parse_params_generator ||= builder_._get_parse_params_generator
end
end


# Convert the given value. The value must match the from_schema.
# Returns an equivalent value in the to_schema.
def inspect # :nodoc:
"#<#{self.class}:0x#{object_id.to_s(16)}>"
end

def to_s # :nodoc:
inspect
end


# Returns a value equivalent to the given value in the given format.
#
# The convert_params are passed to this conversion's customization
# blocks (if any).
#
# Raises Versionomy::Errors::ConversionError if the conversion failed.
# Typically, this is due to a failure of the parsing or unparsing.

def convert_value(value_, format_, convert_params_=nil)
begin
unparse_params_ = value_.unparse_params
if @unparse_params_modifier
unparse_params_ = @unparse_params_modifier.call(unparse_params_)
unparse_params_ = @unparse_params_modifier.call(unparse_params_, convert_params_)
end
string_ = value_.unparse(unparse_params_)
if @string_modifier
string_ = @string_modifier.call(string_)
string_ = @string_modifier.call(string_, convert_params_)
end
if @parse_params_generator
parse_params_ = @parse_params_generator.call(convert_params_)
else
parse_params_ = nil
end
new_value_ = format_.parse(string_, @parse_params)
new_value_ = format_.parse(string_, parse_params_)
return new_value_
rescue Errors::UnparseError => ex_
raise Errors::ConversionError, "Unparsing failed: #{ex_.inspect}"
Expand All @@ -87,33 +109,56 @@ def convert_value(value_, format_, convert_params_=nil)
end


# Call methods of this class in the block passed to
# Versionomy::Conversion::Parsing#new to fine-tune the behavior of
# the converter.

class Builder

include ::Blockenspiel::DSL


def initialize
def initialize # :nodoc:
@string_modifier = nil
@parse_params = nil
@parse_params_generator = nil
@unparse_params_modifier = nil
end


def parse_params(params_)
@parse_params = params_
end

# Provide a block that generates the params used to parse the new
# value. The block should take one parameter, the convert_params
# passed to convert_value (which may be nil). It should return the
# parse params that should be used.

def to_modify_string(&block_)
@string_modifier = block_
def to_generate_parse_params(&block_)
@parse_params_generator = block_
end


# Provide a block that can modify the params used to unparse the
# old value. The block should take two parameters: first, the
# original unparse params from the old value (which may be nil),
# and second, the convert_params passed to convert_value (which
# may also be nil). It should return the unparse params that
# should actually be used.

def to_modify_unparse_params(&block_)
@unparse_params_modifier = block_
end


# Provide a block that can modify the unparsed string prior to
# it being passed to the parser. The block should take two
# parameters: first, the string resulting from unparsing the old
# value, and second, the convert_params passed to convert_value
# (which may be nil). It should return the string to be parsed to
# get the new value.

def to_modify_string(&block_)
@string_modifier = block_
end


def _get_string_modifier # :nodoc:
@string_modifier
end
Expand All @@ -122,8 +167,8 @@ def _get_unparse_params_modifier # :nodoc:
@unparse_params_modifier
end

def _get_parse_params # :nodoc:
@parse_params
def _get_parse_params_generator # :nodoc:
@parse_params_generator
end

end
Expand Down

0 comments on commit 5118922

Please sign in to comment.