Sorbet signature for nested hash - sorbet

I have a method that parses YAML files. The returned object is a nested Hash, where the keys are always Strings and the leaf-values are always strings, e.g.
{
"a" => "foo",
"b" => {
"c" => "bar",
"d" => "baz"
}
}
I don't know in advance how deep the hash is.
The closest I got to typing the return value was the following signature:
T.any(T::Hash[String,String], T::Hash[String,T::Hash[String, T.untyped]])
This is obviously a bad solution, since it doesn't check anything beneath the second nesting, but the documentation about custom types seems a bit sparse.
Is there any way to type nested hashes, using a custom type, nested types or something similar?

Unfortunately, you won't be able to do much more than what you got to at this point. Even though Shapes are supported, they are an experimental feature.
If you really want to go with hashes, you could express it as:
MyType = T.type_alias {T::Hash[String, T.any(String, T::Hash[String, T.untyped])]}
Alternatively, you could use a T::Struct:
class MyType < T::Struct
const :key, String
const :value, T.any(String, MyType)
end
You'd still have the uncertainty of what the type of the value is, but with flow sensitivity, it should be easy to process the structure:
class Processor
extend T::Sig
sig {params(my_object: MyType).returns(String)}
def process(my_object)
key = my_object.key
obj_value = my_object.value # this is needed for flow sensitivity below
value = case obj_value
when String
value
when MyType
process(obj_value)
else
T.absurd(obj_value) # this makes sure that if you add a new type to `value`, Sorbet will make you handle it
end
return "key: #{key}, value: #{value}"
end
end

Related

How to test #type decorator in NestJs using jest? [duplicate]

