-
Notifications
You must be signed in to change notification settings - Fork 1
Model
A model is a class representing the business logic of your application. Models are subclasses of ISModel
class. An ISModel
instance has a dictionary of key/value object called attributes. You can add an attribute, clear it, or update it.
When a model instance is represented by a server-backed instance, these attributes are populated from the server when you do fetch on the model instance.
For example, if you have a database table on the server called notes, a Note instance will represent a specific-row in the database to be used by the iOS client. If the notes table has three columns (title, body, and create_at), then when you fetch the model instance, the attributes dictionary will have three key/value entries, each for one of these column values.
Usually, server-backed applications utilize some sort of "id" column to uniquely identify a given row in a table. In this case, an "id" attribute is added to the attributes dictionary. By default, the "id" key is called id. However, you can change this
behavior in your subclass of ISModel
by overriding idAttribute
method and returning the key value (e.g., @"name").
In your subclass, you can override the (NSDictionary*) defaults
method to return default values of your model when it's created. The default behavior is to return nil
.
Whatever you use for the "id" key, an id
property is always present in the model instance and synchronized with the "id" attribute. A model instance is considered new if its id
property is nil
.
The method has:(NSString*) attr
returns YES
if the model instance has a attribue attr
.
To retrieve the value for a given attribue, use get:(NSString*) attr
.
To remove an attribute, use unSet:(NSString*) attr
.
Whenever a given attribute is changed (set, unset, updated), two events are triggered; one for the attribute and the other for the model itself. For example, if you change the value of the attribute name
, a @"change:name" and @"change" events are triggered by the model instance. Other components of your app (such as views) can $watch()
this model instance and update themselves accordingly.
Methods that change attributes take an optional second argument called options in the form of a dictionary. For example, here is the declaration of the set:withOptions method:
- (ISModel*) set:(NSDictionary*) attrs withOptions:(NSDictionary*) options
The most important option is the silent option. Its key is SILENT_KEY
. The 'IS_SILENT' macro is a convientient macro that helps you retrieve the truthiness of this option as shown below:
BOOL silent = IS_SILENT(options);
If you pass in this key in the options of an operation (e.g., set) with the value YES
, no errors are triggered nor any errors handlers are invoked (see discussion below).
A model can implement a validate:
method. Whenever we change the attributes object of a given model, the validate:
method is invoked. Inside this method, you can do some business-level validations. If this method returns an array of one or more errors, the change of attributes operation fails and either the ValidationErrorHandler
is invoked (if present) or an "error" message is triggered by the model instance. You can access the array of errors from the triggered message using the ERRORS_KEY
key in the notification of the $watch()
block handler.
The ValidationErrorHandler
that can be passed in the options of any method that changes the attributes and is invoked whenever the model fails the validation is decalared as follows:
typedef void(^ValidationErrorHandler)(id origin, NSArray *errors, NSDictionary* options);
Whenever a model fails validation, it invokes this handler block passing itself as the origin
, the errors array from the validate:
as errors
and the options in the original method invocation as options
.
The following shows an example of a validate:
method:
- (NSArray*) validate:(NSDictionary*) attrs{
NSString *title = [attrs objectForKey:@"title"];
NSMutableArray *arr = $marray();
if(title && [title length] < 5){
[arr addObject:@"Title cannot be less than 5 characters"];
}
return arr;
}
All model and collection instances have a unique client id that can be accessed by the cid
property of the instance.
A model instance can belong to a collection instance. This linkage is maintained by the collection
property of the model.
For a model that is server-backed, it needs to provide a url value. The url
method, by default, looks to see if the model is inside a collection, if that's the case and the model is new, the collection's url' becomes the url value. If there is a collection and the model is not new, the collection's
url' and the encoded value of the model's id
value becomes the model's url. For example /notes/1
is the url
value returned where /notes
is the collection's url
and 1
is the id
value of the model instance.
If the model does not belong to a collection, then the value of its urlRoot
method is substituted and used as above instead of the collection's url
. Otherwise, an exception is thrown.
For the complete url to be used in accessing the server-backed version of the model instance, a base url is prepended.
This base url can be empty (provided the url value calculated above is sufficient) or it can be something else.
The value of the base url is provided by the baseUrl
instance method of the model. The default, it returns the collection's
baseUrl
. If the model does not have a collection, it looks to see if the model's instance has one stored in its modelBaseUrl
instance variable and returns it. If not, it looks for it in the model's class _baseUrl
static value.
You can set the instance value using the setBaseUrl:(NSString*) url
instance method. You can set the class base url using the `setBaseUrl:(NSString*) _url' class method.
In addtion, you can provide fine-grained model urls for the four different synchronization methods by overriding the following methods as follows:
- (NSString*) fetchUrl{
return nil;
}
- (NSString*) createUrl{
return nil;
}
- (NSString*) updateUrl{
return nil;
}
- (NSString*) destroyUrl{
return nil;
}
If an operation (such as fetch
) is invoked on the instance and the corresponding url method (e.h., fetchUrl
) does not return nil
, the value returned is used instead of the value returned from the url
instance method. The base url is calculated as normal.
To fetch a model instance is to go to the server and retrieve the attributes and set them on the model instance. The fetch method is declared as follows:
- (void) fetch:(NSDictionary*) options
The following options can be used:
-
'SUCCESS_HANDLER_KEY'. This is a block of type
SuccessHandler
to be invoked when the retrieval of the model is successful.SuccessHandler
is declared as follows:typedef void(^SuccessHandler)(id model, NSData *data);
-
FAILURE_HANDLER_KEY
. This is a block of typeErrorHandler
to be invoked when the retrieval of the model fails.ErrorHandler
is declared as follows:typedef void(^ErrorHandler)(id model, NSArray *errors);
After contacting the server, the error handler is invoked in the case of having network or app erros. An example of a network error is if the server is down. Models can define their own app errors in their customized Sync class. If there is an error and the error handler is not specified, an "error" event is triggered by the model.