ratnikov-ajax_resource 0.02 → 0.03

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/jake.yml ADDED
@@ -0,0 +1,25 @@
1
+ ---
2
+ source_directory: src
3
+ build_directory: build
4
+ packer:
5
+ shrink_vars: true
6
+ private: true
7
+ base62: true
8
+
9
+ packages:
10
+ base:
11
+ - init
12
+ - attribute_mod
13
+ - routing
14
+ - view
15
+ - base
16
+
17
+ utils:
18
+ - semaphore
19
+ - form
20
+ - errors
21
+
22
+ bundles:
23
+ ajax_resource:
24
+ - base
25
+ - utils
@@ -0,0 +1,48 @@
1
+ AjaxResource.AttributeMod = function() {
2
+ this._attributes = {};
3
+ this._errors = [];
4
+ };
5
+
6
+ AjaxResource.AttributeMod.prototype.attributes = function() {
7
+ return this._attributes;
8
+ };
9
+
10
+ AjaxResource.AttributeMod.prototype.errors = function() {
11
+ return this._errors;
12
+ };
13
+
14
+ AjaxResource.AttributeMod.prototype.has_errors = function() {
15
+ return this.errors().length !== 0;
16
+ };
17
+
18
+ AjaxResource.AttributeMod.prototype.valid = function() {
19
+ return !this.has_errors();
20
+ };
21
+
22
+ AjaxResource.AttributeMod.prototype.id = function() {
23
+ if (typeof this.attributes().id !== "undefined") {
24
+ return this.attributes().id;
25
+ } else {
26
+
27
+ // if there is no id attribute, return null as id
28
+ return null;
29
+ }
30
+ };
31
+
32
+ AjaxResource.AttributeMod.prototype.is_new = function() {
33
+ return this.id() === null;
34
+ };
35
+
36
+ AjaxResource.AttributeMod.prototype.update_attributes = function(json) {
37
+ if (typeof(json.errors) !== "undefined") {
38
+ this._errors = json.errors;
39
+ delete json.errors;
40
+ } else {
41
+
42
+ // if there are no errors reported by json, emptying out errors
43
+ this._errors = [];
44
+ }
45
+
46
+ // use all other properties as attributes
47
+ jQuery.extend(this._attributes, json);
48
+ };
data/src/base.js ADDED
@@ -0,0 +1,149 @@
1
+
2
+ AjaxResource.Base = function(spec) {
3
+ AjaxResource.AttributeMod.apply(this);
4
+
5
+ if (typeof spec.resource !== "undefined") {
6
+ this._resource_name = spec.resource;
7
+ } else {
8
+ throw("Must specify resource");
9
+ }
10
+
11
+ // include the routes
12
+ this._assign_routes(spec);
13
+ };
14
+
15
+ AjaxResource.Base.prototype._assign_routes = function(spec) {
16
+ var route_spec = {};
17
+
18
+ if (typeof spec.controller !== "undefined") {
19
+ route_spec.controller = spec.controller;
20
+ } else {
21
+ route_spec.controller = this.resource_name() + 's';
22
+ }
23
+
24
+ AjaxResource.Routing.apply(this, [ route_spec ]);
25
+ };
26
+
27
+ jQuery.extend(AjaxResource.Base.prototype, AjaxResource.AttributeMod.prototype);
28
+ jQuery.extend(AjaxResource.Base.prototype, AjaxResource.Routing.prototype);
29
+ jQuery.extend(AjaxResource.Base.prototype, AjaxResource.View.prototype);
30
+
31
+ AjaxResource.Base.prototype.resource_name = function() {
32
+ return this._resource_name;
33
+ };
34
+
35
+ AjaxResource.Base.prototype.serialized_attributes = function() {
36
+ var base = this;
37
+ var serialized = {};
38
+ jQuery.each(this.attributes(), function(key, value) {
39
+ serialized[base.resource_name() + '[' + key + ']'] = value;
40
+ });
41
+ return serialized;
42
+ };
43
+
44
+ AjaxResource.Base.prototype.parse_json = function(json) {
45
+ var resource_json = json[this.resource_name()];
46
+
47
+ if (typeof resource_json !== "undefined") {
48
+
49
+ if (typeof resource_json.html !== "undefined") {
50
+ // set to use custom html if specified
51
+ this.set_custom(resource_json.html);
52
+ delete resource_json.html;
53
+ } else {
54
+ // otherwise set to use default html
55
+ this.set_custom(null);
56
+ }
57
+
58
+ // update the attributes with the resource_json
59
+ this.update_attributes(resource_json);
60
+
61
+ return true;
62
+ } else {
63
+ // if no resource json was parsed, return false
64
+ return false;
65
+ }
66
+ };
67
+
68
+ AjaxResource.Base.prototype.create = function(success_callback) {
69
+
70
+ // sanity check
71
+ if (!this.is_new()) {
72
+ throw("Cannot create an existing record");
73
+ }
74
+
75
+ var post_data = jQuery.extend({ _method : 'post' }, this.serialized_attributes());
76
+ var resource = this;
77
+
78
+ return jQuery.ajax({
79
+ type: 'POST',
80
+ url: this.collection_path(),
81
+ data: post_data,
82
+ success: function(json) {
83
+ if (resource.parse_json(json)) {
84
+ success_callback(resource);
85
+ } else {
86
+ // do nothing on error
87
+ }
88
+ },
89
+ dataType: 'json'
90
+ });
91
+ };
92
+
93
+ AjaxResource.Base.prototype.update = function(success_callback) {
94
+
95
+ // sanity check
96
+ if (this.is_new()) {
97
+ throw("Cannot update a new record");
98
+ }
99
+
100
+ var post_data = jQuery.extend({ _method : 'put' }, this.serialized_attributes());
101
+ var resource = this;
102
+
103
+ return jQuery.ajax({
104
+ type: 'POST',
105
+ url : this.member_path(this.id()),
106
+ data: post_data,
107
+ success: function(json) {
108
+ if (resource.parse_json(json)) {
109
+ success_callback(resource);
110
+ } else {
111
+ // do nothing on error
112
+ }
113
+ },
114
+ dataType: 'json'
115
+ });
116
+ };
117
+
118
+ AjaxResource.Base.prototype.save = function(callback) {
119
+ if (this.is_new()) {
120
+ return this.create(callback);
121
+ } else {
122
+ return this.update(callback);
123
+ }
124
+ };
125
+
126
+ AjaxResource.Base.prototype.destroy = function(success_callback) {
127
+
128
+ // sanity check
129
+ if (this.is_new()) {
130
+ throw("Cannot destroy a new record");
131
+ }
132
+
133
+ var post_data = { _method : 'delete' };
134
+ var resource = this;
135
+
136
+ return jQuery.ajax({
137
+ type : 'POST',
138
+ url : this.member_path(this.id()),
139
+ data : post_data,
140
+ success: function(json) {
141
+ if (resource.parse_json(json)) {
142
+ success_callback(resource);
143
+ } else {
144
+ // do nothing on error
145
+ }
146
+ },
147
+ dataType: 'json'
148
+ });
149
+ };
data/src/errors.js ADDED
@@ -0,0 +1,53 @@
1
+
2
+ AjaxResource.Errors = function(selector) {
3
+ this._error_div = jQuery(selector).find('div.error');
4
+ };
5
+
6
+ AjaxResource.Errors.prototype.error_div = function() {
7
+ return this._error_div;
8
+ };
9
+
10
+ AjaxResource.Errors.prototype.ul = function() {
11
+ return this.error_div().find("ul");
12
+ };
13
+
14
+ AjaxResource.Errors.prototype.append = function(errors) {
15
+ var self = this;
16
+ jQuery.each(errors, function() {
17
+ if (this.length == 2) {
18
+ var desc = this[0];
19
+ var msg = this[1];
20
+ } else {
21
+ throw "Unsupported error format for: "+this;
22
+ }
23
+
24
+ var full_message;
25
+ if (desc !== 'base') {
26
+ full_message = desc + ' ' + msg;
27
+ } else {
28
+ // Use only the error message for base errors
29
+ full_message = msg;
30
+ }
31
+
32
+ self.ul().append("<li>" + full_message + "</li>");
33
+ });
34
+ };
35
+
36
+ AjaxResource.Errors.prototype.clear = function() {
37
+ this.ul().html("");
38
+ this.hide();
39
+ };
40
+
41
+ AjaxResource.Errors.prototype.set = function(errors) {
42
+ this.clear();
43
+ this.append(errors);
44
+ this.show();
45
+ };
46
+
47
+ AjaxResource.Errors.prototype.hide = function() {
48
+ this.error_div().hide();
49
+ };
50
+
51
+ AjaxResource.Errors.prototype.show = function() {
52
+ this.error_div().show();
53
+ };
data/src/form.js ADDED
@@ -0,0 +1,111 @@
1
+
2
+ AjaxResource.Form = function(form, options) {
3
+ var self = this;
4
+
5
+ this._form = jQuery(form);
6
+ this._error_panel = new AjaxResource.Errors(form);
7
+
8
+ if (typeof options === "undefined") {
9
+ options = {};
10
+ }
11
+
12
+ if (typeof options.model !== "undefined") {
13
+ this._model = options.model;
14
+ } else {
15
+ throw("Must specify model");
16
+ }
17
+
18
+ this._on_save = options.on_save;
19
+
20
+ this._semaphore = new AjaxResource.Semaphore();
21
+
22
+ this.submit_button().bind("click", function(event) {
23
+ event.preventDefault();
24
+ self.submit();
25
+ });
26
+ };
27
+
28
+ AjaxResource.Form.prototype.form = function() {
29
+ return this._form;
30
+ };
31
+
32
+ AjaxResource.Form.prototype.submit_button = function() {
33
+ return this.form().find(":submit");
34
+ };
35
+
36
+ AjaxResource.Form.prototype.error_panel = function(){
37
+ return this._error_panel;
38
+ }
39
+
40
+ AjaxResource.Form.prototype.fetch_model = function() {
41
+ if (jQuery.isFunction(this._model)) {
42
+ return this._model();
43
+ } else {
44
+ return this._model;
45
+ }
46
+ };
47
+
48
+ AjaxResource.Form.prototype.semaphore = function() {
49
+ return this._semaphore;
50
+ };
51
+
52
+ AjaxResource.Form.prototype.on_save = function(model) {
53
+ if (typeof this._on_save !== "undefined") {
54
+ this._on_save(model);
55
+ } else {
56
+ // do nothing since no on_save callback was specified
57
+ }
58
+ };
59
+
60
+ AjaxResource.Form.prototype.parse_fields = function() {
61
+ var parsed_attributes = {};
62
+ jQuery(this.form()).find(":input").each(function() {
63
+ var regex = '(.*)\\[(.*)\\]';
64
+ var match = new RegExp(regex).exec(jQuery(this).attr("name"));
65
+ if (match !== null) {
66
+ var resource_name = match[1];
67
+ var attr_name = match[2];
68
+
69
+ // initialize the hash for the resource if not present yet
70
+ if (typeof parsed_attributes[resource_name] === "undefined") {
71
+ parsed_attributes[resource_name] = {};
72
+ }
73
+
74
+ parsed_attributes[resource_name][attr_name] = jQuery(this).attr("value");
75
+ } else {
76
+ // input field name does not match the foo[bar] format so ignoring
77
+ }
78
+ });
79
+
80
+ return parsed_attributes;
81
+ };
82
+
83
+ AjaxResource.Form.prototype.submit = function() {
84
+ var self = this;
85
+ if (this.semaphore().available()) {
86
+ var model = this.fetch_model();
87
+
88
+ // update the model from the attributes within the form
89
+ model.parse_json(this.parse_fields());
90
+
91
+ model.save(function(saved_model) {
92
+ if (saved_model.valid()) {
93
+ // if the model is valid, clear error_panel and execute the on_saved callback
94
+
95
+ self.error_panel().clear();
96
+ self.on_save(saved_model);
97
+ } else {
98
+ // if returned model contained error_panel, report them
99
+ self.error_panel().set(saved_model.errors());
100
+ }
101
+
102
+ // the request was handled, decrease the semaphore
103
+ self.semaphore().dec();
104
+ });
105
+
106
+ // just initiated request, increment semaphore to prevent further submissions
107
+ this.semaphore().inc();
108
+ } else {
109
+ // do nothing if semaphore is not available
110
+ }
111
+ };
data/src/init.js ADDED
@@ -0,0 +1,3 @@
1
+ var AjaxResource = function() {
2
+ return {};
3
+ }();
data/src/routing.js ADDED
@@ -0,0 +1,41 @@
1
+
2
+ /*
3
+ * Creates a new routing object.
4
+ *
5
+ * Following options are required:
6
+ *
7
+ * - controller: the name of the controller to use for routing.
8
+ *
9
+ * Following options are available:
10
+ *
11
+ * - prefix: prefix to use by the route. By default is ''
12
+ */
13
+ AjaxResource.Routing = function(spec) {
14
+ if (typeof spec.controller !== "undefined") {
15
+ this._controller_name = spec.controller;
16
+ } else {
17
+ throw "Must specify controller to use";
18
+ }
19
+
20
+ if (typeof spec.prefix !== "undefined") {
21
+ this._prefix = spec.prefix;
22
+ } else {
23
+ this._prefix = '';
24
+ }
25
+ };
26
+
27
+ AjaxResource.Routing.prototype.controller_name = function() {
28
+ return this._controller_name;
29
+ };
30
+
31
+ AjaxResource.Routing.prototype.prefix = function() {
32
+ return this._prefix;
33
+ };
34
+
35
+ AjaxResource.Routing.prototype.collection_path = function() {
36
+ return this.prefix() + '/' + this.controller_name();
37
+ };
38
+
39
+ AjaxResource.Routing.prototype.member_path = function(identifier) {
40
+ return this.prefix() + '/' + this.controller_name() + '/' + identifier;
41
+ };
data/src/semaphore.js ADDED
@@ -0,0 +1,49 @@
1
+
2
+ AjaxResource.Semaphore = function(options) {
3
+ if (typeof options === "undefined") {
4
+ options = {};
5
+ }
6
+
7
+ this._value = 0;
8
+
9
+ this._on_available = options.on_available;
10
+ this._on_unavailable = options.on_unavailable;
11
+ };
12
+
13
+ AjaxResource.Semaphore.prototype.dec = function() {
14
+ if (this._value > 0) {
15
+ this._value -= 1;
16
+
17
+ if (this.available()) {
18
+ this.on_available();
19
+ }
20
+
21
+ } else {
22
+ // do nothing since value should not go below zero
23
+ }
24
+ };
25
+
26
+ AjaxResource.Semaphore.prototype.inc = function() {
27
+ var was_available = this.available();
28
+ this._value += 1;
29
+
30
+ if (was_available && !this.available()) {
31
+ this.on_unavailable();
32
+ }
33
+ };
34
+
35
+ AjaxResource.Semaphore.prototype.on_available = function() {
36
+ if (typeof this._on_available !== "undefined") {
37
+ this._on_available();
38
+ }
39
+ };
40
+
41
+ AjaxResource.Semaphore.prototype.on_unavailable = function() {
42
+ if (typeof this._on_unavailable !== "undefined") {
43
+ this._on_unavailable();
44
+ }
45
+ };
46
+
47
+ AjaxResource.Semaphore.prototype.available = function() {
48
+ return this._value === 0;
49
+ };
data/src/view.js ADDED
@@ -0,0 +1,24 @@
1
+
2
+ AjaxResource.View = function() { };
3
+
4
+ AjaxResource.View.prototype.default_view = function() {
5
+ return this.resource_name() + ' (id: '+this.id()+')';
6
+ };
7
+
8
+ AjaxResource.View.prototype.set_custom = function(html) {
9
+ if (html !== null) {
10
+ this._html = html;
11
+ } else {
12
+ // if null, delete the entry, dropping back to default view
13
+ delete this._html;
14
+ }
15
+ };
16
+
17
+ AjaxResource.View.prototype.html = function() {
18
+ if (typeof this._html !== "undefined") {
19
+ return this._html;
20
+ } else {
21
+ // fall back to default view
22
+ return this.default_view();
23
+ }
24
+ };
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ratnikov-ajax_resource
3
3
  version: !ruby/object:Gem::Version
4
- version: "0.02"
4
+ version: "0.03"
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dmitry Ratnikov
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-02-23 00:00:00 -08:00
12
+ date: 2009-02-27 00:00:00 -08:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -31,9 +31,16 @@ extensions: []
31
31
  extra_rdoc_files: []
32
32
 
33
33
  files:
34
- - build/ajax_resource-min.js
35
- - build/ajax_resource-src.js
36
34
  - tasks/ajax_resource.rb
35
+ - jake.yml
36
+ - src/init.js
37
+ - src/view.js
38
+ - src/attribute_mod.js
39
+ - src/routing.js
40
+ - src/base.js
41
+ - src/semaphore.js
42
+ - src/errors.js
43
+ - src/form.js
37
44
  has_rdoc: false
38
45
  homepage: http://github.com/ratnikov/ajax_resource/tree/master
39
46
  post_install_message: