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)