lib/rant/rantvar.rb in rant-0.3.4 vs lib/rant/rantvar.rb in rant-0.3.6
- old
+ new
@@ -8,12 +8,17 @@
# the GNU LGPL, Lesser General Public License version 2.1.
#
# This file provides support for the +var+ attribute of the Rant
# application (Rant::RantApp#var).
+# Most constants (classes, modules etc.) of Rant live in this module,
+# thus it acts as a namespace.
+#
+# If you're looking for general info about Rant, read the
+# README[link:files/README.html].
module Rant
- VERSION = '0.3.4'
+ VERSION = '0.3.6'
# Those are the filenames for rantfiles.
# Case matters!
RANTFILES = [ "Rantfile",
"rantfile",
@@ -65,10 +70,24 @@
val_desc[7..-1] = "..." if val_desc.length > 10
"#{val_desc} doesn't match constraint: #@constraint"
end
end
+ class NotAConstraintFactoryError < Error
+ attr_reader :obj
+ def initialize(obj, msg = nil)
+ @msg = msg
+ @obj = obj
+ end
+ def message
+ # TODO: handle @msg
+ obj_desc = @obj.inspect
+ obj_desc[7..-1] = "..." if obj_desc.length > 10
+ "#{obj_desc} is not a valid constraint factory"
+ end
+ end
+
class InvalidVidError < Error
def initialize(vid, msg = nil)
@msg = msg
@vid = vid
end
@@ -93,39 +112,73 @@
def initialize
# holds all values
@store = {}
# holds constraints for values in @store
@constraints = {}
+ # set by default query
+ @current_var = nil
end
def query(*args, &block)
# currently ignoring block
case args.size
when 0
raise QueryError, "no arguments", caller
when 1
arg = args.first
if Hash === arg
- init_all arg
+ if arg.size == 1
+ arg.each { |k,v|
+ @current_var = k
+ self[k] = v if self[k].nil?
+ }
+ self
+ else
+ init_all arg
+ end
else
self[arg]
end
when 2..3
- vid, constraint, val = *args
+ @current_var, cf, val = *args
+ self.is cf
+ self[@current_var] = val if val
+ else
+ raise QueryError, "to many arguments"
+ end
+ end
+
+ def is ct, *ct_args
+ constrain @current_var,
+ get_factory(ct).rant_constraint(*ct_args)
+ self
+ end
+
+ def restrict vid, ct, *ct_args
+ if vid.respond_to? :to_ary
+ vid.to_ary.each { |v| restrict(v, ct, *ct_args) }
+ else
+ constrain vid,
+ get_factory(ct).rant_constraint(*ct_args)
+ end
+ self
+ end
+
+ def get_factory id
+ if String === id || Symbol === id
begin
- constraint =
- Constraints.const_get(constraint).new
+ id = Constraints.const_get(id)
rescue
- raise QueryError,
- "no such constraint: #{constraint}", caller
+ raise NotAConstraintFactoryError.new(ct), caller
end
- constrain vid, constraint
- self[vid] = val if val
- else
- raise QueryError, "to many arguments"
end
+ unless id.respond_to? :rant_constraint
+ raise NotAConstraintFactoryError.new(id), caller
+ end
+ id
end
+ private :get_factory
# Get var with name +vid+.
def [](vid)
vid = RantVar.valid_vid vid
val = @store[vid]
@@ -196,10 +249,14 @@
else
@store[vid] = constraint.default
end
end
+ def has_var?(vid)
+ !self[vid].nil?
+ end
+
end # class Space
module Constraint
def matches? val
filter val
@@ -209,13 +266,69 @@
end
end
module Constraints
+ class String
+ include Constraint
+
+ class << self
+ alias rant_constraint new
+ end
+
+ def filter(val)
+ if val.respond_to? :to_str
+ val.to_str
+ elsif Symbol === val
+ val.to_s
+ else
+ raise ConstraintError.new(self, val)
+ end
+ end
+ def default
+ ""
+ end
+ def to_s
+ "string"
+ end
+ end
+
+ class ToString < String
+ class << self
+ alias rant_constraint new
+ end
+ def filter(val)
+ val.to_s
+ end
+ end
+
+ class ::Range
+ def rant_constraint
+ case first
+ when ::Integer
+ IntegerInRange.new(self)
+ when ::Float
+ FloatInRange.new(self)
+ else
+ raise NotAConstraintFactoryError.new(self)
+ end
+ end
+ end
+
class Integer
include Constraint
+ class << self
+ def rant_constraint(range = nil)
+ if range
+ IntegerInRange.new(range)
+ else
+ self.new
+ end
+ end
+ end
+
def filter(val)
Kernel::Integer(val)
rescue
raise ConstraintError.new(self, val)
end
@@ -225,13 +338,83 @@
def to_s
"integer"
end
end
+ class IntegerInRange < Integer
+ def initialize(range)
+ @range = range
+ end
+ def filter(val)
+ i = super
+ if @range === i
+ i
+ else
+ raise ConstraintError.new(self, val)
+ end
+ end
+ def default
+ @range.min
+ end
+ def to_s
+ super + " #{@range}"
+ end
+ end
+
+ class Float
+ include Constraint
+
+ class << self
+ def rant_constraint(range = nil)
+ if range
+ FloatInRange.new(range)
+ else
+ self.new
+ end
+ end
+ end
+
+ def filter(val)
+ Kernel::Float(val)
+ rescue
+ raise ConstraintError.new(self, val)
+ end
+ def default
+ 0.0
+ end
+ def to_s
+ "float"
+ end
+ end
+
+ class FloatInRange < Float
+ def initialize(range)
+ @range = range
+ end
+ def filter(val)
+ i = super
+ if @range === i
+ i
+ else
+ raise ConstraintError.new(self, val)
+ end
+ end
+ def default
+ @range.first
+ end
+ def to_s
+ super + " #{@range}"
+ end
+ end
+
class AutoList
include Constraint
+ class << self
+ alias rant_constraint new
+ end
+
def filter(val)
if val.respond_to? :to_ary
val.to_ary
elsif val.nil?
raise ConstraintError.new(self, val)
@@ -248,10 +431,14 @@
end
class List
include Constraint
+ class << self
+ alias rant_constraint new
+ end
+
def filter(val)
if val.respond_to? :to_ary
val.to_ary
else
raise ConstraintError.new(self, val)
@@ -264,9 +451,63 @@
"list (Array)"
end
end
Array = List
+
+ class Bool
+ include Constraint
+ class << self
+ alias rant_constraint new
+ end
+ def filter(val)
+ if ::Symbol === val or ::Integer === val
+ val = val.to_s
+ end
+ if val == true
+ true
+ elsif val == false || val == nil
+ false
+ elsif val.respond_to? :to_str
+ case val.to_str
+ when /^\s*true\s*$/i: true
+ when /^\s*false\s*$/i: false
+ when /^\s*y(es)?\s*$/i: true
+ when /^\s*n(o)?\s*$/: false
+ when /^\s*on\s*$/i: true
+ when /^\s*off\s*$/i: false
+ when /^\s*1\s*$/: true
+ when /^\s*0\s*$/: false
+ else
+ raise ConstraintError.new(self, val)
+ end
+ else
+ raise ConstraintError.new(self, val)
+ end
+ end
+ def default
+ false
+ end
+ def to_s
+ "bool"
+ end
+ end
+
+ class BoolTrue < Bool
+ def default
+ true
+ end
+ end
+
+ #--
+ # perhaps this should stay a secret ;)
+ #++
+ def true.rant_constraint
+ BoolTrue.rant_constraint
+ end
+ def false.rant_constraint
+ Bool.rant_constraint
+ end
end # module Constraints
# A +vid+ has to be a String to be valid.
def valid_vid(obj)