remoteable 1.0.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.
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in remoteable.gemspec
4
+ gemspec
@@ -0,0 +1,67 @@
1
+ # Remoteable
2
+
3
+ Remoteable is meant to be a simple set of conventions about how data is loaded into
4
+ a browser. The main idea goes back to the idea of how a browser loads built-in elements that aren't
5
+ inline text. Specifically, the whole idea of remoteable came from the realization that we've been using the
6
+ "Asynchronous" from "AJAX" since the first day we wrote an `<img>` tag. For example:
7
+
8
+ <img src="http://upload.wikimedia.org/wikipedia/commons/6/63/Wikipedia-logo.png">
9
+
10
+ What does the browser do when it sees this in the HTML? It goes off, starts downloading the picture and
11
+ then renders the picture and inserts it where the `<img>` tag lives in the rendered document. What
12
+ we've been doing by manually managing AJAX requests that go out, get some content, and inject it into the
13
+ resulting document is akin to manually downloading the images and injecting the result into the DOM. This
14
+ took me a bit to really grasp. Here's the core remoteable idea:
15
+
16
+ <div id="mydiv" data-src="http://somewhere.com/users/3">
17
+
18
+ In this instance treat the rendered user div on the page as a single resource that you go out and download and
19
+ then inject into the DOM as a replacement for the `<div>` placeholder.
20
+
21
+ Triggering it to load itself can be done a number of ways. You can target the div yourself:
22
+
23
+ $("#mydiv").trigger("refresh")
24
+
25
+ You can do a search for divs that have a common URL
26
+
27
+ $("*:remoteable(http://somewhere.com/users/3)").trigger("refresh")
28
+
29
+ You can refresh all the remoteables:
30
+
31
+ $.remoteables.trigger("refresh")
32
+
33
+ And so on.
34
+
35
+ ## Delegates
36
+
37
+ There are many times where one part of the page needs to be updated with some content. Given that a remoteable
38
+ div has a data-src it's very easy to tell the div to refresh itself, but a common pattern is changing the
39
+ div's `data-src` attribute and asking it to refresh. Handling this pattern is as simple as:
40
+
41
+ <div data-remote-delegate="#some_selector" data-src="http://somewhere.com/users/3">
42
+
43
+ When a click event hits this element (either directly or through event bubbling), remoteable will wrap the `data-remote-delegate`
44
+ value in a jQuery selector and call `remoteable("update")` with its own `data-src` url and asks it to refresh. This is roughly
45
+ equivalent to:
46
+
47
+ $("#some_selector").remoteable("update", {url: "http://somewhere.com/users/3"}).trigger("refresh.remoteable");
48
+
49
+ This way, the target element(s) become remoteable (if they weren't already), and the rest of the remoteable flow happens
50
+ as normal. Note, the original event continues to bubble but preventDefault is called on it.
51
+
52
+ If the target element is already loading a request that request will be cancelled first.
53
+
54
+ ## Content on the way back
55
+
56
+ When the remoteable request completes the target element will check the response type. If it's HTML it will replace its content
57
+ with what was returned by the AJAX request. If you'd rather that the returned content *replace* the remoteable, just add
58
+ `data-replace-self=true` to the element:
59
+
60
+ <div data-src="http://somewhere.com/users/3" data-replace-self="true">
61
+
62
+ This is useful for divs that show loading status and should be removed once loading is complete. This way you don't have to worry
63
+ about cleaning up the loading div as it is handled automatically.
64
+
65
+ ## Redirects
66
+
67
+ TODO: writeup
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,61 @@
1
+ module RemoteableHelper
2
+ def remote_content_tag(tag, options = {}, &block)
3
+ normalize_remoteable_params!(options)
4
+
5
+ content_tag(tag, options) do
6
+ yield if options['data-loaded']
7
+ end
8
+ end
9
+
10
+ def link_to_refresh(link, target, options = {})
11
+ normalize_remoteable_params!(options)
12
+
13
+ link_to link, target, options
14
+ end
15
+
16
+ def link_to_remoteable(link, options = {})
17
+ normalize_remoteable_params!(options)
18
+
19
+ link_to_function link, "", options
20
+ end
21
+
22
+ def refresh_remoteable(options)
23
+ normalize_remoteable_params!(options)
24
+
25
+ %Q{$.remoteables("#{j options["data-src"]}").remoteable("refresh")}
26
+ end
27
+
28
+ private
29
+
30
+ def polymorphic_link_from_options(url, options = {})
31
+ return url if url.is_a?(String)
32
+ args = [url]
33
+ args << {:format => options[:format]} if options[:format]
34
+ polymorphic_url(*args)
35
+ end
36
+
37
+ def normalize_remoteable_params!(options)
38
+ data_method = options[:method] || "get"
39
+
40
+ if(options[:url])
41
+ url = polymorphic_link_from_options(options[:url], options)
42
+ options['data-src'] = url
43
+ options['data-method'] = data_method
44
+ end
45
+
46
+ options['data-loaded'] = !!options[:loaded]
47
+ Rails.logger.info("Data loaded: #{options['data-loaded']}")
48
+ options[:class] = options[:loaded_class] if options[:loaded] && options[:loaded_class]
49
+
50
+ options[:style] = "display: none;" if options[:hide_until_loaded] && !options['data-loaded']
51
+
52
+ options.delete(:method)
53
+ options.delete(:url)
54
+ options.delete(:format)
55
+ options.delete(:loaded)
56
+ options.delete(:hide_until_loaded)
57
+ options.delete(:loaded_class)
58
+ end
59
+
60
+
61
+ end
@@ -0,0 +1,185 @@
1
+ (function($) {
2
+
3
+ $.extend(
4
+ $.expr[":"], {
5
+ remoteable: function(elem, i, match, array) {
6
+ var url = match[3].split(",")[0];
7
+ var method = match[3].split(",")[1] || "get";
8
+ return $(elem).data("src") == url && $(elem).data("method") == method;
9
+ }
10
+ }
11
+ );
12
+
13
+ $.remoteables = function(url, method) {
14
+ var remoteables = "*[data-src]";
15
+ if(url) {
16
+ remoteables += "[data-src^='" + url + "']";
17
+ }
18
+ if(method) {
19
+ remoteables += "[data-method='" + method + "']";
20
+ }
21
+
22
+ return $(remoteables);
23
+ }
24
+
25
+ var httpMethods = {
26
+ put: function(options) {
27
+ if(options.data == undefined) {
28
+ options.data = {};
29
+ }
30
+ options.data._method = 'PUT';
31
+ options.type = "POST";
32
+ return $.ajax(options);
33
+ },
34
+ delete: function(options) {
35
+ if(options.data == undefined) {
36
+ options.data = {};
37
+ }
38
+ options.data._method = 'DELETE';
39
+ options.type = "POST";
40
+ return $.ajax(options);
41
+ },
42
+ post: function(options) {
43
+ options.type = "POST";
44
+ return $.ajax(options);
45
+ },
46
+ get: function(options) {
47
+ options.type = "GET";
48
+ return $.ajax(options);
49
+ }
50
+ };
51
+
52
+ var methods = {
53
+ init: function(options) {
54
+ options = options || {};
55
+ return this.each(function() {
56
+ var $this = $(this),
57
+ data = $this.data('remoteable');
58
+ // If the plugin hasn't been initialized yet
59
+ if(!data) {
60
+ /*
61
+ Do more setup stuff here
62
+ */
63
+ $(this).data('remoteable', {
64
+ target: $this
65
+ });
66
+
67
+ var self = $(this);
68
+ var method = options["method"] || self.data("method") || "get";
69
+ self.attr("data-method", method);
70
+ var url = options["url"] || self.data("src");
71
+ self.attr("data-src", url);
72
+
73
+ self.on("click.remoteable", function(event) {
74
+ if(self.data("remote-delegate")) {
75
+ $(self.data('remote-delegate')).remoteable("update", {url: url});
76
+ } else {
77
+ if(self.is("a")) {
78
+ event.preventDefault();
79
+ if(self.data("remoteable").currentRequest) {
80
+ self.data("remoteable").currentRequest.abort();
81
+ }
82
+ self.data("remoteable").currentRequest = httpMethods[method].call(self, {
83
+ url: url,
84
+ complete: function(jqXHR, textStatus) {
85
+ self.data("remoteable").currentRequest = null;
86
+ }
87
+ });
88
+ event.stopPropagation();
89
+ }
90
+ }
91
+ });
92
+
93
+ self.on("refresh.remoteable", function(event) {
94
+ var url = self.data("src");
95
+ event.preventDefault();
96
+ event.stopPropagation();
97
+
98
+ if(self.is(":not(a)")) {
99
+ self.addClass("remoteable-processing");
100
+ if(self.data("remoteable").currentRequest) {
101
+ self.data("remoteable").currentRequest.abort();
102
+ }
103
+ self.data("remoteable").currentRequest = httpMethods[method].call(self, {
104
+ url: url,
105
+ complete: function(jqXHR, textStatus) {
106
+ self.removeClass("remoteable-processing");
107
+ self.trigger("ajax:complete.remoteable", [jqXHR, textStatus]);
108
+ }
109
+ });
110
+ }
111
+ });
112
+
113
+ self.on("ajax:complete.remoteable", function(event, jqXHR, textStatus) {
114
+ event.stopPropagation();
115
+ event.preventDefault();
116
+ self.data("remoteable").currentRequest = null;
117
+ var ct = jqXHR.getResponseHeader("content-type") || "";
118
+ if(ct.indexOf('html') > -1) {
119
+ var returned = $(jqXHR.responseText);
120
+ if(self.data("replace-self")) {
121
+ self.replaceWith(returned);
122
+ } else {
123
+ self.html(returned);
124
+ self.trigger("refreshed.remoteable", [self.data("src")]);
125
+ }
126
+ }
127
+ if(ct.indexOf('json') > -1) {
128
+ var url = $.parseJSON(jqXHR.responseText).redirect_to;
129
+ self.remoteable("update", {url: url});
130
+ }
131
+ });
132
+
133
+ }
134
+ });
135
+ },
136
+
137
+ destroy: function() {
138
+ return this.each(function() {
139
+ var $this = $(this),
140
+ data = $this.data('remoteable');
141
+
142
+ $this.removeData('remoteable');
143
+ });
144
+ },
145
+
146
+ refreshAll: function(options) {
147
+ $.remoteables().remoteable("refresh");
148
+ },
149
+
150
+ initAll: function(options) {
151
+ $.remoteables().remoteable();
152
+ },
153
+
154
+ update: function(options) {
155
+ return this.each(function() {
156
+ var self = $(this);
157
+ self.remoteable();
158
+ if(options.url) {
159
+ self.data("src", options.url);
160
+ }
161
+ self.trigger("refresh.remoteable");
162
+ });
163
+ },
164
+
165
+ refresh: function(options) {
166
+ return this.each(function() {
167
+ var self = $(this);
168
+ self.trigger("refresh.remoteable");
169
+ });
170
+ }
171
+ };
172
+
173
+ $.fn.remoteable = function(method) {
174
+
175
+ if(methods[method]) {
176
+ return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
177
+ } else if(typeof method === 'object' || !method) {
178
+ return methods.init.apply(this, arguments);
179
+ } else {
180
+ $.error('Method ' + method + ' does not exist on jQuery.remoteable');
181
+ }
182
+
183
+ };
184
+
185
+ })(jQuery);
@@ -0,0 +1,7 @@
1
+ require "remoteable/version"
2
+
3
+ module Remoteable
4
+ # Your code goes here...
5
+ end
6
+
7
+ require 'remoteable/engine' if defined?(Rails)
@@ -0,0 +1,7 @@
1
+ module Remoteable
2
+ class Engine < Rails::Engine
3
+ config.to_prepare do
4
+ ActionController::Base.send(:helper, RemoteableHelper)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,3 @@
1
+ module Remoteable
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "remoteable/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "remoteable"
7
+ s.version = Remoteable::VERSION
8
+ s.authors = ["Andrew Eberbach"]
9
+ s.email = ["andrew@ebertech.ca"]
10
+ s.homepage = ""
11
+ s.summary = %q{Remoteable AJAX content helper}
12
+
13
+ s.rubyforge_project = "remoteable"
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ s.require_paths = ["lib"]
19
+
20
+ # specify any dependencies here; for example:
21
+ # s.add_development_dependency "rspec"
22
+ # s.add_runtime_dependency "rest-client"
23
+ end
metadata ADDED
@@ -0,0 +1,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: remoteable
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease:
6
+ segments:
7
+ - 1
8
+ - 0
9
+ - 0
10
+ version: 1.0.0
11
+ platform: ruby
12
+ authors:
13
+ - Andrew Eberbach
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-03-31 00:00:00 Z
19
+ dependencies: []
20
+
21
+ description:
22
+ email:
23
+ - andrew@ebertech.ca
24
+ executables: []
25
+
26
+ extensions: []
27
+
28
+ extra_rdoc_files: []
29
+
30
+ files:
31
+ - .gitignore
32
+ - Gemfile
33
+ - README.md
34
+ - Rakefile
35
+ - app/helpers/remoteable_helper.rb
36
+ - lib/assets/javascripts/remoteable.js
37
+ - lib/remoteable.rb
38
+ - lib/remoteable/engine.rb
39
+ - lib/remoteable/version.rb
40
+ - remoteable.gemspec
41
+ homepage: ""
42
+ licenses: []
43
+
44
+ post_install_message:
45
+ rdoc_options: []
46
+
47
+ require_paths:
48
+ - lib
49
+ required_ruby_version: !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ hash: 3
55
+ segments:
56
+ - 0
57
+ version: "0"
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ none: false
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ hash: 3
64
+ segments:
65
+ - 0
66
+ version: "0"
67
+ requirements: []
68
+
69
+ rubyforge_project: remoteable
70
+ rubygems_version: 1.8.10
71
+ signing_key:
72
+ specification_version: 3
73
+ summary: Remoteable AJAX content helper
74
+ test_files: []
75
+
76
+ has_rdoc: