Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/jonnyreeves/as3-vanilla

Extract strongly typed Objects from dynamic objects without writing a single line of code!
https://github.com/jonnyreeves/as3-vanilla

Last synced: 2 days ago
JSON representation

Extract strongly typed Objects from dynamic objects without writing a single line of code!

Awesome Lists containing this project

README

        

AS3 Vanilla
===========

[![Build Status](https://travis-ci.org/jonnyreeves/as3-vanilla.svg?branch=master)](https://travis-ci.org/jonnyreeves/as3-vanilla)

A lightweight library which enables a developer to extract a strongly typed Model Object from a untyped dynamic object
without having to write a single line of parsing or marshalling code. An example use case would be turning data
returned from a JSON endpoint into a Model, if you've ever written the following code, then you can benefit from this
library:

```actionscript
// Use a JSON library to convert a JSON String into an AS3 Object.
var jsonObject : Object = JSON.decode('{"name":"Jonny", "age": 28, "music":["nin","mew"]}');

// Copy all the data into a new PersonVO so the rest of the system can use it.
var myPerson : PersonVO = new PersonVO();
myPerson.name = jsonObject["name"];
myPerson.age = jsonObject["age"];
myPerson.music = jsonObject["music"];

trace(myPerson.name) // Jonny.
```

Using the Vanilla library, you can turn the above code into this:

```actionscript
// Use a JSON library to convert a JSON String into an AS3 Object.
var jsonObject : Object = JSON.decode('{"name":"Jonny", "age": 28, "music":["nin","mew"]}');

// Use Vanilla to convert it into a PersonVO.
var myPerson : PersonVO = new Vanilla().extract(jsonObject, PersonVO);

trace(myPerson.name); // "Jonny"
```

Things get even easier when you make use of the `extract` convenience method:

```actionscript
var jsonObject : Object = JSON.decode('{"name":"Jonny", "age": 28, "music":["nin","mew"]}');
var myPerson : PersonVO = extract(jsonObject, PersonVO);
```

Got a complex object graph? Well that's where Vanilla really shines, making light work of parsing and
marshalling nested objects, for example:

```actionscript
// The PersonVO Model contains an 'address' field which is a complex datatype (AddressVO).
package app.model {
class PersonVO {
public var name : String;
public var address : AddressVO;
}
}

// Here's the class definition for AddressVO.
package app.model {
class AddressVO {
public var line1 : String;
public var line2 : String;
public var city : String;
}
}

var jsonObject : Object = JSON.decode('{"name":"Jonny","address":{"line1":"My House","line2":"My Road","city":"London"}}');
var myPerson : PersonVO = extract(jsonObject, PersonVO);

trace(myPerson.address.city); // "London"
```

Although Vanilla library does make use of Metadata, it is by no means required - the goal of this library
is to make the marshalling as transparent and painless as possible; if the source Object's fields maps perfectly to
the fields of your Model object (as in the example above) then everything should 'just work'(tm).

Mapping Fields
--------------
Sometimes the fields on your Model object don't quite match up to the fields in your source object; not a problem,
you can use Metadata to define the mappings in your Model object:

```actionscript
package app.model {
public class PersonVO {
public var name : String;
public var age : uint;
[Marshall(field="music")] public var musicTastes : Array;
}
}
```

Mapping to Constructor Arguments
--------------------------------
If you're a fan of immutable models then you will want to define some Metadata to map the fields in your source object
to the constructor arguments of your Model object:

```actionscript
package app.model {
// Don't forget, constructor metadata is annotated to the class, not the constructor method!
[Marshall(field="name", field="age", field="music")]
public class PersonModel {
private var _name : String;
private var _age : uint;
private var _musicTastes : Array;

public function PersonModel(name, age, music : Array) {
_name = name;
_age = age;
_music = music;
}
}
}
```

Mapping to Methods / Mutators
-----------------------------
You can map the fields of your source object to a setter method in your Model object:

```actionscript
package app.model {
public class PersonModel {
private var _name : String;
private var _age : uint;
private var _music : Array;

[Marshall(field="name", field="age")]
public function init(name : String, age : uint) : void {
_name = name;
_age = age;
}

[Marshall(field="music")]
public function setMusic(value : Array) : void {
_music = value;
}
}
```

Ignoring fields
-----------------------------
You can ignore fields by using [Transient] metadata:

```actionscript
package app.model {
public class PersonModel {
[Transient]
public var name : String;
public var age : uint;
}
}
```
The name field is ignored from the mapping but the age is not.

Automatic Coercion
------------------
Vanilla will automatically coerce Arrays found in your source object into Vectors defined in your target Model
Class, take the following example:

```actionscript
// The Target Model Class Definition.
class app.model {
public class ColourList {
public var colours : Vector.;
}
}

var source : Object = { colours: [ "red", "white", "blue" ] };
var result : ColourList = extract(source, ColourList);

trace(result.colours); // "red","white","blue"
trace(getQualifiedClassName(result.colours)); // __AS3__.vec::Vector.
```

If you aren't using Vectors in your project, then you will have to give Vanilla a hand and provide it with a type hint
as to what the Array is expected to contain, for example:

```actionscript
// The Target Model Class Definition.
class app.model {
public class Contact {
public var name : String;

[Marshall (type="app.model.PhoneNumber")]
public var phoneNumbers : Array;
}
}

// A DTO which the Contact class makes use of.
class app.model {
public class PhoneNumber {
public var number : Number;
public var type : String;
}
}

// Let's extract this JSON representation into a instance of the Contact model.
var source : Object = {
name: "Jonny",
phoneNumbers: [
{ number: 114752471, type: "home" },
{ number: 12344122, type: "mobile" }
]
};

var result : Contact = extract(source, Contact);
trace(result.phoneNumbers); // [object PhoneNumber],[object PhoneNumber]
trace((result.phoneNumbers[0] as PhoneNumber).type) // "home".
```

Dependencies
------------
`as3commons-reflect` is used for all reflection; in order to use as3-vanilla you will need to download and add
the following SWCs to your project's libs folder.

* [ascommons-reflect-1.4.1](http://projects.yoolab.org/maven/content/repositories/releases/org/as3commons/as3commons-reflect/1.4.1/as3commons-reflect-1.4.1.swc)
* [as3commons-lang-0.3.4](http://projects.yoolab.org/maven/content/repositories/releases/org/as3commons/as3commons-lang/0.3.4/as3commons-lang-0.3.4.swc)
* [as3commons-logging-2.0](http://projects.yoolab.org/maven/content/repositories/releases/org/as3commons/as3commons-logging/2.0/as3commons-logging-2.0.swc)

Future Features / Wish List
---------------------------
* The user should be able to define mapping rules without having to use Metadata.
* Java style mutators (eg: `setFoo()`) could be mapped automatically.
* By using `as3commons-bytecode` we could perform ByteCode reflection to retrieve the names of parameters; this would
would remove the need for constructor marshalling metadata.
* Possibly move away from as3commons-reflect (It has too many dependencies).