class Rtml::Test::VariableScope class Variable attr_reader :type, :perms, :format attr_reader :value attr_reader :name def initialize(name, options = {}) options.stringify_keys!.reverse_merge(default_options) @name = name.to_s %w(type perms format).each do |key| instance_variable_set("@#{key}", options[key].to_s) if options.key?(key) instance_variable_set("@#{key}", default_options[key]) unless instance_variable_get("@#{key}") end self.value = options.delete('value') if options.key?('value') end # TODO: implement :format option def perform_operation(op) if op.keys?(:lo, :op) && op[:op] == 'number' # list length raise Rtml::Errors::VariableError, "No :ro expected" if op.key?(:ro) self.value = (op[:lo].to_s.split(/\;/).length) elsif op.key?(:lo) && !op.keys?(:op, :ro) # assignment self.value = op[:lo] elsif op.keys?(:lo, :op, :ro) # operation self.value = case op[:op] when 'plus' then cast_value(op[:lo]) + cast_value(op[:ro]) when 'minus' then cast_value(op[:lo]) - cast_value(op[:ro]) when 'format' then raise Rtml::Errors::VariableError, "setvar[op=format] not yet implemented" when 'item' then op[:lo].to_s.split(/\;/)[op[:ro].to_i] else raise Rtml::Errors::VariableError, "Unknown operation: #{op[:op].inspect}" end else raise Rtml::Errors::VariableError, "Expected :lo, :op, and :ro; or just :lo for assignment" end end def value=(value) @value = cast_value(value) end def cast_value(value) case type when 'integer' then value.to_i when 'string' then value.to_s when 'opaque' then value.to_s when 'date' then value.kind_of?(DateTime) ? value : DateTime.parse(value.to_s) else raise "Unknown type: #{type.inspect}" end end private def default_options {'type' => 'string', 'perms' => 'rwxrw', 'format' => nil, 'value' => ''} end end def initialize @variables = {} end # Returns a "snapshot" of this variable scope's current state. All known variables are returned, along with their # types, values, etc. def snapshot @variables.values.collect do |variable| { :type => variable.type, :perms => variable.perms, :format => variable.format, :name => variable.name, :value => variable.value } end end def inspect @variables.collect do |name, variable| "#{name}={value:#{variable.value.inspect} type:#{variable.type.inspect} perms:#{variable.perms.inspect} format:#{variable.format.inspect}}" end.join("; ") end # Accepts a hash according to the TML rules (for example, keys should be :lo, :op, :ro) def true_condition?(condition) condition.reverse_merge!(:op => 'equal') raise Rtml::Errors::VariableError, "Expected :lo, :op and :ro" unless condition.keys?(:lo, :op, :ro) lo = literal_value(condition[:lo]) ro = literal_value(condition[:ro]) case condition[:op] when 'equal' then lo == ro when 'not_equal' then lo != ro when 'less' then lo < ro when 'less_or_equal' then lo <= ro when 'contains' then lo =~ /#{Regexp::escape ro}/ else raise Rtml::Errors::VariableError, "Invalid operation: #{condition[:op].inspect}" end end def update(name, value) find_variable(name).value = literal_value(value) end # Accepts a hash, and updates each listed variable with the values in the hash. def update_with(hash) hash.each do |name, value| update(name, value) end end def literal_value(value_or_variable_name) if value_or_variable_name.kind_of?(String) && value_or_variable_name =~ /^tmlvar\:/ value(value_or_variable_name[7..-1]) else value_or_variable_name end end def perform_operation_on(variable_name, operation) operation[:lo] = literal_value(operation[:lo]) if operation.key?(:lo) operation[:ro] = literal_value(operation[:ro]) if operation.key?(:ro) find_variable(variable_name).perform_operation(operation) end def exist?(name) name = name.to_s unless name.kind_of?(String) @variables[name] end def find_variable(name) name = name.to_s unless name.kind_of?(String) @variables[name] || raise(Rtml::Errors::VariableError, "Undeclared variable #{name.inspect}") end def declare_variable(name, options = {}) name = name.to_s unless name.kind_of?(String) if @variables.key?(name) raise Rtml::Errors::VariableError, "Already declared variable #{name.inspect} (it's a #{@variables[name].type})" end @variables[name] = Variable.new(name, options) end def variable_names @variables.keys end def value(name) find_variable(name).value end alias_method :[], :value alias_method :[]=, :update alias_method :assign, :update end