class Sqreen::BindingAccessor

the value located at the given binding

Constants

CLASS_VAR_KIND
CONSTANT_KIND
GLOBAL_VAR_KIND
INDEX_KIND
INSTANCE_VAR_KIND
INTEGER_KIND
KNOWN_TRANSFORMS
LOCAL_VAR_KIND
METHOD_KIND
NIL_KIND
PathElem
RUBY_IDENTIFIER_CHAR
SQREEN_VAR_KIND
STRING_KIND
SYMBOL_KIND
TRANSFORM_ARGS_REGEXP

Attributes

expression[R]
final_transform[R]
path[R]
transform_args[R]

Public Class Methods

new(expression, convert = false) click to toggle source

Expression to be accessed @param expression [String] expression to read @param convert [Boolean] wheter to convert objects to

simpler types (Array, Hash, String...)
# File lib/sqreen/binding_accessor.rb, line 21
def initialize(expression, convert = false)
  @transform_args = []
  @final_transform = nil
  @expression = expression
  @path = []
  @convert = convert
  parse(expression)
end

Public Instance Methods

==(other)
Alias for: eql?
access(binding, framework = nil, instance = nil, arguments = nil, cbdata = nil, last_return = nil) click to toggle source

Access data from the expression

# File lib/sqreen/binding_accessor.rb, line 31
def access(binding, framework = nil, instance = nil, arguments = nil, cbdata = nil, last_return = nil)
  env = [framework, instance, arguments, cbdata, last_return]
  value = nil
  @path.each do |component|
    value = resolve_component(value, component, binding, env)
  end
  value
end
eql?(other) click to toggle source
# File lib/sqreen/binding_accessor.rb, line 53
def eql?(other)
  self.class == other.class &&
    expression == other.expression &&
    @convert == other.instance_variable_get('@convert')
end
Also aliased as: ==
hash() click to toggle source

implement eql? and hash for uniq

# File lib/sqreen/binding_accessor.rb, line 49
def hash
  expression.hash ^ @convert.hash
end
resolve(*args) click to toggle source

access and transform expression for the given binding

# File lib/sqreen/binding_accessor.rb, line 41
def resolve(*args)
  value = access(*args)
  value = transform(value) if @final_transform
  return convert(value) if @convert
  value
end

Protected Instance Methods

convert(value) click to toggle source
# File lib/sqreen/binding_accessor.rb, line 250
def convert(value)
  case value
  when ::Exception
    { 'message' => value.message, 'backtrace' => value.backtrace }
  else
    value
  end
end
error_state(msg) click to toggle source
# File lib/sqreen/binding_accessor.rb, line 246
def error_state(msg)
  "#{msg} at #{@scan.pos} after #{@scan.string[0...@scan.pos]} (#{@scan.string})"
end
extract_transform(expression) click to toggle source
# File lib/sqreen/binding_accessor.rb, line 155
def extract_transform(expression)
  parts = expression.split('|')
  self.final_transform = parts.pop if parts.size > 1
  parts.join('|').rstrip
end
final_transform=(transform) click to toggle source
# File lib/sqreen/binding_accessor.rb, line 162
def final_transform=(transform)
  transform.strip!

  transform.sub!(TRANSFORM_ARGS_REGEXP, '')
  @transform_args = if Regexp.last_match
                      Regexp.last_match(1).split(/,\s*/)
                    else
                      []
                    end

  unless KNOWN_TRANSFORMS.include?(transform)
    raise Sqreen::Exception, "Invalid transform #{transform}"
  end
  @final_transform = transform
end
get_local(name, bind) click to toggle source
# File lib/sqreen/binding_accessor.rb, line 77
def get_local(name, bind)
  bind.local_variable_get(name)
end
parse(expression) click to toggle source
# File lib/sqreen/binding_accessor.rb, line 132
def parse(expression)
  expression = extract_transform(expression)
  @scan = StringScanner.new(expression)
  until @scan.eos?
    pos = @scan.pos
    scalar = scan_scalar
    if scalar
      @path.push scalar
      return
    end
    if @path.empty?
      scan_push_variable
    else
      scan_push_method
    end
    scan_push_indexes
    scan_push_more_constant if @scan.scan(/\./).nil?
    raise Sqreen::Exception, error_state('Scan stuck') if @scan.pos == pos
  end
ensure
  @scan = nil
