ratnikov-ajax_resource 0.02 → 0.03

Sign up to get free protection for your applications and to get access to all the features.
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: