class Object

Public Instance Methods

dereference_value(value, scope) click to toggle source
# File lib/cli.rb, line 380
def dereference_value(value, scope)
  if value.is_a?(Hash) && value.length == 1 && Array(value.each_value)[0] == nil
    result = scope
    for name in Array(value.each_key)[0].split('.')
      result = get_property(result, name)
    end
    value = result
  elsif value.is_a?(Hash)
    for key, item in value
      value[key] = dereference_value(item, scope)
    end
  elsif value.is_a?(Array)
    for item, index in value.each_with_index
      value[index] = dereference_value(item, scope)
    end
  end
  return value
end
get_property(owner, name) click to toggle source
# File lib/cli.rb, line 400
def get_property(owner, name)
  if owner.is_a?(Method)
    owner = owner.call()
  end
  if owner.class == Hash
    return owner[name]
  elsif owner.class == Array
    return owner[name.to_i]
  elsif owner.is_a?(Class)
    return owner.method(name)
  end
  return owner.public_method(name)
end
parse_feature(feature) click to toggle source
# File lib/cli.rb, line 110
def parse_feature(feature)

  # General
  if feature.is_a?(String)
    match = /^(?:\((.*)\))?(\w.*)$/.match(feature)
    skip, comment = match[1], match[2]
    if !!skip
      skip = !skip.split('|').include?('rb')
    end
    return {'assign' => nil, 'comment' => comment, 'skip' => skip}
  end
  left, right = Array(feature.each_pair)[0]

  # Left side
  call = false
  match = /^(?:\((.*)\))?(?:([^=]*)=)?([^=].*)?$/.match(left)
  skip, assign, property = match[1], match[2], match[3]
  if !!skip
    skip = !skip.split('|').include?('rb')
  end
  if !assign && !property
    raise Exception.new('Non-valid feature')
  end
  if !!property
    call = true
    if property.end_with?('==')
      property = property[0..-3]
      call = false
    end
  end

  # Right side
  args = []
  kwargs = {}
  result = right
  if !!call
    result = nil
    for item in right
      if item.is_a?(Hash) && item.length == 1
        item_left, item_right = Array(item.each_pair)[0]
        if item_left == '=='
          result = item_right
          next
        end
        if item_left.end_with?('=')
          kwargs[item_left[0..-2]] = item_right
          next
        end
      end
      args.push(item)
    end
  end

  # Text repr
  text = property
  if !!assign
    text = "#{assign} = #{property || JSON.generate(result)}"
  end
  if !!call
    items = []
    for item in args
      items.push(JSON.generate(item))
    end
    for name, item in kwargs.each_pair
      items.push("#{name}=#{JSON.generate(item)}")
    end
    text = "#{text}(#{items.join(', ')})"
  end
  if !!result && !assign
    text = "#{text} == #{result == 'ERROR' ? result : JSON.generate(result)}"
  end
  text = text.gsub(/{"([^{}]*?)": null}/, '\1')

  return {
    'comment' => nil,
    'skip' => skip,
    'call' => call,
    'assign' => assign,
    'property' => property,
    'args' => args,
    'kwargs' => kwargs,
    'result' => result,
    'text' => text,
  }

end
parse_spec(path) click to toggle source
# File lib/cli.rb, line 43
def parse_spec(path)

  # Package
  documents = []
  contents = File.read(path)
  YAML.load_stream(contents) do |document|
    documents.push(document)
  end
  feature = parse_feature(documents[0][0])
  if feature['skip']
    return nil
  end
  package = feature['comment']

  # Features
  skip = false
  features = []
  for feature in documents[0]
    feature = parse_feature(feature)
    features.push(feature)
    if feature['comment']
      skip = feature['skip']
    end
    feature['skip'] = skip || feature['skip']
  end

  # Scope
  scope = {}
  scope['$import'] = BuiltinFunctions.new().public_method(:builtin_import)
  if documents.length > 1 && documents[1]['rb']
    eval("module UserScope\n" + documents[1]['rb'] + "\nend")
    for name in UserScope.constants
      scope["$#{name}"] = UserScope.const_get(name)
    end
    default = Class.new
    methods = Class.new.extend(UserScope)
    for name in methods.public_methods
      if !default.respond_to?(name)
        scope["$#{name}"] = methods.public_method(name)
      end
    end
  end

  # Stats
  stats = {'features' => 0, 'comments' => 0, 'skipped' => 0, 'tests' => 0}
  for feature in features
    stats['features'] += 1
    if feature['comment']
      stats['comments'] += 1
    else
      stats['tests'] += 1
      if feature['skip']
        stats['skipped'] += 1
      end
    end
  end

  return {
    'package' => package,
    'features' => features,
    'scope' => scope,
    'stats' => stats,
  }

