dorba / blade

The core tools for Blade rich client development.
89 stars 14 forks source link

Method to define simple JSON like classes, and import global functions #6

Closed volkanceylan closed 12 years ago

volkanceylan commented 12 years ago

Hello,

I'm planning to use Blade in place of ScriptSharp, and would also like to contribute if possible. Some questions:

  1. Is there a way to define a class that when instantiated creates a code like

[ScriptJson???] public class Dummy { public int a; public int b; }

C# ---> var x = new Dummy() { a = 5, b = 4 } ; JS ---> var x = { a: 5, b: 4 };

Managed to do something close by using ScriptName("Object") but it generates too verbose code with inline functions etc.

  1. How to import externally defined global functions?
  2. A way to generate script code directly, like Script.Literal?

Thanks.

dorba commented 12 years ago

There's not currently an attribute to do what you want, but I've considered adding one, and aliasing it with the DataContract attribute, so you can more easily reuse server class entities on the client.

You could get the JS above using an anonymous type, for example: var x = new { a = 5, b = 4 };

But it sounds like you want to have an actual class representation of that as well? In that case would you generate something that returned a default object? Or just instantiate the object inline and leave any unset property values to be undefined?

For the last 2 questions: 1)In a normal Blade project you can mark any declaration with the [ScriptExternal] attribute to keep it from generating output. Also for defining a full external library, you can simply create a standard .NET class library and set the Profile to Blade in the project properties. In that case, you don't need any special attributes, since the script compiler doesn't run. Take a look at the Knockout and jQuery projects in source/Library for examples.

2)There is no equivalent to Script.Literal at the moment. Something like this should be fairly easy to add, I just wanted to give myself a chance to think up some alternate ideas that may solve the same issue. For instance, some sort of special comment or function that could translate to literal script.

volkanceylan commented 12 years ago

jQuery UI components like dialog, sortable, and many other javascript objects take a options parameter. These are not actual classes but simple dictionaries. Anonymous type would help, but i prefer to have intellisense support. Unlike native C# classes, when they are created (through object initializer syntax or not) unitialized properties to become undefined is fine, as its expected behaviour.

For question 1, yes, ScriptExternal attribute doesn't generate a script class but when you call a static method of that class, it puts the class name in front of it like ImportedClass.externalMethod. I was asking about global functions, that doesnt belong to any namespace (though as they usually belong to the window variable, i could use that with ScriptName attribute)

Ventajou commented 12 years ago

For jQuery support, I added [ScriptName("$")] to the jQuery class, and [ScriptName("")] to jQuery.Select(). This way, jQuery.Select("something") will become $("something") and other static calls like jQuery.ajax() will become $.ajax()

dorba commented 12 years ago

If you wanted to code against a static method in the global namespace you need to do something like this:

[ScriptExternal]
[ScriptName("")]
public class GlobalStuff
{
    public static void MyFunc() { }
}

Then calling it should generate script that treats MyFunc as a global function. Also, if you're class is inside of a namespace you can apply the [ScriptIgnoreNamespace] attribute to remove the namespace from the class.

As for having classes with inline object literal instantiation, I think it's a good idea, I'll look into it and let you know what I come up with.

dorba commented 12 years ago

If I add an attribute to do this, there are quite a few caveats. It wold cause some discontinuity in how the OO parts work. For instance, is and as keywords search the prototype chain. These wouldn't work since the chain would only contain Object's prototype in this case. This would also cause some different behaviors when deriving from the class. Also, you wouldn't be allowed to defined a constructor on the class, or anything with a method body (method, or property get/set accessor body). Essentially it would require quite a few restrictions and compile time checks to generate errors and/or warnings if you're doing something that doesn't make sense.

dorba commented 12 years ago

Ok, I added an attribute that should accomplish what you want. It's the ScriptObjectLiteralAttribute. Here's a usage example:

[ScriptObjectLiteral]
public class JsonClass
{
    public string One { get; set; }
    public string Two { get; set; }
}

So when you use it, for example:

var json = new JsonClass { One = "a", Two = "b" };

You'll get script like:

var json = {
    One: 'a',
    Two: 'b'
};

I added this to the installer and updated it in the release folder. So if you installed via the MSI, you'll just need to uninstall your current version and reinstall the latest.

I'm working on a unit testing framework at the moment, and planning to write test cases for most of the core library once it's ready. As I find other edge cases, I may enforce additional compiler constraints around the usage of this attribute. But as long as you stick to using fields and/or auto properties you should be fine.

Let me know how it works.

volkanceylan commented 12 years ago

Yes, i'm aware of the caveats you mentioned, but when using those classes, as they are not actual classes but something like type safe dictionaries, one should expect some .NET methods to fail. Thanks a lot for the change, now i can start porting my existing code to Blade. I'll inform you after some testing.