A start point for all programms is a public static void main(String[] args)
. Start point
for web applications is a route
.
Sirius.js
have a four types of routes.
Obviously first it's common url, when hash change, it's call bound method. Example:
Obviously first it's common url, when hash change, it's call bound method. Example:
"#/:action" : {controller: MyController, action: "my_action" }
And when our url address equal #/anything
will be call MyController.my_action
Other type of routes, is a 404
. When in url changed, but does not have a suitable route,
then will be called (if defined) 404 route.
404: function() { alert("404 Not Found"); }
Third types of routing is a event. Event in application it's the same as url changes.
"click #my-id" : {controller: MyController, action: "my_event_action"}
Fourth type is a custom event:
"custom:event" : {controller: MyController, action: "my_custom_event_action"}
As you can see, routing contain two parts: left
and right
.
In left part you may define route for url changes, 404, events, and custom events.
When it's a url changes, then you may define parameters inside url and they will be passed into action
:
"#/static" //call when url will be equal #/static "#/:title" //when url equal #/anything, and in bound action, pass parameter "#/:title/[0-9]+" //when url #/anything/1 then in action pass "anything" and "1", but //when url is a #/anything/not-number then route ignored "#/a/b/*" //when is route start with #/a/b/c/d/ and in bounded method pass "c", "d", ...
When it's a event, then must be start with eventname
, and end with selector
:
"click #id" "dblclick div.my-class" "keypress input"
Under the hood, it's only bind you selector
with given eventname
.
When it's a custom event, then must be contain colon :
When it's a 404
it's just 404
:)
It's all about left part.
Right part it's must be function
or object
. When it's a function everything is just.
But when it's object...
Object must be containcontroller
and action
fields. These fields are required.
"#/" : { controller: MyController, action: "my_action" }
Also object might be contain a helpers: after\before\guard\data
.
You might define in object
needful method that will be called, before action
or after..
var MyController = { action: function() { }, before_my_action: function() { } after_my_action: function() { } } //in routes "#" : { controller: MyController, action: "action", before: "before_my_action", after: "after_my_action" }
Then the sequence of calls:
before_my_action
action
after_my_action
But when you named before\after function as before_X
or after_X
,
where X
is name of action
, then you can not specify it's in object:
var MyController = { action: function() { }, before_action: function() { } after_action: function() { } } //in routes "#" : { controller: MyController, action: "action" }
Calls:
before_action
action
after_action
And finally before
or after
might be a simple function.
"#" : { controler: MyController, action: "action", before: function() { ... }, after: function() { ... } }
It's often necessary to limit calls only when something happened - password entry, image loaded, etc...
For it you might create a guard function, for example for Enter
pressed:
var MyController { action: function() {} before_action: function() {}, after_action: function() {}, guard_action: function(event) { if (event.which == 13) //Enter return true; return false; } } "keypress input" : { controller: MyController, action: "action", guard: "guard_action" } // or automatically find guard function "keypress input" : { controller: MyController, action: "action"}
Then when you press `Enter` then action will be called: Calls:
guard_action
before_action
action
after_action
Otherwise only:
guard_action
When you click on element, with high probability we can say that you write the next code:
//jQuery //click on element function(event) { var target = $(event.target).attr("id"); //or class or data-* or ... }
With sirius.js
you may extract data from target
with:
//1 "click element" : { controller: Controller, action: "action", data: ["id", "class"] } //or 2 "click element" : { controller: Controller, action: "action", data: "data-id" }
Controller.action
var Controller = { action: function(event, param1, param2) { // param1=id param2= class for 1) and param1 = data-id for 2. ... } }
That's all you need to know about routing.
As you can see, controllers it's only object with methods.
var MyController = { action0 : function() {}, action1 : function() {}, other_action : function() {} }
You might use any templates for Sirius
applcations. EJS
, underscore.template
or others.
Collections - is a wrap on javascript arrays. It's have a methods for server synchronization, methods for manipulation collection, and methods for traversal.
1 2 3 4 5 6 | constructor: (klass, klasses, options = { every: 0 on_add : (model) -> on_remove: (model) -> remote : () -> }) |
Sirius.BaseModel
every
time, it's must be return a
json with models, after this, it json will be converted into actual models and add into collectionclass Person
:
1 2 3 4 5 | class Person @attrs: ["id", "name"] @guid_for: "id" compare: (other_person) -> other_person.get("id") == @.get("id") |
Sirius.Collection
for storage all Person
.
The simple example
1 | people = new Sirius.Collection(Person) |
Collection
:
1 2 3 4 5 6 | people.add(new Person({name : "Joe"}) ann = new Person({name: "Ann"}) people.push(ann) # push alias for add people.size() # => 2 people.remove(mike) people.size() # => 1 |
collection.sync(every)
method, after it, start synchronized with server.
For stop synchronization, call collection.unsync()
.
find
, find_all
, filter
and others, see in documentation.
1 2 3 4 5 6 7 8 9 10 11 12 | people.find("name", "Joe") # => model or null people.find("name", "Joe") # => array of models people.each((model) -> ...) people.filter((model) -> model.name == "Ann") # => [ models... ] people.index(model) # => numeric or null people.size() # => actual size of collection people.all() # => [ all models ... ] |
With Sirius
you might use any model library, or built in.
Example:
class Person @attrs : ["id", {"name" : "Nobody"}, "age"] // 1 @guid_for : "id" // 2 @form_name : "form-name" // 3 @has_many : ["group"] // 4 @has_one : ["city"] // 5 @validate: // 6 name : format : with: /^[A-Z].+/ presence : true length : min: 3 age: numericality: only_integers: true validate_with: (value) -> #...validate true @to: // 7 id: tag: "b", class: "model-guid" to_string: () -> // 8 "My name is #{@.get('name'}" class Group extends BaseModel @attrs: ["name", "person_id"] @belongs_to: [{model: "person", back: "id"}] // 9 @to: name: tag: "span" person = new Person({age: "abc"}) // 10 person.get("age") # => "abc" person.get("name") # => Nobody person.valid() # => false person.save() # => false, not valid age person.set("age", 25) person.save() # => true person.set("name", "qwerty") person.save(true) # => exception! person.add_group(new Gropup()) person.add_city(new City()) # => ok person.add_city(new City()) # => exception, already exist person.to_html() # => <b class='model-guid'>-guid-</b>...
@attrs
it's array with attributes names, when element is object, then key is a attribute name,
and value is a default value for attribute
@guid_for
s for "id" (or any specific attribute) will be generate guids
@form_nam
use for find element with this name, and convert from html into Model
@has_many
array with model names, and when create a model,
then automatically generate the next methods: add_x
, where x
is a models from @has_many
@has_one
, array with model name, generate add_x
methods,
but specify model determine only once
@validate
, object with basic validators, or with user defined.
@to
it's use for convert to html,
define as object { "attribute" : key-value }
, key is a "tag"
,
when not defined then it's a div
, and other keys it's attributes for html element
to_string
- user defined function
@belongs_to
- must contain objects with model name, and
back
is a attribute for model, and it's for set model_back
(for example: person_id
) attribute
age
Default validators and parameters
Convert model into array of element instances
var m = new MyModel({"id": 10, "title": "my title", "description": "text..."}); m.to_html() // => "<b class = 'my-model-id'>10</b><span class = 'my-model-title'>my title</span><div>text...</div>" person.to_html() // => "<b class='person-id'>1</b> <span class='person-name'>Abc</span> <p class = 'group'> <span>group-0</span> <div>1</div> <span>group-1</span> <div>1</div> </p>
//html form <form name="my_model" id="my-model-form"> <input type="hidden" name="id" class="my-id" value="1" /> <input type="text" name="title" class="title" value="new title" /> <textarea name="description">text...</textarea> </form> var m = MyModel.from_html("my-model-form"); m.get("id") // => 1 m.get("title") // => new title m.get("description") // => text...
Convert model instance in json
var m = new MyModel({"id": 10, "description", "text"}); m.to_json() // => {"id":10,"title":"default title","description":"text"} m.to_json(true) // => {"my_model":{"id":10,"title":"default title","description":"text"}} person = new Person({id: 1, name: "Abc"}) group0 = new Group({name: "group-0"}) group1 = new Group({name: "group-1"}) person.add_group(group0) person.add_group(group1) person.to_json() // => // {"id":1, // "name":"Abc", // "group":[ // {"name":"group-0","person_id":1}, // {"name":"group-1","person_id":1} // ] // }
var json = JSON.stringify({"id": 10, "description": "text"}); var m = MyModel.from_json(json); m.get("id") // => 10 m.get("description") // => "text" m.get("title") // => "default title" var json = JSON.stringify({"id":1,"group":[{"name":"group-0","person_id":1},{"name":"group-1","person_id":1}]}) var person = Person.from_json(json, {group: Group}); person.get('group') // => [Group, Group] var person0 = Person.from_json(json) person.get('group') // => [{name: 'group-0', ... }, {name: 'group-1', ...}]
Redefine it's method in you model, and call super
when want to check model,
or when synchronize with server, or specify you action...
class Person extends BaseModel # .... save: () -> #withou exceptions if super # ajax synchronize else false
Return true, when current model instance is valid.
1 2 3 4 5 | class Person @attrs: ["id"] @guid_for : "id" compare: (other) -> @.get("id") == other.get("id") |
1 2 3 4 5 6 | class Person @attrs: ["name"] after_create() -> alert "Hi! I'm #{@.get("name"}" new Person({"name": "Sirius"}) # => alert Hi! I'm Sirius |
By default, sirius
had a two adapters for jQuery
and for prototypejs
.
You might create own.
Adapter must define next methods:
class YouAdapter bind: (selector, event, fn) -> # 1 form_to_json: (selector) -> # 2 fire: (element, event, params...) -> # 3 get_property: (event, properties...) -> # 4
Methods:
bind
for selector
create event
listener with callback fn
form_to_json
convert specific element selector
to jsonfire
call custom event
for element
with params
get_property
for given event
get all properties
For example, JQueryAdapter
class JQueryAdapter extends Adapter bind: (selector, event, fn) -> jQuery(document).on(event, selector, fn) form_to_json: (selector) -> o = {} a = jQuery(selector).serializeArray() for obj in a do(obj) => name = obj["name"] value = obj["value"] if o[name] if !o[name].push o[name] = [o[name]] else o[name].push(value || '') else o[name] = value || '' JSON.stringify(o) fire: (element, event, params...) -> jQuery(element).trigger(event, params) get_property: (event, properties) -> for p in properties then jQuery(event.target).attr(p)
run
with options:
Controller = .... routes = .... SirisusApplication.run route : routes
Options:
route [Object]
a routing objectlogger [Function]
you own logger function, by default it's only console.log
log [Boolean]
- when true, all messages pass into logger
adapter[Adapter]
- adapter instancestart
- start url
Redirect with Sirius.redirect
function, for example in controller:
Controller = action: () -> if ... Sirius.redirect("#/")
When Sirius application
start, then generate application:run
event.
Next event it's a application:hashchange
, when change url hash.
Other event it's a application:404
when route not matched.
For all this events, you might define routes:
"application:start" : function() { alert("go!"); }, "application:hashchange" : function(current, prev) { alert("change"); } "application:404" : function(current, prev) { alert("404"); }