Re: Creating Ruby Classes from XSD?
- From: Gregory Seidman <gsslist+ruby@xxxxxxxxxxxxxxxxxx>
- Date: Sat, 25 Feb 2006 02:16:34 +0900
On Fri, Feb 24, 2006 at 10:27:52PM +0900, Geoffrey Lane wrote:
[...]
} XSD schema (for all of its imperfections) has the ability to specify
} constraints on the data that help validate it. Up front your service
} can define things like:
}
} (pseudo-XSD)
} US Address:
} Street1 = required
} Street2 = optional
} City = required
} State = [A-Z]{2}
} USZip = [0-9]{5}(-[0-9]{4})?
}
} Those kinds of constraints can be done in code, but no language I
} know of can make an interface to a method that explicit.
Converting those constraints to Ruby is a challenge, but far from
impossible. First off, I'd store the fields in a hash and override
method_missing to implement accessors and mutators as requested (the same
way ActiveRecord implements find_by_*).
The regex constraints are relatively simple. They just require
minor adjustments to account for the differences in regex flavors between
XSD and Ruby. I'd store a hash of symbol to regex-and-optional for use by a
generalized setter method.
The required/optional issue is dealt with in the constructor. The
constructor takes a (possibly nested) hash, raises an exception if a
required field is missing or an unknown field is present, and uses mutators
to set the fields. You might also have a constructor that converts an XML
(sub-)tree into a (nested) hash before passing it to the other constructor.
Implementing your example:
class XSDclass
def set_field(symbol, value)
constraint = constraints[symbol]
fail "No such field" unless constraint
valregex = constraint[:valregex]
if valregex && valregex !~ value
fail "Invalid value '#{value}' for field #{symbol}"
end
@fields[symbol] = value
end
def method_missing(method_id, *arguments)
if constraints[method_id] && arguments.length == 0
return @fields[method_id]
elsif method_id.to_s[-1].chr == '='
setter_for = method_id.to_s[0...-1].to_sym
if constraints[setter_for] && arguments.length == 1
return set_field(setter_for, arguments[0])
end
end
super
end
private
def constraints
{}
end
def initialize(hashed_fields)
required_fields = constraints.select { |k,v| not v[:optional] }
required_fields.map! { |f| f[0] }
missing = (required_fields - hashed_fields.keys).join(', ')
excess = (hashed_fields.keys - constraints.keys).join(', ')
if (missing.length + excess.length) != 0
errors = "Validation error constructing #{self.class}:\n\n"
errors << "\tMissing fields: #{missing}\n" if missing.length > 0
errors << "\tUnknown fields: #{excess}\n" if excess.length > 0
fail errors
end
@fields = {}
hashed_fields.each { |k,v| set_field(k,v) }
end
end
class US_Address < XSDclass
Constraints = { :street1 => { :optional => false },
:street2 => { :optional => true },
:city => { :optional => false },
:state => { :valregex => /[A-Z]{2}/,
:optional => false },
:uszip => { :valregex => /[0-9]{5}(-[0-9]{4})?/,
:optional => false }
}
def constraints
Constraints
end
def initialize(hashed_fields)
super
end
end
The conversion of XSD into a Ruby class definition, conversion of regular
expressions, a better implementation of the method_missing override (which
creates accessors/mutators for the next use), and the XML subtree
constructor are left as exercises to the reader. Note that I have actually
tested the code above; it works.
} Geoff Lane <geoff@xxxxxxxxxxx>
--Greg
.
- References:
- Creating Ruby Classes from XSD?
- From: Justin Bailey
- Re: Creating Ruby Classes from XSD?
- From: Logan Capaldo
- Re: Creating Ruby Classes from XSD?
- From: Henrik Martensson
- Re: Creating Ruby Classes from XSD?
- From: Geoffrey Lane
- Creating Ruby Classes from XSD?
- Prev by Date: Re: Creating Ruby Classes from XSD?
- Next by Date: Re: Noob: Building Yarv 0.4.0
- Previous by thread: Re: Creating Ruby Classes from XSD?
- Next by thread: Re: Creating Ruby Classes from XSD?
- Index(es):
Relevant Pages
|