sudojs-rails 0.2.9 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +91 -74
- data/lib/sudojs/version.rb +1 -1
- data/vendor/assets/javascripts/sudo-x.js +330 -36
- data/vendor/assets/javascripts/sudo.js +8 -7
- metadata +4 -4
data/README.md
CHANGED
@@ -75,33 +75,40 @@ and directories (with your shiny new `Spam` namespace).
|
|
75
75
|
|
76
76
|
#### sudojs:install USAGE
|
77
77
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
78
|
+
Description:
|
79
|
+
Install sudo.js along with it's particular directory hierarchy.
|
80
|
+
|
81
|
+
Example:
|
82
|
+
rails g sudojs:install namespace [which_sudo] [html_extension] [js_extension] [css_extension] [--skip-css=true/false]
|
83
|
+
|
84
|
+
The only mandatory argument is the initial `namespace`.
|
85
|
+
|
86
|
+
The install generator uses these optional arguments to expand the values in the sudo_js.yml file:
|
87
|
+
1. `which_sudo` => which version of sudo.js are you loading? As sudo.js can be rebuilt into any number
|
88
|
+
of custom configurations, indicate here what name should be placed into the application manifest.
|
89
|
+
Defaults to 'sudo-x' if omitted.
|
90
|
+
2. `html_extension` => Use the full extension, i.e. `.haml`. defaults to `.erb`.
|
91
|
+
3. `js_extension` => defaults to `.js` pass `.js.coffee` if you are of the coffee persuasion.
|
92
|
+
4. `css_extension` => defaults to `.css`. Sass for example would need `.css.scss`
|
93
|
+
|
94
|
+
NOTE: to skip any and all generating of css files pass the option `--skip-css=true` when invoking the install generator.
|
95
|
+
Obviously, you could then leave the `css_extension` argument out. The option defaults to false.
|
96
|
+
|
97
|
+
This will create:
|
98
|
+
app/
|
99
|
+
assets/
|
100
|
+
stylesheets/
|
101
|
+
manifests/
|
102
|
+
application.css
|
103
|
+
javascripts/
|
104
|
+
application/
|
105
|
+
yourNamespace.js
|
106
|
+
model.js
|
107
|
+
manifests/
|
108
|
+
application.js
|
109
|
+
views/
|
110
|
+
config/
|
111
|
+
sudo_js.yml
|
105
112
|
|
106
113
|
#### sudojs:class USAGE
|
107
114
|
|
@@ -109,53 +116,63 @@ After installing a second generator becomes available, run `rails g` again and `
|
|
109
116
|
The reason for this is that `:class` depends on a yaml file to be placed in `config/` to function. Executing the command
|
110
117
|
`rails g sudojs:class -h` would reveal this:
|
111
118
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
119
|
+
Description:
|
120
|
+
Create a skeletal sudo.js Class and place the files correctly for a given controller#name.
|
121
|
+
|
122
|
+
Example:
|
123
|
+
`rails g sudojs:class View foo#baz`
|
124
|
+
|
125
|
+
Where `View` is a Class Object that this new Object will inherit from. Sudo.js itself
|
126
|
+
will recognize 6 types:
|
127
|
+
|
128
|
+
1. 'Base'
|
129
|
+
2. 'Model'
|
130
|
+
3. 'Container'
|
131
|
+
4. 'View'
|
132
|
+
5. 'ViewController'
|
133
|
+
6. 'Dataview'
|
134
|
+
|
135
|
+
The inheritance for any of these will be set as `_.<Name>` as we assume sudo has taken the
|
136
|
+
global `_` char as an alias. If the argument is any other string we assume you are inheriting
|
137
|
+
from a custom class and pass it through as is.
|
138
|
+
|
139
|
+
Where `foo#baz` *can* match a controller#action (like `home#show`) it does not
|
140
|
+
have to. The controller argument (`foo`) allows the generator to place the file correctly and
|
141
|
+
create/modify the correct manifest file. The name argument will be the proper name of the
|
142
|
+
Class Object itself.
|
143
|
+
|
144
|
+
If a corresponding views/controller/name.html* is found, a skeletal instantiation of the newly
|
145
|
+
created View will be placed there.
|
146
|
+
|
147
|
+
This will create (or modify):
|
148
|
+
app/
|
149
|
+
assests/
|
150
|
+
stylesheets/
|
151
|
+
manifests/
|
152
|
+
foo.css
|
153
|
+
views/
|
154
|
+
foo/
|
155
|
+
baz.css(*)
|
156
|
+
javascripts/
|
157
|
+
manifests/
|
158
|
+
foo.js
|
159
|
+
views/
|
160
|
+
foo/
|
161
|
+
baz.js(*)
|
162
|
+
|
163
|
+
And modify (if found):
|
164
|
+
app/
|
165
|
+
views/
|
166
|
+
foo/
|
167
|
+
baz.html(*)
|
168
|
+
|
169
|
+
Note:
|
170
|
+
If the `route` is application level (application#baz) the file will be placed
|
171
|
+
in the application level directory and the manifests/application file will be modified
|
172
|
+
|
173
|
+
Note:
|
174
|
+
If the `route` is application level (application#baz) the file will be placed
|
175
|
+
in the application level directory and the manifests/application file will be modified
|
159
176
|
|
160
177
|
## Contributing
|
161
178
|
|
data/lib/sudojs/version.rb
CHANGED
@@ -6,11 +6,11 @@ var sudo = {
|
|
6
6
|
//
|
7
7
|
// `namespace`
|
8
8
|
delegates: {},
|
9
|
-
// The sudo.
|
10
|
-
// can be `implemented` (mixed-in)
|
9
|
+
// The sudo.extensions namespace holds the objects that are stand alone `modules` which
|
10
|
+
// can be `implemented` (mixed-in) in sudo Class Objects
|
11
11
|
//
|
12
12
|
// `namespace`
|
13
|
-
|
13
|
+
extensions: {},
|
14
14
|
// ###getPath
|
15
15
|
// Extract a value located at `path` relative to the passed in object
|
16
16
|
//
|
@@ -433,9 +433,9 @@ sudo.Container.prototype.removeFromParent = function removeFromParent() {
|
|
433
433
|
sudo.Container.prototype.role = 'container';
|
434
434
|
// ###send
|
435
435
|
// The call to the specific method on a (un)specified target happens here.
|
436
|
-
// If this Object is part of a `sudo.
|
436
|
+
// If this Object is part of a `sudo.Container` maintained hierarchy
|
437
437
|
// the 'target' may be left out, causing the `bubble()` method to be called.
|
438
|
-
// What this does is allow children of a `sudo.
|
438
|
+
// What this does is allow children of a `sudo.Container` to simply pass
|
439
439
|
// events upward, delegating the responsibility of deciding what to do to the parent.
|
440
440
|
//
|
441
441
|
// `param` {*} Any number of arguments is supported, but the first is the only one searched for info.
|
@@ -820,7 +820,7 @@ sudo.template = function template(str, data, scope) {
|
|
820
820
|
// that the child will use to place itself in it's `renderTarget`.
|
821
821
|
// 4. Has a `render` method that when called re-hydrates it's $el by passing its
|
822
822
|
// internal data store to its template
|
823
|
-
// 5. Handles event binding/unbinding by implementing the sudo.
|
823
|
+
// 5. Handles event binding/unbinding by implementing the sudo.extensions.listener
|
824
824
|
// extension object
|
825
825
|
//
|
826
826
|
//`constructor`
|
@@ -828,9 +828,9 @@ sudo.Dataview = function(el, data) {
|
|
828
828
|
var d = data || {}, t;
|
829
829
|
sudo.View.call(this, el, d);
|
830
830
|
// implements the listener extension
|
831
|
-
$.extend(this, sudo.
|
831
|
+
$.extend(this, sudo.extensions.listener);
|
832
832
|
// dataview's models are observable
|
833
|
-
$.extend(this.model, sudo.
|
833
|
+
$.extend(this.model, sudo.extensions.observable);
|
834
834
|
// dont autoRender on the setting of events,
|
835
835
|
// add to this to prevent others if needed
|
836
836
|
this.autoRenderBlacklist = {event: true, events: true};
|
@@ -888,11 +888,182 @@ sudo.Dataview.prototype.render = function render(change) {
|
|
888
888
|
};
|
889
889
|
// `private`
|
890
890
|
sudo.Dataview.prototype.role = 'dataview';
|
891
|
+
// ##Navigator Class Object
|
892
|
+
|
893
|
+
// Abstracts location and history events, parsing their information into a
|
894
|
+
// normalized object that is then set to an Observable class instance
|
895
|
+
//
|
896
|
+
// `constructor`
|
897
|
+
sudo.Navigator = function(data) {
|
898
|
+
this.started = false;
|
899
|
+
this.slashStripper = /^\/+|\/+$/g;
|
900
|
+
this.leadingStripper = /^[#\/]|\s+$/g;
|
901
|
+
this.trailingStripper = /\/$/;
|
902
|
+
this.construct(data);
|
903
|
+
};
|
904
|
+
// Navigator inherits from `sudo.Model`
|
905
|
+
sudo.Navigator.prototype = Object.create(sudo.Model.prototype);
|
906
|
+
// ###getFragment
|
907
|
+
// 'Fragment' is defined as any URL information after the 'root' path
|
908
|
+
// including the `search` or `hash`
|
909
|
+
//
|
910
|
+
// `returns` {String} `fragment`
|
911
|
+
// `returns` {String} the normalized current fragment
|
912
|
+
sudo.Navigator.prototype.getFragment = function getFragment(fragment) {
|
913
|
+
var root = this.data.root;
|
914
|
+
if(!fragment) {
|
915
|
+
// intentional use of coersion
|
916
|
+
if (this.isPushState) {
|
917
|
+
fragment = window.location.pathname;
|
918
|
+
root = root.replace(this.trailingStripper, '');
|
919
|
+
if(!fragment.indexOf(root)) fragment = fragment.substr(root.length);
|
920
|
+
} else {
|
921
|
+
fragment = this.getHash();
|
922
|
+
}
|
923
|
+
}
|
924
|
+
return decodeURIComponent(fragment.replace(this.leadingStripper, ''));
|
925
|
+
};
|
926
|
+
// `returns` {String} the normalized current `hash`
|
927
|
+
sudo.Navigator.prototype.getHash = function getHash(fragment) {
|
928
|
+
fragment || (fragment = window.location.href);
|
929
|
+
var match = fragment.match(/#(.*)$/);
|
930
|
+
return match ? match[1] : '';
|
931
|
+
};
|
932
|
+
// `returns` {String} the normalized current `search`
|
933
|
+
sudo.Navigator.prototype.getSearch = function getSearch(fragment) {
|
934
|
+
fragment || (fragment = window.location.href);
|
935
|
+
var match = fragment.match(/\?(.*)$/);
|
936
|
+
return match ? match[1] : '';
|
937
|
+
};
|
938
|
+
// ###getUrl
|
939
|
+
// fetch the URL in the form <root + fragment>
|
940
|
+
//
|
941
|
+
// `returns` {String}
|
942
|
+
sudo.Navigator.prototype.getUrl = function getUrl() {
|
943
|
+
// note that delegate(_role_) returns the deleagte
|
944
|
+
return this.data.root + this.data.fragment;
|
945
|
+
};
|
946
|
+
// ###go
|
947
|
+
// If the passed in 'fragment' is different than the currently stored one,
|
948
|
+
// push a new state entry / hash event and set the data where specified
|
949
|
+
//
|
950
|
+
// `param` {string} `fragment`
|
951
|
+
// `returns` {*} call to `setData`
|
952
|
+
sudo.Navigator.prototype.go = function go(fragment) {
|
953
|
+
if(!this.started) return false;
|
954
|
+
if(!this.urlChanged(fragment)) return;
|
955
|
+
// TODO ever use replaceState?
|
956
|
+
if(this.isPushState) {
|
957
|
+
window.history.pushState({}, document.title, this.getUrl());
|
958
|
+
} else if(this.isHashChange) {
|
959
|
+
window.location.hash = '#' + this.data.fragment;
|
960
|
+
}
|
961
|
+
return this.setData();
|
962
|
+
};
|
963
|
+
// ###handleChange
|
964
|
+
// Bound to either the `popstate` or `hashchange` events, if the
|
965
|
+
// URL has indeed changed then parse the relevant data and set it -
|
966
|
+
// triggering change observers
|
967
|
+
//
|
968
|
+
// `returns` {*} call to `setData` or undefined
|
969
|
+
sudo.Navigator.prototype.handleChange = function handleChange(e) {
|
970
|
+
if(this.urlChanged()) {
|
971
|
+
return this.setData();
|
972
|
+
}
|
973
|
+
};
|
974
|
+
// ###parseQuery
|
975
|
+
// Parse and return a hash of the key value pairs contained in
|
976
|
+
// the current `query`
|
977
|
+
//
|
978
|
+
// `returns` {object}
|
979
|
+
sudo.Navigator.prototype.parseQuery = function parseQuery() {
|
980
|
+
var obj = {}, seg = this.data.query,
|
981
|
+
i, s;
|
982
|
+
if(seg) {
|
983
|
+
seg = seg.split('&');
|
984
|
+
for(i = 0; i < seg.length; i++) {
|
985
|
+
if(!seg[i]) continue;
|
986
|
+
s = seg[i].split('=');
|
987
|
+
obj[s[0]] = s[1];
|
988
|
+
}
|
989
|
+
return obj;
|
990
|
+
}
|
991
|
+
};
|
992
|
+
// ###setData
|
993
|
+
// Using the current `fragment` (minus any search or hash data) as a key,
|
994
|
+
// use `parseQuery` as the value for the key, setting it into the specified
|
995
|
+
// model (a stated `Observable` or `this.data`)
|
996
|
+
//
|
997
|
+
// `returns` {object} `this`
|
998
|
+
sudo.Navigator.prototype.setData = function setData() {
|
999
|
+
var frag = this.data.fragment,
|
1000
|
+
// data is set in a specified model or in self
|
1001
|
+
observable = this.data.observable || this;
|
1002
|
+
if(this.data.query) {
|
1003
|
+
// we want to set the key minus any search/hash
|
1004
|
+
frag = frag.indexOf('?') !== -1 ? frag.split('?')[0] : frag.split('#')[0];
|
1005
|
+
}
|
1006
|
+
observable.set(frag, this.parseQuery());
|
1007
|
+
return this;
|
1008
|
+
};
|
1009
|
+
// ###start
|
1010
|
+
// Gather the necessary information about the current environment and
|
1011
|
+
// bind to either (push|pop)state or hashchange
|
1012
|
+
//
|
1013
|
+
// `returns` {object} `this`
|
1014
|
+
sudo.Navigator.prototype.start = function start() {
|
1015
|
+
var hasPushState, atRoot, loc, tmp;
|
1016
|
+
if(this.started) return;
|
1017
|
+
hasPushState = window.history && window.history.pushState;
|
1018
|
+
this.started = true;
|
1019
|
+
// setup the initial configuration
|
1020
|
+
this.isHashChange = this.data.useHashChange && 'onhashchange' in window ||
|
1021
|
+
(!hasPushState && 'onhashchange' in window);
|
1022
|
+
this.isPushState = !this.isHashChange && !!hasPushState;
|
1023
|
+
// normalize the root to always contain a leading and trailing slash
|
1024
|
+
this.data['root'] = ('/' + this.data['root'] + '/').replace(this.slashStripper, '/');
|
1025
|
+
// Get a snapshot of the current fragment
|
1026
|
+
this.urlChanged();
|
1027
|
+
// monitor URL changes via popState or hashchange
|
1028
|
+
if (this.isPushState) {
|
1029
|
+
$(window).on('popstate', this.handleChange.bind(this));
|
1030
|
+
} else if (this.isHashChange) {
|
1031
|
+
$(window).on('hashchange', this.handleChange.bind(this));
|
1032
|
+
} else return;
|
1033
|
+
// Does the current URL need to changed? (hashchange vs popstate)
|
1034
|
+
atRoot = window.location.pathname.replace(/[^\/]$/, '$&/') === this.data['root'];
|
1035
|
+
// somehow a pushstate URL got here (and here is hashchange)
|
1036
|
+
if(this.isHashChange && !atRoot) {
|
1037
|
+
window.location.replace(this.data['root'] + window.location.search + '#' +
|
1038
|
+
this.data.fragment);
|
1039
|
+
// return early as browser will redirect
|
1040
|
+
return true;
|
1041
|
+
// the converse of the above
|
1042
|
+
} else if(this.isPushState && atRoot && window.location.hash) {
|
1043
|
+
tmp = this.getHash().replace(this.leadingStripper, '');
|
1044
|
+
window.history.replaceState({}, document.title, this.data['root'] +
|
1045
|
+
tmp + window.location.search);
|
1046
|
+
}
|
1047
|
+
// TODO provide option to `go` from inital `start` state?
|
1048
|
+
return this;
|
1049
|
+
};
|
1050
|
+
// Is a passed in fragment different from the currently set one?
|
1051
|
+
//
|
1052
|
+
// `param` {String} `fragment`
|
1053
|
+
sudo.Navigator.prototype.urlChanged = function urlChanged(fragment) {
|
1054
|
+
var current = this.getFragment(fragment);
|
1055
|
+
// nothing has changed
|
1056
|
+
if (current === this.data.fragment) return false;
|
1057
|
+
this.data.fragment = current;
|
1058
|
+
this.data.query = this.getSearch(current) || this.getHash(current);
|
1059
|
+
return true;
|
1060
|
+
};
|
891
1061
|
// ## Observable Extension Object
|
1062
|
+
//
|
892
1063
|
// Implementaion of the ES6 Harmony Observer pattern.
|
893
1064
|
// Extend a `sudo.Model` class with this object if
|
894
1065
|
// data-mutation-observation is required
|
895
|
-
sudo.
|
1066
|
+
sudo.extensions.observable = {
|
896
1067
|
// ###_deliver_
|
897
1068
|
// Called from deliverChangeRecords when ready to send
|
898
1069
|
// changeRecords to observers.
|
@@ -1120,7 +1291,7 @@ sudo.ext.observable = {
|
|
1120
1291
|
// in a changeRecord recieved via observe().
|
1121
1292
|
//
|
1122
1293
|
// `namespace`
|
1123
|
-
sudo.
|
1294
|
+
sudo.extensions.bindable = {
|
1124
1295
|
// List of attributes - $.attr() to be used.
|
1125
1296
|
//
|
1126
1297
|
// `private`
|
@@ -1233,14 +1404,6 @@ sudo.ext.bindable = {
|
|
1233
1404
|
readOnly: true,
|
1234
1405
|
selected: true
|
1235
1406
|
},
|
1236
|
-
// ###setBinding
|
1237
|
-
// Use case when you know there is only a single binding,
|
1238
|
-
// create the expected methods and return
|
1239
|
-
//
|
1240
|
-
// `returns` {Object} `this`
|
1241
|
-
setBinding: function setBinding() {
|
1242
|
-
return this._setBinding_(this.model.data.binding);
|
1243
|
-
},
|
1244
1407
|
// ###_setBinding_
|
1245
1408
|
// Given a single explicit binding, create it. Called from
|
1246
1409
|
// _setbindings_ as a convenience for normalizing the
|
@@ -1299,7 +1462,7 @@ sudo.ext.bindable = {
|
|
1299
1462
|
// C. data: A hash that will be passed as the custom jQuery Event.data object
|
1300
1463
|
// D. fn -> If a {String} bound to the named function on this object, if a
|
1301
1464
|
// function assumed to be anonymous and called with no scope manipulation
|
1302
|
-
sudo.
|
1465
|
+
sudo.extensions.listener = {
|
1303
1466
|
// ###bindEvents
|
1304
1467
|
// Bind the events in the data store to this object's $el
|
1305
1468
|
//
|
@@ -1343,6 +1506,137 @@ sudo.ext.listener = {
|
|
1343
1506
|
return this;
|
1344
1507
|
}
|
1345
1508
|
};
|
1509
|
+
// ##sudo persistable extension
|
1510
|
+
//
|
1511
|
+
// A mixin providing restful CRUD operations for a sudo.Model instance.
|
1512
|
+
//
|
1513
|
+
// create : POST
|
1514
|
+
// read : GET
|
1515
|
+
// update : PUT or PATCH (configurable)
|
1516
|
+
// destroy : DELETE
|
1517
|
+
//
|
1518
|
+
// Before use be sure to set an `ajax` property {object} with at least
|
1519
|
+
// a `baseUrl: ...` key. The model's id (if present -- indicating a persisted model)
|
1520
|
+
// is appended to the baseUrl (baseUrl/id) by default. You can override this behavior
|
1521
|
+
// by simply setting a `url: ...` in the `ajax` options hash or pass in the same when
|
1522
|
+
// calling any of the methods (or override the model.url() method).
|
1523
|
+
//
|
1524
|
+
// Place any other default options in the `ajax` hash
|
1525
|
+
// that you would want sent to a $.ajax({...}) call. Again, you can also override those
|
1526
|
+
// defaults by passing in a hash of options to any method:
|
1527
|
+
// `this.model.update({patch: true})` etc...
|
1528
|
+
sudo.extensions.persistable = {
|
1529
|
+
// ###create
|
1530
|
+
//
|
1531
|
+
// Save this model on the server. If a subset of this model's attributes
|
1532
|
+
// has not been stated (ajax:{data:{...}}) send all of the model's data.
|
1533
|
+
// Anticipate that the server response will send back the
|
1534
|
+
// state of the model on the server and set it here (via a success callback).
|
1535
|
+
//
|
1536
|
+
// `param` {object} `params` Hash of options for the XHR call
|
1537
|
+
// `returns` {object} The jQuery XHR object
|
1538
|
+
create: function create(params) {
|
1539
|
+
return this._sendData_('POST', params);
|
1540
|
+
},
|
1541
|
+
// ###destroy
|
1542
|
+
//
|
1543
|
+
// (because `delete` is reserved)
|
1544
|
+
//
|
1545
|
+
// `param` {object} `params` Optional hash of options for the XHR
|
1546
|
+
// `returns` {object} jqXhr
|
1547
|
+
destroy: function _delete(params) {
|
1548
|
+
return this._sendData_('DELETE', params);
|
1549
|
+
},
|
1550
|
+
// ###_normalizeParams_
|
1551
|
+
// Abstracted logic for preparing the options object. This looks at
|
1552
|
+
// the set `ajax` property, allowing any passed in params to override.
|
1553
|
+
//
|
1554
|
+
// Sets defaults: JSON dataType and a success callback that simply `sets()` the
|
1555
|
+
// data returned from the server
|
1556
|
+
//
|
1557
|
+
// `returns` {object} A normalized params object for the XHR call
|
1558
|
+
_normalizeParams_: function _normalizeParams_(meth, opts, params) {
|
1559
|
+
opts || (opts = this.data.ajax);
|
1560
|
+
opts.url || (opts.url = this.url(opts.baseUrl));
|
1561
|
+
opts.type || (opts.type = meth);
|
1562
|
+
opts.dataType || (opts.dataType = 'json');
|
1563
|
+
// the default success callback is to set the data returned from the server
|
1564
|
+
// or just the status as `ajaxStatus` if no data was returned
|
1565
|
+
opts.success || (opts.success = function(data, status, jqXhr) {
|
1566
|
+
data ? this.sets(data) : this.set('ajaxStatus', status);
|
1567
|
+
}.bind(this));
|
1568
|
+
// allow the passed in params to override any set in this model's `ajax` options
|
1569
|
+
return params ? $.extend(opts, params) : opts;
|
1570
|
+
},
|
1571
|
+
// ###read
|
1572
|
+
//
|
1573
|
+
// Fetch this models state from the server and set it here. The
|
1574
|
+
// `Model.sets()` method is used with the returned data (we are
|
1575
|
+
// asssuming the default json dataType). Pass in (via the params arg)
|
1576
|
+
// a success function to override this default.
|
1577
|
+
//
|
1578
|
+
// Maps to the http GET method.
|
1579
|
+
//
|
1580
|
+
// `param` {object} `params`. Optional info for the XHR call. If
|
1581
|
+
// present will override any set in this model's `ajax` options object.
|
1582
|
+
// `returns` {object} The jQuery XHR object
|
1583
|
+
read: function post(params) {
|
1584
|
+
return $.ajax(this._normalizeParams_('GET', null, params));
|
1585
|
+
},
|
1586
|
+
// ###save
|
1587
|
+
//
|
1588
|
+
// Convenience method removing the need to know if a model is new (not yet persisted)
|
1589
|
+
// or has been loaded/refreshed from the server.
|
1590
|
+
//
|
1591
|
+
// `param` {object} `params` Hash of options for the XHR call
|
1592
|
+
// `returns` {object} The jQuery XHR object
|
1593
|
+
save: function save(params) {
|
1594
|
+
return ('id' in this.data) ? this.update(params) : this.create(params);
|
1595
|
+
},
|
1596
|
+
// ###_sendData_
|
1597
|
+
// The Create, Update and Patch methods all send data to the server,
|
1598
|
+
// varying only in their HTTP method. Abstracted logic is here.
|
1599
|
+
//
|
1600
|
+
// `returns` {object} jqXhr
|
1601
|
+
_sendData_: function _sendData_(meth, params) {
|
1602
|
+
opts = this.data.ajax;
|
1603
|
+
opts.contentType || (opts.contentType = 'application/json');
|
1604
|
+
opts.data || (opts.data = this.data);
|
1605
|
+
// non GET requests do not 'processData'
|
1606
|
+
if(!('processData' in opts)) opts.processData = false;
|
1607
|
+
return $.ajax(this._normalizeParams_(meth, opts, params));
|
1608
|
+
},
|
1609
|
+
// ###update
|
1610
|
+
//
|
1611
|
+
// If this model has been persisted to/from the server (it has an `id` attribute)
|
1612
|
+
// send the specified data (or all the model's data) to the server at `url` via
|
1613
|
+
// the `PUT` http verb or `PATCH` if {patch: true} is in the ajax options (or the
|
1614
|
+
// passed in params)
|
1615
|
+
//
|
1616
|
+
// NOTE: update does not check is this is a new model or not, do that yourself
|
1617
|
+
// or use the `save()` method (that does check).
|
1618
|
+
//
|
1619
|
+
// `param` {object} `params` Optional hash of options for the XHR
|
1620
|
+
// `returns` {object|bool} the jqXhr if called false if not
|
1621
|
+
update: function update(params) {
|
1622
|
+
return this._sendData_((this.data.ajax.patch || params && params.patch) ?
|
1623
|
+
'PATCH' : 'PUT', params);
|
1624
|
+
},
|
1625
|
+
// ###url
|
1626
|
+
//
|
1627
|
+
// Takes the base url and appends this models id if present
|
1628
|
+
// (narmalizing the trailong slash if needed).
|
1629
|
+
// Override if you need to change the format of the calculated url.
|
1630
|
+
//
|
1631
|
+
// `param` {string} `base` the baseUrl set in this models ajax options
|
1632
|
+
url: function url(base) {
|
1633
|
+
// could possibly be 0...
|
1634
|
+
if('id' in this.data) {
|
1635
|
+
return base + (base.charAt(base.length - 1) === '/' ?
|
1636
|
+
'' : '/') + encodeURIComponent(this.data.id);
|
1637
|
+
} else return base;
|
1638
|
+
}
|
1639
|
+
};
|
1346
1640
|
//##Change Delegate
|
1347
1641
|
|
1348
1642
|
// Delegates, if present, can override or extend the behavior
|
@@ -1373,7 +1667,7 @@ sudo.delegates.Change.prototype.filter = function(change) {
|
|
1373
1667
|
// assemble the object to return to the method
|
1374
1668
|
obj.type = change.type;
|
1375
1669
|
obj.value = name.indexOf('.') === -1 ? change.object[change.name] :
|
1376
|
-
|
1670
|
+
sudo.getPath(name, change.object);
|
1377
1671
|
obj.oldValue = change.oldValue;
|
1378
1672
|
return this.delegator[filters[name]].call(this.delegator, obj);
|
1379
1673
|
}
|
@@ -1401,28 +1695,28 @@ sudo.delegates.Data.prototype = Object.create(sudo.Model.prototype);
|
|
1401
1695
|
//
|
1402
1696
|
// `param` {Object} `obj`
|
1403
1697
|
sudo.delegates.Data.prototype.filter = function(obj) {
|
1404
|
-
var filters = this.data.filters,
|
1405
|
-
|
1406
|
-
|
1407
|
-
|
1408
|
-
|
1409
|
-
|
1410
|
-
|
1411
|
-
|
1412
|
-
|
1413
|
-
|
1414
|
-
|
1415
|
-
|
1416
|
-
|
1417
|
-
|
1418
|
-
|
1698
|
+
var filters = this.data.filters,
|
1699
|
+
ary = Object.keys(filters), key, i, o, k;
|
1700
|
+
for(i = 0; i < ary.length; i++) {
|
1701
|
+
key = ary[i];
|
1702
|
+
// keys and paths need different handling
|
1703
|
+
if(key.indexOf('.') === -1) {
|
1704
|
+
if(key in obj) this.delegator[filters[key]].call(
|
1705
|
+
this.delegator, obj[key]);
|
1706
|
+
} else {
|
1707
|
+
// the chars after the last refinement are the key we need to check for
|
1708
|
+
k = key.slice(key.lastIndexOf('.') + 1);
|
1709
|
+
// and the ones prior are the object
|
1710
|
+
o = sudo.getPath(key.slice(0, key.lastIndexOf('.')), obj);
|
1711
|
+
if(o && k in o) this.delegator[filters[key]].call(
|
1712
|
+
this.delegator, o[k]);
|
1419
1713
|
}
|
1420
1714
|
}
|
1421
1715
|
};
|
1422
1716
|
// `private`
|
1423
1717
|
sudo.delegates.Data.prototype.role = 'data';
|
1424
1718
|
|
1425
|
-
sudo.version = "0.9.
|
1719
|
+
sudo.version = "0.9.2";
|
1426
1720
|
window.sudo = sudo;
|
1427
1721
|
if(typeof window._ === "undefined") window._ = sudo;
|
1428
1722
|
}).call(this, this);
|
@@ -6,11 +6,11 @@ var sudo = {
|
|
6
6
|
//
|
7
7
|
// `namespace`
|
8
8
|
delegates: {},
|
9
|
-
// The sudo.
|
10
|
-
// can be `implemented` (mixed-in)
|
9
|
+
// The sudo.extensions namespace holds the objects that are stand alone `modules` which
|
10
|
+
// can be `implemented` (mixed-in) in sudo Class Objects
|
11
11
|
//
|
12
12
|
// `namespace`
|
13
|
-
|
13
|
+
extensions: {},
|
14
14
|
// ###getPath
|
15
15
|
// Extract a value located at `path` relative to the passed in object
|
16
16
|
//
|
@@ -433,9 +433,9 @@ sudo.Container.prototype.removeFromParent = function removeFromParent() {
|
|
433
433
|
sudo.Container.prototype.role = 'container';
|
434
434
|
// ###send
|
435
435
|
// The call to the specific method on a (un)specified target happens here.
|
436
|
-
// If this Object is part of a `sudo.
|
436
|
+
// If this Object is part of a `sudo.Container` maintained hierarchy
|
437
437
|
// the 'target' may be left out, causing the `bubble()` method to be called.
|
438
|
-
// What this does is allow children of a `sudo.
|
438
|
+
// What this does is allow children of a `sudo.Container` to simply pass
|
439
439
|
// events upward, delegating the responsibility of deciding what to do to the parent.
|
440
440
|
//
|
441
441
|
// `param` {*} Any number of arguments is supported, but the first is the only one searched for info.
|
@@ -584,10 +584,11 @@ sudo.View.prototype.$ = function(sel) {
|
|
584
584
|
return this.$el.find(sel);
|
585
585
|
};
|
586
586
|
// ## Observable Extension Object
|
587
|
+
//
|
587
588
|
// Implementaion of the ES6 Harmony Observer pattern.
|
588
589
|
// Extend a `sudo.Model` class with this object if
|
589
590
|
// data-mutation-observation is required
|
590
|
-
sudo.
|
591
|
+
sudo.extensions.observable = {
|
591
592
|
// ###_deliver_
|
592
593
|
// Called from deliverChangeRecords when ready to send
|
593
594
|
// changeRecords to observers.
|
@@ -808,7 +809,7 @@ sudo.ext.observable = {
|
|
808
809
|
return this.deliverChangeRecords();
|
809
810
|
}
|
810
811
|
};
|
811
|
-
sudo.version = "0.9.
|
812
|
+
sudo.version = "0.9.2";
|
812
813
|
window.sudo = sudo;
|
813
814
|
if(typeof window._ === "undefined") window._ = sudo;
|
814
815
|
}).call(this, this);
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sudojs-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-02-02 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
@@ -78,7 +78,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
78
78
|
version: '0'
|
79
79
|
segments:
|
80
80
|
- 0
|
81
|
-
hash:
|
81
|
+
hash: -2092482263484943963
|
82
82
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
83
83
|
none: false
|
84
84
|
requirements:
|
@@ -87,7 +87,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
87
87
|
version: '0'
|
88
88
|
segments:
|
89
89
|
- 0
|
90
|
-
hash:
|
90
|
+
hash: -2092482263484943963
|
91
91
|
requirements: []
|
92
92
|
rubyforge_project:
|
93
93
|
rubygems_version: 1.8.24
|