Giter Site home page Giter Site logo

csbiology / dynamicobj Goto Github PK

View Code? Open in Web Editor NEW
11.0 11.0 4.0 316 KB

F# library supporting Dynamic Objects including inheritance in functional style

Home Page: https://csbiology.github.io/DynamicObj/

License: MIT License

F# 97.12% Batchfile 0.11% Shell 0.17% C# 2.60%

dynamicobj's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

dynamicobj's Issues

Print does not work, while JsonConvert.SerializeObject works fine

I have a nested dynamic object type:

let response =
    [
        "StatusCode", string result.Value.Response.StatusCode
        "Time", (System.DateTime.Now.ToUniversalTime() - st).ToString()
    ]
    |> List.map (fun x -> fst x, snd x |> Some)
let request =
    let query =
        let queryLogger = Logger()
        ctx.Request.Query |> Seq.iter (fun x -> queryLogger.setProp(x.Key, Some x.Value) )
        queryLogger
    let headers =
        let queryLogger = Logger()
        ctx.Request.Headers |> Seq.iter (fun x -> queryLogger.setProp(x.Key, x.Value |> String.concat "," |> Some ) )
        queryLogger 
    let userAgent =
        ctx.Request.Headers
        |> Seq.tryFind (fun x -> x.Key ="User-Agent")
        |> Option.map (fun x -> x.Value |> String.concat ",")
        |> Option.defaultValue ""
    let contentType =
        ctx.Request.Headers
        |> Seq.tryFind (fun x -> x.Key ="Content-Type")
        |> Option.map (fun x -> x.Value |> String.concat ",")
        |> Option.defaultValue ""
    [
        "Path", box ctx.Request.Path
        "PathBase", box ctx.Request.PathBase
        "Method", box ctx.Request.Method
        "Host", box ctx.Request.Host.Host
        "Port",
            if ctx.Request.Host.Port.HasValue then string ctx.Request.Host.Port.Value else ""
            |> box
        "QueryString",
            if ctx.Request.QueryString.HasValue then string ctx.Request.Host.Port.Value else ""
            |> box
        "Query", if ctx.Request.Query.Count > 0 then box query else null
        "Headers", if ctx.Request.Headers.Count > 0 then box headers else null
        "UserAgent", box userAgent
        "ContentType", box contentType
    ]
    |> List.map (fun x -> fst x, snd x |> Some)
let logger =
    let props =
        [
            "Timestamp", st.ToString("yyyy.MM.dd hh:mm:ss.fffff") |> box
            "Request", Logger.init(request) |> box
            "Response", Logger.init(response) |> box
        ]
        |> List.map (fun x -> fst x, snd x |> Some)
    Logger.init(props)

The json export works:

logger |> JsonConvert.SerializeObject |> printfn "%A"
{
    "Timestamp": "2022.03.25 12:53:37.89496",
    "Request": {
      "Path": "/api/IHelpdeskAPI/getCaptcha",
      "PathBase": "",
      "Method": "GET",
      "Host": "localhost",
      "Port": "8085",
      "QueryString": "",
      "Query": null,
      "Headers": {
        "Connection": "close",
        "Content-Type": "application/json; charset=utf-8",
        "Accept": "*/*",
        "Accept-Encoding": "gzip, deflate, br",
        "Accept-Language": "en-GB,en-US;q=0.9,en;q=0.8",
        "Cookie": "ajs_anonymous_id=%22bf0866e7-3877-4d25-8805-c1b2a4b5bd71%22; isDarkmode=false",
        "Host": "localhost:8085",
        "Referer": "http://localhost:8080/?topic=Tools_Swate",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.109 Safari/537.36 OPR/84.0.4316.42",
        "sec-fetch-dest": "empty",
        "sec-fetch-mode": "cors",
        "sec-fetch-site": "same-origin",
        "sec-ch-ua-platform": "\"Windows\"",
        "sec-ch-ua-mobile": "?0",
        "x-remoting-proxy": "true",
        "sec-ch-ua": "\" Not A;Brand\";v=\"99\", \"Chromium\";v=\"98\", \"Opera\";v=\"84\""
      },
      "UserAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.109 Safari/537.36 OPR/84.0.4316.42",
      "ContentType": "application/json; charset=utf-8"
    },
    "Response": {
      "StatusCode": "200",
      "Time": "00:00:24.8929431"
    }
  }

But DynObj.print logger returns an error:

