sudojs-rails 0.2.9 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|