end
parse_specs(path) click to toggle source

Helpers

# File lib/cli.rb, line 9
def parse_specs(path)

  # Paths
  paths = []
  if path
    if File.file?(path)
      paths = [path]
    elsif File.directory?(path)
      paths = Dir.glob("#{path}/*.yml")
    end
  end
  if !path
    if paths.empty?
      paths = Dir.glob('packspec.yml')
    end
    if paths.empty?
      paths = Dir.glob("packspec/*.yml")
    end
  end

  # Specs
  specs = []
  for path in paths
    spec = parse_spec(path)
    if spec
      specs.push(spec)
    end
  end

  return specs

end
set_property(owner, name, value) click to toggle source
# File lib/cli.rb, line 415
def set_property(owner, name, value)
  if owner.class == Hash
    owner[name] = value
    return
  elsif owner.class == Array
    owner[name.to_i] = value
    return
  end
  return owner.const_set(name, value)
end
test_feature(feature, scope, exit_first=false) click to toggle source
# File lib/cli.rb, line 247
def test_feature(feature, scope, exit_first=false)

  # Comment
  if !!feature['comment']
    message = "\n # #{feature['comment']}\n".bold
    puts(message)
    return true
  end

  # Skip
  if !!feature['skip']
    message = " #{Emoji.find_by_alias('heavy_minus_sign').raw}  ".yellow
    message += feature['text']
    puts(message)
    return true
  end

  # Dereference
  if !!feature['call']
    feature['args'] = dereference_value(feature['args'], scope)
    feature['kwargs'] = dereference_value(feature['kwargs'], scope)
  end
  feature['result'] = dereference_value(feature['result'], scope)

  # Execute
  exception = nil
  result = feature['result']
  if !!feature['property']
    begin
      property = scope
      for name in feature['property'].split('.')
        property = get_property(property, name)
      end
      if !!feature['call']
        args = feature['args'].dup
        if !feature['kwargs'].empty?
          args.push(Hash[feature['kwargs'].map{|k, v| [k.to_sym, v]}])
        end
        if property.respond_to?('new')
          result = property.new(*args)
        else
          result = property.call(*args)
        end
      else
        result = property
        if result.is_a?(Method)
          result = result.call()
        end
      end
    rescue Exception => exc
      exception = exc
      result = 'ERROR'
    end
  end

  # Assign
  if !!feature['assign']
    owner = scope
    names = feature['assign'].split('.')
    for name in names[0..-2]
      owner = get_property(owner, name)
    end
    set_property(owner, names[-1], result)
  end

  # Compare
  if feature['result'] != nil
    success = result == feature['result']
  else
    success = result != 'ERROR'
  end
  if success
    message = " #{Emoji.find_by_alias('heavy_check_mark').raw}  ".green
    message += feature['text']
    puts(message)
  else
    begin
      result_text = JSON.generate(result)
    rescue Exception
      result_text = result.to_s
    end
    message = " #{Emoji.find_by_alias('x').raw}  ".red
    message += "#{feature['text']}\n"
    if exception
      message += "Exception: #{exception}".red.bold
    else
      message += "Assertion: #{result_text} != #{JSON.generate(feature['result'])}".red.bold
    end
    puts(message)
    if exit_first
      puts('---')
      puts('Scope (current execution scope):')
      puts("[#{scope.keys.join(', ')}]")
      if exception
          puts('---')
          raise exception
      else
          exit(1)
      end
    end
  end

  return success

end
test_spec(spec, exit_first=false) click to toggle source
# File lib/cli.rb, line 216
def test_spec(spec, exit_first=false)

  # Message
  message = Emoji.find_by_alias('heavy_minus_sign').raw * 3 + "\n\n"
  puts(message)

  # Test spec
  passed = 0
  for feature in spec['features']
    result = test_feature(feature, spec['scope'], exit_first)
    if result
      passed += 1
    end
  end
  success = (passed == spec['stats']['features'])

  # Message
  color = 'green'
  message = ("\n " + Emoji.find_by_alias('heavy_check_mark').raw + '  ').green.bold
  if !success
    color = 'red'
    message = ("\n " + Emoji.find_by_alias('x').raw + '  ').red.bold
  end
  message += "#{spec['package']}: #{passed - spec['stats']['comments'] - spec['stats']['skipped']}/#{spec['stats']['tests'] - spec['stats']['skipped']}\n".colorize(color).bold
  puts(message)

  return success

end
test_specs(specs, exit_first=false) click to toggle source
# File lib/cli.rb, line 198
def test_specs(specs, exit_first=false)

  # Message
  message = "\n #  Ruby\n".bold
  puts(message)

  # Test specs
  success = true
  for spec in specs
    spec_success = test_spec(spec, exit_first)
    success = success && spec_success
  end

  return success

end