I am asking you for help. I have created a DTO that looks like it (this is a smaller version) :
export class OfImportDto {
#IsString({
message: "should be a valid product code"
})
productCode: string;
#IsString({
message: "Enter the proper product description"
})
productDescription: string;
#IsDateString({
message: "should be a valid date format, for example : 2017-06-07T14:34:08+04:00"
})
manufacturingDate : Date
#IsInt({
message: "should be a valid planned quantity number"
})
#IsPositive()
plannedQuantity: number;
the thing is that i am asking to test that, with a unit test and not a E2E test. And I Don't know how to do that. For instance, I would like to unit test
1/ if my product code is well a string, a string should be created, if not, throw my exception
2/ if my product description is well a string, a string should be created, if not, throw my exception
...
and so on.
So, can I made a spec.ts file to test that? If yes, how?
If not, is it better to test it within the service.spec.ts? If so, how?
Thank you very much, any help would be very helpful :)
You should create a separate DTO specific file like of-import.dto.spec.ts for unit tests of your DTOs. Let's see how to test a DTO in Nest.js step by step.
TLDR: Your DTO's unit test
To understand it line by line, continue reading:
it('should throw when the planned quantity is a negative number.', async () => {
const importInfo = { productCode: 4567, plannedQuanity: -10 }
const ofImportDto = plainToInstance(OfImportDto, importInfo)
const errors = await validate(ofImportDto)
expect(errors.length).not.toBe(0)
expect(stringified(errors)).toContain(`Planned Quantity must be a positive number.`)
}
Create a plain object to test
The following is an object that you would like to test for validation:
const importInfo = { productCode: 4567, plannedQuanity: -10 }
If your test object has nested objects or it's too big, you can skip those complex properties. We'll see how to deal with that.
Convert the test object to the type of DTO
Use plainToinstace() function from the class-transformer package:
const ofImportDto = plainToInstance(OfImportDto, importInfo)
This will turn your plain test object to the object of the type of your DTO, that is, OfImportDto.
If you have any transformations in your DTO, like trimming spaces of the values of properties, they are applied at this point. If you just intend to test the transformations, you can assert now, you don't have to call the following validate() function for testing transformations.
Emulate the validation
Use the validate() function from the class-validator package:
const errors = await validate(ofImportDto)
If you skipped some properties while creating the test object, you can deal with that like the following:
const errors = await validate(ofImportDto, { skipMissingProperties: true })
Now the validation will ignore the missing properties.
Assert the errors
Assert that the errors array is not empty and it contains your custom error message:
expect(errors.length).not.toBe(0)
expect(stringified(errors)).toContain(`Planned Quantity must be a positive number.`)
Here stringified() is a helper function to convert the errors object to a JSON string, so we can search if it contains our custom error message:
export function stringified(errors: ValidationError[]): string {
return JSON.stringify(errors)
}
That's it! Hope that helps.
It would be possible to create a OfImportDTO.spec.ts file (or whatever your original file is called), but the thing is, there isn't any logic here to test. The closest thing you could do is create an instance of a Validator from class-validator and then instantiate an instance of the OfImportDto and then check that the class passes validation. If you add logic to it (e.g. getters and setters with specific functions) then it could make sense for unit testing, but otherwise, this is basically an interface being called a class so it exists at runtime for class-validator

Printing objects in Perl 6

Why do I get different results?
class Car {
has $.wheels;
}
my $my_car = Car.new( wheels => 4 );
say $my_car ; # Car.new(wheels => 4)
say "$my_car"; # Car<94582644384824>
put $my_car ; # Car<94582644384824>
I suppose that in the 2nd and 3rd cases $my_car is stringified, but what does the result mean?
The say command calls .gist on its argument. The put command calls .Str on its argument. And this also happens when you interpolate your object.
The default gist method looks at the public attributes of an object, and creates a string from that.
You can control how your object gets stringified by supplying your own Str method.

f# - how to serialize option and DU as value or null (preferably with json.net)

How can i get my json from web api to format only value or null for Option types and Discriminated Unions preferably using Newtonsoft.
I am currently using Newtonsoft and only have to add this to web api for it to work:
config.Formatters.JsonFormatter.SerializerSettings <- new JsonSerializerSettings()
When i consume the data on my side, i can easily convert it back to an F# item using: JsonConvert.DeserializeObject<'a>(json)
The api will be consumed by NON .NET clients as well so i would like a more standard formatted json result.
I would like to to fix my issue, w/o having to add code or decorators to all of my records/DU in order for it to work. I have lots of records with lots of properties, some are Option.
ex (this is how DU is serializing):
// When value
"animal": {
"case": "Dog"
}
// When no value
"animal": null
This is what I need:
// When value
"animal": "Dog"
// When no value
"animal": null
This is how an Option type is serializing:
"DocumentInfo": {
"case": "Some",
"fields": [
{
"docId": "77fb9dd0-bfbe-42e0-9d29-d5b1f5f0a9f7",
"docType": "Monkey Business",
"docName": "mb.doc",
"docContent": "why cant it just give me the values?"
}
]
}
This is what I need:
"DocumentInfo": {
"docId": "77fb9dd0-bfbe-42e0-9d29-d5b1f5f0a9f7",
"docType": "Monkey Business",
"docName": "mb.doc",
"docContent": "why cant it just give me the values?"
}
Thank you :-)
You could try using Chiron. I haven't used it myself so I can't give you an extensive example, but https://neoeinstein.github.io/blog/2015/12-13-chiron-json-ducks-monads/index.html has some bits of sample code. (And see https://neoeinstein.github.io/blog/2016/04-02-chiron-computation-expressions/index.html as well for some nicer syntax). Basically, Chiron knows how to serialize and deserialize the basic F# types (strings, numbers, options, etc.) already, and you can teach it to serialize any other type by providing that type with two static methods, ToJson and FromJson:
static member ToJson (x:DocumentInfo) = json {
do! Json.write "docId" x.docId
do! Json.write "docType" x.docType
do! Json.write "docName" x.docName
do! Json.write "docContent" x.docContent
}
static member FromJson (_:DocumentInfo) = json {
let! i = Json.read "docId"
let! t = Json.read "docType"
let! n = Json.read "docName"
let! c = Json.read "docContent"
return { docId = i; docType = t; docName = n; docContent = c }
}
By providing those two static methods on your DocumentInfo type, Chiron will automatically know how to serialize a DocumentInfo option. At least, that's my understanding -- but the Chiron documentation is sadly lacking (by which I mean literally lacking: it hasn't been written yet), so I haven't really used it myself. So this may or may not be the answer you need, but hopefully it'll be of some help to you even if you don't end up using it.
I have found the solution that allows me to use Newtonsoft (JSON.NET), apply custom converters for my types where needed and not require any changes to my DU's or Records.
The short answer is, create a custom converter for Json.Net and use the Read/Write Json overrides:
type CustomDuConverter() =
inherit JsonConverter() (...)
Unfortunately the ones I have found online that were already created doesn't work as is for my needs listed above, but will with slight modification. A great example is to look at: https://gist.github.com/isaacabraham/ba679f285bfd15d2f53e
To apply your custom serializer in Web Api for every call, use:
config.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new CustomDuConverter())
To deserialize use (example that will deserialize to DU):
JsonConvert.DeserializeObject<Animal>("Dog", customConverter)
ex:
type Animal = Dog | Cat
json:
"animal": "Dog"
This will allow you to create a clean Api for consumers and allow you to consume 3rd party Json data into your types that use Option, etc.

