├── .gitignore
├── libs
└── actionjson-1.3.swc
├── src
├── manifest.xml
├── mesh
│ ├── mesh_internal.as
│ ├── Mesh.as
│ ├── core
│ │ ├── range
│ │ │ ├── from.as
│ │ │ ├── RangeBuilder.as
│ │ │ ├── IntRange.as
│ │ │ ├── CharRange.as
│ │ │ └── DateRange.as
│ │ ├── inflection
│ │ │ ├── pluralize.as
│ │ │ ├── singularize.as
│ │ │ ├── humanize.as
│ │ │ ├── underscore.as
│ │ │ ├── camelize.as
│ │ │ └── ordinalize.as
│ │ ├── reflection
│ │ │ ├── reflect.as
│ │ │ ├── path.as
│ │ │ ├── clazz.as
│ │ │ ├── className.as
│ │ │ ├── Metadata.as
│ │ │ ├── Method.as
│ │ │ ├── Property.as
│ │ │ ├── Definition.as
│ │ │ └── newInstance.as
│ │ ├── number
│ │ │ ├── round.as
│ │ │ ├── random.as
│ │ │ └── Fraction.as
│ │ ├── string
│ │ │ ├── sentenceize.as
│ │ │ └── capitalize.as
│ │ ├── BatchedListDelegate.as
│ │ ├── functions
│ │ │ └── closure.as
│ │ ├── object
│ │ │ ├── inspect.as
│ │ │ ├── isEmpty.as
│ │ │ ├── merge.as
│ │ │ └── copy.as
│ │ ├── state
│ │ │ ├── TransitionEvent.as
│ │ │ ├── StateEvent.as
│ │ │ ├── Action.as
│ │ │ ├── Transition.as
│ │ │ └── State.as
│ │ ├── array
│ │ │ ├── intersection.as
│ │ │ ├── flatten.as
│ │ │ └── ArrayProxy.as
│ │ ├── IBatchedListDelegate.as
│ │ ├── Set.as
│ │ ├── BatchedList.as
│ │ └── proxy
│ │ │ └── DataProxy.as
│ ├── model
│ │ ├── store
│ │ │ ├── DataCache.as
│ │ │ ├── ICommitResponder.as
│ │ │ ├── Query.as
│ │ │ ├── CommitResponder.as
│ │ │ ├── Cache.as
│ │ │ ├── RecordCache.as
│ │ │ ├── Store.as
│ │ │ ├── ResultsList.as
│ │ │ ├── Data.as
│ │ │ └── QueryBuilder.as
│ │ ├── IPersistable.as
│ │ ├── associations
│ │ │ ├── HasOneAssociation.as
│ │ │ ├── HasManyAssociation.as
│ │ │ └── HasAssociation.as
│ │ ├── validators
│ │ │ ├── FormatValidator.as
│ │ │ ├── ExclusionValidator.as
│ │ │ ├── InclusionValidator.as
│ │ │ ├── PresenceValidator.as
│ │ │ ├── EachValidator.as
│ │ │ ├── Validator.as
│ │ │ ├── LengthValidator.as
│ │ │ └── NumericValidator.as
│ │ ├── source
│ │ │ ├── AssociationCollectionSnapshot.as
│ │ │ ├── IPersistenceResponder.as
│ │ │ ├── IRetrievalResponder.as
│ │ │ ├── DataSource.as
│ │ │ ├── DataSourceRetrievalOperation.as
│ │ │ └── Snapshot.as
│ │ ├── ID.as
│ │ ├── ILoadable.as
│ │ └── RecordState.as
│ ├── operations
│ │ ├── EmptyOperation.as
│ │ ├── ParallelOperation.as
│ │ ├── ProgressOperationEvent.as
│ │ ├── ResultOperationEvent.as
│ │ ├── QueueProgress.as
│ │ ├── SequentialOperation.as
│ │ ├── FinishedOperationEvent.as
│ │ ├── OperationQueueEvent.as
│ │ ├── Progress.as
│ │ ├── OperationEvent.as
│ │ ├── Timeout.as
│ │ ├── Attempt.as
│ │ ├── FaultOperationEvent.as
│ │ ├── MethodOperation.as
│ │ ├── ServiceOperation.as
│ │ ├── FactoryOperation.as
│ │ ├── URLLoaderOperation.as
│ │ └── FileReferenceUploadOperation.as
│ └── view
│ │ └── helpers
│ │ ├── number
│ │ ├── withDelimiter.as
│ │ ├── toPercentage.as
│ │ └── withPrecision.as
│ │ └── text
│ │ └── pluralizeByCount.as
└── metadata.xml
└── tests
└── mesh
├── Account.as
├── Order.as
├── model
├── AggregateTestMockRecord.as
├── validators
│ ├── FormatValidatorTests.as
│ ├── InclusionValidatorTests.as
│ ├── ExclusionValidatorTests.as
│ ├── PresenceValidatorTests.as
│ ├── LengthValidatorTests.as
│ └── NumericValidatorTests.as
├── AggregateTests.as
├── ValidationTests.as
├── RecordDirtyTests.as
├── store
│ ├── DataTests.as
│ └── ResultsListTests.as
├── InitializeAssociatedRecordsTests.as
├── associations
│ └── HasOneAssociationTests.as
├── RecordLoadTests.as
└── RecordCommitTests.as
├── Customer.as
├── Name.as
├── AsyncTest.as
├── operations
├── MockOperation.as
└── MethodOperationTests.as
├── core
├── number
│ ├── FractionTests.as
│ └── RoundTests.as
├── array
│ └── FlattenTests.as
├── inflection
│ ├── HumanizeTests.as
│ ├── SingularizeTests.as
│ ├── PluralizeTests.as
│ ├── CamelizeTests.as
│ └── UnderscoreTests.as
├── string
│ ├── CapitalizeTests.as
│ └── SentenceizeTests.as
├── reflection
│ ├── MethodTests.as
│ └── TypeTests.as
├── proxy
│ └── DataProxyTests.as
├── object
│ └── CopyTests.as
└── state
│ └── StateMachineTests.as
├── Person.as
└── view
└── helpers
├── text
└── PluralizeByCountTests.as
└── number
├── WithDelimiterTests.as
└── WithPrecisionTests.as
/.gitignore:
--------------------------------------------------------------------------------
1 | .*
2 | *FlexUnit*
3 | /.*
4 | /bin
5 | /target
6 | **/.*
7 |
--------------------------------------------------------------------------------
/libs/actionjson-1.3.swc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danschultz/mesh/HEAD/libs/actionjson-1.3.swc
--------------------------------------------------------------------------------
/src/manifest.xml:
--------------------------------------------------------------------------------
1 |
2 | IPersistable interface defines an interface for objects that
7 | * can be persisted.
8 | *
9 | * @author Dan Schultz
10 | */
11 | public interface IPersistable
12 | {
13 | function save(responder:ICommitResponder = null):*;
14 | }
15 | }
--------------------------------------------------------------------------------
/src/mesh/core/reflection/reflect.as:
--------------------------------------------------------------------------------
1 | package mesh.core.reflection
2 | {
3 | /**
4 | * Returns either a cached reflection object for the given class or object.
5 | *
6 | * @param classOrObject The class or object to reflect.
7 | * @return A reflection object.
8 | */
9 | public function reflect(classOrObject:Object):Type
10 | {
11 | return Type.reflect(classOrObject);
12 | }
13 | }
--------------------------------------------------------------------------------
/src/mesh/model/associations/HasOneAssociation.as:
--------------------------------------------------------------------------------
1 | package mesh.model.associations
2 | {
3 | import mesh.model.Record;
4 |
5 | public class HasOneAssociation extends HasAssociation
6 | {
7 | /**
8 | * @copy HasAssociation#HasAssociation()
9 | */
10 | public function HasOneAssociation(owner:Record, property:String, options:Object = null)
11 | {
12 | super(owner, property, options);
13 | }
14 | }
15 | }
--------------------------------------------------------------------------------
/src/mesh/model/associations/HasManyAssociation.as:
--------------------------------------------------------------------------------
1 | package mesh.model.associations
2 | {
3 | import mesh.model.Record;
4 |
5 | public class HasManyAssociation extends AssociationCollection
6 | {
7 | /**
8 | * @copy AssociationCollection#AssociationCollection()
9 | */
10 | public function HasManyAssociation(source:Record, property:String, options:Object = null)
11 | {
12 | super(source, property, options);
13 | }
14 | }
15 | }
--------------------------------------------------------------------------------
/tests/mesh/model/AggregateTestMockRecord.as:
--------------------------------------------------------------------------------
1 | package mesh.model
2 | {
3 | import mesh.Name;
4 |
5 | public class AggregateTestMockRecord extends Record
6 | {
7 | [Bindable] public var name:Name;
8 | [Bindable] public var firstName:String;
9 | [Bindable] public var last:String;
10 |
11 | public function AggregateTestMockRecord(values:Object=null)
12 | {
13 | super(values);
14 | aggregate("name", Name, ["first:firstName", "last"]);
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/src/mesh/core/number/round.as:
--------------------------------------------------------------------------------
1 | package mesh.core.number
2 | {
3 | /**
4 | * Rounds a number to the given precision.
5 | *
6 | * @param number The number to round.
7 | * @param precision The number of fractional digits.
8 | * @return The rounded number.
9 | */
10 | public function round(number:Number, precision:int = 0):Number
11 | {
12 | precision = Math.pow(10, (precision < 0 ? 0 : precision));
13 | return Math.round(number * precision) / precision;
14 | }
15 | }
--------------------------------------------------------------------------------
/src/mesh/core/string/sentenceize.as:
--------------------------------------------------------------------------------
1 | package mesh.core.string
2 | {
3 | /**
4 | * Capitalizes the first letter of each sentence in the string.
5 | *
6 | * @param sentences The sentences to capitalize.
7 | * @return A capitalized sentence.
8 | */
9 | public function sentenceize(sentences:String):String
10 | {
11 | return sentences.replace(/(^|[\.|\?|!]\s+)(.)/gm, function():String
12 | {
13 | return arguments[1] + arguments[2].toUpperCase();
14 | });
15 | }
16 | }
--------------------------------------------------------------------------------
/src/mesh/core/reflection/path.as:
--------------------------------------------------------------------------------
1 | package mesh.core.reflection
2 | {
3 | import flash.utils.getQualifiedClassName;
4 |
5 | /**
6 | * Returns the package name of the given object or class.
7 | *
8 | * @param objectOrClass The object or class to get the package for.
9 | * @return The package that the object or class belongs to.
10 | */
11 | public function path(objectOrClass:Object):String
12 | {
13 | return getQualifiedClassName(objectOrClass).split("::").shift();
14 | }
15 | }
--------------------------------------------------------------------------------
/src/mesh/core/string/capitalize.as:
--------------------------------------------------------------------------------
1 | package mesh.core.string
2 | {
3 | /**
4 | * Capitalizes the first letter of a string and the first letter after each
5 | * whitespace character.
6 | *
7 | * @param str The string to capitalize.
8 | * @return A capitalized string.
9 | */
10 | public function capitalize(str:String):String
11 | {
12 | return str.replace(/(^|\s+)(.)/g, function():String
13 | {
14 | return arguments[1] + arguments[2].toUpperCase();
15 | });
16 | }
17 | }
--------------------------------------------------------------------------------
/tests/mesh/Customer.as:
--------------------------------------------------------------------------------
1 | package mesh
2 | {
3 | import mesh.model.associations.HasManyAssociation;
4 |
5 | [Bindable]
6 | public class Customer extends Person
7 | {
8 | [HasOne]
9 | public var account:Account;
10 |
11 | public var accountId:int;
12 |
13 | [HasMany(inverse="customer", recordType="mesh.Order")]
14 | public var orders:HasManyAssociation;
15 |
16 | public function Customer(properties:Object=null)
17 | {
18 | super(properties);
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/src/mesh/core/inflection/humanize.as:
--------------------------------------------------------------------------------
1 | package mesh.core.inflection
2 | {
3 | import mesh.core.string.sentenceize;
4 |
5 | /**
6 | * First converts the given string to an underscored string, then capitalizes the first
7 | * word and turns underscores into spaces.
8 | *
9 | * @param str The string to humanize.
10 | * @return A humanized string.
11 | */
12 | public function humanize(str:String):String
13 | {
14 | return sentenceize(underscore(str).replace(/_/g, " "));
15 | }
16 | }
--------------------------------------------------------------------------------
/src/mesh/core/inflection/underscore.as:
--------------------------------------------------------------------------------
1 | package mesh.core.inflection
2 | {
3 | /**
4 | * Converts a camelized string, or string with whitespaces into a lower cased
5 | * string with underscores.
6 | *
7 | * @param str The string to convert.
8 | * @return An underscored string.
9 | */
10 | public function underscore(str:String):String
11 | {
12 | return camelize(str.replace(/-/g, "_")).replace(/([A-Z]+)([A-Z][a-z])/g, "$1_$2").replace(/([a-z\d])([A-Z])/g, "$1_$2").toLowerCase();
13 | }
14 | }
--------------------------------------------------------------------------------
/src/mesh/core/reflection/clazz.as:
--------------------------------------------------------------------------------
1 | package mesh.core.reflection
2 | {
3 | import flash.utils.getDefinitionByName;
4 | import flash.utils.getQualifiedClassName;
5 |
6 | /**
7 | * Returns the class for the given object. The object can either be an instance of
8 | * a class or a class reference itself.
9 | *
10 | * @param obj The object to get the class for.
11 | * @return The class reference for the object.
12 | */
13 | public function clazz(obj:Object):Class
14 | {
15 | return getDefinitionByName(getQualifiedClassName(obj)) as Class;
16 | }
17 | }
--------------------------------------------------------------------------------
/tests/mesh/Name.as:
--------------------------------------------------------------------------------
1 | package mesh
2 | {
3 | public class Name
4 | {
5 | public function Name(first:String, last:String)
6 | {
7 | _first = first;
8 | _last = last;
9 | }
10 |
11 | public function equals(name:Name):Boolean
12 | {
13 | return first == name.first && last == name.last;
14 | }
15 |
16 | private var _first:String;
17 | public function get first():String
18 | {
19 | return _first;
20 | }
21 |
22 | private var _last:String;
23 | public function get last():String
24 | {
25 | return _last;
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/src/mesh/operations/EmptyOperation.as:
--------------------------------------------------------------------------------
1 | package mesh.operations
2 | {
3 | /**
4 | * An operation that when executed will finish immediately. This operation is useful
5 | * as a null object.
6 | *
7 | * @author Dan Schultz
8 | */
9 | public class EmptyOperation extends Operation
10 | {
11 | /**
12 | * Constructor.
13 | */
14 | public function EmptyOperation()
15 | {
16 | super();
17 | }
18 |
19 | /**
20 | * @inheritDoc
21 | */
22 | override protected function executeRequest():void
23 | {
24 | finish(true);
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/src/mesh/core/range/RangeBuilder.as:
--------------------------------------------------------------------------------
1 | package mesh.core.range
2 | {
3 | import mesh.core.reflection.newInstance;
4 |
5 | public class RangeBuilder
6 | {
7 | private var _clazz:Class;
8 | private var _from:*;
9 |
10 | public function RangeBuilder(clazz:Class, from:*)
11 | {
12 | _clazz = clazz;
13 | _from = from;
14 | }
15 |
16 | public function to(value:*):Range
17 | {
18 | return newInstance(_clazz, _from, value, false);
19 | }
20 |
21 | public function toButNotIncluding(value:*):Range
22 | {
23 | return newInstance(_clazz, _from, value, true);
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/src/mesh/core/reflection/className.as:
--------------------------------------------------------------------------------
1 | package mesh.core.reflection
2 | {
3 | import flash.utils.getQualifiedClassName;
4 |
5 | /**
6 | * Returns the name of the class for the given object. The returned name does not
7 | * include the package or namespace that the class resides in. The object can either
8 | * be an instance of a class, or the class itself.
9 | *
10 | * @param obj The object to get the class name for.
11 | * @return The name of the class.
12 | */
13 | public function className(obj:Object):String
14 | {
15 | return getQualifiedClassName(obj).split("::").pop();
16 | }
17 | }
--------------------------------------------------------------------------------
/tests/mesh/AsyncTest.as:
--------------------------------------------------------------------------------
1 | package mesh
2 | {
3 | import flash.events.Event;
4 | import flash.events.EventDispatcher;
5 |
6 | import org.flexunit.async.Async;
7 |
8 | public class AsyncTest extends EventDispatcher
9 | {
10 | public function AsyncTest(testCase:Object, timeout:int, handler:Function)
11 | {
12 | var wrapper:Function = function(event:Event, data:Object = null):void
13 | {
14 | handler();
15 | };
16 | addEventListener(Event.COMPLETE, Async.asyncHandler(testCase, wrapper, timeout));
17 | }
18 |
19 | public function complete():void
20 | {
21 | dispatchEvent( new Event(Event.COMPLETE) );
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/tests/mesh/operations/MockOperation.as:
--------------------------------------------------------------------------------
1 | package mesh.operations
2 | {
3 |
4 | public class MockOperation extends Operation
5 | {
6 | public function MockOperation()
7 | {
8 |
9 | }
10 |
11 | public function mimicFault(summary:String, detail:String):void
12 | {
13 | fault(summary, detail);
14 | }
15 |
16 | private var _resultThrowsRTE:Boolean;
17 | public function mimicResult(data:Object, throwsRTE:Boolean):void
18 | {
19 | _resultThrowsRTE = throwsRTE;
20 | result(data);
21 | }
22 |
23 | override protected function parseResult(data:Object):Object
24 | {
25 | if (_resultThrowsRTE) {
26 | throw new Error();
27 | }
28 | return data;
29 | }
30 | }
31 | }
--------------------------------------------------------------------------------
/src/mesh/core/BatchedListDelegate.as:
--------------------------------------------------------------------------------
1 | package mesh.core
2 | {
3 | /**
4 | * A base delegate class to make implementation simpler.
5 | *
6 | * @see IBatchedListDelegate
7 | *
8 | * @author Dan Schultz
9 | */
10 | public class BatchedListDelegate implements IBatchedListDelegate
11 | {
12 | /**
13 | * Constructor.
14 | */
15 | public function BatchedListDelegate()
16 | {
17 |
18 | }
19 |
20 | /**
21 | * @inheritDoc
22 | */
23 | public function requestLength(list:BatchedList):void
24 | {
25 |
26 | }
27 |
28 | /**
29 | * @inheritDoc
30 | */
31 | public function requestBatch(list:BatchedList, index:uint, batchSize:uint):void
32 | {
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/src/mesh/core/inflection/camelize.as:
--------------------------------------------------------------------------------
1 | package mesh.core.inflection
2 | {
3 | /**
4 | * Returns a camelized version of a string, where the underscores and whitespaces
5 | * are removed.
6 | *
7 | * @param str The string to camelize.
8 | * @param uppercaseFirstLetter true if the first letter of the result
9 | * is uppercased.
10 | * @return A camelized string.
11 | */
12 | public function camelize(str:String, uppercaseFirstLetter:Boolean = true):String
13 | {
14 | str = str.replace(/(?:_|\s)+(.)/g, function():String
15 | {
16 | return arguments[1].toUpperCase();
17 | });
18 | return (uppercaseFirstLetter ? str.substr(0, 1).toUpperCase() : str.substr(0, 1).toLowerCase()) + str.substr(1);
19 | }
20 | }
--------------------------------------------------------------------------------
/tests/mesh/core/number/FractionTests.as:
--------------------------------------------------------------------------------
1 | package mesh.core.number
2 | {
3 | import mesh.core.object.inspect;
4 |
5 | import org.flexunit.assertThat;
6 | import org.hamcrest.object.equalTo;
7 |
8 | public class FractionTests
9 | {
10 | [Test]
11 | public function testGCD():void
12 | {
13 | var tests:Array = [
14 | {a:1, b:2, expected:1},
15 | {a:0, b:3, expected:3},
16 | {a:3, b:0, expected:3},
17 | {a:108, b:30, expected:6},
18 | {a:-108, b:30, expected:6},
19 | {a:108, b:-30, expected:6},
20 | {a:-108, b:-30, expected:6}
21 | ];
22 |
23 | for each (var test:Object in tests) {
24 | assertThat("test failed: " + inspect(test), Fraction.gcd(test.a, test.b), equalTo(test.expected));
25 | }
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/tests/mesh/Person.as:
--------------------------------------------------------------------------------
1 | package mesh
2 | {
3 | import mesh.model.Record;
4 | import mesh.model.validators.NumericValidator;
5 | import mesh.model.validators.PresenceValidator;
6 |
7 | public class Person extends Record
8 | {
9 | public static var validate:Object =
10 | {
11 | name: [{validator:PresenceValidator}],
12 | age: [{validator:NumericValidator, greaterThan:0, integer:true}]
13 | };
14 |
15 | [Bindable] public var age:Number;
16 | [Bindable] public var firstName:String;
17 | [Bindable] public var lastName:String;
18 | [Bindable] public var name:Name;
19 |
20 | public function Person(properties:Object = null)
21 | {
22 | super(properties);
23 | aggregate("name", Name, ["first:firstName", "last:lastName"]);
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/src/mesh/model/store/ICommitResponder.as:
--------------------------------------------------------------------------------
1 | package mesh.model.store
2 | {
3 | /**
4 | * The ICommitResponder interface is an interface that classes can implement to
5 | * handle callbacks from a commit.
6 | *
7 | * @author Dan Schultz
8 | */
9 | public interface ICommitResponder
10 | {
11 | /**
12 | * The commit calls this method when it has failed to persist its records.
13 | *
14 | * @param summary The summary of the error.
15 | * @param detail A detailed message of the error.
16 | * @param code An error code.
17 | */
18 | function failed(summary:String, detail:String = "", code:String = ""):void;
19 |
20 | /**
21 | * The commit call this method when persistence of all its records are successful.
22 | */
23 | function success():void;
24 | }
25 | }
--------------------------------------------------------------------------------
/src/mesh/model/validators/FormatValidator.as:
--------------------------------------------------------------------------------
1 | package mesh.model.validators
2 | {
3 | /**
4 | * A validator that tests a value against a regular expression.
5 | *
6 | * @author Dan Schultz
7 | */
8 | public class FormatValidator extends EachValidator
9 | {
10 | /**
11 | * @copy Validator#Validator()
12 | */
13 | public function FormatValidator(options:Object)
14 | {
15 | super(options);
16 |
17 | if (!options.hasOwnProperty("message")) {
18 | options.message = "is invalid";
19 | }
20 | }
21 |
22 | /**
23 | * @inheritDoc
24 | */
25 | override protected function validateProperty(obj:Object, property:String, value:Object):void
26 | {
27 | if (!options.format.test(value.toString())) {
28 | obj.errors.add(property, options.message);
29 | }
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/src/mesh/model/source/AssociationCollectionSnapshot.as:
--------------------------------------------------------------------------------
1 | package mesh.model.source
2 | {
3 | import mesh.core.List;
4 |
5 | public class AssociationCollectionSnapshot extends List
6 | {
7 | public function AssociationCollectionSnapshot(source:Array, added:Array, removed:Array)
8 | {
9 | super(source);
10 | _added = added;
11 | _removed = removed;
12 | }
13 |
14 | private var _added:Array;
15 | /**
16 | * The records that have been added to the association.
17 | */
18 | public function get added():Array
19 | {
20 | return _added.concat();
21 | }
22 |
23 | private var _removed:Array;
24 | /**
25 | * The records that have been removed from the association.
26 | */
27 | public function get removed():Array
28 | {
29 | return _removed.concat();
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/src/mesh/model/ID.as:
--------------------------------------------------------------------------------
1 | package mesh.model
2 | {
3 | /**
4 | * A class for representing object ID's.
5 | *
6 | * @author Dan Schultz
7 | */
8 | public class ID
9 | {
10 | /**
11 | * Checks if an ID on an object is populated. An ID is populated if the value is not null, and
12 | * one of these cases are met:
13 | *
14 | *
true if the ID is populated.
22 | */
23 | public static function isPopulated(obj:Object, idField:String = "id"):Boolean
24 | {
25 | return obj != null && obj[idField] != null && (obj[idField] != 0 || obj[idField] != "");
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/src/mesh/core/range/IntRange.as:
--------------------------------------------------------------------------------
1 | package mesh.core.range
2 | {
3 | /**
4 | * A range for integers.
5 | *
6 | * @see Range#from()
7 | * @author Dan Schultz
8 | */
9 | public class IntRange extends Range
10 | {
11 | /**
12 | * @copy Range#Range()
13 | */
14 | public function IntRange(from:*, to:*, exclusive:Boolean = false)
15 | {
16 | super(from, to, exclusive);
17 | }
18 |
19 | /**
20 | * @inheritDoc
21 | */
22 | override protected function decrease(value:*, size:int):*
23 | {
24 | return value - size;
25 | }
26 |
27 | /**
28 | * @inheritDoc
29 | */
30 | override protected function increase(value:*, size:int):*
31 | {
32 | return value + size;
33 | }
34 |
35 | /**
36 | * @inheritDoc
37 | */
38 | override public function get length():int
39 | {
40 | return max - min + (!isExclusive ? 1 : 0);
41 | }
42 | }
43 | }
--------------------------------------------------------------------------------
/src/mesh/operations/ParallelOperation.as:
--------------------------------------------------------------------------------
1 | package mesh.operations
2 | {
3 | /**
4 | * A type of compound operation that executes all of its operations at the same time.
5 | *
6 | * @author Dan Schultz
7 | */
8 | public class ParallelOperation extends CompoundOperation
9 | {
10 | /**
11 | * @copy operations.CompoundOperation#CompoundOperation()
12 | */
13 | public function ParallelOperation(operations:Array = null)
14 | {
15 | super(operations);
16 | }
17 |
18 | /**
19 | * @inheritDoc
20 | */
21 | override public function during(operation:Operation):Operation
22 | {
23 | add(operation);
24 | return this;
25 | }
26 |
27 | /**
28 | * @inheritDoc
29 | */
30 | override protected function startExecution():void
31 | {
32 | for each (var operation:Operation in operationSet) {
33 | executeOperation(operation);
34 | }
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/tests/mesh/core/array/FlattenTests.as:
--------------------------------------------------------------------------------
1 | package mesh.core.array
2 | {
3 | import org.flexunit.assertThat;
4 | import org.hamcrest.collection.array;
5 | import org.hamcrest.object.equalTo;
6 |
7 | public class FlattenTests
8 | {
9 | [Test]
10 | public function testFlatten():void
11 | {
12 | var tests:Array = [
13 | {elements:[1, 2, 3], expected:[1, 2, 3]},
14 | {elements:[1, 2, 3, [4, 5]], expected:[1, 2, 3, 4, 5]},
15 | {elements:[1, 2, [3, [4, 5]]], expected:[1, 2, 3, 4, 5]},
16 | {elements:[1, 2, [3, [4, 5]]], depth:1, expected:[1, 2, 3, [4, 5]]},
17 | {elements:[[1, 2], [3, 4]], expected:[1, 2, 3, 4]},
18 | {elements:1, expected:[1]}
19 | ];
20 |
21 | for each (var test:Object in tests) {
22 | assertThat("test failed with elements: " + test.elements, test.depth != null ? flatten(test.elements, test.depth) : flatten(test.elements), array.apply(null, test.expected));
23 | }
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/src/mesh/model/validators/ExclusionValidator.as:
--------------------------------------------------------------------------------
1 | package mesh.model.validators
2 | {
3 | import collections.HashSet;
4 |
5 | /**
6 | * A helper that validates that a property's value does not belong to a given set of values.
7 | * This set can be an array or any enumerable object.
8 | *
9 | * @author Dan Schultz
10 | */
11 | public class ExclusionValidator extends EachValidator
12 | {
13 | /**
14 | * @copy Validator#Validator()
15 | */
16 | public function ExclusionValidator(options:Object)
17 | {
18 | if (!options.hasOwnProperty("message")) {
19 | options.message = "is reserved";
20 | }
21 |
22 | super(options);
23 | }
24 |
25 | /**
26 | * @inheritDoc
27 | */
28 | override protected function validateProperty(obj:Object, property:String, value:Object):void
29 | {
30 | if (new HashSet(options.within).contains(value)) {
31 | obj.errors.add(property, options.message);
32 | }
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/tests/mesh/view/helpers/text/PluralizeByCountTests.as:
--------------------------------------------------------------------------------
1 | package mesh.view.helpers.text
2 | {
3 | import org.flexunit.assertThat;
4 | import org.hamcrest.object.equalTo;
5 |
6 | public class PluralizeByCountTests
7 | {
8 | private var _tests:Array;
9 |
10 | [Before]
11 | public function setup():void
12 | {
13 | _tests = [
14 | {args:[-1, "person"], expected:"-1 people"},
15 | {args:[0, "person"], expected:"0 people"},
16 | {args:[1, "person"], expected:"1 person"},
17 | {args:[2, "person"], expected:"2 people"},
18 | {args:[1, "cake", "desserts"], expected:"1 cake"},
19 | {args:[2, "cake", "desserts"], expected:"2 desserts"}
20 | ];
21 | }
22 |
23 | [Test]
24 | public function testPluralize():void
25 | {
26 | for each (var test:Object in _tests) {
27 | assertThat("test failed for args '" + test.args + "'", pluralizeByCount.apply(null, test.args), equalTo(test.expected));
28 | }
29 | }
30 | }
31 | }
--------------------------------------------------------------------------------
/tests/mesh/model/validators/FormatValidatorTests.as:
--------------------------------------------------------------------------------
1 | package mesh.model.validators
2 | {
3 |
4 | import org.flexunit.assertThat;
5 | import org.hamcrest.object.equalTo;
6 |
7 | import mesh.core.object.inspect;
8 |
9 | public class FormatValidatorTests
10 | {
11 | [Test]
12 | public function testValidate():void
13 | {
14 | var tests:Array = [
15 | {
16 | object:{str:"Hello123", errors:new Errors(null)},
17 | options:{property:"str", format:/\A[a-zA-Z]+\z/},
18 | passes:false
19 | },
20 | {
21 | object:{str:"Hello", errors:new Errors(null)},
22 | options:{property:"str", format:/\A[a-zA-Z]+\z/},
23 | passes:true
24 | }
25 | ];
26 |
27 | for each (var test:Object in tests) {
28 | new FormatValidator(test.options).validate(test.object);
29 | assertThat("validation failed for test " + inspect(test.options), test.object.errors.length == 0, equalTo(test.passes));
30 | }
31 | }
32 | }
33 | }
--------------------------------------------------------------------------------
/src/mesh/model/validators/InclusionValidator.as:
--------------------------------------------------------------------------------
1 | package mesh.model.validators
2 | {
3 | import collections.HashSet;
4 |
5 | /**
6 | * A helper that validates that a property's value belongs to a given set of values. This
7 | * set can be an array or any enumerable object.
8 | *
9 | * @author Dan Schultz
10 | */
11 | public class InclusionValidator extends EachValidator
12 | {
13 | /**
14 | * @copy Validator#Validator()
15 | */
16 | public function InclusionValidator(options:Object)
17 | {
18 | if (!options.hasOwnProperty("message")) {
19 | options.message = "is not included in the list";
20 | }
21 |
22 | super(options);
23 | }
24 |
25 | /**
26 | * @inheritDoc
27 | */
28 | override protected function validateProperty(obj:Object, property:String, value:Object):void
29 | {
30 | if (!new HashSet(options.within).contains(value)) {
31 | obj.errors.add(property, options.message);
32 | }
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/src/mesh/core/reflection/Metadata.as:
--------------------------------------------------------------------------------
1 | package mesh.core.reflection
2 | {
3 | /**
4 | * A class that represents the metadata that has been defined on a class, variable, or
5 | * method.
6 | *
7 | * @author Dan Schultz
8 | */
9 | public class Metadata extends Definition
10 | {
11 | /**
12 | * @copy Definition#Definition()
13 | */
14 | public function Metadata(description:XML, belongsTo:Definition)
15 | {
16 | super(description, belongsTo);
17 | }
18 |
19 | private var _arguments:Object;
20 | /**
21 | * Returns an arguments hash that represents the key-value pairs that are
22 | * defined for this metadata.
23 | */
24 | public function get arguments():Object
25 | {
26 | if (_arguments == null) {
27 | _arguments = {};
28 |
29 | for each (var argXML:XML in description..arg) {
30 | _arguments[argXML.@key.toString()] = argXML.@value.toString();
31 | }
32 | }
33 | return _arguments;
34 | }
35 | }
36 | }
--------------------------------------------------------------------------------
/tests/mesh/model/AggregateTests.as:
--------------------------------------------------------------------------------
1 | package mesh.model
2 | {
3 | import mesh.Name;
4 |
5 | import org.flexunit.assertThat;
6 | import org.hamcrest.object.equalTo;
7 |
8 | public class AggregateTests
9 | {
10 | private var _record:AggregateTestMockRecord;
11 |
12 | [Before]
13 | public function setup():void
14 | {
15 | _record = new AggregateTestMockRecord();
16 | }
17 |
18 | [Test]
19 | public function testPropertyChangeUpdatesAggregate():void
20 | {
21 | _record.firstName = "Thom";
22 | _record.last = "Yorke";
23 | assertThat(_record.name.first, equalTo(_record.firstName));
24 | assertThat(_record.name.last, equalTo(_record.last));
25 | }
26 |
27 | [Test]
28 | public function testAggregateChangeUpdatesProperties():void
29 | {
30 | _record.name = new Name("Thom", "Yorke");
31 | assertThat(_record.firstName, equalTo(_record.name.first));
32 | assertThat(_record.last, equalTo(_record.name.last));
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/mesh/model/ILoadable.as:
--------------------------------------------------------------------------------
1 | package mesh.model
2 | {
3 | /**
4 | * The ILoadable interface defines an interface for classes where their
5 | * data is loaded externally.
6 | *
7 | * @author Dan Schultz
8 | */
9 | public interface ILoadable
10 | {
11 | /**
12 | * Loads the data for this object, if it has not been loaded yet. If the data has
13 | * already been loaded, then it will not be reloaded. Use refresh()
14 | * to reload the data.
15 | *
16 | * @return This instance.
17 | */
18 | function load():*;
19 |
20 | /**
21 | * Forces a reload of the data for this object. Unlike load(), this
22 | * method will load the data for this object even if it's already been retrieved.
23 | *
24 | * @see #load()
25 | * @return This instance.
26 | */
27 | function refresh():*;
28 |
29 | /**
30 | * Checks if the data has been loaded.
31 | */
32 | function get isLoaded():Boolean;
33 | }
34 | }
--------------------------------------------------------------------------------
/src/mesh/operations/ProgressOperationEvent.as:
--------------------------------------------------------------------------------
1 | package mesh.operations
2 | {
3 | import flash.events.ProgressEvent;
4 |
5 | /**
6 | * An event that is dispatched by an operation to indicate its progress.
7 | *
8 | * @author Dan Schultz
9 | */
10 | public class ProgressOperationEvent extends OperationEvent
11 | {
12 | /**
13 | * An event type for when an operation has progressed.
14 | */
15 | public static const PROGRESS:String = "progress";
16 |
17 | /**
18 | * @copy OperationEvent#OperationEvent()
19 | */
20 | public function ProgressOperationEvent(type:String)
21 | {
22 | super(type);
23 | }
24 |
25 | /**
26 | * @copy Operation#unitsComplete
27 | */
28 | public function get unitsComplete():Number
29 | {
30 | return operation.progress.complete;
31 | }
32 |
33 | /**
34 | * @copy Operation#unitsTotal
35 | */
36 | public function get unitsTotal():Number
37 | {
38 | return operation.progress.total;
39 | }
40 | }
41 | }
--------------------------------------------------------------------------------
/src/mesh/core/range/CharRange.as:
--------------------------------------------------------------------------------
1 | package mesh.core.range
2 | {
3 | /**
4 | * A range for single character strings.
5 | *
6 | * @see Range#from()
7 | * @author Dan Schultz
8 | */
9 | public class CharRange extends Range
10 | {
11 | /**
12 | * @copy Range#Range()
13 | */
14 | public function CharRange(from:*, to:*, exclusive:Boolean=false)
15 | {
16 | super(from, to, exclusive);
17 | }
18 |
19 | /**
20 | * @inheritDoc
21 | */
22 | override protected function decrease(value:*, size:int):*
23 | {
24 | return String.fromCharCode(value.charCodeAt(0)-size);
25 | }
26 |
27 | /**
28 | * @inheritDoc
29 | */
30 | override protected function increase(value:*, size:int):*
31 | {
32 | return String.fromCharCode(value.charCodeAt(0)+size);
33 | }
34 |
35 | /**
36 | * @inheritDoc
37 | */
38 | override public function get length():int
39 | {
40 | return max.charCodeAt(0) - min.charCodeAt(0) + (!isExclusive ? 1 : 0);
41 | }
42 | }
43 | }
--------------------------------------------------------------------------------
/src/mesh/model/source/IPersistenceResponder.as:
--------------------------------------------------------------------------------
1 | package mesh.model.source
2 | {
3 | import mesh.model.Record;
4 |
5 | /**
6 | * The IPersistenceResponder interface is an interface that classes can
7 | * implement to handle persistence callbacks from a data source.
8 | *
9 | * @author Dan Schultz
10 | */
11 | public interface IPersistenceResponder
12 | {
13 | /**
14 | * The data source calls this method when it has successfully persisted a record.
15 | *
16 | * @param snapshot The snapshot that was persisted.
17 | * @param id An ID to set on the record.
18 | */
19 | function saved(snapshot:Snapshot, id:Object = null):void;
20 |
21 | /**
22 | * The data source calls this method when data retrieval has failed.
23 | *
24 | * @param summary The summary of the error.
25 | * @param detail A detailed message of the error.
26 | * @param code An error code.
27 | */
28 | function failed(summary:String, detail:String = "", code:String = ""):void;
29 | }
30 | }
--------------------------------------------------------------------------------
/src/mesh/view/helpers/number/withDelimiter.as:
--------------------------------------------------------------------------------
1 | package mesh.view.helpers.number
2 | {
3 | import mesh.core.object.merge;
4 |
5 | /**
6 | * Formats a number with a delimiter (123,000) and a separator (123,000.1).
7 | *
8 | * 9 | * Options: 10 | *
delimiter (default=",") - The thousands delimiter.separator (default=".") - The separator between the integer
13 | * and fractions.IRetrievalResponder interface is an interface that classes can
7 | * implement to handle retrieval callbacks from a data source.
8 | *
9 | * @author Dan Schultz
10 | */
11 | public interface IRetrievalResponder
12 | {
13 | /**
14 | * The data source calls this method when a record's data has been loaded.
15 | *
16 | * @param data The loaded data.
17 | */
18 | function loaded(data:Data):void;
19 |
20 | /**
21 | * The data source calls this method when it has finished retrieving the data for
22 | * the request.
23 | */
24 | function finished():void;
25 |
26 | /**
27 | * The data source calls this method when data retrieval has failed.
28 | *
29 | * @param summary The summary of the error.
30 | * @param detail A detailed message of the error.
31 | * @param code An error code.
32 | */
33 | function failed(summary:String, detail:String = "", code:String = ""):void;
34 | }
35 | }
--------------------------------------------------------------------------------
/tests/mesh/core/string/CapitalizeTests.as:
--------------------------------------------------------------------------------
1 | package mesh.core.string
2 | {
3 | import org.flexunit.assertThat;
4 | import org.hamcrest.object.equalTo;
5 |
6 | public class CapitalizeTests
7 | {
8 | private var _tests:Array = [];
9 |
10 | [Before]
11 | public function setup():void
12 | {
13 | _tests.push({input:"the quick brown fox", expected:"The Quick Brown Fox"});
14 | _tests.push({input:"the quIck broWn foX", expected:"The QuIck BroWn FoX"});
15 | _tests.push({input:"the_quick_brown_fox", expected:"The_quick_brown_fox"});
16 | _tests.push({input:"the_ quick_ brown_ fox", expected:"The_ Quick_ Brown_ Fox"});
17 | _tests.push({input:"the _ quick _ brown _ fox", expected:"The _ Quick _ Brown _ Fox"});
18 | _tests.push({input:"the quick brown fox", expected:"The Quick Brown Fox"});
19 | }
20 |
21 | [Test]
22 | public function testCapitalize():void
23 | {
24 | for each (var test:Object in _tests) {
25 | assertThat("capitalize failed for '" + test.input + "'", capitalize(test.input), equalTo(test.expected));
26 | }
27 | }
28 | }
29 | }
--------------------------------------------------------------------------------
/tests/mesh/model/ValidationTests.as:
--------------------------------------------------------------------------------
1 | package mesh.model
2 | {
3 | import mesh.Name;
4 | import mesh.Person;
5 |
6 | import org.flexunit.assertThat;
7 | import org.hamcrest.collection.arrayWithSize;
8 | import org.hamcrest.collection.emptyArray;
9 | import org.hamcrest.object.equalTo;
10 |
11 | public class ValidationTests
12 | {
13 | [Test]
14 | public function testValidatePasses():void
15 | {
16 | var customer:Person = new Person();
17 | customer.name = new Name("John", "Doe");
18 | customer.age = 10;
19 |
20 | assertThat(customer.isValid(), equalTo(true));
21 | assertThat(customer.isInvalid(), equalTo(false));
22 | assertThat(customer.errors.toArray(), emptyArray());
23 | }
24 |
25 | [Test]
26 | public function testValidateFails():void
27 | {
28 | var customer:Person = new Person();
29 | customer.name = null;
30 | customer.age = 0;
31 |
32 | assertThat(customer.isValid(), equalTo(false));
33 | assertThat(customer.isInvalid(), equalTo(true));
34 | assertThat(customer.errors.toArray(), arrayWithSize(2));
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/tests/mesh/core/inflection/SingularizeTests.as:
--------------------------------------------------------------------------------
1 | package mesh.core.inflection
2 | {
3 | import org.flexunit.assertThat;
4 | import org.hamcrest.object.equalTo;
5 |
6 | public class SingularizeTests
7 | {
8 | private var _tests:Array;
9 |
10 | [Before]
11 | public function setup():void
12 | {
13 | _tests = [
14 | // regulars
15 | {word:"dresses", expected:"dress"},
16 | {word:"horns", expected:"horn"},
17 | {word:"people", expected:"person"},
18 | {word:"oranges", expected:"orange"},
19 |
20 | // irregulars
21 | {word:"mice", expected:"mouse"},
22 |
23 | // uncountables
24 | {word:"moose", expected:"moose"},
25 | {word:"money", expected:"money"},
26 | {word:"deer", expected:"deer"},
27 |
28 | // already singular
29 | {word:"address", expected:"address"}
30 | ];
31 | }
32 |
33 | [Test]
34 | public function testSingularize():void
35 | {
36 | for each (var test:Object in _tests) {
37 | assertThat("test failed for " + test.word, singularize(test.word), equalTo(test.expected));
38 | }
39 | }
40 | }
41 | }
--------------------------------------------------------------------------------
/tests/mesh/view/helpers/number/WithDelimiterTests.as:
--------------------------------------------------------------------------------
1 | package mesh.view.helpers.number
2 | {
3 | import mesh.core.object.inspect;
4 |
5 | import org.flexunit.assertThat;
6 | import org.hamcrest.object.equalTo;
7 |
8 | public class WithDelimiterTests
9 | {
10 | private var _tests:Array;
11 |
12 | [Before]
13 | public function setup():void
14 | {
15 | _tests = [
16 | {number:12345678, options:null, expected:"12,345,678"},
17 | {number:12345678.05, options:null, expected:"12,345,678.05"},
18 | {number:12345678, options:{delimiter:"."}, expected:"12.345.678"},
19 | {number:12345678, options:{separator:","}, expected:"12,345,678"},
20 | {number:12345678.05, options:{delimiter:" ", separator:","}, expected:"12 345 678,05"}
21 | ];
22 | }
23 |
24 | [Test]
25 | public function testNumberWithDelimiter():void
26 | {
27 | for each (var test:Object in _tests) {
28 | assertThat("test failed for number," + test.number + ", with options: " + inspect(test.options), withDelimiter(test.number, test.options), equalTo(test.expected));
29 | }
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/src/mesh/core/range/DateRange.as:
--------------------------------------------------------------------------------
1 | package mesh.core.range
2 | {
3 | /**
4 | * A range for dates.
5 | *
6 | * @see Range#from()
7 | * @author Dan Schultz
8 | */
9 | public class DateRange extends Range
10 | {
11 | /**
12 | * @copy Range#Range()
13 | */
14 | public function DateRange(from:*, to:*, exclusive:Boolean=false)
15 | {
16 | super(from, to, exclusive);
17 | }
18 |
19 | /**
20 | * @inheritDoc
21 | */
22 | override protected function decrease(value:*, size:int):*
23 | {
24 | var newValue:Date = new Date(value.getTime());
25 | newValue.setDate(newValue.date - size);
26 | return newValue;
27 | }
28 |
29 | /**
30 | * @inheritDoc
31 | */
32 | override protected function increase(value:*, size:int):*
33 | {
34 | var newValue:Date = new Date(value.getTime());
35 | newValue.setDate(newValue.date + size);
36 | return newValue;
37 | }
38 |
39 | /**
40 | * @inheritDoc
41 | */
42 | override public function get length():int
43 | {
44 | return ((max.time - min.time) / 86400000) + (!isExclusive ? 1 : 0);
45 | }
46 | }
47 | }
--------------------------------------------------------------------------------
/tests/mesh/core/inflection/PluralizeTests.as:
--------------------------------------------------------------------------------
1 | package mesh.core.inflection
2 | {
3 | import org.flexunit.assertThat;
4 | import org.hamcrest.object.equalTo;
5 |
6 | public class PluralizeTests
7 | {
8 | private var _tests:Array;
9 |
10 | [Before]
11 | public function setup():void
12 | {
13 | _tests = [
14 | // regulars
15 | {word:"dress", expected:"dresses"},
16 | {word:"horn", expected:"horns"},
17 | {word:"person", expected:"people"},
18 | {word:"orange", expected:"oranges"},
19 |
20 | // irregulars
21 | {word:"mouse", expected:"mice"},
22 |
23 | // uncountables
24 | {word:"moose", expected:"moose"},
25 | {word:"money", expected:"money"},
26 | {word:"deer", expected:"deer"},
27 |
28 | // already plural
29 | {word:"cats", expected:"cats"},
30 | {word:"addresses", expected:"addresses"}
31 | ];
32 | }
33 |
34 | [Test]
35 | public function testPluralize():void
36 | {
37 | for each (var test:Object in _tests) {
38 | assertThat("test failed for " + test.word, pluralize(test.word), equalTo(test.expected));
39 | }
40 | }
41 | }
42 | }
--------------------------------------------------------------------------------
/src/mesh/operations/ResultOperationEvent.as:
--------------------------------------------------------------------------------
1 | package mesh.operations
2 | {
3 | import flash.events.Event;
4 |
5 | /**
6 | * An event dispatched by an operation to indicate that a result was received
7 | * during its execution.
8 | *
9 | * @author Dan Schultz
10 | */
11 | public class ResultOperationEvent extends OperationEvent
12 | {
13 | /**
14 | * An event type for when an operation has received a result during execution.
15 | */
16 | public static const RESULT:String = "result";
17 |
18 | /**
19 | * Constructor.
20 | *
21 | * @param data The parsed result's data.
22 | */
23 | public function ResultOperationEvent(data:Object)
24 | {
25 | super(RESULT);
26 | _data = data;
27 | }
28 |
29 | /**
30 | * @private
31 | */
32 | override public function clone():Event
33 | {
34 | return new ResultOperationEvent(data);
35 | }
36 |
37 | private var _data:*;
38 | /**
39 | * The parsed result's data.
40 | */
41 | public function get data():*
42 | {
43 | return _data;
44 | }
45 | public function set data(value:*):void
46 | {
47 | _data = value;
48 | }
49 | }
50 | }
--------------------------------------------------------------------------------
/tests/mesh/model/RecordDirtyTests.as:
--------------------------------------------------------------------------------
1 | package mesh.model
2 | {
3 | import mesh.Name;
4 | import mesh.Person;
5 | import mesh.model.source.FixtureDataSource;
6 | import mesh.model.store.Store;
7 |
8 | import org.flexunit.assertThat;
9 | import org.hamcrest.object.equalTo;
10 |
11 | public class RecordDirtyTests
12 | {
13 | private var _data:Object;
14 | private var _fixtures:FixtureDataSource;
15 | private var _store:Store;
16 |
17 | [Before]
18 | public function setup():void
19 | {
20 | _data = {id:1, firstName:"Jimmy", lastName:"Page"};
21 | _fixtures = new FixtureDataSource(Person);
22 | _fixtures.add(_data);
23 | _store = new Store(_fixtures);
24 | }
25 |
26 | [Test]
27 | /**
28 | * Make sure that changing a property of a record marks it as dirty.
29 | */
30 | public function testPropertyChange():void
31 | {
32 | var person:Person = _store.query(Person).find(1).load();
33 | person.name = new Name("Steve", "Jobs");
34 |
35 | assertThat(person.changes.hasChanges, equalTo(true));
36 | assertThat(person.state.isRemote, equalTo(true));
37 | assertThat(person.state.isSynced, equalTo(false));
38 | }
39 | }
40 | }
--------------------------------------------------------------------------------
/src/mesh/core/reflection/Method.as:
--------------------------------------------------------------------------------
1 | package mesh.core.reflection
2 | {
3 | import flash.utils.getDefinitionByName;
4 |
5 | /**
6 | * A class that represents the functions that has been defined on a class, variable, or
7 | * method.
8 | *
9 | * @author Dan Schultz
10 | */
11 | public class Method extends Definition
12 | {
13 | /**
14 | * @copy Definition#Definition()
15 | */
16 | public function Method(description:XML, belongsTo:Definition)
17 | {
18 | super(description, belongsTo);
19 | }
20 |
21 | /**
22 | * true if this is a method that has been defined with
23 | * static.
24 | */
25 | public function get isStatic():Boolean
26 | {
27 | return description.parent().name() == "type";
28 | }
29 |
30 | /**
31 | * The definition that represents the type that is returned from a function call to
32 | * this method. If no return type is defined, this method returns null.
33 | */
34 | public function get returnType():Type
35 | {
36 | return description.@returnType.toString() == "void" ? null : Type.reflect(getDefinitionByName(description.@returnType.toString()) as Class);
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/src/mesh/core/functions/closure.as:
--------------------------------------------------------------------------------
1 | package mesh.core.functions
2 | {
3 | /**
4 | * Generates a function that can reference the variables defined within the scope
5 | * of the defined function, or the outer function that created it. For instance:
6 | *
7 | * OperationQueue.
7 | *
8 | * @author Dan Schultz
9 | */
10 | public class QueueProgress extends Progress
11 | {
12 | private var _queue:OperationQueue;
13 |
14 | /**
15 | * Constructor.
16 | *
17 | * @param queue The queue that owns this progress.
18 | */
19 | public function QueueProgress(queue:OperationQueue)
20 | {
21 | _queue = queue;
22 | }
23 |
24 | /**
25 | * @inheritDoc
26 | */
27 | override public function get complete():Number
28 | {
29 | var temp:Number = 0;
30 | for each (var executing:Operation in _queue.executing) {
31 | temp += executing.progress.complete;
32 | }
33 | return confirmed + temp;
34 | }
35 |
36 | private var _confirmed:Number = 0;
37 | /**
38 | * The number of units that have been confirmed to be completed.
39 | */
40 | public function get confirmed():Number
41 | {
42 | return _confirmed;
43 | }
44 | public function set confirmed(value:Number):void
45 | {
46 | _confirmed = value;
47 | }
48 |
49 | }
50 | }
--------------------------------------------------------------------------------
/src/mesh/operations/SequentialOperation.as:
--------------------------------------------------------------------------------
1 | package mesh.operations
2 | {
3 | /**
4 | * A type of compound operation where each operation is executed in a sequence,
5 | * and only one operation is permitted to execute at a time.
6 | *
7 | * @author Dan Schultz
8 | */
9 | public class SequentialOperation extends CompoundOperation
10 | {
11 | /**
12 | * @copy operations.CompoundOperation#CompoundOperation()
13 | */
14 | public function SequentialOperation(operations:Array = null)
15 | {
16 | super(operations);
17 | }
18 |
19 | /**
20 | * @inheritDoc
21 | */
22 | override protected function startExecution():void
23 | {
24 | executeOperation(nextOperation(finishedOperationsCount));
25 | }
26 |
27 | /**
28 | * @inheritDoc
29 | */
30 | override protected function nextOperation(finishedOperationsCount:int):Operation
31 | {
32 | return operationSet.toArray()[finishedOperationsCount];
33 | }
34 |
35 | /**
36 | * Adds the given operation to this sequence.
37 | *
38 | * @inheritDoc
39 | */
40 | override public function then(operation:Operation):Operation
41 | {
42 | add(operation);
43 | return this;
44 | }
45 | }
46 | }
--------------------------------------------------------------------------------
/src/mesh/core/object/inspect.as:
--------------------------------------------------------------------------------
1 | package mesh.core.object
2 | {
3 | import mesh.core.functions.closure;
4 |
5 | import mx.utils.ObjectUtil;
6 | import mesh.core.reflection.Property;
7 | import mesh.core.reflection.Type;
8 | import mesh.core.reflection.reflect;
9 |
10 | /**
11 | * Returns a string that contains the name of the given object's class and the values for each
12 | * of its fixed and dynamic properties.
13 | *
14 | * @param obj The object to inspect.
15 | * @return A string.
16 | */
17 | public function inspect(obj:Object):String
18 | {
19 | if (obj == null) {
20 | return "null";
21 | }
22 |
23 | if (ObjectUtil.isSimple(obj)) {
24 | return ObjectUtil.toString(obj);
25 | }
26 |
27 | var type:Type = reflect(obj);
28 | var properties:Array = type.properties.map(closure(function(property:Property):String
29 | {
30 | return property.name;
31 | }));
32 |
33 | for (var key:String in obj) {
34 | properties.push(key);
35 | }
36 |
37 | var result:String = "#<" + type.name;
38 | for each (var property:String in properties.sort()) {
39 | result += ", " + property + ": " + inspect(obj[property]);
40 | }
41 |
42 | return result + ">";
43 | }
44 | }
--------------------------------------------------------------------------------
/src/mesh/core/state/TransitionEvent.as:
--------------------------------------------------------------------------------
1 | package mesh.core.state
2 | {
3 | import flash.events.Event;
4 |
5 | /**
6 | * An event that is dispatched from a transition.
7 | *
8 | * @author Dan Schultz
9 | */
10 | public class TransitionEvent extends Event
11 | {
12 | /**
13 | * Dispatched by a transition when it has been triggered.
14 | */
15 | public static const TRANSITIONED:String = "transitioned";
16 |
17 | /**
18 | * Constructor.
19 | *
20 | * @param type The event type.
21 | * @param transition The transition that triggered the event.
22 | */
23 | public function TransitionEvent(type:String, transition:Transition, bubbles:Boolean=false, cancelable:Boolean=false)
24 | {
25 | super(type, bubbles, cancelable);
26 | _transition = transition;
27 | }
28 |
29 | /**
30 | * @inheritDoc
31 | */
32 | override public function clone():Event
33 | {
34 | return new TransitionEvent(type, transition, bubbles, cancelable);
35 | }
36 |
37 | private var _transition:Transition;
38 | /**
39 | * The transition that triggered the event.
40 | */
41 | public function get transition():Transition
42 | {
43 | return _transition;
44 | }
45 | }
46 | }
--------------------------------------------------------------------------------
/src/mesh/operations/FinishedOperationEvent.as:
--------------------------------------------------------------------------------
1 | package mesh.operations
2 | {
3 | import flash.events.Event;
4 |
5 | /**
6 | * An event that is dispatched by an operation to indicate that its execution
7 | * has finished, either successfully or unsuccessfully.
8 | *
9 | * @author Dan Schultz
10 | */
11 | public class FinishedOperationEvent extends OperationEvent
12 | {
13 | /**
14 | * An event type for when an operation has finished executing.
15 | */
16 | public static const FINISHED:String = "finished";
17 |
18 | /**
19 | * Constructor.
20 | *
21 | * @param successful Indicates whether the operation finished successfully.
22 | */
23 | public function FinishedOperationEvent(successful:Boolean)
24 | {
25 | super(FINISHED);
26 | _successful = successful;
27 | }
28 |
29 | /**
30 | * @private
31 | */
32 | override public function clone():Event
33 | {
34 | return new FinishedOperationEvent(successful);
35 | }
36 |
37 | private var _successful:Boolean;
38 | /**
39 | * true if the operation execution finished successfully.
40 | */
41 | public function get successful():Boolean
42 | {
43 | return _successful;
44 | }
45 | }
46 | }
--------------------------------------------------------------------------------
/src/mesh/core/array/intersection.as:
--------------------------------------------------------------------------------
1 | package mesh.core.array
2 | {
3 | /**
4 | * Returns the intersection of the elements of each array. The intersection is where
5 | * the result is an array that contains the elements that are common amoung each input
6 | * array.
7 | *
8 | * @param args The arrays to intersect with.
9 | * @return The intersecting elements.
10 | */
11 | public function intersection(...args):Array
12 | {
13 | if (args.length == 0) {
14 | return [];
15 | }
16 |
17 | if (args.length == 1) {
18 | return args[0];
19 | }
20 |
21 | var result:Array = [];
22 | var lcd:Array;
23 | if (args.length == 2) {
24 | // Use the array with the least number of elements as the least common denominator.
25 | lcd = args.splice(args[0].length < args[1].length ? 0 : 1, 1).shift();
26 | var against:Array = args.shift();
27 | for each (var element:* in lcd) {
28 | if (against.indexOf(element) != -1) {
29 | result.push(element);
30 | }
31 | }
32 | return result;
33 | }
34 |
35 | lcd = args.sortOn("length", Array.NUMERIC).shift();
36 | while (args.length > 0) {
37 | result = result.concat(intersection(lcd, args.shift()));
38 | }
39 | return result;
40 | }
41 | }
--------------------------------------------------------------------------------
/src/mesh/core/number/random.as:
--------------------------------------------------------------------------------
1 | package mesh.core.number
2 | {
3 | /**
4 | * Generates a pseudo-random number. If max and min are zero, the
5 | * result is a number where 0 <= n < 1. If max is non-zero, then the result is
6 | * a number where min <= n <= max. If max is less than 0, it is first absoluted
7 | * then treated as a non-zero number.
8 | *
9 | *
10 | * This method attempts to generate a well-balanced random number. This is done by first adding
11 | * 0.5 to the max, and subtracting 0.5 from the min. The altered max
12 | * and min is then multiplied against Math.random(), and the result rounded to the
13 | * nearest integer.
14 | *
singular word based on its count. The word
7 | * is pluralized if and only if count < 1 < count. If plural
8 | * is given, then that word will be used. Otherwise an inflection will be used.
9 | *
10 | * 11 | * Examples: 12 | *
count is not 1.
23 | * @see mesh.core.inflection.pluralize()
24 | */
25 | public function pluralizeByCount(count:Number, singular:String, plural:String = null):String
26 | {
27 | return count.toString() + " " + (count == 1 ? singular : (plural != null) ? plural : pluralize(singular));
28 | }
29 | }
--------------------------------------------------------------------------------
/src/mesh/core/object/isEmpty.as:
--------------------------------------------------------------------------------
1 | package mesh.core.object
2 | {
3 | import mx.utils.StringUtil;
4 |
5 | /**
6 | * Checks if the given object is empty. If the object is a string, it checks if the
7 | * string is empty or only contains whitespace. If the object is a number, it checks if
8 | * the number is NaN. If the object contains either a length or
9 | * isEmpty property or method, the result will be evaluated.
10 | *
11 | * @param obj The object to check.
12 | * @return true if the object is empty.
13 | */
14 | public function isEmpty(obj:Object):Boolean
15 | {
16 | if (obj == null) {
17 | return true;
18 | }
19 |
20 | if (obj is String) {
21 | return StringUtil.trim(obj as String).length == 0;
22 | }
23 |
24 | if (obj is Number) {
25 | return isNaN(obj as Number);
26 | }
27 |
28 | if (obj.hasOwnProperty("isEmpty")) {
29 | if (obj.isEmpty is Function) {
30 | return obj.isEmpty();
31 | }
32 | return obj.isEmpty;
33 | }
34 |
35 | if (obj.hasOwnProperty("length")) {
36 | if (obj.length is Function) {
37 | return obj.length() == 0;
38 | }
39 | return obj.length == 0;
40 | }
41 |
42 | return false;
43 | }
44 | }
--------------------------------------------------------------------------------
/tests/mesh/model/store/DataTests.as:
--------------------------------------------------------------------------------
1 | package mesh.model.store
2 | {
3 | import mesh.Person;
4 |
5 | import org.flexunit.assertThat;
6 | import org.hamcrest.object.equalTo;
7 | import org.hamcrest.object.notNullValue;
8 |
9 | [RunWith("org.flexunit.runners.Parameterized")]
10 | public class DataTests
11 | {
12 | public static var READ_DATA:Array = [
13 | [{name:"Jimmy Page"}, "name"]
14 | ];
15 |
16 | public static var SET_DATA:Array = [
17 | [{}, "name", "Jimmy Page"]
18 | ];
19 |
20 | [Test]
21 | public function testIdFieldOption():void
22 | {
23 | var ID:int = 1;
24 | var data:Data = new Data(Person, {personId:ID}, {idField:"personId"});
25 | assertThat(data.id, equalTo(ID));
26 | }
27 |
28 | [Test(dataProvider="READ_DATA")]
29 | public function testReadProperty(obj:Object, property:String):void
30 | {
31 | var data:Data = new Data(Person, obj);
32 | assertThat(data[property], notNullValue());
33 | }
34 |
35 | [Test(dataProvider="SET_DATA")]
36 | public function testSetProperty(obj:Object, property:String, value:Object):void
37 | {
38 | var data:Data = new Data(Person, obj);
39 | data[property] = value;
40 | assertThat(data[property], equalTo(value));
41 | }
42 | }
43 | }
--------------------------------------------------------------------------------
/tests/mesh/core/inflection/CamelizeTests.as:
--------------------------------------------------------------------------------
1 | package mesh.core.inflection
2 | {
3 | import org.flexunit.assertThat;
4 | import org.hamcrest.object.equalTo;
5 |
6 | public class CamelizeTests
7 | {
8 | private var _tests:Array = [];
9 |
10 | [Before]
11 | public function setup():void
12 | {
13 | _tests.push({input:["the quick brown fox", false], expected:"theQuickBrownFox"});
14 | _tests.push({input:["the quick brown fox", true], expected:"TheQuickBrownFox"});
15 | _tests.push({input:["the quIck broWn foX", true], expected:"TheQuIckBroWnFoX"});
16 | _tests.push({input:["The quIck broWn foX", false], expected:"theQuIckBroWnFoX"});
17 | _tests.push({input:["the_quick_brown_fox", true], expected:"TheQuickBrownFox"});
18 | _tests.push({input:["the_ quick_ brown_ fox", true], expected:"TheQuickBrownFox"});
19 | _tests.push({input:["the _ quick _ brown _ fox", true], expected:"TheQuickBrownFox"});
20 | _tests.push({input:["the quick brown fox", true], expected:"TheQuickBrownFox"});
21 | }
22 |
23 | [Test]
24 | public function testCamelize():void
25 | {
26 | for each (var test:Object in _tests) {
27 | assertThat("camelize failed for '" + test.input[0] + "'", camelize.apply(null, test.input), equalTo(test.expected));
28 | }
29 | }
30 | }
31 | }
--------------------------------------------------------------------------------
/src/mesh/core/state/StateEvent.as:
--------------------------------------------------------------------------------
1 | package mesh.core.state
2 | {
3 | import flash.events.Event;
4 |
5 | /**
6 | * An event dispatched by a State when the state machine either enters or
7 | * exits a state.
8 | *
9 | * @author Dan Schultz
10 | */
11 | public class StateEvent extends Event
12 | {
13 | /**
14 | * Dispatched by a state when the machine enters this state.
15 | */
16 | public static const ENTER:String = "enter";
17 |
18 | /**
19 | * Dispatched by a state whent he machine exits this state.
20 | */
21 | public static const EXIT:String = "exit";
22 |
23 | /**
24 | * Constructor.
25 | *
26 | * @param type The event type.
27 | * @param state The event's state.
28 | */
29 | public function StateEvent(type:String, state:State, bubbles:Boolean = false, cancelable:Boolean = false)
30 | {
31 | super(type, bubbles, cancelable);
32 | _state = state;
33 | }
34 |
35 | /**
36 | * @inheritDoc
37 | */
38 | override public function clone():Event
39 | {
40 | return new StateEvent(type, state, bubbles, cancelable);
41 | }
42 |
43 | private var _state:State;
44 | /**
45 | * The state for this event.
46 | */
47 | public function get state():State
48 | {
49 | return _state;
50 | }
51 | }
52 | }
--------------------------------------------------------------------------------
/src/mesh/operations/OperationQueueEvent.as:
--------------------------------------------------------------------------------
1 | package mesh.operations
2 | {
3 | import flash.events.Event;
4 |
5 | /**
6 | * An event that is dispatched by the OperationQueue.
7 | *
8 | * @author Dan Schultz
9 | */
10 | public class OperationQueueEvent extends Event
11 | {
12 | /**
13 | * An event type that is dispatched when the queue has started.
14 | */
15 | public static const STARTED:String = "started";
16 |
17 | /**
18 | * An event type that is dispatched when there's any progress within the queue.
19 | */
20 | public static const PROGRESS:String = "progress";
21 |
22 | /**
23 | * An event type that is dispatched when the queue has been paused.
24 | */
25 | public static const PAUSED:String = "paused";
26 |
27 | /**
28 | * An event type that is dispatched when there are no elements left in the queue.
29 | */
30 | public static const IDLE:String = "idle";
31 |
32 | /**
33 | * Constructor.
34 | *
35 | * @param type The event type.
36 | */
37 | public function OperationQueueEvent(type:String)
38 | {
39 | super(type);
40 | }
41 |
42 | /**
43 | * The queue that dispatched this event.
44 | */
45 | public function get queue():OperationQueue
46 | {
47 | return OperationQueue( target );
48 | }
49 | }
50 | }
--------------------------------------------------------------------------------
/tests/mesh/core/inflection/UnderscoreTests.as:
--------------------------------------------------------------------------------
1 | package mesh.core.inflection
2 | {
3 | import org.flexunit.assertThat;
4 | import org.hamcrest.object.equalTo;
5 |
6 | public class UnderscoreTests
7 | {
8 | private var _tests:Array = [];
9 |
10 | [Before]
11 | public function setup():void
12 | {
13 | _tests.push({input:"TheQuickBrownFox", expected:"the_quick_brown_fox"});
14 | _tests.push({input:"THEQuickBrownFox", expected:"the_quick_brown_fox"});
15 | _tests.push({input:"TheQUICKBrownFox", expected:"the_quick_brown_fox"});
16 | _tests.push({input:"TheQUICKBrownFOX", expected:"the_quick_brown_fox"});
17 | _tests.push({input:"The Quick Brown Fox", expected:"the_quick_brown_fox"});
18 | _tests.push({input:"The quick brown fox", expected:"the_quick_brown_fox"});
19 | _tests.push({input:"the quick brown fox", expected:"the_quick_brown_fox"});
20 | _tests.push({input:"the quick brown fox", expected:"the_quick_brown_fox"});
21 | _tests.push({input:"the _ quick - brown-fox", expected:"the_quick_brown_fox"});
22 | }
23 |
24 | [Test]
25 | public function testUnderscore():void
26 | {
27 | for each (var test:Object in _tests) {
28 | assertThat("underscore failed for '" + test.input + "'", underscore.call(null, test.input), equalTo(test.expected));
29 | }
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/tests/mesh/core/number/RoundTests.as:
--------------------------------------------------------------------------------
1 | package mesh.core.number
2 | {
3 | import org.flexunit.assertThat;
4 | import org.hamcrest.object.equalTo;
5 |
6 | public class RoundTests
7 | {
8 | private var _tests:Array;
9 |
10 | [Before]
11 | public function setup():void
12 | {
13 | _tests = [
14 | {number:123.4567, precision:3, expected:123.457},
15 | {number:123.4567, precision:2, expected:123.46},
16 | {number:123.4445, precision:2, expected:123.44},
17 | {number:123.4567, precision:1, expected:123.5},
18 | {number:123.5, precision:0, expected:124},
19 | {number:123.4, precision:0, expected:123},
20 | {number:-123.4, precision:0, expected:-123},
21 | {number:-123.5, precision:0, expected:-123},
22 | {number:-123.6, precision:0, expected:-124},
23 | {number:-123.4567, precision:1, expected:-123.5},
24 | {number:-123.4567, precision:3, expected:-123.457},
25 | {number:-123.4567, precision:2, expected:-123.46},
26 | {number:-123.4445, precision:2, expected:-123.44},
27 | ];
28 | }
29 |
30 | [Test]
31 | public function testRound():void
32 | {
33 | for each (var test:Object in _tests) {
34 | assertThat("test failed for number," + test.number + ", with precision: " + test.precision, round(test.number, test.precision), equalTo(test.expected));
35 | }
36 | }
37 | }
38 | }
--------------------------------------------------------------------------------
/tests/mesh/model/InitializeAssociatedRecordsTests.as:
--------------------------------------------------------------------------------
1 | package mesh.model
2 | {
3 | import mesh.Customer;
4 | import mesh.Order;
5 | import mesh.model.source.FixtureDataSource;
6 | import mesh.model.source.MultiDataSource;
7 | import mesh.model.store.Store;
8 |
9 | import org.flexunit.assertThat;
10 | import org.hamcrest.object.notNullValue;
11 |
12 | public class InitializeAssociatedRecordsTests
13 | {
14 | private var _store:Store;
15 |
16 | [Before]
17 | public function setup():void
18 | {
19 | var customers:FixtureDataSource = new FixtureDataSource(Customer);
20 | customers.add({id:1, firstName:"Jimmy", lastName:"Page", accountId:1});
21 |
22 | var dataSource:MultiDataSource = new MultiDataSource();
23 | dataSource.map(Customer, customers);
24 | dataSource.map(Order, new FixtureDataSource(Order));
25 |
26 | _store = new Store(dataSource);
27 | }
28 |
29 | [Test]
30 | public function testInitializeHasOneAssociation():void
31 | {
32 | var customer:Customer = _store.query(Customer).find(1).load();
33 | assertThat(customer.account, notNullValue());
34 | }
35 |
36 | [Test]
37 | public function testInitializeHasManyAssociation():void
38 | {
39 | var customer:Customer = _store.query(Customer).find(1).load();
40 | assertThat(customer.orders, notNullValue());
41 | }
42 | }
43 | }
--------------------------------------------------------------------------------
/tests/mesh/model/validators/InclusionValidatorTests.as:
--------------------------------------------------------------------------------
1 | package mesh.model.validators
2 | {
3 |
4 | import mx.collections.ArrayCollection;
5 |
6 | import org.flexunit.assertThat;
7 | import org.hamcrest.object.equalTo;
8 |
9 | import mesh.core.object.inspect;
10 |
11 | public class InclusionValidatorTests
12 | {
13 | [Test]
14 | public function testValidate():void
15 | {
16 | var tests:Array = [
17 | {
18 | object:{str:"Hello", errors:new Errors(null)},
19 | options:{property:"str", within:["Hello", "Hi"]},
20 | passes:true
21 | },
22 | {
23 | object:{str:"Hello", errors:new Errors(null)},
24 | options:{property:"str", within:["Hi"]},
25 | passes:false
26 | },
27 | {
28 | object:{str:"Hello", errors:new Errors(null)},
29 | options:{property:"str", within:new ArrayCollection(["Hello", "Hi"])},
30 | passes:true
31 | },
32 | {
33 | object:{str:"Hello", errors:new Errors(null)},
34 | options:{property:"str", within:new ArrayCollection(["a", "b"])},
35 | passes:false
36 | }
37 | ];
38 |
39 | for each (var test:Object in tests) {
40 | new InclusionValidator(test.options).validate(test.object);
41 | assertThat("validation failed for test " + inspect(test.options), test.object.errors.length == 0, equalTo(test.passes));
42 | }
43 | }
44 | }
45 | }
--------------------------------------------------------------------------------
/tests/mesh/model/validators/ExclusionValidatorTests.as:
--------------------------------------------------------------------------------
1 | package mesh.model.validators
2 | {
3 |
4 | import mx.collections.ArrayCollection;
5 |
6 | import org.flexunit.assertThat;
7 | import org.hamcrest.object.equalTo;
8 |
9 | import mesh.core.object.inspect;
10 |
11 | public class ExclusionValidatorTests
12 | {
13 | [Test]
14 | public function testValidate():void
15 | {
16 | var tests:Array = [
17 | {
18 | object:{str:"Hello", errors:new Errors(null)},
19 | options:{property:"str", within:["Hello", "Hi"]},
20 | passes:false
21 | },
22 | {
23 | object:{str:"Hello", errors:new Errors(null)},
24 | options:{property:"str", within:["Hi"]},
25 | passes:true
26 | },
27 | {
28 | object:{str:"Hello", errors:new Errors(null)},
29 | options:{property:"str", within:new ArrayCollection(["Hello", "Hi"])},
30 | passes:false
31 | },
32 | {
33 | object:{str:"Hello", errors:new Errors(null)},
34 | options:{property:"str", within:new ArrayCollection(["a", "b", "c"])},
35 | passes:true
36 | }
37 | ];
38 |
39 | for each (var test:Object in tests) {
40 | new ExclusionValidator(test.options).validate(test.object);
41 | assertThat("validation failed for test " + inspect(test.options), test.object.errors.length == 0, equalTo(test.passes));
42 | }
43 | }
44 | }
45 | }
--------------------------------------------------------------------------------
/src/mesh/core/array/flatten.as:
--------------------------------------------------------------------------------
1 | package mesh.core.array
2 | {
3 | /**
4 | * Returns a new one-dimensional array that is a recursive flattening of elements.
5 | * Meaning, for each element in elements that is an array, insert its elements
6 | * into the new array.
7 | *
8 | * 9 | * Examples: 10 | *
obj1 and the
5 | * values from obj2. If the objects have conflicting keys, the resulting
6 | * object will contain the value from obj2. You can overwrite this
7 | * functionality by specifying a block function. This function must
8 | * have the following signature: function(key:String, old:Object, new:Object):Object.
9 | *
10 | * @param obj1 The original object.
11 | * @param obj2 The object to merge with obj1.
12 | * @param block A function to determine how to merge conflicting keys.
13 | * @return The merged object.
14 | */
15 | public function merge(obj1:Object, obj2:Object, block:Function = null):Object
16 | {
17 | obj1 = (obj1 == null ? {} : obj1);
18 | obj2 = (obj2 == null ? {} : obj2);
19 |
20 | if (block == null) {
21 | block = function(key:String, oldValue:Object, newValue:Object):Object
22 | {
23 | return newValue;
24 | };
25 | }
26 |
27 | var result:Object = {};
28 |
29 | for (var key1:String in obj1) {
30 | result[key1] = obj1[key1];
31 | }
32 |
33 | for (var key2:String in obj2) {
34 | result[key2] = !result.hasOwnProperty(key2) ? obj2[key2] : block(key2, result[key2], obj2[key2]);
35 | }
36 |
37 | return result;
38 | }
39 | }
--------------------------------------------------------------------------------
/src/mesh/core/array/ArrayProxy.as:
--------------------------------------------------------------------------------
1 | package mesh.core.array
2 | {
3 | import flash.utils.Proxy;
4 | import flash.utils.flash_proxy;
5 |
6 | /**
7 | * The ArrayProxy is a class that allows an inheriting class to behave like
8 | * an array that supports for each..in loops.
9 | *
10 | * @author Dan Schultz
11 | */
12 | public class ArrayProxy extends Proxy
13 | {
14 | private var _copy:Function;
15 |
16 | /**
17 | * Constructor.
18 | *
19 | * @param copy A function that copies the elements of the array being proxied.
20 | */
21 | public function ArrayProxy(copy:Function)
22 | {
23 | super();
24 | _copy = copy;
25 | }
26 |
27 | // Proxy methods to support for each..in loops.
28 |
29 | /**
30 | * @private
31 | */
32 | override flash_proxy function nextName(index:int):String
33 | {
34 | return (index-1).toString();
35 | }
36 |
37 | private var _iteratingItems:Array;
38 | private var _len:int;
39 | /**
40 | * @private
41 | */
42 | override flash_proxy function nextNameIndex(index:int):int
43 | {
44 | if (index == 0) {
45 | _iteratingItems = _copy();
46 | _len = _iteratingItems.length;
47 | }
48 | return index < _len ? index+1 : 0;
49 | }
50 |
51 | /**
52 | * @private
53 | */
54 | override flash_proxy function nextValue(index:int):*
55 | {
56 | return _iteratingItems[index-1];
57 | }
58 | }
59 | }
--------------------------------------------------------------------------------
/src/mesh/operations/Progress.as:
--------------------------------------------------------------------------------
1 | package mesh.operations
2 | {
3 | /**
4 | * The Progress class contains information about the progress of an
5 | * operation. It holds the number of units that have completed, and the total number
6 | * of units needed to complete the operation.
7 | *
8 | * @author Dan Schultz
9 | */
10 | public class Progress
11 | {
12 | /**
13 | * Constructor.
14 | *
15 | * @param total The total units needed to complete.
16 | */
17 | public function Progress()
18 | {
19 |
20 | }
21 |
22 | /**
23 | * Returns the percentage as a string.
24 | *
25 | * @return A string.
26 | */
27 | public function toString():String
28 | {
29 | var percentage:Number = complete/total;
30 | return percentage.toString() + (!isNaN(percentage) ? "%" : "");
31 | }
32 |
33 | private var _complete:Number = 0;
34 | /**
35 | * The number of units that have completed.
36 | */
37 | public function get complete():Number
38 | {
39 | return _complete;
40 | }
41 | public function set complete(value:Number):void
42 | {
43 | _complete = value;
44 | }
45 |
46 | private var _total:Number = 0;
47 | /**
48 | * The total number of units to complete the operation.
49 | */
50 | public function get total():Number
51 | {
52 | return _total;
53 | }
54 | public function set total(value:Number):void
55 | {
56 | _total = value;
57 | }
58 | }
59 | }
--------------------------------------------------------------------------------
/src/mesh/core/IBatchedListDelegate.as:
--------------------------------------------------------------------------------
1 | package mesh.core
2 | {
3 | /**
4 | * A delegate that provides data to a BatchedList. The batched list
5 | * looks to its delegate to fetch elements that do not yet exist in the list. A
6 | * base delegate class is provided to make implementing this interface simpler.
7 | *
8 | * @see BatchedList
9 | * @see BatchedListDelegate
10 | *
11 | * @author Dan Schultz
12 | */
13 | public interface IBatchedListDelegate
14 | {
15 | /**
16 | * Called by the list when it needs to know the total number of elements that
17 | * it contains. After the delegate has fetched the length, it must set the
18 | * length of the list by calling the provideLength() method.
19 | *
20 | * @param list The list requesting its length.
21 | * @se BatchedList#provideLength()
22 | */
23 | function requestLength(list:BatchedList):void;
24 |
25 | /**
26 | * Called by the list when it needs to fetch a batch of elements. After the
27 | * delegate has fetched the batch, it must call the provideBatch()
28 | * method to insert the batch into the list.
29 | *
30 | * @param list The list requesting the batch.
31 | * @param index The index of the batch.
32 | * @param batchSize The size of the batch to fetch.
33 | *
34 | * @see BatchedList#provideBatch()
35 | */
36 | function requestBatch(list:BatchedList, index:uint, batchSize:uint):void;
37 | }
38 | }
--------------------------------------------------------------------------------
/src/mesh/operations/OperationEvent.as:
--------------------------------------------------------------------------------
1 | package mesh.operations
2 | {
3 | import flash.events.Event;
4 |
5 | /**
6 | * An Operation dispatches this event for simple statuses.
7 | *
8 | * @see mesh.operations.Operation
9 | *
10 | * @author Dan Schultz
11 | */
12 | public class OperationEvent extends Event
13 | {
14 | /**
15 | * The event type for when an operation is canceled.
16 | */
17 | public static const CANCELED:String = "canceled";
18 |
19 | /**
20 | * The event type for when an operation is queued.
21 | */
22 | public static const QUEUED:String = "queued";
23 |
24 | /**
25 | * The event type before an operation is executed.
26 | */
27 | public static const BEFORE_EXECUTE:String = "beforeExecute";
28 |
29 | /**
30 | * The event type after an operation is executed.
31 | */
32 | public static const AFTER_EXECUTE:String = "afterExecute";
33 |
34 | /**
35 | * Constructor.
36 | *
37 | * @param type The event type.
38 | */
39 | public function OperationEvent(type:String)
40 | {
41 | super(type, bubbles, cancelable);
42 | }
43 |
44 | /**
45 | * @private
46 | */
47 | override public function clone():Event
48 | {
49 | return new OperationEvent(type);
50 | }
51 |
52 | /**
53 | * The operation that dispatched this event.
54 | */
55 | public function get operation():Operation
56 | {
57 | return target as Operation;
58 | }
59 | }
60 | }
--------------------------------------------------------------------------------
/src/mesh/model/validators/EachValidator.as:
--------------------------------------------------------------------------------
1 | package mesh.model.validators
2 | {
3 |
4 |
5 | /**
6 | * A validator base class that will validate a set of properties on the same
7 | * object.
8 | *
9 | * @author Dan Schultz
10 | */
11 | public class EachValidator extends Validator
12 | {
13 | /**
14 | * @copy Validator#Validator()
15 | */
16 | public function EachValidator(options:Object)
17 | {
18 | super(options);
19 | }
20 |
21 | /**
22 | * @inheritDoc
23 | */
24 | override public function validate(obj:Object):void
25 | {
26 | for each (var property:String in properties) {
27 | validateProperty(obj, property, obj[property]);
28 | }
29 | }
30 |
31 | /**
32 | * Called by validate() to validate a single property on an object. This
33 | * method is intended to be overridden by sub-classes to run the validation.
34 | *
35 | * @param obj The object being validated.
36 | * @param property The property to validate.
37 | * @param value The object's property value.
38 | */
39 | protected function validateProperty(obj:Object, property:String, value:Object):void
40 | {
41 |
42 | }
43 |
44 | /**
45 | * The properties that the validator is evaluating, as a list of Strings.
46 | */
47 | protected function get properties():Array
48 | {
49 | if (options.hasOwnProperty("properties")) {
50 | return options.properties;
51 | }
52 | return [options.property];
53 | }
54 | }
55 | }
--------------------------------------------------------------------------------
/tests/mesh/core/string/SentenceizeTests.as:
--------------------------------------------------------------------------------
1 | package mesh.core.string
2 | {
3 | import org.flexunit.assertThat;
4 | import org.hamcrest.object.equalTo;
5 |
6 | public class SentenceizeTests
7 | {
8 | private var _tests:Array = [];
9 |
10 | [Before]
11 | public function setup():void
12 | {
13 | _tests.push({input:"the quick brown fox, jumped over the lazy dog.", expected:"The quick brown fox, jumped over the lazy dog."});
14 | _tests.push({input:"The Quick Brown Fox, Jumped Over the Lazy Dog.", expected:"The Quick Brown Fox, Jumped Over the Lazy Dog."});
15 | _tests.push({input:"the quick brown fox, jumped over the lazy dog", expected:"The quick brown fox, jumped over the lazy dog"});
16 | _tests.push({input:"the quick brown fox; jumped over the lazy dog", expected:"The quick brown fox; jumped over the lazy dog"});
17 | _tests.push({input:"the quick brown fox. jumped over the lazy dog", expected:"The quick brown fox. Jumped over the lazy dog"});
18 | _tests.push({input:"the quick brown fox! jumped over the lazy dog", expected:"The quick brown fox! Jumped over the lazy dog"});
19 | _tests.push({input:"the quick brown fox? jumped over the lazy dog.", expected:"The quick brown fox? Jumped over the lazy dog."});
20 | }
21 |
22 | [Test]
23 | public function testSentenceize():void
24 | {
25 | for each (var test:Object in _tests) {
26 | assertThat("sentenceize failed for '" + test.input + "'", sentenceize(test.input), equalTo(test.expected));
27 | }
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/src/mesh/model/store/Query.as:
--------------------------------------------------------------------------------
1 | package mesh.model.store
2 | {
3 | import flash.events.EventDispatcher;
4 |
5 | import mesh.model.source.DataSource;
6 |
7 | /**
8 | * A query contains the information necessary to fetch data from a store or data source.
9 | *
10 | * @author Dan Schultz
11 | */
12 | public class Query extends EventDispatcher
13 | {
14 | /**
15 | * Constructor.
16 | *
17 | * @param dataSource The data source to load records from.
18 | * @param records The records to query.
19 | * @param recordType The type of records to query.
20 | */
21 | public function Query(dataSource:DataSource, records:RecordCache, recordType:Class)
22 | {
23 | _dataSource = dataSource;
24 | _records = records;
25 | _recordType = recordType;
26 | }
27 |
28 | /**
29 | * Executes the query.
30 | */
31 | public function execute():*
32 | {
33 |
34 | }
35 |
36 | private var _dataSource:DataSource;
37 | /**
38 | * The data source to load records from.
39 | */
40 | protected function get dataSource():DataSource
41 | {
42 | return _dataSource;
43 | }
44 |
45 | private var _recordType:Class;
46 | /**
47 | * The type of records to query.
48 | */
49 | protected function get recordType():Class
50 | {
51 | return _recordType;
52 | }
53 |
54 | private var _records:RecordCache;
55 | /**
56 | * The records to query.
57 | */
58 | protected function get records():RecordCache
59 | {
60 | return _records;
61 | }
62 | }
63 | }
--------------------------------------------------------------------------------
/src/mesh/operations/Timeout.as:
--------------------------------------------------------------------------------
1 | package mesh.operations
2 | {
3 | /**
4 | * The timeout for a network operation.
5 | *
6 | * @author Dan Schultz
7 | */
8 | public class Timeout
9 | {
10 | private var _operation:NetworkOperation;
11 | private var _scale:Number;
12 |
13 | public function Timeout(milliseconds:Number, operation:NetworkOperation)
14 | {
15 | _scale = 1;
16 | _value = milliseconds;
17 | _operation = operation;
18 | }
19 |
20 | /**
21 | * Sets the timeout to be interpreted as milliseconds.
22 | *
23 | * @return The network operation.
24 | */
25 | public function milliseconds():NetworkOperation
26 | {
27 | return _operation;
28 | }
29 |
30 | /**
31 | * Sets the timeout to be interpreted as minutes.
32 | *
33 | * @return The network operation.
34 | */
35 | public function minutes():NetworkOperation
36 | {
37 | _scale = 60000;
38 | return _operation;
39 | }
40 |
41 | /**
42 | * Sets the timeout to be interpreted as seconds.
43 | *
44 | * @return The network operation.
45 | */
46 | public function seconds():NetworkOperation
47 | {
48 | _scale = 1000;
49 | return _operation;
50 | }
51 |
52 | public function toString():String
53 | {
54 | return (value/1000).toString() + " seconds";
55 | }
56 |
57 | public function valueOf():Object
58 | {
59 | return value;
60 | }
61 |
62 | private var _value:Number;
63 | public function get value():Number
64 | {
65 | return _value / _scale;
66 | }
67 | }
68 | }
--------------------------------------------------------------------------------
/src/mesh/model/store/CommitResponder.as:
--------------------------------------------------------------------------------
1 | package mesh.model.store
2 | {
3 | /**
4 | * A responder that contains callbacks for failed and successful persistence events.
5 | *
6 | * @author Dan Schultz
7 | */
8 | public class CommitResponder implements ICommitResponder
9 | {
10 | /**
11 | * Constructor.
12 | *
13 | * @param success The success callback function.
14 | * @param failed The failed callback function.
15 | */
16 | public function CommitResponder(success:Function = null, failed:Function = null)
17 | {
18 | successHandler = success;
19 | failedHandler = failed;
20 | }
21 |
22 | /**
23 | * @inheritDoc
24 | */
25 | public function failed(summary:String, detail:String = "", code:String = ""):void
26 | {
27 | if (failedHandler != null) {
28 | failedHandler(summary, detail, code);
29 | }
30 | }
31 |
32 | /**
33 | * @inheritDoc
34 | */
35 | public function success():void
36 | {
37 | if (successHandler != null) {
38 | successHandler();
39 | }
40 | }
41 |
42 | /**
43 | * The function that is executed when a commit has failed. This method expects the following
44 | * method signature: function(summary:String, detail:String = "", code:String = ""):void.
45 | */
46 | public var failedHandler:Function;
47 |
48 | /**
49 | * The function that is executed when a commit has finished successfully. This method expects
50 | * the following method signature: function():void.
51 | */
52 | public var successHandler:Function;
53 | }
54 | }
--------------------------------------------------------------------------------
/src/mesh/operations/Attempt.as:
--------------------------------------------------------------------------------
1 | package mesh.operations
2 | {
3 | /**
4 | * Stores the retries for a network operation.
5 | *
6 | * @author Dan Schultz.
7 | */
8 | public class Attempt
9 | {
10 | private var _operation:NetworkOperation;
11 | private var _delays:Array = [0];
12 |
13 | public function Attempt(maxAttempts:int, operation:NetworkOperation)
14 | {
15 | _maxAttempts = Math.max(1, maxAttempts);
16 | _operation = operation;
17 | }
18 |
19 | public function getDelayForAttemptInMilliseconds(attempt:int):Number
20 | {
21 | attempt = Math.min(_delays.length, attempt);
22 | return _delays[attempt-1] * 1000;
23 | }
24 |
25 | /**
26 | * Sets the delay, in seconds, for each retry attempt. The delay for the first
27 | * retry is the first argument, the delay for the second retry is the second
28 | * argument and so on. To set the delay for all attempts, pass in a single
29 | * argument.
30 | *
31 | * @param delays The delays for each retry attempt.
32 | * @return The operation.
33 | */
34 | public function withDelay(... delays):NetworkOperation
35 | {
36 | _delays = delays;
37 | return _operation;
38 | }
39 |
40 | /**
41 | * Removes the delay between each retry attempt.
42 | *
43 | * @return The operation.
44 | */
45 | public function withoutDelay():NetworkOperation
46 | {
47 | _delays = [0];
48 | return _operation;
49 | }
50 |
51 | private var _maxAttempts:int;
52 | public function get maxAttempts():int
53 | {
54 | return _maxAttempts;
55 | }
56 | }
57 | }
--------------------------------------------------------------------------------
/src/mesh/core/object/copy.as:
--------------------------------------------------------------------------------
1 | package mesh.core.object
2 | {
3 | /**
4 | * Copies the enumerable values defined on from to to. If
5 | * to is a non-dynamic class, and a key exists on from, but
6 | * not on to, its value is not copied.
7 | *
8 | * 9 | * Additional options can be passed in to configure the copy: 10 | * 11 | *
Array - A list of properties to not copy.Array - A list of properties to copy.from that is a defined function
21 | * on to.
22 | */
23 | public function copy(from:Object, to:Object, options:Object = null):void
24 | {
25 | options = options == null ? {} : options;
26 |
27 | var includes:Array = (options.includes is Array) ? options.includes : [];
28 | var excludes:Array = (options.excludes is Array) ? options.excludes : [];
29 |
30 | for (var key:String in from) {
31 | if (includes.indexOf(key) == -1) {
32 | includes.push(key);
33 | }
34 | }
35 |
36 | for each (key in includes) {
37 | try {
38 | if (to[key] != from[key]) {
39 | if (excludes.indexOf(key) == -1) {
40 | to[key] = from[key];
41 | }
42 | }
43 | } catch (e:ReferenceError) {
44 |
45 | } catch (e:TypeError) {
46 |
47 | }
48 | }
49 | }
50 | }
--------------------------------------------------------------------------------
/tests/mesh/view/helpers/number/WithPrecisionTests.as:
--------------------------------------------------------------------------------
1 | package mesh.view.helpers.number
2 | {
3 | import mesh.core.object.inspect;
4 |
5 | import org.flexunit.assertThat;
6 | import org.hamcrest.object.equalTo;
7 |
8 | public class WithPrecisionTests
9 | {
10 | private var _tests:Array;
11 |
12 | [Before]
13 | public function setup():void
14 | {
15 | _tests = [
16 | {number:111.2345, options:null, expected:"111.23"},
17 | {number:111.2345, options:{precision:3}, expected:"111.235"},
18 | {number:12, options:{precision:3}, expected:"12.000"},
19 | {number:234.5, options:{precision:0}, expected:"235"},
20 | {number:111.234, options:{significant:true}, expected:"110"},
21 | {number:111.234, options:{precision:1, significant:true}, expected:"100"},
22 | {number:2, options:{precision:1, significant:true}, expected:"2"},
23 | {number:15, options:{precision:1, significant:true}, expected:"20"},
24 | {number:13, options:{precision:5, significant:true}, expected:"13.000"},
25 | {number:13, options:{precision:5, significant:true, stripInsignificantZeros:true}, expected:"13"},
26 | {number:389.32314, options:{precision:4, significant:true}, expected:"389.3"},
27 | {number:1111.2345, options:{precision:2, separator:",", delimiter:"."}, expected:"1.111,23"},
28 | ];
29 | }
30 |
31 | [Test]
32 | public function testNumberWithPrecision():void
33 | {
34 | for each (var test:Object in _tests) {
35 | assertThat("test failed for number," + test.number + ", with options: " + inspect(test.options), withPrecision(test.number, test.options), equalTo(test.expected));
36 | }
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/src/metadata.xml:
--------------------------------------------------------------------------------
1 |
2 | Set class is a list collection that ensures that only one copy of an element
7 | * belongs to the list. This set is implemented as an IList and dispatches collection
8 | * change events when elements are added and removed. The set can also be assigned to data providers
9 | * of list and tree controls in Flex.
10 | *
11 | * 12 | * This set supports ordered iteration. 13 | *
14 | * 15 | * @author Dan Schultz 16 | */ 17 | public class Set extends List 18 | { 19 | private var _elements:Dictionary = new Dictionary(); 20 | 21 | /** 22 | * @copy List#List() 23 | */ 24 | public function Set(source:Array = null) 25 | { 26 | super(source); 27 | } 28 | 29 | /** 30 | * @inheritDoc 31 | */ 32 | override public function addItemAt(item:Object, index:int):void 33 | { 34 | if (!contains(item)) { 35 | _elements[item] = true; 36 | super.addItemAt(item, length); 37 | } 38 | } 39 | 40 | /** 41 | * @inheritDoc 42 | */ 43 | override public function contains(item:Object):Boolean 44 | { 45 | return _elements[item] != null; 46 | } 47 | 48 | /** 49 | * @private 50 | */ 51 | override public function setItemAt(item:Object, index:int):Object 52 | { 53 | // Disabled 54 | return null; 55 | } 56 | 57 | /** 58 | * @inheritDoc 59 | */ 60 | override public function removeItemAt(index:int):Object 61 | { 62 | var item:Object = getItemAt(index); 63 | if (item != null) { 64 | delete _elements[item]; 65 | } 66 | super.removeItemAt(index); 67 | return item; 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /src/mesh/operations/FaultOperationEvent.as: -------------------------------------------------------------------------------- 1 | package mesh.operations 2 | { 3 | import flash.events.Event; 4 | 5 | /** 6 | * An event dispatched by an operation to indicate that an error or fault 7 | * has occurred during its execution. 8 | * 9 | * @author Dan Schultz 10 | */ 11 | public class FaultOperationEvent extends OperationEvent 12 | { 13 | /** 14 | * An event type for when an operation has errored or faulted during execution. 15 | */ 16 | public static const FAULT:String = "fault"; 17 | 18 | /** 19 | * Constructor. 20 | * 21 | * @param summary A simple description of the fault. 22 | * @param detail A detailed description of the fault. 23 | */ 24 | public function FaultOperationEvent(summary:String, detail:String = "", code:String = "") 25 | { 26 | super(FAULT); 27 | 28 | _summary = summary == null ? "" : summary; 29 | _detail = detail == null ? "" : detail; 30 | _code = code == null ? "" : code; 31 | } 32 | 33 | /** 34 | * @private 35 | */ 36 | override public function clone():Event 37 | { 38 | return new FaultOperationEvent(summary, detail); 39 | } 40 | 41 | private var _summary:String; 42 | /** 43 | * A simple description of the fault. 44 | */ 45 | public function get summary():String 46 | { 47 | return _summary; 48 | } 49 | 50 | private var _detail:String; 51 | /** 52 | * A detailed description of the fault. 53 | */ 54 | public function get detail():String 55 | { 56 | return _detail; 57 | } 58 | 59 | private var _code:String; 60 | /** 61 | * A specific code given to the fault. 62 | */ 63 | public function get code():String 64 | { 65 | return _code; 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /tests/mesh/model/validators/PresenceValidatorTests.as: -------------------------------------------------------------------------------- 1 | package mesh.model.validators 2 | { 3 | 4 | import org.flexunit.assertThat; 5 | import org.hamcrest.object.equalTo; 6 | 7 | import mesh.core.object.inspect; 8 | 9 | public class PresenceValidatorTests 10 | { 11 | [Test] 12 | public function testValidate():void 13 | { 14 | var tests:Array = [ 15 | { 16 | object:{str:"hello", errors:new Errors(null)}, 17 | options:{property:"str"}, 18 | passes:true 19 | }, 20 | { 21 | object:{str:"", errors:new Errors(null)}, 22 | options:{property:"str"}, 23 | passes:false 24 | }, 25 | { 26 | object:{str:" ", errors:new Errors(null)}, 27 | options:{property:"str"}, 28 | passes:false 29 | }, 30 | { 31 | object:{num:0, errors:new Errors(null)}, 32 | options:{property:"num"}, 33 | passes:true 34 | }, 35 | { 36 | object:{num:NaN, errors:new Errors(null)}, 37 | options:{property:"num"}, 38 | passes:false 39 | }, 40 | { 41 | object:{elements:[1], errors:new Errors(null)}, 42 | options:{property:"elements"}, 43 | passes:true 44 | }, 45 | { 46 | object:{elements:[], errors:new Errors(null)}, 47 | options:{property:"elements"}, 48 | passes:false 49 | }, 50 | { 51 | object:{includes:false, errors:new Errors(null)}, 52 | options:{property:"includes"}, 53 | passes:true 54 | } 55 | ]; 56 | 57 | for each (var test:Object in tests) { 58 | new PresenceValidator(test.options).validate(test.object); 59 | assertThat("validation failed for test " + inspect(test.options), test.object.errors.length == 0, equalTo(test.passes)); 60 | } 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /src/mesh/operations/MethodOperation.as: -------------------------------------------------------------------------------- 1 | package mesh.operations 2 | { 3 | /** 4 | * An synchronous operation that calls a method on an object. The data returned 5 | * from the method call will be passed to theResultOperationEvent.data
6 | * property. If an error is thrown during the execution of the method, the operation
7 | * will fault and dispatch the FaultOperationEvent.FAULT event.
8 | *
9 | * 10 | * Example: Using a method operation: 11 | * 12 | *
HTTPMultiServices or RemoteObjects.
12 | *
13 | * @author Dan Schultz
14 | */
15 | public class ServiceOperation extends NetworkOperation
16 | {
17 | private var _service:AbstractService;
18 | private var _name:String;
19 | private var _args:Array;
20 |
21 | /**
22 | * Constructor.
23 | *
24 | * @param service The service to execute the operation.
25 | * @param name The name of the operation to execute.
26 | * @param args A list of arguments to pass to the operation.
27 | */
28 | public function ServiceOperation(service:AbstractService, name:String, ... args)
29 | {
30 | super();
31 | _service = service;
32 | _name = name;
33 | _args = args;
34 | }
35 |
36 | /**
37 | * @inheritDoc
38 | */
39 | override protected function request():void
40 | {
41 | super.request();
42 |
43 | var token:AsyncToken = _service.getOperation(_name).send.apply(null, _args);
44 | token.addResponder(new AsyncResponder(handleAsyncTokenResult, handleAsyncTokenFault, token));
45 | }
46 |
47 | private function handleAsyncTokenResult(event:ResultEvent, token:AsyncToken):void
48 | {
49 | result(event.result);
50 | }
51 |
52 | private function handleAsyncTokenFault(event:FaultEvent, token:AsyncToken):void
53 | {
54 | fault(event.fault.faultString, event.fault.faultDetail, event.fault.faultCode);
55 | }
56 | }
57 | }
--------------------------------------------------------------------------------
/src/mesh/model/source/Snapshot.as:
--------------------------------------------------------------------------------
1 | package mesh.model.source
2 | {
3 | import mesh.model.Record;
4 | import mesh.model.RecordState;
5 | import mesh.model.associations.Association;
6 | import mesh.model.associations.AssociationCollection;
7 |
8 | import mx.utils.ObjectUtil;
9 |
10 | /**
11 | * The RecordSnapshot class represents a record at a specific time.
12 | *
13 | * @author Dan Schultz
14 | */
15 | public class Snapshot
16 | {
17 | /**
18 | * Creates a new snapshot from an object.
19 | *
20 | * @param record The record to create the snapshot of.
21 | */
22 | public function Snapshot(record:Record)
23 | {
24 | _record = record;
25 | snap();
26 | }
27 |
28 | private function snap():void
29 | {
30 | _data = ObjectUtil.copy(record);
31 |
32 | for each (var association:Association in record.associations) {
33 | if (association is AssociationCollection) {
34 | var collection:AssociationCollection = (association as AssociationCollection);
35 | _data[association.property] = new AssociationCollectionSnapshot(collection.toArray(), collection.added, collection.removed);
36 | }
37 | }
38 |
39 | _state = record.state;
40 | }
41 |
42 | private var _data:Object;
43 | /**
44 | * The copied data.
45 | */
46 | public function get data():Object
47 | {
48 | return _data;
49 | }
50 |
51 | private var _record:Record;
52 | /**
53 | * The record that this snapshot is based off of.
54 | */
55 | public function get record():Record
56 | {
57 | return _record;
58 | }
59 |
60 | private var _state:RecordState;
61 | /**
62 | * The state of the record when the snapshot was created.
63 | */
64 | public function get state():RecordState
65 | {
66 | return _state;
67 | }
68 | }
69 | }
--------------------------------------------------------------------------------
/src/mesh/model/store/Cache.as:
--------------------------------------------------------------------------------
1 | package mesh.model.store
2 | {
3 | import flash.utils.Dictionary;
4 |
5 | import mesh.core.reflection.reflect;
6 |
7 | /**
8 | * The Cache is an internal structure used by the Store to store
9 | * its elements.
10 | *
11 | * @author Dan Schultz
12 | */
13 | public class Cache
14 | {
15 | private var _indexes:Dictionary = new Dictionary();
16 |
17 | public function Cache()
18 | {
19 |
20 | }
21 |
22 | /**
23 | * Determines the class type of an object. This method can be overridden by sub-classes.
24 | * By default, this method returns the class type of the object.
25 | *
26 | * @param data The object to determine the type for.
27 | * @return A class type.
28 | */
29 | protected function determineType(data:Object):Class
30 | {
31 | return reflect(data).clazz;
32 | }
33 |
34 | /**
35 | * Returns a list of all the data with the given type.
36 | *
37 | * @param type The types to retrieve.
38 | * @return An indexed list.
39 | */
40 | public function findIndex(type:Class):Index
41 | {
42 | return index(type);
43 | }
44 |
45 | /**
46 | * Inserts an item into the cache.
47 | *
48 | * @param item The item to insert.
49 | */
50 | public function insert(item:Object):void
51 | {
52 | findIndex(determineType(item)).add(item);
53 | }
54 |
55 | /**
56 | * Removes an item from the cache.
57 | *
58 | * @param item The item to remove.
59 | */
60 | public function remove(item:Object):void
61 | {
62 | findIndex(determineType(item)).remove(item);
63 | }
64 |
65 | private function index(type:Class):Index
66 | {
67 | if (_indexes[type] == null) {
68 | _indexes[type] = new Index();
69 | }
70 | return _indexes[type];
71 | }
72 | }
73 | }
--------------------------------------------------------------------------------
/tests/mesh/model/store/ResultsListTests.as:
--------------------------------------------------------------------------------
1 | package mesh.model.store
2 | {
3 | import mesh.Person;
4 | import mesh.model.source.FixtureDataSource;
5 | import mesh.operations.MethodOperation;
6 |
7 | import mx.collections.ArrayList;
8 |
9 | import org.flexunit.assertThat;
10 | import org.hamcrest.object.equalTo;
11 |
12 | public class ResultsListTests
13 | {
14 | private var _fixtures:FixtureDataSource;
15 | private var _records:RecordCache;
16 |
17 | [Before]
18 | public function setup():void
19 | {
20 | _fixtures = new FixtureDataSource(Person);
21 | _records = new RecordCache(new Store(_fixtures), _fixtures, new DataCache());
22 | }
23 |
24 | [Test]
25 | public function testLoad():void
26 | {
27 | var called:Boolean;
28 | var results:ResultsList = new ResultsList(new ArrayList(), new MethodOperation(function():void
29 | {
30 | called = true;
31 | }));
32 | results.load();
33 |
34 | assertThat(called, equalTo(true));
35 | assertThat(results.isLoaded, equalTo(true));
36 | }
37 |
38 | [Test]
39 | public function testLoadOnlyOnce():void
40 | {
41 | var called:Boolean;
42 | var results:ResultsList = new ResultsList(new ArrayList(), new MethodOperation(function():void
43 | {
44 | called = true;
45 | }));
46 | results.load();
47 |
48 | called = false;
49 | results.load();
50 |
51 | assertThat(called, equalTo(false));
52 | }
53 |
54 | [Test]
55 | public function testRefresh():void
56 | {
57 | var called:Boolean;
58 | var results:ResultsList = new ResultsList(new ArrayList(), new MethodOperation(function():void
59 | {
60 | called = true;
61 | }));
62 | results.load();
63 |
64 | called = false;
65 | results.refresh();
66 |
67 | assertThat(called, equalTo(true));
68 | }
69 | }
70 | }
--------------------------------------------------------------------------------
/tests/mesh/core/proxy/DataProxyTests.as:
--------------------------------------------------------------------------------
1 | package mesh.core.proxy
2 | {
3 | import mx.events.PropertyChangeEvent;
4 |
5 | import org.flexunit.assertThat;
6 | import org.hamcrest.object.equalTo;
7 | import org.hamcrest.object.notNullValue;
8 |
9 | public class DataProxyTests
10 | {
11 | private var _mock:Mock;
12 | private var _proxy:DataProxy;
13 |
14 | [Before]
15 | public function setup():void
16 | {
17 | _mock = new Mock();
18 | _proxy = new DataProxy(_mock);
19 | }
20 |
21 | [Test]
22 | public function testProxyMethodCall():void
23 | {
24 | var say:String = "hello world";
25 | assertThat(_proxy.say(say), equalTo(say));
26 | }
27 |
28 | [Test]
29 | public function testProxyGetProperty():void
30 | {
31 | var name:String = "John Doe";
32 | _mock.name = name;
33 | assertThat(_proxy.name, equalTo(name));
34 | }
35 |
36 | [Test]
37 | public function testProxySetProperty():void
38 | {
39 | var name:String = "John Doe";
40 | _proxy.name = name;
41 | assertThat(_proxy.name, equalTo(name));
42 | }
43 |
44 | [Test]
45 | public function testDispatchesPropertyChangeEvents():void
46 | {
47 | var e:PropertyChangeEvent;
48 |
49 | _proxy.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, function(event:PropertyChangeEvent):void
50 | {
51 | e = event;
52 | });
53 | _proxy.name = "John";
54 | assertThat(e, notNullValue());
55 | }
56 |
57 | [Test]
58 | public function testForwardsPropertyChangeEvents():void
59 | {
60 | var e:PropertyChangeEvent;
61 |
62 | _proxy.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, function(event:PropertyChangeEvent):void
63 | {
64 | e = event;
65 | });
66 | _mock.name = "John";
67 | assertThat(e, notNullValue());
68 | }
69 | }
70 | }
71 |
72 | class Mock
73 | {
74 | [Bindable] public var name:String;
75 |
76 | public function say(str:String):String
77 | {
78 | return str;
79 | }
80 | }
--------------------------------------------------------------------------------
/src/mesh/model/validators/Validator.as:
--------------------------------------------------------------------------------
1 | package mesh.model.validators
2 | {
3 | /**
4 | * A class that represents a test to run on an object to ensure a correct state.
5 | *
6 | * @author Dan Schultz
7 | */
8 | public class Validator
9 | {
10 | /**
11 | * Constructor.
12 | *
13 | * @param options A set of options for this validator, as key-value pairs.
14 | */
15 | public function Validator(options:Object)
16 | {
17 | _options = options;
18 | }
19 |
20 | /**
21 | * A utility method for sub-classes to parse and populate the fields for a numeric range.
22 | * This method will take a string defined on options[rangeField] in the format
23 | * i..k, and set options[lowerField] = i and options[upperField] = k.
24 | *
25 | * @param rangeField The field defining a range.
26 | * @param lowerField The field defining the lower bounds of the range.
27 | * @param upperField The field defining the upper bounds of the range.
28 | */
29 | protected function populateRangeInOptions(rangeField:String, lowerField:String, upperField:String):void
30 | {
31 | if (options.hasOwnProperty(rangeField)) {
32 | var parsedRange:Array = options[rangeField].split("..");
33 | options[lowerField] = parsedRange[0];
34 | options[upperField] = parsedRange[1];
35 | }
36 | }
37 |
38 | /**
39 | * Executes the validation on the given object, and returns a list of validation errors
40 | * for validations that have failed. If all validations pass, this method returns an
41 | * empty array.
42 | *
43 | * @param obj The object to validate.
44 | */
45 | public function validate(obj:Object):void
46 | {
47 |
48 | }
49 |
50 | private var _options:Object;
51 | /**
52 | * The options that were defined for this validator as key-value pairs.
53 | */
54 | public function get options():Object
55 | {
56 | return _options;
57 | }
58 | }
59 | }
--------------------------------------------------------------------------------
/src/mesh/core/state/Action.as:
--------------------------------------------------------------------------------
1 | package mesh.core.state
2 | {
3 | /**
4 | * An Action defines an event that can be triggered from a state machine.
5 | * Actions contain transitions that change the current state of the state machine. These
6 | * transitions are invoked whenever the action is triggered.
7 | *
8 | *
9 | * To trigger an action, invoke the action's trigger() method, or call the
10 | * state machine's triggerAction() method.
11 | *
function():Boolean.
50 | * @return This action.
51 | */
52 | public function transitionTo(to:Object, from:Object, guard:Function = null):Action
53 | {
54 | from = from is Array ? from : [from];
55 | for each (var fromState:Object in from) {
56 | _transitions.push( new Transition(_machine, _machine.createState(fromState.toString()), _machine.createState(to.toString()) , guard) );
57 | }
58 | return this;
59 | }
60 | }
61 | }
--------------------------------------------------------------------------------
/src/mesh/model/store/RecordCache.as:
--------------------------------------------------------------------------------
1 | package mesh.model.store
2 | {
3 | import flash.errors.IllegalOperationError;
4 |
5 | import mesh.core.reflection.newInstance;
6 | import mesh.mesh_internal;
7 | import mesh.model.Record;
8 | import mesh.model.RecordState;
9 | import mesh.model.source.DataSource;
10 |
11 | use namespace mesh_internal;
12 |
13 | /**
14 | * The index of records belonging to the store.
15 | *
16 | * @author Dan Schultz
17 | */
18 | public class RecordCache extends Cache
19 | {
20 | private var _store:Store;
21 | private var _cache:DataCache;
22 | private var _dataSource:DataSource;
23 |
24 | /**
25 | * Constructor.
26 | */
27 | public function RecordCache(store:Store, dataSource:DataSource, cache:DataCache)
28 | {
29 | _store = store;
30 | _dataSource = dataSource;
31 | _cache = cache;
32 | }
33 |
34 | /**
35 | * @inheritDoc
36 | */
37 | override public function insert(item:Object):void
38 | {
39 | var record:Record = Record( item );
40 |
41 | if (record.store == null) {
42 | record.store = _store;
43 | }
44 |
45 | if (record.store != _store) {
46 | throw new IllegalOperationError("Cannot insert record into multiple caches.");
47 | }
48 |
49 | super.insert(item);
50 | }
51 |
52 | /**
53 | * Either creates, or returns an existing record from the store with the given data.
54 | *
55 | * @param data The data to assign on the record.
56 | * @param state The state of the record.
57 | * @return A record.
58 | */
59 | public function materialize(data:Data, state:RecordState = null):*
60 | {
61 | _cache.insert(data);
62 |
63 | var record:Record = findIndex(data.type).byId(data.id);
64 | if (record == null) {
65 | record = newInstance(data.type);
66 | record.id = data.id;
67 | insert(record);
68 | }
69 |
70 | data.transferValues(record);
71 |
72 | if (state != null) {
73 | record.changeState(state);
74 | }
75 |
76 | return record;
77 | }
78 | }
79 | }
--------------------------------------------------------------------------------
/tests/mesh/model/validators/LengthValidatorTests.as:
--------------------------------------------------------------------------------
1 | package mesh.model.validators
2 | {
3 |
4 | import org.flexunit.assertThat;
5 | import org.hamcrest.object.equalTo;
6 |
7 | import mesh.core.object.inspect;
8 |
9 | public class LengthValidatorTests
10 | {
11 | [Test]
12 | public function testValidate():void
13 | {
14 | var tests:Array = [
15 | {
16 | object:{str:"Hello", errors:new Errors(null)},
17 | options:{property:"str", minimum:5},
18 | passes:true
19 | },
20 | {
21 | object:{str:"Hello", errors:new Errors(null)},
22 | options:{property:"str", minimum:6},
23 | passes:false
24 | },
25 | {
26 | object:{str:"Hello", errors:new Errors(null)},
27 | options:{property:"str", maximum:5},
28 | passes:true
29 | },
30 | {
31 | object:{str:"Hello", errors:new Errors(null)},
32 | options:{property:"str", maximum:4},
33 | passes:false
34 | },
35 | {
36 | object:{str:"Hello", errors:new Errors(null)},
37 | options:{property:"str", between:"0..5"},
38 | passes:true
39 | },
40 | {
41 | object:{str:"Hello", errors:new Errors(null)},
42 | options:{property:"str", between:"1..5"},
43 | passes:true
44 | },
45 | {
46 | object:{str:"Hello", errors:new Errors(null)},
47 | options:{property:"str", between:"0..4"},
48 | passes:false
49 | },
50 | {
51 | object:{str:"Hello", errors:new Errors(null)},
52 | options:{property:"str", between:"0..6"},
53 | passes:true
54 | },
55 | {
56 | object:{str:"Hello", errors:new Errors(null)},
57 | options:{property:"str", length:"5"},
58 | passes:true
59 | },
60 | {
61 | object:{str:"Hello", errors:new Errors(null)},
62 | options:{property:"str", length:"4"},
63 | passes:false
64 | }
65 | ];
66 |
67 | for each (var test:Object in tests) {
68 | new LengthValidator(test.options).validate(test.object);
69 | assertThat("validation failed for test " + inspect(test.options), test.object.errors.length == 0, equalTo(test.passes));
70 | }
71 | }
72 | }
73 | }
--------------------------------------------------------------------------------
/src/mesh/model/validators/LengthValidator.as:
--------------------------------------------------------------------------------
1 | package mesh.model.validators
2 | {
3 | /**
4 | * Validates the length of an object, such as the length of string or number of
5 | * elements in an array.
6 | *
7 | * @author Dan Schultz
8 | */
9 | public class LengthValidator extends EachValidator
10 | {
11 | private static const CHECKS:Object =
12 | {
13 | length:function(propertyValue:Number, validationValue:Number):Boolean
14 | {
15 | return propertyValue == validationValue;
16 | },
17 | minimum:function(propertyValue:Number, validationValue:Number):Boolean
18 | {
19 | return propertyValue >= validationValue;
20 | },
21 | maximum:function(propertyValue:Number, validationValue:Number):Boolean
22 | {
23 | return propertyValue <= validationValue;
24 | }
25 | };
26 |
27 | private static const MESSAGES:Object =
28 | {
29 | length:"wrongLength",
30 | minimum:"tooShort",
31 | maximum:"tooLong"
32 | };
33 |
34 | /**
35 | * @copy Validator#Validator()
36 | */
37 | public function LengthValidator(options:Object)
38 | {
39 | if (!options.hasOwnProperty("lengthProperty")) {
40 | options.lengthProperty = "length";
41 | }
42 |
43 | if (!options.hasOwnProperty("wrongLength")) {
44 | options.wrongLength = "must be a length of {count}";
45 | }
46 |
47 | if (!options.hasOwnProperty("tooShort")) {
48 | options.tooShort = "is too short (minimum is {count})";
49 | }
50 |
51 | if (!options.hasOwnProperty("tooLong")) {
52 | options.tooLong = "is too long (maximum is {count})";
53 | }
54 |
55 | super(options);
56 | populateRangeInOptions("between", "minimum", "maximum");
57 | }
58 |
59 | /**
60 | * @inheritDoc
61 | */
62 | override protected function validateProperty(obj:Object, property:String, value:Object):void
63 | {
64 | for (var check:String in CHECKS) {
65 | if (options.hasOwnProperty(check)) {
66 | if (!CHECKS[check](value[options.lengthProperty], options[check])) {
67 | obj.errors.add(property, options[MESSAGES[check]], {count: options[check]});
68 | }
69 | }
70 | }
71 | }
72 | }
73 | }
--------------------------------------------------------------------------------
/src/mesh/core/reflection/Property.as:
--------------------------------------------------------------------------------
1 | package mesh.core.reflection
2 | {
3 | import flash.utils.getDefinitionByName;
4 |
5 | /**
6 | * A class that represents a variable, getter or setter definition on a class.
7 | *
8 | * @author Dan Schultz
9 | */
10 | public class Property extends Definition
11 | {
12 | /**
13 | * @copy Definition#Definition()
14 | */
15 | public function Property(description:XML, belongsTo:Definition)
16 | {
17 | super(description, belongsTo);
18 | }
19 |
20 | /**
21 | * Returns the value for this property on an object. If the property does not exist or is
22 | * write only, an error is thrown.
23 | *
24 | * @param object The object to get the value from.
25 | * @return The property's value.
26 | */
27 | public function value(object:Object):*
28 | {
29 | return isStatic ? reflect(object).clazz[name] : object[name];
30 | }
31 |
32 | /**
33 | * true if this is a variable that has been defined with
34 | * const.
35 | */
36 | public function get isConstant():Boolean
37 | {
38 | return description.name() == "constant";
39 | }
40 |
41 | /**
42 | * true if this is a property that has been defined with
43 | * static.
44 | */
45 | public function get isStatic():Boolean
46 | {
47 | return description.parent().name() == "type" && description.parent().@isStatic == "true";
48 | }
49 |
50 | /**
51 | * true if this is a property that can be read from.
52 | */
53 | public function get isReadable():Boolean
54 | {
55 | return description.name() != "accessor" || description.@access.toString().search("read") != -1;
56 | }
57 |
58 | /**
59 | * true if this is a property that can be read from.
60 | */
61 | public function get isWritable():Boolean
62 | {
63 | return description.name() == "variable" || (!isConstant && description.@access.toString().search("write") != -1);
64 | }
65 |
66 | /**
67 | * The type that is defined for the property.
68 | */
69 | public function get type():Type
70 | {
71 | return Type.reflect(getDefinitionByName(description.@type.toString()));
72 | }
73 | }
74 | }
--------------------------------------------------------------------------------
/src/mesh/view/helpers/number/toPercentage.as:
--------------------------------------------------------------------------------
1 | package mesh.view.helpers.number
2 | {
3 | /**
4 | * Formats a number to a percentage string (i.e. 95%).
5 | *
6 | * 7 | * Options: 8 | *
precision:int - The precision of the number. (default=2)significant:Boolean - If true, the precision will
11 | * be the number of significant digits. (default=false)stripInsignificantZeros:Boolean - If true, all insignificant
13 | * zeros after the separator will be removed. (default=false)delimiter:String - The thousands delimiter. (default=",")separator:String - The separator between the integer and fractions.
16 | * (default=".")21 | * Examples: 22 | *
URLLoader to perform
12 | * a network operation.
13 | *
14 | * @see http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/URLLoader.html URLLoader
15 | * @author Dan Schultz
16 | */
17 | public class URLLoaderOperation extends NetworkOperation
18 | {
19 | private var _request:URLRequest;
20 | private var _loader:URLLoader;
21 |
22 | /**
23 | * Constructor.
24 | *
25 | * @param request The request to execute.
26 | * @param dataFormat The data format of the result. Use one of the constants defined on
27 | * URLLoaderDataFormat.
28 | * @see http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/URLLoaderDataFormat.html URLLoaderDataFormat
29 | */
30 | public function URLLoaderOperation(request:URLRequest, dataFormat:String = URLLoaderDataFormat.TEXT)
31 | {
32 | super();
33 |
34 | _request = request;
35 | _loader = new URLLoader();
36 | _loader.dataFormat = dataFormat;
37 | }
38 |
39 | /**
40 | * @inheritDoc
41 | */
42 | override protected function request():void
43 | {
44 | super.request();
45 |
46 | _loader.addEventListener(IOErrorEvent.IO_ERROR, handleLoaderIOError);
47 | _loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, handleLoaderSecurityError);
48 | _loader.addEventListener(Event.COMPLETE, handleLoaderComplete);
49 | _loader.load(_request);
50 | }
51 |
52 | /**
53 | * @inheritDoc
54 | */
55 | override protected function cancelRequest():void
56 | {
57 | super.cancelRequest();
58 |
59 | try {
60 | _loader.close();
61 | } catch (e:Error) {
62 |
63 | }
64 | }
65 |
66 | private function handleLoaderIOError(event:IOErrorEvent):void
67 | {
68 | fault(event.text, event.text);
69 | }
70 |
71 | private function handleLoaderSecurityError(event:SecurityErrorEvent):void
72 | {
73 | fault(event.text, event.text);
74 | }
75 |
76 | private function handleLoaderComplete(event:Event):void
77 | {
78 | result(_loader.data);
79 | }
80 | }
81 | }
--------------------------------------------------------------------------------
/tests/mesh/operations/MethodOperationTests.as:
--------------------------------------------------------------------------------
1 | package mesh.operations
2 | {
3 | import org.flexunit.assertThat;
4 | import org.hamcrest.object.equalTo;
5 | import org.hamcrest.object.notNullValue;
6 | import org.hamcrest.object.nullValue;
7 |
8 | public class MethodOperationTests
9 | {
10 | [Test]
11 | public function testExecuteWithoutRTE():void
12 | {
13 | var resultEvent:ResultOperationEvent;
14 | function handleOperationResult(event:ResultOperationEvent):void
15 | {
16 | resultEvent = event;
17 | };
18 |
19 | var finishedEvent:FinishedOperationEvent;
20 | function handleOperationFinish(event:FinishedOperationEvent):void
21 | {
22 | finishedEvent = event;
23 | };
24 |
25 | var str:String = "Hello World";
26 | var operation:MethodOperation = new MethodOperation(str.substr, 0, 5);
27 | operation.addEventListener(ResultOperationEvent.RESULT, handleOperationResult);
28 | operation.addEventListener(FinishedOperationEvent.FINISHED, handleOperationFinish);
29 | operation.execute();
30 |
31 | assertThat(resultEvent.data, equalTo(str.substr(0, 5)));
32 | assertThat(finishedEvent.successful, equalTo(true));
33 | assertThat(operation.isExecuting, equalTo(false));
34 | }
35 |
36 | [Test]
37 | public function testExecuteWithRTE():void
38 | {
39 | var resultEvent:ResultOperationEvent;
40 | function handleOperationResult(event:ResultOperationEvent):void
41 | {
42 | resultEvent = event;
43 | };
44 |
45 | var faultEvent:FaultOperationEvent;
46 | function handleOperationFault(event:FaultOperationEvent):void
47 | {
48 | faultEvent = event;
49 | };
50 |
51 | var finishedEvent:FinishedOperationEvent;
52 | function handleOperationFinish(event:FinishedOperationEvent):void
53 | {
54 | finishedEvent = event;
55 | };
56 |
57 | var str:String = "Hello World";
58 | var operation:MethodOperation = new MethodOperation(str.substr, 0, 5, 10);
59 | operation.addEventListener(ResultOperationEvent.RESULT, handleOperationResult);
60 | operation.addEventListener(FaultOperationEvent.FAULT, handleOperationFault);
61 | operation.addEventListener(FinishedOperationEvent.FINISHED, handleOperationFinish);
62 | operation.execute();
63 |
64 | assertThat(resultEvent, nullValue());
65 | assertThat(faultEvent, notNullValue());
66 | assertThat(finishedEvent.successful, equalTo(false));
67 | assertThat(operation.isExecuting, equalTo(false));
68 | }
69 | }
70 | }
--------------------------------------------------------------------------------
/src/mesh/model/store/Store.as:
--------------------------------------------------------------------------------
1 | package mesh.model.store
2 | {
3 | import mesh.core.reflection.newInstance;
4 | import mesh.mesh_internal;
5 | import mesh.model.Record;
6 | import mesh.model.RecordState;
7 | import mesh.model.source.DataSource;
8 |
9 | use namespace mesh_internal;
10 |
11 | /**
12 | * The Store holds all records belonging to your application.
13 | *
14 | * @author Dan Schultz
15 | */
16 | public class Store
17 | {
18 | /**
19 | * Constructor.
20 | *
21 | * @param dataSource The data source.
22 | */
23 | public function Store(dataSource:DataSource)
24 | {
25 | _dataSource = dataSource;
26 | _cache = new DataCache();
27 | _records = new RecordCache(this, _dataSource, _cache);
28 | }
29 |
30 | /**
31 | * Adds a record to the store.
32 | *
33 | * @param record The record to add.
34 | */
35 | public function add(record:Record):void
36 | {
37 | records.insert(record);
38 | }
39 |
40 | /**
41 | * Creates a new unpersisted record in the store.
42 | *
43 | * @param recordType The type of record to create.
44 | * @return A new record.
45 | */
46 | public function create(recordType:Class):*
47 | {
48 | var record:Record = newInstance(recordType);
49 | record.changeState(RecordState.created());
50 | records.insert(record);
51 | return record;
52 | }
53 |
54 | /**
55 | * Returns a query builder for the given record type.
56 | *
57 | * @param recordType The type of record to query.
58 | * @return A query builder.
59 | */
60 | public function query(recordType:Class):QueryBuilder
61 | {
62 | return new QueryBuilder(_dataSource, _records, recordType);
63 | }
64 |
65 | /**
66 | * @copy Records#materialize()
67 | */
68 | public function materialize(data:Data, state:RecordState = null):*
69 | {
70 | return _records.materialize(data, state);
71 | }
72 |
73 | private var _cache:DataCache;
74 | /**
75 | * @private
76 | */
77 | mesh_internal function get cache():DataCache
78 | {
79 | return _cache;
80 | }
81 |
82 | private var _dataSource:DataSource;
83 | /**
84 | * @private
85 | */
86 | mesh_internal function get dataSource():DataSource
87 | {
88 | return _dataSource;
89 | }
90 |
91 | private var _records:RecordCache;
92 | /**
93 | * @private
94 | */
95 | mesh_internal function get records():RecordCache
96 | {
97 | return _records;
98 | }
99 | }
100 | }
--------------------------------------------------------------------------------
/src/mesh/core/state/Transition.as:
--------------------------------------------------------------------------------
1 | package mesh.core.state
2 | {
3 | import flash.events.EventDispatcher;
4 |
5 | /**
6 | * Dispatched when the transition was triggered.
7 | */
8 | [Event(name="transitioned", type="mesh.core.state.TransitionEvent")]
9 |
10 | /**
11 | * A Transition defines a state machine's change from one state to another state.
12 | * The transition may be given an optional guard function that is evaluated when the transition
13 | * is triggered. If the guard returns false the transition fails. This guard
14 | * function must have the following signature: function():Boolean.
15 | *
16 | * @author Dan Schultz
17 | */
18 | public class Transition extends EventDispatcher
19 | {
20 | private var _machine:StateMachine;
21 | private var _guard:Function;
22 | private var _onTransition:Function;
23 |
24 | /**
25 | * Constructor.
26 | *
27 | * @param machine The machine the transition belongs to.
28 | * @param from The transition's start state.
29 | * @param to The transition's end state.
30 | * @param guard The guard function that is invoked upon the triggering of the transition.
31 | */
32 | public function Transition(machine:StateMachine, from:State, to:State, guard:Function = null)
33 | {
34 | super();
35 | _machine = machine;
36 | _from = from;
37 | _to = to;
38 | _guard = guard;
39 | }
40 |
41 | /**
42 | * Adds or replaces the callback function that is on a transition. The function should have
43 | * the following signature: function():void.
44 | *
45 | * @param block A function.
46 | * @return This instance.
47 | */
48 | public function onTransition(block:Function):Transition
49 | {
50 | _onTransition = block;
51 | return this;
52 | }
53 |
54 | /**
55 | * Triggers the transition. If successful, the transition's state machine state will be put
56 | * into the end state.
57 | */
58 | public function trigger():void
59 | {
60 | if (_from.equals(_machine.current) && (_guard == null || _guard())) {
61 | _machine.transitionTo(_to);
62 | dispatchEvent( new TransitionEvent(TransitionEvent.TRANSITIONED, this) );
63 | }
64 | }
65 |
66 | private var _from:State;
67 | /**
68 | * The transition's start state.
69 | */
70 | public function get from():State
71 | {
72 | return _from;
73 | }
74 |
75 | private var _to:State;
76 | /**
77 | * The transition's end state.
78 | */
79 | public function get to():State
80 | {
81 | return _to;
82 | }
83 | }
84 | }
--------------------------------------------------------------------------------
/src/mesh/model/store/ResultsList.as:
--------------------------------------------------------------------------------
1 | package mesh.model.store
2 | {
3 | import mesh.core.List;
4 | import mesh.mesh_internal;
5 | import mesh.model.Record;
6 | import mesh.operations.FinishedOperationEvent;
7 | import mesh.operations.Operation;
8 |
9 | import mx.collections.IList;
10 | import mx.collections.ListCollectionView;
11 |
12 | use namespace mesh_internal;
13 |
14 | public class ResultsList extends List
15 | {
16 | private var _resultsWrapper:ListCollectionView;
17 |
18 | public function ResultsList(results:IList, loadOperation:Operation)
19 | {
20 | super();
21 |
22 | _loadOperation = loadOperation;
23 | _loadOperation.addEventListener(FinishedOperationEvent.FINISHED, function(event:FinishedOperationEvent):void
24 | {
25 | _isLoaded = event.successful;
26 | });
27 |
28 | _resultsWrapper = new ListCollectionView(results);
29 | _resultsWrapper.filterFunction = filterRecord;
30 | _resultsWrapper.refresh();
31 | list = _resultsWrapper;
32 | }
33 |
34 | /**
35 | * @private
36 | */
37 | override public function addItemAt(item:Object, index:int):void
38 | {
39 | // Disabled so clients can't add records directly to the result.
40 | }
41 |
42 | private function filterRecord(record:Record):Boolean
43 | {
44 | return !(record.state.isDestroyed && record.state.isSynced);
45 | }
46 |
47 | /**
48 | * @copy mesh.model.Record#load()
49 | */
50 | public function load():*
51 | {
52 | if (!isLoaded) {
53 | refresh();
54 | }
55 | return this;
56 | }
57 |
58 | /**
59 | * @copy mesh.model.Record#refresh()
60 | */
61 | public function refresh():*
62 | {
63 | loadOperation.queue();
64 | loadOperation.execute();
65 | return this;
66 | }
67 |
68 | /**
69 | * @private
70 | */
71 | override public function removeItemAt(index:int):Object
72 | {
73 | // Disabled so clients can't remove records directly from the result.
74 | return null;
75 | }
76 |
77 | /**
78 | * @private
79 | */
80 | override public function setItemAt(item:Object, index:int):Object
81 | {
82 | // Disabled so clients can't replace records directly in the result.
83 | return null;
84 | }
85 |
86 | private var _isLoaded:Boolean;
87 | /**
88 | * @copy mesh.model.Record#isLoaded
89 | */
90 | public function get isLoaded():Boolean
91 | {
92 | return _isLoaded;
93 | }
94 |
95 | private var _loadOperation:Operation;
96 | /**
97 | * @copy mesh.model.Record#loadOperation
98 | */
99 | public function get loadOperation():Operation
100 | {
101 | return _loadOperation;
102 | }
103 | }
104 | }
--------------------------------------------------------------------------------
/tests/mesh/model/associations/HasOneAssociationTests.as:
--------------------------------------------------------------------------------
1 | package mesh.model.associations
2 | {
3 | import mesh.Account;
4 | import mesh.Customer;
5 | import mesh.Order;
6 | import mesh.mesh_internal;
7 | import mesh.model.source.FixtureDataSource;
8 | import mesh.model.source.MultiDataSource;
9 | import mesh.model.store.Data;
10 | import mesh.model.store.Store;
11 |
12 | import org.flexunit.assertThat;
13 | import org.hamcrest.object.equalTo;
14 | import org.hamcrest.object.notNullValue;
15 |
16 | use namespace mesh_internal;
17 |
18 | public class HasOneAssociationTests
19 | {
20 | private var _store:Store;
21 | private var _accounts:FixtureDataSource;
22 | private var _customers:FixtureDataSource;
23 |
24 | [Before]
25 | public function setup():void
26 | {
27 | _customers = new FixtureDataSource(Customer);
28 | _customers.add({id:1, firstName:"Jimmy", lastName:"Page", accountId:1});
29 |
30 | _accounts = new FixtureDataSource(Account);
31 | _accounts.add({id:1, customerId:1});
32 |
33 | var dataSources:MultiDataSource = new MultiDataSource();
34 | dataSources.map(Customer, _customers);
35 | dataSources.map(Account, _accounts);
36 | dataSources.map(Order, new FixtureDataSource(Order));
37 |
38 | _store = new Store(dataSources);
39 | }
40 |
41 | [Test]
42 | /**
43 | * Test loading the has-one association.
44 | */
45 | public function testLoad():void
46 | {
47 | var customer:Customer = _store.query(Customer).find(1).load();
48 | customer.account.load();
49 | assertThat(customer.account.customer, equalTo(customer));
50 | }
51 |
52 | [Test]
53 | /**
54 | * Test that the associated record changes, when the foreign key changes.
55 | */
56 | public function testResetAssociationOnForeignKeyChange():void
57 | {
58 | var customer:Customer = _store.query(Customer).find(1).load();
59 | customer.accountId = 2;
60 | assertThat(customer.account.id, equalTo(customer.accountId));
61 | }
62 |
63 | [Test]
64 | /**
65 | * Test that the foreign key is updated when the association is set.
66 | */
67 | public function testPopulateForeignKeyWhenAssociationSet():void
68 | {
69 | var customer:Customer = _store.query(Customer).find(1).load();
70 | var account:Account = _store.materialize( new Data(Account, {id:2}) );
71 | customer.account = account;
72 | assertThat(customer.accountId, equalTo(customer.account.id));
73 | }
74 |
75 | [Test]
76 | public function testAssociatedRecordsAreInsertedIntoTheStore():void
77 | {
78 | var customer:Customer = _store.query(Customer).find(1).load();
79 | var account:Account = new Account();
80 | customer.account = account;
81 | assertThat(account.store, notNullValue());
82 | }
83 | }
84 | }
--------------------------------------------------------------------------------
/tests/mesh/core/reflection/TypeTests.as:
--------------------------------------------------------------------------------
1 | package mesh.core.reflection
2 | {
3 | import flash.display.DisplayObject;
4 | import flash.events.Event;
5 | import flash.events.ProgressEvent;
6 |
7 | import mx.core.IBorder;
8 | import mx.core.IFlexDisplayObject;
9 |
10 | import org.flexunit.assertThat;
11 | import org.hamcrest.collection.arrayWithSize;
12 | import org.hamcrest.collection.hasItem;
13 | import org.hamcrest.core.allOf;
14 | import org.hamcrest.core.isA;
15 | import org.hamcrest.core.not;
16 | import org.hamcrest.object.equalTo;
17 | import org.hamcrest.object.hasProperty;
18 |
19 | import spark.components.Label;
20 |
21 | public class TypeTests
22 | {
23 | [Test]
24 | public function testClassName():void
25 | {
26 | assertThat(new Type(Event).className, equalTo("Event"));
27 | assertThat(new Type(Number).className, equalTo("Number"));
28 | }
29 |
30 | [Test]
31 | public function testPackageName():void
32 | {
33 | assertThat(new Type(Event).packageName, equalTo("flash.events"));
34 | assertThat(new Type(Number).packageName, equalTo(""));
35 | }
36 |
37 | [Test]
38 | public function testName():void
39 | {
40 | assertThat(new Type(Event).name, equalTo("flash.events::Event"));
41 | }
42 |
43 | [Test]
44 | public function testParent():void
45 | {
46 | assertThat(new Type(ProgressEvent).parent.className, equalTo("Event"));
47 | }
48 |
49 | [Test]
50 | public function testParents():void
51 | {
52 | assertThat(new Type(ProgressEvent).parents, arrayWithSize(2));
53 | }
54 |
55 | [Test]
56 | public function testImplementing():void
57 | {
58 | assertThat(new Type(Label).implementing, hasItem(allOf(isA(Type), hasProperty("className", equalTo("IUIComponent")))));
59 | assertThat(new Type(Label).implementing, not(hasItem(hasProperty("className", equalTo("IBorder")))));
60 | }
61 |
62 | [Test]
63 | public function testIsA():void
64 | {
65 | assertThat(new Type(ProgressEvent).isA(ProgressEvent), equalTo(true));
66 | assertThat(new Type(ProgressEvent).isA(Event), equalTo(true));
67 | assertThat(new Type(ProgressEvent).isA(new Type(Event)), equalTo(true));
68 | assertThat(new Type(ProgressEvent).isA(DisplayObject), equalTo(false));
69 | assertThat(new Type(Label).isA(IFlexDisplayObject), equalTo(true));
70 | assertThat(new Type(Label).isA(IBorder), equalTo(false));
71 | }
72 |
73 | [Test]
74 | public function testReflectInstanceOfClass():void
75 | {
76 | var instance:Class = String;
77 | assertThat(Type.reflect(instance).isA(String), equalTo(true));
78 | assertThat(Type.reflect(instance).isA(Class), equalTo(false));
79 |
80 | instance = Class;
81 | assertThat(Type.reflect(instance).isA(Class), equalTo(true));
82 | }
83 | }
84 | }
--------------------------------------------------------------------------------
/src/mesh/core/reflection/Definition.as:
--------------------------------------------------------------------------------
1 | package mesh.core.reflection
2 | {
3 | import flash.errors.IllegalOperationError;
4 |
5 | /**
6 | * A definition is a base class for any property, variable, constant, metadata, or
7 | * method that is specified on a class.
8 | *
9 | * @author Dan Schultz
10 | */
11 | public class Definition
12 | {
13 | /**
14 | * Constructor.
15 | *
16 | * @param name The name of the definition.
17 | * @param belongsTo The definition that this belongs to.
18 | * @param description The XML that describes this definition.
19 | */
20 | public function Definition(description:XML, belongsTo:Definition)
21 | {
22 | _belongsTo = belongsTo;
23 | _description = description;
24 | }
25 |
26 | /**
27 | * Checks that two definitions are equal. Two definitions are equal when they belong
28 | * to the same definition, and their names are equal.
29 | *
30 | * @param definition The definition to check.
31 | * @return true if the definitions are equal.
32 | */
33 | public function equals(definition:Definition):Boolean
34 | {
35 | return this == definition || (definition != null && name == definition.name && belongsTo.equals(definition.belongsTo));
36 | }
37 |
38 | /**
39 | * Returns the name for this definition.
40 | *
41 | * @return The name.
42 | */
43 | public function hashCode():Object
44 | {
45 | return name;
46 | }
47 |
48 | /**
49 | * Returns the name of the definition.
50 | *
51 | * @return The definition's name.
52 | */
53 | public function toString():String
54 | {
55 | return name;
56 | }
57 |
58 | private var _belongsTo:Definition;
59 | /**
60 | * The definition that this definition belongs to.
61 | */
62 | protected function get belongsTo():Definition
63 | {
64 | return _belongsTo;
65 | }
66 |
67 | private var _description:XML;
68 | /**
69 | * The raw XML description for this definition.
70 | */
71 | public function get description():XML
72 | {
73 | return _description;
74 | }
75 |
76 | private var _metadata:Array;
77 | /**
78 | * A list of Metadata definitions that are specified on this definition.
79 | */
80 | public function get metadata():Array
81 | {
82 | if (_metadata == null) {
83 | _metadata = [];
84 |
85 | for each (var metadataXML:XML in description..metadata) {
86 | if (metadataXML.@name.toString() != "__go_to_definition_help") {
87 | _metadata.push(new Metadata(metadataXML, this));
88 | }
89 | }
90 | }
91 | return _metadata.concat();
92 | }
93 |
94 | private var _name:String;
95 | /**
96 | * The name of the definition, such as the property's or method's name.
97 | */
98 | public function get name():String
99 | {
100 | if (_name == null) {
101 | _name = description.@name.toString()
102 | }
103 | return _name;
104 | }
105 | }
106 | }
--------------------------------------------------------------------------------
/src/mesh/core/state/State.as:
--------------------------------------------------------------------------------
1 | package mesh.core.state
2 | {
3 | import flash.events.EventDispatcher;
4 |
5 | /**
6 | * Dispatched when the machine enters this state.
7 | */
8 | [Event(name="enter", type="mesh.core.state.StateEvent")]
9 |
10 | /**
11 | * Dispatched when the machine exits this state.
12 | */
13 | [Event(name="exit", type="mesh.core.state.StateEvent")]
14 |
15 | /**
16 | * An individual state within the state machine.
17 | *
18 | * @author Dan Schultz
19 | */
20 | public class State extends EventDispatcher
21 | {
22 | private var _machine:StateMachine;
23 |
24 | private var _onEnter:Function;
25 | private var _onExit:Function;
26 |
27 | /**
28 | * Constructor.
29 | *
30 | * @param machine The state machine the state belongs to.
31 | * @param name The name for this state.
32 | */
33 | public function State(machine:StateMachine, name:String)
34 | {
35 | super();
36 | _name = name;
37 | _machine = machine;
38 |
39 | addEventListener(StateEvent.ENTER, function(event:StateEvent):void
40 | {
41 | if (_onEnter != null) {
42 | _onEnter();
43 | }
44 | });
45 | addEventListener(StateEvent.EXIT, function(event:StateEvent):void
46 | {
47 | if (_onExit != null) {
48 | _onExit();
49 | }
50 | });
51 | }
52 |
53 | public function equals(state:Object):Boolean
54 | {
55 | return state is State && name == state.name;
56 | }
57 |
58 | /**
59 | * Adds or replaces the callback function that is invoked when the state machine
60 | * enters this state. The function should have the following signature:
61 | * function():void.
62 | *
63 | * @param block A function.
64 | * @return This instance.
65 | */
66 | public function onEnter(block:Function):State
67 | {
68 | _onEnter = block;
69 | return this;
70 | }
71 |
72 | /**
73 | * Called by the state machine when this state becomes the current state.
74 | */
75 | internal function enter():void
76 | {
77 | dispatchEvent( new StateEvent(StateEvent.ENTER, this) );
78 | }
79 |
80 | /**
81 | * Adds or replaces the callback function that is invoked when the state machine
82 | * exits this state. The function should have the following signature:
83 | * function():void.
84 | *
85 | * @param block A function.
86 | * @return This instance.
87 | */
88 | public function onExit(block:Function):State
89 | {
90 | _onExit = block;
91 | return this;
92 | }
93 |
94 | /**
95 | * Called by the state machine when this state is transitioned to a new state.
96 | */
97 | internal function exit():void
98 | {
99 | dispatchEvent( new StateEvent(StateEvent.EXIT, this) );
100 | }
101 |
102 | /**
103 | * @private
104 | */
105 | override public function toString():String
106 | {
107 | return _name;
108 | }
109 |
110 | private var _name:String;
111 | /**
112 | * The state's name.
113 | */
114 | public function get name():String
115 | {
116 | return _name;
117 | }
118 | }
119 | }
--------------------------------------------------------------------------------
/src/mesh/core/BatchedList.as:
--------------------------------------------------------------------------------
1 | package mesh.core
2 | {
3 | /**
4 | * A type of List that does not have all of its elements
5 | * when its first created, but incrementally loads its elements as they're
6 | * requested.
7 | *
8 | * @author Dan Schultz
9 | */
10 | public class BatchedList extends List
11 | {
12 | private var _batches:Array = [];
13 | private var _delegate:IBatchedListDelegate;
14 |
15 | /**
16 | * Constructor.
17 | *
18 | * @param delegate The object responsible for fetching batches.
19 | * @param batchSize The size of each batch.
20 | */
21 | public function BatchedList(delegate:IBatchedListDelegate, batchSize:uint)
22 | {
23 | super();
24 | _delegate = delegate;
25 | _batchSize = batchSize;
26 | }
27 |
28 | private function batchIndexForListIndex(index:int):int
29 | {
30 | return Math.floor(index/batchSize);
31 | }
32 |
33 | /**
34 | * @inheritDoc
35 | */
36 | override public function getItemAt(index:int, prefetch:int=0):Object
37 | {
38 | // The length hasn't been populated yet, so fetch the list length.
39 | if (isNaN(_providedLength)) {
40 | _delegate.requestLength(this);
41 | return undefined;
42 | }
43 |
44 | // The index is in bounds, so try to return the item if it's loaded,
45 | // or request the batch that would contain the item.
46 | if (index >= 0 && index < length) {
47 | var batchIndex:int = batchIndexForListIndex(index);
48 | if (_batches[batchIndex] != null) {
49 | return super.getItemAt(index);
50 | } else {
51 | _delegate.requestBatch(this, batchIndex, batchSize);
52 | return undefined;
53 | }
54 | }
55 |
56 | throw new RangeError(index.toString() + " is out of range.");
57 | }
58 |
59 | /**
60 | * Called by the delegate to insert the elements from a fetch.
61 | *
62 | * @param arrayOrList The elements of the batch.
63 | * @param index The index to insert the elements at.
64 | */
65 | public function provideBatch(arrayOrList:Object, index:int):void
66 | {
67 | _batches[index] = arrayOrList;
68 | addAllAt(arrayOrList, index > length ? length : index);
69 | }
70 |
71 | /**
72 | * Delegates call this method to provide the total number of elements
73 | * in the list.
74 | *
75 | * @param length The number of elements in the list.
76 | */
77 | public function provideLength(length:Number):void
78 | {
79 | _providedLength = length;
80 | }
81 |
82 | /**
83 | * Resets the elements in this list and forces the elements to be reloaded.
84 | */
85 | public function reset():void
86 | {
87 | removeAll();
88 | _batches = [];
89 | _providedLength = NaN;
90 | }
91 |
92 | private var _batchSize:uint;
93 | /**
94 | * The size of each batch in a request.
95 | */
96 | public function get batchSize():uint
97 | {
98 | return _batchSize;
99 | }
100 |
101 | private var _providedLength:Number;
102 | /**
103 | * @inheritDoc
104 | */
105 | override public function get length():int
106 | {
107 | if (isNaN(_providedLength)) {
108 | _delegate.requestLength(this);
109 | return 0;
110 | }
111 | return _providedLength;
112 | }
113 | }
114 | }
--------------------------------------------------------------------------------
/src/mesh/model/validators/NumericValidator.as:
--------------------------------------------------------------------------------
1 | package mesh.model.validators
2 | {
3 | import mesh.core.inflection.humanize;
4 |
5 | /**
6 | * Validates if a property's value is a number and also the bounds of that value. This validator has
7 | * the following options:
8 | *
9 | * integeroddevenequalTogreaterThangreaterThanOrEqualTolessThanlessThanOrEqualTobetween:i..k123.45 has
8 | * a precision of 2 when significant is false, and a precision of
9 | * 5 when significant is true.
10 | *
11 | * 12 | * Options: 13 | *
precision:int - The precision of the number. (default=2)significant:Boolean - If true, the precision will
16 | * be the number of significant digits. (default=false)stripInsignificantZeros:Boolean - If true, all insignificant
18 | * zeros after the separator will be removed. (default=false)delimiter:String - The thousands delimiter. (default=",")separator:String - The separator between the integer and fractions.
21 | * (default=".")26 | * Examples: 27 | *
ExternalData class wraps data that is retrieved from a data source and is
13 | * inserted into the store.
14 | *
15 | * @author Dan Schultz
16 | */
17 | public dynamic class Data extends Proxy
18 | {
19 | private var _data:Object;
20 | private var _options:Object;
21 |
22 | /**
23 | * Constructor. When instantiating a new data object, you can pass an options hash with the
24 | * following options:
25 | *
26 | * idField:String - (default="id") The name of the ID field on
28 | * the data object.deserializer:Function - A function that's called when transfering the
30 | * values from the data to a record. The function must have the following signature:
31 | * function(record:Record, data:Object):void.
32 | * true if the data is equal.
52 | */
53 | public function equals(obj:Object):Boolean
54 | {
55 | if (obj is Data) {
56 | return (key == (obj as Data).key) || (type == (obj as Data).type && id == (obj as Data).id);
57 | }
58 | return false;
59 | }
60 |
61 | /**
62 | * @inheritDoc
63 | */
64 | override flash_proxy function getProperty(name:*):*
65 | {
66 | return _data[name];
67 | }
68 |
69 | /**
70 | * Returns the key that has been given to this data;
71 | *
72 | * @return The data's key.
73 | */
74 | public function hashCode():Object
75 | {
76 | return key;
77 | }
78 |
79 | /**
80 | * @inheritDoc
81 | */
82 | override flash_proxy function setProperty(name:*, value:*):void
83 | {
84 | _data[name] = value;
85 | }
86 |
87 | private function transfer(record:Record, data:Object):void
88 | {
89 | for (var key:String in data) {
90 | if (record.hasOwnProperty(key)) {
91 | try {
92 | record[key] = data[key];
93 | } catch (e:Error) {
94 |
95 | }
96 | }
97 | }
98 | }
99 |
100 | /**
101 | * Transfers the values on this data to the given object.
102 | *
103 | * @param to The object to set the values on.
104 | */
105 | public function transferValues(to:Object):void
106 | {
107 | _options.deserializer(to, _data);
108 | to.id = id;
109 | }
110 |
111 | /**
112 | * The ID that was assigned to this data by the data source.
113 | */
114 | public function get id():Object
115 | {
116 | return _data[_options.idField];
117 | }
118 |
119 | private var _key:Object;
120 | /**
121 | * A global unique key given to this data by the store.
122 | */
123 | public function get key():Object
124 | {
125 | if (_key == null) {
126 | _key = UIDUtil.createUID();
127 | }
128 | return _key;
129 | }
130 |
131 | private var _type:Class;
132 | /**
133 | * The record type that maps to this data.
134 | */
135 | public function get type():Class
136 | {
137 | return _type;
138 | }
139 | }
140 | }
--------------------------------------------------------------------------------
/src/mesh/core/proxy/DataProxy.as:
--------------------------------------------------------------------------------
1 | package mesh.core.proxy
2 | {
3 | import flash.events.Event;
4 | import flash.events.EventDispatcher;
5 | import flash.events.IEventDispatcher;
6 | import flash.utils.Proxy;
7 | import flash.utils.flash_proxy;
8 |
9 | import mx.events.PropertyChangeEvent;
10 |
11 | use namespace flash_proxy;
12 |
13 | /**
14 | * The DataProxy class represents a base Proxy implementation. A
15 | * proxy wraps an arbitrary object and forwards function and property calls to it. In
16 | * addition, this proxy will forward any property change events dispatched by its wrapped
17 | * object. This allows for UI controls to bind directly to the proxy class.
18 | *
19 | * @author Dan Schultz
20 | */
21 | public dynamic class DataProxy extends Proxy implements IEventDispatcher
22 | {
23 | private var _dispatcher:EventDispatcher;
24 |
25 | /**
26 | * Constructor.
27 | */
28 | public function DataProxy(obj:Object = null)
29 | {
30 | super();
31 | _dispatcher = new EventDispatcher(this);
32 | object = obj;
33 | }
34 |
35 | /**
36 | * @inheritDoc
37 | */
38 | override flash_proxy function callProperty(name:*, ...parameters):*
39 | {
40 | if (object != null) {
41 | return object[name].apply(null, parameters);
42 | }
43 | return undefined;
44 | }
45 |
46 | /**
47 | * @inheritDoc
48 | */
49 | override flash_proxy function getProperty(name:*):*
50 | {
51 | if (object != null) {
52 | return object[name];
53 | }
54 | return undefined;
55 | }
56 |
57 | private function handlePropertyChange(event:PropertyChangeEvent):void
58 | {
59 | dispatchEvent(event);
60 | }
61 |
62 | /**
63 | * @inheritDoc
64 | */
65 | override flash_proxy function hasProperty(name:*):Boolean
66 | {
67 | try {
68 | return getProperty(name) !== undefined;
69 | } catch (e:Error) {
70 |
71 | }
72 | return false;
73 | }
74 |
75 | /**
76 | * @inheritDoc
77 | */
78 | override flash_proxy function setProperty(name:*, value:*):void
79 | {
80 | if (object != null) {
81 | object[name] = value;
82 | }
83 | }
84 |
85 | private var _object:*;
86 | [Bindable]
87 | /**
88 | * The object being proxied.
89 | */
90 | flash_proxy function get object():*
91 | {
92 | return _object;
93 | }
94 | flash_proxy function set object(value:*):void
95 | {
96 | if (_object is IEventDispatcher) {
97 | (_object as IEventDispatcher).removeEventListener(PropertyChangeEvent.PROPERTY_CHANGE, handlePropertyChange);
98 | }
99 |
100 | _object = value;
101 |
102 | if (_object is IEventDispatcher) {
103 | (_object as IEventDispatcher).addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, handlePropertyChange);
104 | }
105 | }
106 |
107 | /**
108 | * @inheritDoc
109 | */
110 | public function addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void
111 | {
112 | _dispatcher.addEventListener(type, listener, useCapture, priority, useWeakReference);
113 | }
114 |
115 | /**
116 | * @inheritDoc
117 | */
118 | public function dispatchEvent(event:Event):Boolean
119 | {
120 | return _dispatcher.dispatchEvent(event);
121 | }
122 |
123 | /**
124 | * @inheritDoc
125 | */
126 | public function hasEventListener(type:String):Boolean
127 | {
128 | return _dispatcher.hasEventListener(type);
129 | }
130 |
131 | /**
132 | * @inheritDoc
133 | */
134 | public function removeEventListener(type:String, listener:Function, useCapture:Boolean = false):void
135 | {
136 | _dispatcher.removeEventListener(type, listener, useCapture);
137 | }
138 |
139 | /**
140 | * @inheritDoc
141 | */
142 | public function willTrigger(type:String):Boolean
143 | {
144 | return _dispatcher.willTrigger(type);
145 | }
146 | }
147 | }
--------------------------------------------------------------------------------
/src/mesh/model/associations/HasAssociation.as:
--------------------------------------------------------------------------------
1 | package mesh.model.associations
2 | {
3 | import flash.errors.IllegalOperationError;
4 |
5 | import mesh.model.ID;
6 | import mesh.model.Record;
7 |
8 | import mx.events.PropertyChangeEvent;
9 |
10 | /**
11 | * The base class for any association that links to a single record.
12 | *
13 | * @author Dan Schultz
14 | */
15 | public class HasAssociation extends Association
16 | {
17 | /**
18 | * @copy AssociationProxy#AssociationProxy()
19 | */
20 | public function HasAssociation(owner:Record, property:String, options:Object = null)
21 | {
22 | super(owner, property, options);
23 | checkForRequiredFields();
24 | owner.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, handleOwnerPropertyChange);
25 | }
26 |
27 | /**
28 | * @inheritDoc
29 | */
30 | override protected function associate(record:Record):void
31 | {
32 | super.associate(record);
33 | populateForeignKey();
34 | record.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, handleAssociatedRecordPropertyChange);
35 | }
36 |
37 | private function checkForRequiredFields():void
38 | {
39 | if (recordType == null) throw new IllegalOperationError("Undefined record type for " + this);
40 | if (options.foreignKey != null && !owner.hasOwnProperty(options.foreignKey)) throw new IllegalOperationError("Undefined foreign key '" + options.foreignKey + " for " + this);
41 | }
42 |
43 | private function handleAssociatedRecordPropertyChange(event:PropertyChangeEvent):void
44 | {
45 | if (event.property == "id") {
46 | populateForeignKey();
47 | }
48 | }
49 |
50 | private function handleOwnerPropertyChange(event:PropertyChangeEvent):void
51 | {
52 | if (event.property == foreignKey) {
53 | populateRecord();
54 | }
55 | }
56 |
57 | private function populateRecord():void
58 | {
59 | owner[property] = ID.isPopulated(owner, foreignKey) ? store.query(recordType).find(owner[foreignKey]) : null;
60 | }
61 |
62 | private function populateForeignKey():void
63 | {
64 | // If the foreign key is undefined, try to automagically set it.
65 | var key:String = foreignKey;
66 |
67 | if (owner.hasOwnProperty(key)) {
68 | owner[key] = _record.id;
69 | }
70 | }
71 |
72 | private function unassociateForeignKey():void
73 | {
74 | // If the foreign key is undefined, try to automagically set it.
75 | var key:String = foreignKey;
76 |
77 | if (owner.hasOwnProperty(key)) {
78 | owner[key] = null;
79 | }
80 | }
81 |
82 | /**
83 | * @inheritDoc
84 | */
85 | override protected function unassociate(record:Record):void
86 | {
87 | super.unassociate(record);
88 | record.removeEventListener(PropertyChangeEvent.PROPERTY_CHANGE, handleAssociatedRecordPropertyChange);
89 | unassociateForeignKey();
90 | }
91 |
92 | /**
93 | * The property on the owner that defines the foreign key to load this association.
94 | */
95 | public function get foreignKey():String
96 | {
97 | return options.foreignKey != null ? options.foreignKey : property + "Id";
98 | }
99 |
100 | /**
101 | * The associated type of record. If the type is not defined as an option, then the association
102 | * will look up the type defined on the record through reflection.
103 | */
104 | protected function get recordType():Class
105 | {
106 | try {
107 | return options.recordType != null ? options.recordType : owner.reflect.property(property).type.clazz;
108 | } catch (e:Error) {
109 |
110 | }
111 | return null;
112 | }
113 |
114 | private var _record:Record;
115 | /**
116 | * @inheritDoc
117 | */
118 | override public function set object(value:*):void
119 | {
120 | if (value != _record) {
121 | if (_record != null) unassociate(_record);
122 | _record = value;
123 | super.object = _record;
124 | if (_record != null) associate(_record);
125 | }
126 | }
127 | }
128 | }
--------------------------------------------------------------------------------
/src/mesh/core/number/Fraction.as:
--------------------------------------------------------------------------------
1 | package mesh.core.number
2 | {
3 | /**
4 | * The Fraction class represents a number that is part of a larger whole.
5 | * This class stores fractions in the form of a numerator and denominator.
6 | *
7 | * @author Dan Schultz
8 | */
9 | public class Fraction
10 | {
11 | /**
12 | * Constructor.
13 | *
14 | * @param numerator The numerator of the fraction.
15 | * @param denominator The denominator of the fraction.
16 | */
17 | public function Fraction(numerator:int, denominator:int)
18 | {
19 | _numerator = numerator;
20 | _denominator = denominator;
21 | }
22 |
23 | /**
24 | * Calculates the greatest common divisor of two integers using Stein's algorithm.
25 | *
26 | * @param a The first integer.
27 | * @param b The second integer.
28 | * @return The greatest common divisor of a and b.
29 | * @see http://en.wikipedia.org/wiki/Binary_GCD_algorithm Stein's algorithm
30 | */
31 | public static function gcd(a:int, b:int):int
32 | {
33 | a = Math.abs(a);
34 | b = Math.abs(b);
35 |
36 | if (a == b) {
37 | return b;
38 | }
39 | if (a == 0) {
40 | return b;
41 | }
42 | if (b == 0) {
43 | return a;
44 | }
45 |
46 | if (a%2 == 0) { // if u is even
47 | if (b%2 == 0) { // if u and v are even
48 | return (2*gcd(a/2, b/2));
49 | } else { // u is even and v is odd
50 | return gcd(a/2, b);
51 | }
52 | } else if (b%2 == 0) { // if u is odd and v is even
53 | return gcd(a, b/2);
54 | }
55 |
56 | if (a >= b) {
57 | return gcd((a-b)/2, b);
58 | }
59 | return gcd((b-a)/2, a);
60 | }
61 |
62 | /**
63 | * Calculates the lowest common multiple of two integers using Euclid's algorithm.
64 | *
65 | * @param a The first integer.
66 | * @param b The second integer.
67 | * @return The lowest common multiple of a and b.
68 | * @see http://en.wikipedia.org/wiki/Greatest_common_divisor Euclid's algorithm
69 | */
70 | public static function lcm(a:int, b:int):int
71 | {
72 | return gcd(b, a%b);
73 | }
74 |
75 | /**
76 | * Checks if two fractions or numbers are equal.
77 | *
78 | * @param obj The object to check.
79 | * @return true if the fraction or number are equal.
80 | */
81 | public function equals(obj:Object):Boolean
82 | {
83 | return obj != null && obj.valueOf() === value;
84 | }
85 |
86 | /**
87 | * Returns a string representation of this fraction in the form: numerator + "/" + denominator.
88 | *
89 | * @return A string.
90 | */
91 | public function toString():String
92 | {
93 | return numerator + "/" + denominator;
94 | }
95 |
96 | /**
97 | * Returns a decimal representation of this fraction.
98 | *
99 | * @return A decimal.
100 | */
101 | public function toNumber():Number
102 | {
103 | return value;
104 | }
105 |
106 | /**
107 | * Returns a representation of this fraction as a percentage where 100 represents 1/1.
108 | *
109 | * @return A percentage.
110 | */
111 | public function toPercentage():Number
112 | {
113 | return value*100;
114 | }
115 |
116 | /**
117 | * The primitive value of this fraction, which is a decimal.
118 | *
119 | * @return A decimal.
120 | */
121 | public function valueOf():Object
122 | {
123 | return value;
124 | }
125 |
126 | private var _denominator:int;
127 | /**
128 | * The fractions denominator.
129 | */
130 | public function get denominator():int
131 | {
132 | return _denominator;
133 | }
134 |
135 | private var _numerator:int;
136 | /**
137 | * The fractions numerator.
138 | */
139 | public function get numerator():int
140 | {
141 | return _numerator;
142 | }
143 |
144 | private function get value():Number
145 | {
146 | return numerator/denominator;
147 | }
148 | }
149 | }
--------------------------------------------------------------------------------
/src/mesh/model/store/QueryBuilder.as:
--------------------------------------------------------------------------------
1 | package mesh.model.store
2 | {
3 | import mesh.model.source.DataSource;
4 |
5 | public class QueryBuilder
6 | {
7 | private var _dataSource:DataSource;
8 | private var _records:RecordCache;
9 | private var _recordType:Class;
10 |
11 | public function QueryBuilder(dataSource:DataSource, records:RecordCache, recordType:Class)
12 | {
13 | _dataSource = dataSource;
14 | _records = records;
15 | _recordType = recordType;
16 | }
17 |
18 | /**
19 | * Generates a new query that fetches a single record by its ID.
20 | *
21 | * @param id The ID of the record to fetch.
22 | * @return A query.
23 | */
24 | public function find(id:Object):*
25 | {
26 | return new FindQuery(_dataSource, _records, _recordType, id).execute();
27 | }
28 |
29 | /**
30 | * Generates a new query that fetches all records of a single type.
31 | *
32 | * @return A result list.
33 | */
34 | public function findAll():*
35 | {
36 | return new FindAllQuery(_dataSource, _records, _recordType).execute();
37 | }
38 |
39 | /**
40 | * Returns a new result list that contains the records that meet the given conditions.
41 | *
42 | * @param conditions The conditions for the record.
43 | * @return A result list.
44 | */
45 | public function where(conditions:Object):*
46 | {
47 | return new WhereQuery(_dataSource, _records, _recordType, conditions).execute();
48 | }
49 | }
50 | }
51 |
52 | import mesh.core.reflection.newInstance;
53 | import mesh.mesh_internal;
54 | import mesh.model.Record;
55 | import mesh.model.source.DataSource;
56 | import mesh.model.source.DataSourceRetrievalOperation;
57 | import mesh.model.store.Query;
58 | import mesh.model.store.RecordCache;
59 | import mesh.model.store.ResultsList;
60 |
61 | import mx.collections.ListCollectionView;
62 |
63 | use namespace mesh_internal;
64 |
65 | class FindQuery extends Query
66 | {
67 | private var _id:Object;
68 |
69 | public function FindQuery(dataSource:DataSource, records:RecordCache, recordType:Class, id:Object)
70 | {
71 | super(dataSource, records, recordType);
72 | _id = id;
73 | }
74 |
75 | override public function execute():*
76 | {
77 | var record:Record = records.findIndex(recordType).byId(_id);
78 |
79 | // The record doesn't belong to the store. We need to retrieve it from the data source.
80 | if (record == null) {
81 | record = newInstance(recordType);
82 | record.id = _id;
83 | records.insert(record);
84 | }
85 |
86 | return record;
87 | }
88 | }
89 |
90 | class FindAllQuery extends Query
91 | {
92 | private var _results:ResultsList;
93 |
94 | public function FindAllQuery(dataSource:DataSource, records:RecordCache, recordType:Class)
95 | {
96 | super(dataSource, records, recordType);
97 | }
98 |
99 | override public function execute():*
100 | {
101 | if (_results == null) {
102 | _results = new ResultsList(records.findIndex(recordType), new DataSourceRetrievalOperation(records, dataSource.retrieveAll, [recordType]));
103 | }
104 | return _results;
105 | }
106 | }
107 |
108 | class WhereQuery extends Query
109 | {
110 | private var _results:ResultsList;
111 | private var _conditions:Object;
112 |
113 | public function WhereQuery(dataSource:DataSource, records:RecordCache, recordType:Class, conditions:Object)
114 | {
115 | super(dataSource, records, recordType);
116 | _conditions = conditions;
117 | }
118 |
119 | override public function execute():*
120 | {
121 | if (_results == null) {
122 | var collection:ListCollectionView = new ListCollectionView(records.findIndex(recordType));
123 | collection.filterFunction = function(record:Record):Boolean
124 | {
125 | for (var property:String in _conditions) {
126 | if (record[property] != _conditions[property]) {
127 | return false;
128 | }
129 | }
130 | return true;
131 | };
132 | collection.refresh();
133 | _results = new ResultsList(collection, new DataSourceRetrievalOperation(records, dataSource.search, [recordType, _conditions]));
134 | }
135 | return _results;
136 | }
137 | }
--------------------------------------------------------------------------------
/src/mesh/operations/FileReferenceUploadOperation.as:
--------------------------------------------------------------------------------
1 | package mesh.operations
2 | {
3 | import flash.events.DataEvent;
4 | import flash.events.Event;
5 | import flash.events.IOErrorEvent;
6 | import flash.events.ProgressEvent;
7 | import flash.events.SecurityErrorEvent;
8 | import flash.net.FileReference;
9 | import flash.net.URLRequest;
10 |
11 | import mesh.core.object.merge;
12 |
13 | /**
14 | * An operation executes an upload on a FileReference. This operation defines
15 | * a set of options that are passed to FileReference.upload(). These include:
16 | *
17 | * uploadDataFieldName:String - (default = "Filedata") See
19 | * FileReference.upload() for more information on this option.testUpload:Boolean - (default = false) See
21 | * FileReference.upload() for more information on this option.finishOn:String - (default = Event.COMPLETE) The event
23 | * that signifies when this operation is finished. FileReference is capable
24 | * of dispatching two complete events: Event.COMPLETE and
25 | * DataEvent.UPLOAD_COMPLETE_DATA. Set this option to DataEvent.UPLOAD_COMPLETE_DATA
26 | * if you want the operation to not finish until data has been sent back from the upload
27 | * server.FileReference.upload()
31 | * @author Dan Schultz
32 | */
33 | public class FileReferenceUploadOperation extends NetworkOperation
34 | {
35 | private var _file:FileReference;
36 | private var _request:URLRequest;
37 | private var _options:Object;
38 |
39 | /**
40 | * Constructor.
41 | *
42 | * @param file The file to upload.
43 | * @param request The request that contains the URL to upload to.
44 | * @param options Options to pass to FileReference.upload().
45 | */
46 | public function FileReferenceUploadOperation(file:FileReference, request:URLRequest, options:Object = null)
47 | {
48 | super();
49 | _file = file;
50 | _file.addEventListener(ProgressEvent.PROGRESS, handleFileProgress);
51 | _file.addEventListener(IOErrorEvent.IO_ERROR, handleFileIOError);
52 | _file.addEventListener(SecurityErrorEvent.SECURITY_ERROR, handleFileSecurityError);
53 | _file.addEventListener(DataEvent.UPLOAD_COMPLETE_DATA, handleFileCompleteWithData);
54 | _file.addEventListener(Event.COMPLETE, handleFileComplete);
55 |
56 | _request = request;
57 | _options = merge({uploadDataFieldName:"Filedata", testUpload:false, finishOn:Event.COMPLETE}, options);
58 | }
59 |
60 | /**
61 | * @inheritDoc
62 | */
63 | override protected function cancelRequest():void
64 | {
65 | super.cancelRequest();
66 | _file.cancel();
67 | }
68 |
69 | private function canFinishWithEvent(event:Event):Boolean
70 | {
71 | return event.type == _options.finishOn;
72 | }
73 |
74 | /**
75 | * @inheritDoc
76 | */
77 | override protected function request():void
78 | {
79 | super.request();
80 |
81 | _file.cancel();
82 | _file.upload(_request, _options.uploadDataFieldName, _options.testUpload);
83 | }
84 |
85 | private function handleFileProgress(event:ProgressEvent):void
86 | {
87 | progressed(event.bytesLoaded);
88 | }
89 |
90 | private function handleFileIOError(event:IOErrorEvent):void
91 | {
92 | fault(event.text, event.text);
93 | }
94 |
95 | private function handleFileSecurityError(event:SecurityErrorEvent):void
96 | {
97 | fault(event.text, event.text);
98 | }
99 |
100 | private function handleFileCompleteWithData(event:DataEvent):void
101 | {
102 | result(event.data);
103 | }
104 |
105 | private function handleFileComplete(event:Event):void
106 | {
107 | if (canFinishWithEvent(event)) {
108 | finish(true);
109 | }
110 | }
111 |
112 | /**
113 | * @inheritDoc
114 | */
115 | override protected function get unitsTotal():Number
116 | {
117 | return _file.size;
118 | }
119 | }
120 | }
--------------------------------------------------------------------------------