System.MemberAccessException: Cannot access member.
  at DynamicObj.DynamicObj.op_Dynamic[b](b lookup, String name)
  at DynamicObj.DynObj.loop@93-3(DynamicObj d, DynamicObj object, Int32 identationLevel, FSharpList`1 membersLeft, FSharpList`1 acc)
  at DynamicObj.DynObj.loop@93-3(DynamicObj d, DynamicObj object, Int32 identationLevel, FSharpList`1 membersLeft, FSharpList`1 acc)
  at System.Runtime.CompilerServices.RuntimeHelpers.DispatchTailCalls(IntPtr callersRetAddrSlot, IntPtr callTarget, IntPtr retVal)
  at DynamicObj.DynObj.format(DynamicObj d)
  at DynamicObj.DynObj.print(DynamicObj d)

Casting issues with dynamic object

So this is more or less a collection of things i found missing and i am not sure if these are even possible/how to implement them.

  1. Downcasting dynamic objects. Found this issue, when i played around with the DynamicObj.ofJson function i added. It will return a DynamicObj which cannot be downcaster to any type inheriten DynamicObj. The workaround is to use CopyDynamicPropertiesTo on the inerhiting type.
type Logger() =
    inherit DynamicObj()

let logger = Logger()
logger.SetValue ("key", "value")

let dynObj : DynamicObj = DynamicObj()
dynObj.SetValue ("key", "value")

// upcast
let d2 = logger :> DynamicObj // upcast works without issue
d2.TryGetValue "key" // Some "value"

// downcast
dynObj :?> Logger 
// Error: System.InvalidCastException: Unable to cast object of type 'DynamicObj.DynamicObj' to type 'Logger'.

//workaround
dynObj.CopyDynamicPropertiesTo logger 
  1. Cast DynamicObj to (anonymous) record type. This could be especially nice for the TryGetTypedValue member.
type MyRecordType = {|
    key: string
|}

let dynObj : DynamicObj = DynamicObj()
dynObj.SetValue ("key", "value")

dynObj :?> MyRecordType
// Type constraint mismatch. The type 'MyRecordType'  is not compatible with type 'DynamicObj

Revisit transpilation to JS/Python via Fable

Current impression is:

  • All code that needs reflection only needs to be there in .NET and can be completely ignored for transpilation (via compiler flags)
  • Instead of the ? operator, implement the Item accession method, and conditionally transpile as JSON field access (JS) or dictionary item access (python)

ImmutableDynamicObj map not serialized correctly out of the box

There seems to be additional steps necessary to ensure that Newtonsoft.Json correctly serializes ImmutableDynamicObj. See this comparison:

DynamicObj (works)

let works =
    let foo = DynamicObj()
    foo?bar <- [1;2;3;4]
    foo

works |> JsonConvert.SerializeObject

val it : string = "{"bar":[1,2,3,4]}"

ImmutableDynamicObj (soes not work)

ImmutableDynamicObj()
|> ImmutableDynamicObj.add "foo" "bar" 
|> JsonConvert.SerializeObject

val it : string = "{}"

Unify DO/IDO APIs

Ideally, both APIs should provide the exact same functionality. While the mutable operations on DO have no equivalents, here are some things we should do IMO:

  • Add pipeable versions for add/remove etc, and the new set (e.g. ++, +?) of operators to DO
  • Remove the DynObj namespace (it is confusing), make all functions contained there static members of DynamicObj

[Bug] DynObj.print does not work for nested dyn objects with multiple fields.

Hello, i found AND fixed a bug in the DynObj.print function! I will open a PR soon.

To replicate the issue you can use a dynamic object, replicating the following json:

{
  "serilog": {
    "Level": "Information",
    "MessageTemplate": "{Method} Request at {Path}"
  }
}

OR

let o = DynamicObj()
let o2 = DynamicObj()
o2.SetValue("Level", "Information")
o2.SetValue("MessageTemplate","{Method} Request at {Path}")
o.SetValue("serilog", o2)

Using the current print function will lead to the following error:

System.MemberAccessException: Cannot access member.
   at FSI_0009.loop@55-1(DynamicObj d, DynamicObj object, Int32 identationLevel, FSharpList`1 membersLeft, FSharpList`1 acc)
   at FSI_0009.loop@55-1(DynamicObj d, DynamicObj object, Int32 identationLevel, FSharpList`1 membersLeft, FSharpList`1 acc)
   at FSI_0009.format(DynamicObj d)
   at FSI_0009.print(DynamicObj d)
   at <StartupCode$FSI_0010>.$FSI_0010.main@()
Stopped due to error

This error is produced by line 105. In which the source dynamic object is given into the loop, instead of the current "parent"-dynamic object object.

Will open a PR for this.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.