How to add subscripts to my custom Class in Perl 6?

I am new to Perl 6. I have the following code in my Atom Editor, but I still don't understand how this works. I copied the following code, as the docs.raku.org said, but it seems that it does not work. So I changed the code to this:
use v6;
class HTTPHeader { ... }
class HTTPHeader does Associative {
has %!fields handles <self.AT-KEY self.EXISTS-KEY self.DELETE-KEY self.push
list kv keys values>;
method Str { say self.hash.fmt; }
multi method EXISTS-KEY ($key) { %!fields{normalize-key $key}:exists }
multi method DELETE-KEY ($key) { %!fields{normalize-key $key}:delete }
multi method push (*#_) { %!fields.push: #_ }
sub normalize-key ($key) { $key.subst(/\w+/, *.tc, :g) }
method AT-KEY (::?CLASS:D: $key) is rw {
my $element := %!fields{normalize-key $key};
Proxy.new(
FETCH => method () { $element },
STORE => method ($value) {
$element = do given $value».split(/',' \s+/).flat {
when 1 { .[0] } # a single value is stored as a string
default { .Array } # multiple values are stored as an array
}
}
);
}
}
my $header = HTTPHeader.new;
say $header.WHAT; #-> (HTTPHeader)
"".say;
$header<Accept> = "text/plain";
$header{'Accept-' X~ <Charset Encoding Language>} = <utf-8 gzip en>;
$header.push('Accept-Language' => "fr"); # like .push on a Hash
say $header.hash.fmt;
"".say;
say $header<Accept-Language>.values;
say $header<Accept-Charset>;
the output is:
(HTTPHeader)
Accept text/plain
Accept-Charset utf-8
Accept-Encoding gzip
Accept-Language en fr
(en fr)
utf-8
I konw it works, but the document in docs.raku.org is a little different to this, which doesn't have "self" before the AT-KEY method in the 7th line. Is there any examples that more detail about this?
Is there any examples that more detail about this?
Stack overflow is not really the place to request more detail on a published example. This is the perl6 doco on the community itself - I would suggest that the most appropriate place if you have further queries is the Perl6 users mailing list or, failing that, the IRC channel, perhaps.
Now that you've posted it though, I'm hesitant to let the question go unaddressed so, here are a couple of things to consider;
Firstly - the example you raised is about implementing associative subscripting on a custom (ie user defined) class - it's not typical territory for a self-described newbie. I think you would be better off looking at and implementing the examples at Perl6 intro by Naoum Hankache whose site has been very well received.
Option 1 - Easy implementation via delegation
Secondly, it's critical to understand that the example is showing three options for implementing associative subscripting; the first and simplest uses delegation to a private hash attribute. Perl6 implements associative and positional subscripts (for built-in types) by calling well-defined methods on the object implementing the collection type. By adding the handles trait on the end of the definition of the %!fields attribute, you're simply passing on these method calls to %!fields which - being a hash - will know how to handle them.
Option 2 - Flexible keys
To quote the example:
However, HTTP header field names are supposed to be case-insensitive (and preferred in camel-case). We can accommodate this by taking the *-KEY and push methods out of the handles list, and implementing them separately...
Delegating all key-handling methods to the internal hash means you get hash-like interpretation of your keys - meaning they will be case-sensitive as hash keys are case-sensitive. To avoid that, you take all key-related methods out of the handles clause and implement them yourself. In the example, keys are ran through the "normalizer" before being used as indexes into %!fields making them case-insensitive.
Option 3 - Flexible values
The final part of the example shows how you can control the interpretation of values as they go into the hash-like container. Up to this point, values supplied by assigning to an instance of this custom container had to either be a string or an array of strings. The extra control is achieved by removing the AT_KEY method defined in option 2 and replacing it with a method that supplies a Proxy Object. The proxy object's STORE method will be called if you're assigning to the container and that method scans the supplied string value(s) for ", " (note: the space is compolsory) and if found, will accept the string value as a specification of several string values. At least, that's what I think it does.
So, the example has a lot more packed into it than it looks. You ran into trouble - as Brad pointed out in the comments - because you sort-of mashed option 1 togeather with option 3 when you coppied the example.

Is it possible to pass a variable's name along with the value, when passing through functions?

I want to know if it's possible to retrieve the variables name from when it was passed into a certain function. For example, if I call parseId(myId) to a function with the signature parseId(id), i can obviously retrieve the value of 'id'. However, is there any way I can retrieve 'myId' as a string (without passing it as another value)?
Specifically in vb.net, but I'm interested in how it would work in any given language.
This is all just random thoughts.. feel free to dismiss or not ;-p
Re your comment about use with stored procedures... if you want to go that route, I wouldn't mess around with the local variable names; that is an implementation detail. However, you could expose those details on an interface method and use the names from there, since that is more formalised - for example (C#):
interface ICustomerRepository {
Customer GetById(int id); // perhaps an attribute to name the sproc
}
You can use similar expression-tree parsing (as discussed here) to get the name and value of the parameter, for example:
var repoWrapper = new Repo<ICustomerRepository>();
int custId = 12345;
var cust = repoWrapper.Execute(r => r.GetById(custId));
Here we'd want to resolve the argument to GetById as "id" (not "custId"), with value 12345. This is actually exactly what my protobuf-net RPC code does ;-p (just don't ask me to translate it to VB - it is hard enough to write it in a language you know well...)
No, you can't do that in the normal sense. What are you really trying to accomplish with this?
You can do this in .NET 3.5 and above using expression trees; I'll knock up a C# example, and try to run it through reflector for VB...
C#:
static void Main()
{
int i = 17;
WriteLine(() => i);
}
static void WriteLine<T>(Expression<Func<T>> expression)
{
string name;
switch (expression.Body.NodeType)
{
case ExpressionType.MemberAccess:
name = ((MemberExpression)expression.Body).Member.Name;
break;
default:
throw new NotSupportedException("Give me a chance!");
}
T val = expression.Compile()();
Console.WriteLine(name + "=" + val);
}
The VB is below, but note that the VB compiler seems to use different names (like $VB$Local_i, not i):
Sub Main()
Dim i As Integer = 17
WriteLine(Function() i)
End Sub
Private Sub WriteLine(Of T)(ByVal expression As Expression(Of Func(Of T)))
If (expression.Body.NodeType <> ExpressionType.MemberAccess) Then
Throw New NotSupportedException("Give me a chance!")
End If
Console.WriteLine((DirectCast(expression.Body, MemberExpression).Member.Name
& "=" & Convert.ToString(expression.Compile.Invoke)))
End Sub