require 'test/unit'
require 'solution'

class MyArray < Array; end

class SampleTenLittleHacksTest < Test::Unit::TestCase
  def test_1_symbol_to_proc
    array = [1, -3, 0, 5, -8, 0]
    assert_equal array.map { |item| item.abs }, array.map(&:abs)
    assert_equal array.reject { |item| item.zero? }, array.reject(&:zero?)
    assert_equal array.inject { |a, b| a + b }, array.inject(&:+)
    assert_equal "COLTRANE", :upcase.to_proc['Coltrane'] # Тъй де...
  end

  def test_2_forty_two
    assert_equal 42, 6 * 9
  end

  def test_3_object_dot_and
    list = [-1, -2, -3, -4, nil]
    assert_equal 4, list.&.reverse.&[1].&.abs
    assert_equal nil, list.&.reverse.&[0].&.abs
    assert_equal nil, list.&.reverse.&.i.&.tend.&.to.&.get.&.carried.&.away

    assert !(true & false)
    assert true & true
    assert !(nil & true)
    assert_equal 0b0010, 0b0110 & 0b0011
  end

  def test_4_around_method
    MyArray.around_method(:<<) do |obj, invoke, *args|
      invoke[args[0].to_s] if args[0].to_s.length > 10 and obj.size <= 20
    end

    array = MyArray.new
    50.times do |n|
      array << 10 ** n
    end

    assert_equal (10..30).map { |n| (10 ** n).to_s }, array
  end

  def test_5_swap_methods
    text = "Coltrane"
    text.swap_methods(:upcase, :downcase)
    
    assert_equal "COLTRANE", text.downcase
    assert_equal "coltrane", text.upcase

    assert_equal "COLTRANE", "Coltrane".upcase
  end

  def test_6_map_hash
    assert_equal(
      {"Coltrane" => 8, "Vedder" => 6, "Evans" => 5},
      ["Coltrane", "Vedder", "Evans"].map_hash { |name| [name, name.length] }
    )

    assert_equal(
      {'1' => '5', '3' => '4'},
      ['1;2', '3;4', '1;5'].map_hash { |str| str.split(';') }
    )
  end

  def test_7_proxy
    original = " John Coltrane "
    text = Proxy.new(original)
    
    assert_equal 15, text.length
    assert_equal "John Coltrane", text.strip
    assert_equal "Col", text.slice(6, 3)

    list = []
    proxy = Proxy.new(list)
    
    def proxy.around(name, *args)
      yield(*args.map { |_| _.inspect }).inspect
    end

    proxy << 1729
    proxy << []
    proxy << "foo"

    assert_equal '["\\"foo\\"", "[]", "1729"]', proxy.reverse
  end

  def test_8_array_comparison
    assert [1, 2, 3] < [1, 2, 4]
    assert [1, 2, 3] > [1, 2, 2]
    assert [1, 2] < [1, 2, 3]
    assert [2, 1] > [1, 1, 1]
    assert ['a', 'b', 'c'] > ['a', 'b', 'b']
  end

  def test_9_multi_invert
    inverted = {"Coltrane" => "John", "Evans" => "Bill", "Gates" => "Bill"}.multi_invert
    assert_equal %w{Coltrane}, inverted["John"]
    assert_equal %w{Evans Gates}, inverted["Bill"].sort

    inverted = {'a' => 1, 'b' => 2, 'c' => 3, 'd' => 1}.multi_invert
    assert_equal %w{a d}, inverted[1]
    assert_equal %w{b}, inverted[2]
    assert_equal %w{c}, inverted[3]
  end

  def test_10_expose_all
    obj = Object.new
    obj.instance_eval { @x, @y, @z = 1, 2, 3 }

    assert_raises(NoMethodError) { obj.x }
    assert_raises(NoMethodError) { obj.y }
    assert_raises(NoMethodError) { obj.z }

    obj.expose_all

    assert_equal 1, obj.x
    assert_equal 2, obj.y
    assert_equal 3, obj.z

    new_obj = Object.new
    new_obj.instance_eval { @x, @y, @z = 4, 5, 6 }
    [:x, :y, :z].each do |name|
      assert_raises(NoMethodError) { new_obj.send(name) }
    end
  end
end

