require 'helper'
require 'dr/ruby_ext/core_modules'

module TestCoreExtRefinements
	using DR::CoreRef
	# TODO make refinements work nicely
end

module TestCoreExt
	require 'dr/ruby_ext/core_ext'
	describe DR::CoreExt do
		describe Enumerable do
			it "Can filter enumerable" do
				[1,2,3,4].filter({odd: [1,3], default: :even}).must_equal({:odd=>[1, 3], :even=>[2, 4]})
			end
		end

		describe Hash do
			it "Implements Hash#deep_merge" do
				h1 = { x: { y: [4,5,6] }, z: [7,8,9] }
				h2 = { x: { y: [7,8,9] }, z: 'xyz' }
				h1.deep_merge(h2).must_equal({x: {y: [7, 8, 9]}, z: "xyz"})
				h2.deep_merge(h1).must_equal({x: {y: [4, 5, 6]}, z: [7, 8, 9]})
				h1.deep_merge(h2) { |key, old, new| Array(old) + Array(new) }.must_equal({:x=>{:y=>[4, 5, 6, 7, 8, 9]}, :z=>[7, 8, 9, "xyz"]})
			end

			it "Hash#deep_merge merge array when they start with nil" do
				h1 = { x: { y: [4,5,6] }, z: [7,8,9] }
				h2 = { x: { y: [nil, 7,8,9] }, z: 'xyz' }
				h1.deep_merge(h2).must_equal({x: {y: [4,5,6,7, 8, 9]}, z: "xyz"})
				{x: { y: []} }.deep_merge(h2).must_equal({x: {y: [7, 8, 9]}, z: "xyz"})
				{z: "foo"}.deep_merge(h2).must_equal({x: {y: [7, 8, 9]}, z: "xyz"})
			end

			it "Implements Hash#inverse" do
				h={ploum: 2, plim: 2, plam: 3}
				h.inverse.must_equal({2=>[:ploum, :plim], 3=>[:plam]})
			end
			
			it "Implements Hash#keyed_value" do
				h = { x: { y: { z: "foo" } } }
				h.keyed_value("x/y/z").must_equal("foo")
			end

			it "Implements Hash#set_keyed_value" do
				h = { x: { y: { z: "foo" } } }
				h.set_keyed_value("x/y/z","bar").must_equal({ x: { y: { z: "bar" } } })
				h.set_keyed_value("x/y","bar2").must_equal({ x: { y: "bar2" } })
				h.set_keyed_value("z/y","bar3").must_equal({ x: { y: "bar2" } , z: {y: "bar3"}})
			end

			it "Implements Hash#leafs" do
				{foo: [:bar, :baz], bar: [:plum, :qux]}.leafs([:foo]).must_equal([:plum, :qux, :baz])
			end
		end

		describe UnboundMethod do
			it "Can be converted to a proc" do
				m=String.instance_method(:length)
				["foo", "ploum"].map(&m).must_equal([3,5])
			end
			it "Can call" do
				String.instance_method(:length).call("foo").must_equal(3)
			end
		end

		describe Proc do
			# fails due to ruby bug on double splat
			# it "call_block does not worry about arity of lambda" do
			# 	(->(x,y) {x+y}).call_block(1,2,3).must_equal(3)
			# end

			it "Can do rcurry" do
				l=->(x,y) {"#{x}: #{y}"}
				m=l.rcurry("foo")
				m.call("bar").must_equal("bar: foo")
			end

			it "Can compose functions" do
				somme=->(x,y) {x+y}
				carre=->(x) {x*x}
				carre.compose(somme).(2,3).must_equal(25)
			end

			it "Can uncurry functions" do
				(->(x) {->(y) {x+y}}).uncurry.(2,3).must_equal(5)
				(->(x,y) {x+y}).curry.uncurry.(2,3).must_equal(5)
			end
		end

		describe Array do
			it "Can be converted to proc (providing extra arguments)" do
				["ploum","plam"].map(&[:+,"foo"]).must_equal(["ploumfoo", "plamfoo"])
			end
		end

		describe Object do
			it "this can change the object" do
				"foo".this {|s| s.size}.+(1).must_equal(4)
			end

			it "and_this emulates the Maybe Monad" do
				"foo".and_this {|s| s.size}.must_equal(3)
				assert_nil nil.and_this {|s| s.size}
			end
		end

		describe DR::RecursiveHash do
			it "Generates keys when needed" do
				h=DR::RecursiveHash.new
				h[:foo][:bar]=3
				h.must_equal({foo: {bar: 3}})
			end
		end
	end
end