diff --git a/.github/workflows/build-and-deploy-docs.yml b/.github/workflows/build-and-deploy-docs.yml index b065f62..817961f 100644 --- a/.github/workflows/build-and-deploy-docs.yml +++ b/.github/workflows/build-and-deploy-docs.yml @@ -10,11 +10,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2 - - name: Setup .NET 6 - uses: actions/setup-dotnet@v1 + uses: actions/checkout@v4 + - name: Setup .NET 8 + uses: actions/setup-dotnet@v4 with: - dotnet-version: '6.x.x' + dotnet-version: '8.x.x' - name: make script executable run: chmod u+x build.sh - name: Restore tools diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index b82a0d3..9df20a1 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -17,13 +17,13 @@ jobs: runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 # SETUP .NET - name: Setup .NET - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: - dotnet-version: 6.x.x + dotnet-version: 8.x.x - name: Restore fable run: dotnet tool restore diff --git a/build/BasicTasks.fs b/build/BasicTasks.fs index 7a439bc..93bc5b4 100644 --- a/build/BasicTasks.fs +++ b/build/BasicTasks.fs @@ -159,5 +159,18 @@ let clean = BuildTask.create "Clean" [] { let build = BuildTask.create "Build" [clean] { solutionFile - |> DotNet.build id + |> DotNet.build (fun p -> + let msBuildParams = + {p.MSBuildParams with + Properties = ([ + "warnon", "3390" + ]) + DisableInternalBinLog = true + } + { + p with + MSBuildParams = msBuildParams + } + |> DotNet.Options.withCustomParams (Some "-tl") + ) } \ No newline at end of file diff --git a/build/PackageTasks.fs b/build/PackageTasks.fs index 08d33e9..2754216 100644 --- a/build/PackageTasks.fs +++ b/build/PackageTasks.fs @@ -30,6 +30,7 @@ module BundleDotNet = "Version",versionTag "PackageReleaseNotes", (ProjectInfo.release.Notes |> List.map replaceCommitLink |> String.toLines ) ] @ p.MSBuildParams.Properties) + DisableInternalBinLog = true } { p with diff --git a/build/TestTasks.fs b/build/TestTasks.fs index 719b9ec..b5d712f 100644 --- a/build/TestTasks.fs +++ b/build/TestTasks.fs @@ -60,28 +60,4 @@ let runTests = BuildTask.createEmpty "RunTests" [ (*RunTests.runTestsJsNative; *) RunTests.runTestsDotnet - ] - - - -// to do: use this once we have actual tests -let runTestsWithCodeCov = BuildTask.create "RunTestsWithCodeCov" [clean; build] { - let standardParams = Fake.DotNet.MSBuild.CliArguments.Create () - testProjects - |> Seq.iter(fun testProject -> - Fake.DotNet.DotNet.test(fun testParams -> - { - testParams with - MSBuildParams = { - standardParams with - Properties = [ - "AltCover","true" - "AltCoverCobertura","../../codeCov.xml" - "AltCoverForce","true" - ] - }; - Logger = Some "console;verbosity=detailed" - } - ) testProject - ) -} \ No newline at end of file + ] \ No newline at end of file diff --git a/build/build.fsproj b/build/build.fsproj index e766fb8..a6a71c8 100644 --- a/build/build.fsproj +++ b/build/build.fsproj @@ -1,7 +1,7 @@ - net6.0 + net8.0 Exe @@ -19,15 +19,15 @@ - - - - - - - - - + + + + + + + + + diff --git a/global.json b/global.json index 08fc58c..989a69c 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "6.0.100", + "version": "8.0.100", "rollForward": "latestMinor" } } \ No newline at end of file diff --git a/src/DynamicObj.Immutable/DynamicObj.Immutable.fsproj b/src/DynamicObj.Immutable/DynamicObj.Immutable.fsproj index 39fd78e..ad6a596 100644 --- a/src/DynamicObj.Immutable/DynamicObj.Immutable.fsproj +++ b/src/DynamicObj.Immutable/DynamicObj.Immutable.fsproj @@ -30,7 +30,7 @@ - + diff --git a/src/DynamicObj/DynamicObj.fs b/src/DynamicObj/DynamicObj.fs index 991fa18..0b65f24 100644 --- a/src/DynamicObj/DynamicObj.fs +++ b/src/DynamicObj/DynamicObj.fs @@ -1,16 +1,19 @@ namespace DynamicObj -//open System.Dynamic +#if !FABLE_COMPILER +open System.Dynamic +#endif + open System.Collections.Generic open Fable.Core [] -type DynamicObj() = +type DynamicObj() = + + #if !FABLE_COMPILER + inherit DynamicObject() + #endif - //#if !FABLE_COMPILER - //inherit DynamicObject() - //#endif - let mutable properties = new Dictionary() /// @@ -247,6 +250,32 @@ type DynamicObj() = this.CopyDynamicPropertiesTo(target) target + #if !FABLE_COMPILER + // Some necessary overrides for methods inherited from System.Dynamic.DynamicObject() + // + // Needed mainly for making Newtonsoft.Json Serialization work + override this.TryGetMember(binder:GetMemberBinder,result:obj byref ) = + match this.TryGetPropertyValue binder.Name with + | Some value -> result <- value; true + | None -> false + + override this.TrySetMember(binder:SetMemberBinder, value:obj) = + this.SetProperty(binder.Name,value) + true + + /// Returns both instance and dynamic member names. + /// Important to return both so JSON serialization with Json.NET works. + override this.GetDynamicMemberNames() = this.GetPropertyNames(true) + + //// potential deserialization support + //[] + //member private this._additionalData : IDictionary = new Dictionary() + + //[] + //member private this.OnDeserialized(context:StreamingContext) = () + // map over key value pairs in additional data, box the token values and set dynamic properties via SetProperty. + + #endif /// /// Operator to access a property by name diff --git a/src/DynamicObj/DynamicObj.fsproj b/src/DynamicObj/DynamicObj.fsproj index 618e398..a16e8f3 100644 --- a/src/DynamicObj/DynamicObj.fsproj +++ b/src/DynamicObj/DynamicObj.fsproj @@ -38,7 +38,6 @@ - diff --git a/tests/DynamicObject.Immutable.Tests/DynamicObject.Immutable.Tests.fsproj b/tests/DynamicObject.Immutable.Tests/DynamicObject.Immutable.Tests.fsproj index d14214a..cc1f8e6 100644 --- a/tests/DynamicObject.Immutable.Tests/DynamicObject.Immutable.Tests.fsproj +++ b/tests/DynamicObject.Immutable.Tests/DynamicObject.Immutable.Tests.fsproj @@ -1,7 +1,7 @@ - net6.0 + net8.0 false false diff --git a/tests/DynamicObject.Tests/DynamicObject.Tests.fsproj b/tests/DynamicObject.Tests/DynamicObject.Tests.fsproj index dc118ee..53a9307 100644 --- a/tests/DynamicObject.Tests/DynamicObject.Tests.fsproj +++ b/tests/DynamicObject.Tests/DynamicObject.Tests.fsproj @@ -1,7 +1,7 @@ - net6.0 + net8.0 false false @@ -13,6 +13,7 @@ + @@ -31,6 +32,7 @@ + diff --git a/tests/DynamicObject.Tests/Main.fs b/tests/DynamicObject.Tests/Main.fs index aa9cddf..8b79a73 100644 --- a/tests/DynamicObject.Tests/Main.fs +++ b/tests/DynamicObject.Tests/Main.fs @@ -8,6 +8,7 @@ let all = testSequenced <| testList "DynamicObj" [ DynObj.Tests.main Inheritance.Tests.main Interface.Tests.main + Serialization.Tests.main ] [] diff --git a/tests/DynamicObject.Tests/Serialization.fs b/tests/DynamicObject.Tests/Serialization.fs new file mode 100644 index 0000000..3e7b336 --- /dev/null +++ b/tests/DynamicObject.Tests/Serialization.fs @@ -0,0 +1,106 @@ +module Serialization.Tests + +open System +open System.Collections.Generic +open Fable.Pyxpecto +open DynamicObj + +open DynamicObj + +let test_dynobj = + let obj = new DynamicObj() + obj.SetProperty("dynamic_string", "yes") + obj.SetProperty("dynamic_number", 69) + obj.SetProperty("dynamic_boolean", true) + obj.SetProperty("dynamic_array", ["First"; "Second"]) + obj.SetProperty( + "dynamic_object", + let tmp = new DynamicObj() + tmp.SetProperty("inner", "yup") + tmp + ) + obj + +type DerivedClass1(staticProp: string) = + inherit DynamicObj() + member this.StaticProp = staticProp + +type DerivedClass2( + staticString: string, + staticNumber: float, + staticBoolean: bool, + staticArray: string list, + staticObject: DynamicObj +) = + inherit DynamicObj() + member this.StaticString = staticString + member this.StaticNumber = staticNumber + member this.StaticBoolean = staticBoolean + member this.StaticArray = staticArray + member this.StaticObject = staticObject + +let test_derived_1 = + let obj = DerivedClass1("lol") + obj.SetProperty("dynamicProp", 42) + obj + +let test_derived_2 = + let obj = DerivedClass2( + "lol", + 42.0, + true, + ["First"; "Second"], + test_derived_1 + ) + obj.SetProperty("dynamic_string", "yes") + obj.SetProperty("dynamic_number", 69) + obj.SetProperty("dynamic_boolean", true) + obj.SetProperty("dynamic_array", ["First"; "Second"]) + obj.SetProperty( + "dynamic_object", + let tmp = new DynamicObj() + tmp.SetProperty("inner", "yup") + tmp + ) + obj + +#if !FABLE_COMPILER +let tests_newtonsoft = testList "Newtonsoft (.NET)" [ + testCase "Serialize DynamicObj" <| fun _ -> + let actual = Newtonsoft.Json.JsonConvert.SerializeObject(test_dynobj) + Expect.equal actual """{"dynamic_string":"yes","dynamic_number":69,"dynamic_boolean":true,"dynamic_array":["First","Second"],"dynamic_object":{"inner":"yup"}}""" "" + + testCase "Serialize simplederived class from DynamicObj" <| fun _ -> + let actual = Newtonsoft.Json.JsonConvert.SerializeObject(test_derived_1) + Expect.equal actual """{"StaticProp":"lol","dynamicProp":42}""" "" + + testCase "Serialize complex derived class from DynamicObj" <| fun _ -> + let actual = Newtonsoft.Json.JsonConvert.SerializeObject(test_derived_2) + Expect.equal actual """{"StaticString":"lol","StaticNumber":42.0,"StaticBoolean":true,"StaticArray":["First","Second"],"StaticObject":{"StaticProp":"lol","dynamicProp":42},"dynamic_string":"yes","dynamic_number":69,"dynamic_boolean":true,"dynamic_array":["First","Second"],"dynamic_object":{"inner":"yup"}}""" "" +] + +let tests_system_text_json = ptestList "System.Text.Json (.NET)" [ + testCase "Serialize DynamicObj" <| fun _ -> + let actual = System.Text.Json.JsonSerializer.Serialize(test_dynobj) + Expect.equal actual """{"dynamic_string":"yes","dynamic_number":69,"dynamic_boolean":true,"dynamic_array":["First","Second"],"dynamic_object":{"inner":"yup"}}""" "" + + testCase "Serialize simplederived class from DynamicObj" <| fun _ -> + let actual = System.Text.Json.JsonSerializer.Serialize(test_derived_1) + Expect.equal actual """{"StaticProp":"lol","dynamicProp":42}""" "" + + testCase "Serialize complex derived class from DynamicObj" <| fun _ -> + let actual = System.Text.Json.JsonSerializer.Serialize(test_derived_2) + Expect.equal actual """{"StaticString":"lol","StaticNumber":42.0,"StaticBoolean":true,"StaticArray":["First","Second"],"StaticObject":{"StaticProp":"lol","dynamicProp":42},"dynamic_string":"yes","dynamic_number":69,"dynamic_boolean":true,"dynamic_array":["First","Second"],"dynamic_object":{"inner":"yup"}}""" "" +] +#endif + +// eventually, we want a transpilable, custom serialization +let tests_custom = testList "Custom" [] + +let main = testList "Serialization" [ + tests_custom + #if !FABLE_COMPILER + tests_newtonsoft + tests_system_text_json + #endif +] \ No newline at end of file