end
resolve_component(current_value, component, binding, env) click to toggle source
# File lib/sqreen/binding_accessor.rb, line 86
def resolve_component(current_value, component, binding, env)
  case component[:kind]
  when STRING_KIND, SYMBOL_KIND, INTEGER_KIND
    component[:value]
  when LOCAL_VAR_KIND
    get_local(component[:value], binding)
  when INSTANCE_VAR_KIND
    current_value.instance_variable_get("@#{component[:value]}")
  when CLASS_VAR_KIND
    current_value.class.class_variable_get("@@#{component[:value]}")
  when GLOBAL_VAR_KIND
    instance_eval("$#{component[:value]}")
  when CONSTANT_KIND
    if current_value
      current_value.const_get(component[:value].to_s)
    else
      Object.const_get(component[:value].to_s)
    end
  when METHOD_KIND
    current_value.send(component[:value])
  when INDEX_KIND
    current_value[component[:value]]
  when NIL_KIND
    current_value
  when SQREEN_VAR_KIND
    resolve_sqreen_variable(component[:value], *env)
  else
    raise "Do not know how to handle this component #{component.inspect}"
  end
end
resolve_sqreen_variable(what, framework, instance, args, cbdata, rv) click to toggle source
# File lib/sqreen/binding_accessor.rb, line 117
def resolve_sqreen_variable(what, framework, instance, args, cbdata, rv)
  case what
  when 'data'
    cbdata
  when 'rv'
    rv
  when 'args'
    args
  when 'inst'
    instance
  else
    framework.send(what)
  end
end
scan_push_constant() click to toggle source
# File lib/sqreen/binding_accessor.rb, line 196
def scan_push_constant
  return unless @scan.scan(/([A-Z]#{RUBY_IDENTIFIER_CHAR}+)/)
  @path << PathElem.new(CONSTANT_KIND, @scan[1])
end
scan_push_indexes() click to toggle source
# File lib/sqreen/binding_accessor.rb, line 235
def scan_push_indexes
  while @scan.scan(/\[/)
    scalar = scan_scalar
    raise Sqreen::Exception, error_state('Invalid index') unless scalar
    unless @scan.scan(/\]/)
      raise Sqreen::Exception, error_state('Unfinished index')
    end
    @path << PathElem.new(INDEX_KIND, scalar[:value])
  end
end
scan_push_method() click to toggle source
# File lib/sqreen/binding_accessor.rb, line 225
def scan_push_method
  if @scan.scan(/@@(#{RUBY_IDENTIFIER_CHAR}+)/)
    @path << PathElem.new(CLASS_VAR_KIND, @scan[1])
  elsif @scan.scan(/@(#{RUBY_IDENTIFIER_CHAR}+)/)
    @path << PathElem.new(INSTANCE_VAR_KIND, @scan[1])
  elsif @scan.scan(/(#{RUBY_IDENTIFIER_CHAR}+)/)
    @path << PathElem.new(METHOD_KIND, @scan[1])
  end
end
scan_push_more_constant() click to toggle source
# File lib/sqreen/binding_accessor.rb, line 201
def scan_push_more_constant
  while @scan.scan(/::/)
    unless scan_push_constant
      raise Sqreen::Exception, error_state('No more constant')
    end
  end
end
scan_push_variable() click to toggle source
# File lib/sqreen/binding_accessor.rb, line 209
def scan_push_variable
  if @scan.scan(/\$(#{RUBY_IDENTIFIER_CHAR}+)/)
    @path << PathElem.new(GLOBAL_VAR_KIND, @scan[1])
  elsif @scan.scan(/@@(#{RUBY_IDENTIFIER_CHAR}+)/)
    @path << PathElem.new(CLASS_VAR_KIND, @scan[1])
  elsif @scan.scan(/@(#{RUBY_IDENTIFIER_CHAR}+)/)
    @path << PathElem.new(INSTANCE_VAR_KIND, @scan[1])
  elsif @scan.scan(/#\.(\w+)/)
    @path << PathElem.new(SQREEN_VAR_KIND, @scan[1])
  elsif scan_push_constant
    nil
  elsif @scan.scan(/(#{RUBY_IDENTIFIER_CHAR}+)/u)
    @path << PathElem.new(LOCAL_VAR_KIND, @scan[1])
  end
end
scan_scalar() click to toggle source
# File lib/sqreen/binding_accessor.rb, line 178
def scan_scalar
  if @scan.scan(/\d+/)
    PathElem.new(INTEGER_KIND, @scan[0].to_i)
  elsif @scan.scan(/:(\w+)/)
    PathElem.new(SYMBOL_KIND, @scan[1].to_sym)
  elsif @scan.scan(/'((?:\\.|[^\\'])*)'/)
    PathElem.new(STRING_KIND, @scan[1])
  elsif @scan.scan(/nil/)
    PathElem.new(NIL_KIND, nil)
  end
end
transform(value) click to toggle source
# File lib/sqreen/binding_accessor.rb, line 262
def transform(value)
  send(@final_transform, value, *@transform_args) if @final_transform
end