ajax 0.1.4 → 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +22 -19
- data/VERSION +1 -1
- data/app/controllers/ajax_controller.rb +12 -1
- data/config/initializers/ajax.rb +1 -1
- data/lib/ajax.rb +16 -4
- data/lib/ajax/action_controller.rb +13 -6
- data/lib/ajax/helpers.rb +5 -5
- data/lib/ajax/helpers/request_helper.rb +30 -13
- data/lib/ajax/helpers/robot_helper.rb +3 -2
- data/lib/ajax/helpers/task_helper.rb +54 -0
- data/lib/ajax/helpers/url_helper.rb +3 -3
- data/lib/ajax/routes.rb +5 -2
- data/lib/ajax/spec/extension.rb +3 -3
- data/lib/ajax/spec/helpers.rb +2 -2
- data/lib/rack-ajax.rb +4 -3
- data/lib/rack-ajax/decision_tree.rb +3 -3
- data/lib/rack-ajax/parser.rb +4 -4
- data/public/javascripts/ajax.js +75 -15
- data/rails/install.rb +4 -22
- data/spec/ajax/ajax_spec.rb +19 -0
- data/spec/ajax/helpers_spec.rb +5 -4
- data/spec/ajax/request_helper_spec.rb +27 -8
- data/spec/ajax/tasks_spec.rb +48 -0
- data/spec/integration/ajax_spec.rb +16 -16
- data/spec/rack-ajax/parser_spec.rb +2 -2
- data/spec/spec_helper.rb +6 -0
- data/spec/support/file_macros.rb +28 -0
- data/tasks/ajax_tasks.rake +16 -3
- metadata +22 -9
data/README.rdoc
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
= Ajax
|
2
2
|
|
3
|
-
<b>
|
3
|
+
<b>Ajax augments a traditional Rails application with a completely AJAX frontend, while transparently handling issues important to both the enterprise and end users. Issues like SEO/Crawlability, browser history, deep-linking and testing.</b>
|
4
4
|
|
5
|
-
The Ajax philosophy is that you shouldn't have to develop for AJAX
|
5
|
+
The Ajax philosophy is that <b>you shouldn't have to develop for AJAX</b>: Your code shouldn't change; your tests shouldn't change; and the way Google sees your site shouldn't change (ed: this may {change}[]).
|
6
6
|
|
7
7
|
The beauty of Ajax is that your Rails application only ever sees traditional requests, so it does not have to be "Ajax aware".
|
8
8
|
|
9
|
-
Ajax is being used live in production on altnet.com[http://altnet.com]
|
9
|
+
As of the end of April, 2010, Ajax is being used live in production on altnet.com[http://altnet.com]. Try it out and create a wicked playlists while you're at it!
|
10
10
|
|
11
11
|
== Install
|
12
12
|
|
@@ -18,7 +18,7 @@ Ajax is being used live in production on altnet.com[http://altnet.com], if you w
|
|
18
18
|
|
19
19
|
2. <tt>bundle install</tt>
|
20
20
|
3. <tt>rake ajax:install</tt>
|
21
|
-
|
21
|
+
|
22
22
|
=== Rails 2.x.x
|
23
23
|
|
24
24
|
<b> As a Gem</b>
|
@@ -70,10 +70,10 @@ Ajax is being used live in production on altnet.com[http://altnet.com], if you w
|
|
70
70
|
enabled: true,
|
71
71
|
lazy_load_assets: false
|
72
72
|
});
|
73
|
-
|
73
|
+
|
74
74
|
== Introduction
|
75
75
|
|
76
|
-
|
76
|
+
=== Features and Support
|
77
77
|
|
78
78
|
* SEO/Crawlability/Google Analytics support
|
79
79
|
* Browser History
|
@@ -83,12 +83,13 @@ Ajax handles these common problems:
|
|
83
83
|
* Lazy-loaded Assets
|
84
84
|
* Activating Tabs
|
85
85
|
* Request Rewriting & Redirecting
|
86
|
+
* <b>{Jammit}[http://documentcloud.github.com/jammit/] compatible</b> with {these new helpers}[http://github.com/kjvarga/jammit/commit/27d5f27f4c6d4347c444b5e1a42dbb7c6c85462c]. Also supported are stylesheets with embedded images.
|
86
87
|
|
87
|
-
Ajax
|
88
|
+
<b>Ajax augments a traditional Rails application with a completely AJAX frontend.</b>
|
88
89
|
|
89
90
|
What do I mean by "completely AJAX"? Everyone uses AJAX. What we mean when we say "completely AJAX" is that the main page is only loaded once. Every link now loads content via AJAX.
|
90
91
|
|
91
|
-
But if we do that, the URL will never change and we will have no history, because that is how browsers determine history. It turns out the only way to change the URL without causing the browser to issue a new request, is to modify the named anchor - or "hashed" - part of the URL.
|
92
|
+
But if we do that, the URL will never change and we will have no history, because that is how browsers determine history. It turns out the only way to change the URL without causing the browser to issue a new request, is to modify the named anchor - or "hashed" - part of the URL.
|
92
93
|
|
93
94
|
So now your traditional links auto-magically load content via AJAX into a page container and update the browser URL with the new URL. You have all the benefits of AJAX as well as history and link bookmarkability.
|
94
95
|
|
@@ -120,17 +121,19 @@ By default the current layout is sent in the <tt>Ajax-Info</tt> header. This ca
|
|
120
121
|
|
121
122
|
[title] Sets the page title.
|
122
123
|
[tab] jQuery selector, triggers the <tt>activate</tt> event on matched element(s).
|
123
|
-
[container] jQuery selector, the container to receive the content (default: <tt>default_container</tt>).
|
124
|
+
[container] jQuery selector, the container to receive the content (default: <tt>ajax.default_container</tt>).
|
124
125
|
[assets] Hash of JavaScript and CSS assets to load <tt>{ :javascripts => [], :stylesheets => [] }</tt>
|
125
126
|
[callbacks] List of string callbacks to execute after assets have finished loading.
|
126
127
|
|
127
128
|
=== Robots and External APIS
|
128
129
|
|
129
|
-
<b>We detect robots by their User-Agent strings.</b> If a robot is detected the Ajax handling is bypassed and the
|
130
|
+
<b>We detect robots by their User-Agent strings.</b> If a robot is detected the Ajax handling is bypassed and the robotsees the traditional Rails application. The robot will see traditional links and requests for those pages will load traditionally and bypass all Ajax handling.
|
131
|
+
|
132
|
+
Check out the {robot User-Agent detection}[http://github.com/kjvarga/ajax/blob/master/lib/ajax/helpers/robot_helper.rb]. If we cannot identify a robot, the robot will receive a redirect on each request for a traditional URL. Requests for hashed URLs will only render the framework because there will be no JavaScript to trigger loading the inner contents. It's important that the User-Agent list be kept up-to-date, or some other method of detecting robots is found. IP address perhaps?
|
130
133
|
|
131
|
-
By default any AJAX or non-GET requests pass through unmodified
|
134
|
+
<b>By default any AJAX or non-GET requests pass through unmodified.</b>
|
132
135
|
|
133
|
-
If you need to expose external APIs you can do so using a regular expression that is matched against incoming URLs.
|
136
|
+
If you need to expose external APIs you can do so using a regular expression that is matched against incoming URLs. See <b>Documentation->Configuration->Excepted Links</b>.
|
134
137
|
|
135
138
|
== Compatibility
|
136
139
|
|
@@ -145,7 +148,7 @@ The following JavaScript libraries are required and included in the plugin:
|
|
145
148
|
|
146
149
|
* Rails 2.3.4 running Ruby 1.8.7 and Ruby 1.9
|
147
150
|
* Rails 2.3.5 running Ruby 1.8.7 and Ruby 1.9
|
148
|
-
|
151
|
+
|
149
152
|
=== Browsers:
|
150
153
|
|
151
154
|
(See {jQuery address supported browsers}[http://www.asual.com/jquery/address/docs/].)
|
@@ -170,7 +173,7 @@ It is important to be able to disable the plugin when you don't want it interfer
|
|
170
173
|
<b>Callbacks added directly to the <tt>window.ajax</tt> instance will still be executed, and they will execute immediately.</b>
|
171
174
|
|
172
175
|
To disable the plugin in your environment file:
|
173
|
-
|
176
|
+
|
174
177
|
# config/environments/test.rb
|
175
178
|
Ajax.enabled = false
|
176
179
|
|
@@ -225,7 +228,7 @@ Links can be excepted by passing in strings or regular expressions to <tt>Ajax.e
|
|
225
228
|
# config/initializers/ajax.rb
|
226
229
|
Ajax.exclude_paths %w[ /login /logout /signup /altnet-pro /my-account/edit /user-session/new ]
|
227
230
|
Ajax.exclude_paths [%r[\/my-account\/.*]]
|
228
|
-
|
231
|
+
|
229
232
|
Typically, we except pages that require HTTPS, like signup forms, because including secure forms on an insecure page often triggers a browser warning.
|
230
233
|
|
231
234
|
Excepted links when rendered do not contain the <tt>data-deep-link</tt> attribute if they are rendered with the <tt>link_to</tt> (or any other url) helper method.
|
@@ -241,11 +244,11 @@ You can use <tt>Ajax.get_header</tt> to inspect <tt>Ajax-Info</tt> header values
|
|
241
244
|
In controllers, <tt>ajax_header</tt> uses an <tt>after_filter</tt> to add content to the response. It therefore accepts passing a block instead of a static value, as well as <tt>:only</tt> and <tt>:except</tt> modifiers, e.g:
|
242
245
|
|
243
246
|
# app/controllers/application_controller.rb
|
244
|
-
ajax_header :title { dynamic_page_attribute(:page_title) || "Music @ Altnet" }
|
247
|
+
ajax_header :title { dynamic_page_attribute(:page_title) || "Music @ Altnet" }
|
245
248
|
ajax_header :assets do
|
246
249
|
{ :stylesheets => [current_controller_stylesheet] }
|
247
250
|
end
|
248
|
-
|
251
|
+
|
249
252
|
# app/controllers/browse_controller.rb
|
250
253
|
ajax_header :tab, '#header .nav li:contains(Music)', :only => [:music, :artists, :albums, :tracks, :new_releases]
|
251
254
|
ajax_header :tab, '#header .nav li:contains(Playlists)', :only => :playlists
|
@@ -254,7 +257,7 @@ In controllers, <tt>ajax_header</tt> uses an <tt>after_filter</tt> to add conten
|
|
254
257
|
# app/controllers/activity_controller.rb
|
255
258
|
ajax_header :tab, '#header .nav li:contains(Realtime)'
|
256
259
|
ajax_header :assets, { :javascripts => javascript_files_for_expansion(:juggernaut_jquery) }
|
257
|
-
|
260
|
+
|
258
261
|
Array and Hash values are merged so you can call <tt>ajax_header</tt> multiple times. For example, the asset Hash and Array values will be merged.
|
259
262
|
|
260
263
|
=== In Views
|
@@ -308,7 +311,7 @@ You can bind callbacks directly to the <tt>window.ajax</tt> object in your view,
|
|
308
311
|
window.ajax.prependOnLoad(function() {
|
309
312
|
$(document).trigger('player.init');
|
310
313
|
});
|
311
|
-
|
314
|
+
|
312
315
|
In the <tt>onLoad</tt> callback I'm scoping everything to <tt>window</tt> to <b>avoid scoping issues</b> in different browsers.
|
313
316
|
|
314
317
|
<b><tt>window.ajax.prependOnLoad</tt><b> adds the callback to the front of the queue.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.5
|
@@ -1,3 +1,14 @@
|
|
1
1
|
class AjaxController < ApplicationController
|
2
|
-
|
2
|
+
after_filter :clear_return_to
|
3
|
+
|
4
|
+
# Don't return to the framework path, return to root
|
5
|
+
def clear_return_to
|
6
|
+
return unless Ajax.is_enabled?
|
7
|
+
if session[:return_to] =~ %r[#{Ajax.framework_path}]
|
8
|
+
session[:return_to] = session[:return_to].sub(%r[#{Ajax.framework_path}], '/')
|
9
|
+
Rails.logger.info("[ajax] return_to / instead of #{Ajax.framework_path}")
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
unloadable # needed when installed as a plugin
|
3
14
|
end
|
data/config/initializers/ajax.rb
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
# Ajax.exclude_paths %w[ /login /logout /signup /user-session/new ]
|
6
6
|
# Ajax.exclude_paths [%r[\/my-account\/.*]]
|
7
7
|
|
8
|
-
# If you use a custom <tt>Rack::Ajax.decision_tree</tt>, include your
|
8
|
+
# If you use a custom <tt>Rack::Ajax.decision_tree</tt>, include your
|
9
9
|
# parser extensions in the Rack::Ajax::Parser module.
|
10
10
|
#
|
11
11
|
# The extensions define custom methods that are used in the
|
data/lib/ajax.rb
CHANGED
@@ -2,9 +2,14 @@ require 'ajax/helpers'
|
|
2
2
|
|
3
3
|
module Ajax
|
4
4
|
include Ajax::Helpers
|
5
|
-
|
5
|
+
|
6
6
|
class << self
|
7
|
-
attr_writer :logger
|
7
|
+
attr_writer :logger, :framework_path
|
8
|
+
end
|
9
|
+
|
10
|
+
# Return the full path to the root of the Ajax plugin/gem directory.
|
11
|
+
def self.root
|
12
|
+
@root ||= File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
8
13
|
end
|
9
14
|
|
10
15
|
# Return a logger instance.
|
@@ -28,7 +33,7 @@ module Ajax
|
|
28
33
|
class << self
|
29
34
|
alias_method :enabled?, :is_enabled?
|
30
35
|
end
|
31
|
-
|
36
|
+
|
32
37
|
# Set to false to disable this plugin completely.
|
33
38
|
#
|
34
39
|
# ActionController and ActionView helpers are still mixed in but
|
@@ -38,6 +43,13 @@ module Ajax
|
|
38
43
|
@enabled = !!value
|
39
44
|
end
|
40
45
|
|
46
|
+
# Return the path to the framework page.
|
47
|
+
#
|
48
|
+
# Default: <tt>/ajax/framework</tt>
|
49
|
+
def self.framework_path
|
50
|
+
@framework_path ||= '/ajax/framework'
|
51
|
+
end
|
52
|
+
|
41
53
|
# Return a boolean indicating whether to enable lazy loading assets.
|
42
54
|
# There are currently issues with some browsers when using this feature.
|
43
55
|
#
|
@@ -55,7 +67,7 @@ module Ajax
|
|
55
67
|
def self.lazy_load_assets=(value)
|
56
68
|
@lazy_load_assets = !!value
|
57
69
|
end
|
58
|
-
|
70
|
+
|
59
71
|
# Return a boolean indicating whether the plugin is being mock tested.
|
60
72
|
#
|
61
73
|
# Mocking forces the environment to be returned after Ajax processing
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Ajax
|
2
2
|
module ActionController
|
3
|
-
def self.included(klass)
|
3
|
+
def self.included(klass)
|
4
4
|
klass.class_eval do
|
5
5
|
alias_method_chain :render, :ajax
|
6
6
|
alias_method_chain :redirect_to_full_url, :ajax
|
@@ -62,16 +62,23 @@ module Ajax
|
|
62
62
|
def redirect_to_full_url_with_ajax(url, status)
|
63
63
|
return redirect_to_full_url_without_ajax(url, status) unless Ajax.is_enabled?
|
64
64
|
raise DoubleRenderError if performed?
|
65
|
-
|
65
|
+
|
66
|
+
original_url = url
|
66
67
|
if url == request.headers["Referer"] && !request.headers['Ajax-Info'].blank?
|
67
68
|
url = request.headers['Ajax-Info']['referer']
|
68
69
|
Ajax.logger.debug("[ajax] using referer #{url} from Ajax-Info")
|
69
70
|
end
|
70
71
|
|
71
|
-
if !Ajax.exclude_path?(url)
|
72
|
-
url
|
73
|
-
|
72
|
+
if !Ajax.exclude_path?(url)
|
73
|
+
if url =~ %r[#{Ajax.framework_path}]
|
74
|
+
url = url.sub(%r[#{Ajax.framework_path}], '/')
|
75
|
+
end
|
76
|
+
|
77
|
+
if !Ajax.is_hashed_url?(url)
|
78
|
+
url = Ajax.hashed_url_from_traditional(url)
|
79
|
+
end
|
74
80
|
end
|
81
|
+
Ajax.logger.info("[ajax] rewrote redirect from #{original_url} to #{url}")
|
75
82
|
|
76
83
|
session[:redirected_to] = url
|
77
84
|
if request.xhr?
|
@@ -94,7 +101,7 @@ module Ajax
|
|
94
101
|
#
|
95
102
|
def render_with_ajax(options = nil, extra_options = {}, &block)
|
96
103
|
return render_without_ajax(options, extra_options, &block) unless Ajax.is_enabled?
|
97
|
-
|
104
|
+
|
98
105
|
original_args = [options, extra_options]
|
99
106
|
if request.xhr?
|
100
107
|
|
data/lib/ajax/helpers.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
|
2
|
-
require
|
3
|
-
|
1
|
+
Dir[File.join(File.dirname(__FILE__), 'helpers', '*')].map do |file|
|
2
|
+
require file
|
3
|
+
end
|
4
4
|
|
5
5
|
module Ajax #:nodoc:
|
6
6
|
module Helpers #:nodoc:
|
@@ -8,8 +8,8 @@ module Ajax #:nodoc:
|
|
8
8
|
klass.class_eval do
|
9
9
|
extend RequestHelper
|
10
10
|
extend RobotHelper
|
11
|
-
extend UrlHelper
|
12
|
-
end
|
11
|
+
extend UrlHelper
|
12
|
+
end
|
13
13
|
end
|
14
14
|
end
|
15
15
|
end
|
@@ -14,13 +14,22 @@ module Ajax
|
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
-
#
|
17
|
+
# Set the value at <tt>key</tt> in the <tt>Ajax-Info</tt> header
|
18
|
+
# in <tt>object</tt>.
|
19
|
+
#
|
20
|
+
# <tt>object</tt> can be a Hash or instance of <tt>ActionController::Request</tt>
|
21
|
+
# <tt>key</tt> Symbol or String hash key, converted to String
|
22
|
+
# <tt>value</tt> any value that con be converted to JSON
|
23
|
+
#
|
24
|
+
# All Hash and Array values are deep-merged.
|
25
|
+
# Hash keys are converted to Strings.
|
18
26
|
def set_header(object, key, value)
|
19
27
|
headers = object.is_a?(::ActionController::Response) ? object.headers : object
|
20
|
-
|
28
|
+
key = key.to_s
|
29
|
+
|
21
30
|
info = case headers["Ajax-Info"]
|
22
31
|
when String
|
23
|
-
JSON.parse(headers["Ajax-Info"])
|
32
|
+
JSON.parse(headers["Ajax-Info"]) rescue {}
|
24
33
|
when Hash
|
25
34
|
headers["Ajax-Info"]
|
26
35
|
else
|
@@ -28,34 +37,42 @@ module Ajax
|
|
28
37
|
end
|
29
38
|
|
30
39
|
# Deep merge hashes
|
31
|
-
if info.has_key?(key
|
40
|
+
if info.has_key?(key) &&
|
32
41
|
value.is_a?(Hash) &&
|
33
|
-
info[key
|
34
|
-
value =
|
42
|
+
info[key].is_a?(Hash)
|
43
|
+
value = value.stringify_keys!
|
44
|
+
value = info[key].merge(value, &DEEP_MERGE)
|
35
45
|
end
|
36
46
|
|
37
47
|
# Concat arrays
|
38
|
-
if info.has_key?(key
|
48
|
+
if info.has_key?(key) &&
|
39
49
|
value.is_a?(Array) &&
|
40
|
-
info[key
|
41
|
-
value = info[key
|
50
|
+
info[key].is_a?(Array)
|
51
|
+
value = info[key].concat(value)
|
42
52
|
end
|
43
|
-
|
44
|
-
info[key
|
53
|
+
|
54
|
+
info[key] = value
|
45
55
|
headers["Ajax-Info"] = info.to_json
|
46
56
|
end
|
47
57
|
|
58
|
+
# Return the value at key <tt>key</tt> from the <tt>Ajax-Info</tt> header
|
59
|
+
# in <tt>object</tt>.
|
60
|
+
#
|
61
|
+
# <tt>object</tt> can be a Hash or instance of <tt>ActionController::Request</tt>
|
62
|
+
# <tt>key</tt> Symbol or String hash key, converted to String
|
48
63
|
def get_header(object, key)
|
49
64
|
headers = object.is_a?(::ActionController::Request) ? object.headers : object
|
65
|
+
key = key.to_s
|
66
|
+
|
50
67
|
info = case headers["Ajax-Info"]
|
51
68
|
when String
|
52
|
-
JSON.parse(headers["Ajax-Info"])
|
69
|
+
JSON.parse(headers["Ajax-Info"]) rescue {}
|
53
70
|
when Hash
|
54
71
|
headers["Ajax-Info"]
|
55
72
|
else
|
56
73
|
{}
|
57
74
|
end
|
58
|
-
info[key
|
75
|
+
info[key]
|
59
76
|
end
|
60
77
|
|
61
78
|
# Set one or more paths that can be accessed directly without the AJAX framework.
|
@@ -7,6 +7,7 @@ module Ajax
|
|
7
7
|
{:name => 'Gigabot', :user_agent_regex => /\bGigabot\b/i, :sample_agent_string => 'Gigabot'},
|
8
8
|
{:name => 'libwww-perl', :user_agent_regex => /\blibwww-perl\b/i, :sample_agent_string => 'libwww-perl'},
|
9
9
|
{:name => 'lwp-trivial', :user_agent_regex => /\blwp-trivial\b/i, :sample_agent_string => 'lwp-trivial'},
|
10
|
+
{:name => 'Lynx', :user_agent_regex => /\bLynx\b/i, :sample_agent_string => 'Lynx'},
|
10
11
|
{:name => 'MSNBot', :user_agent_regex => /\bmsnbot\b/i, :sample_agent_string => 'msnbot'},
|
11
12
|
{:name => 'SiteUptime', :user_agent_regex => /\bSiteUptime\b/i, :sample_agent_string => 'SiteUptime'},
|
12
13
|
{:name => 'Slurp', :user_agent_regex => /\bSlurp\b/i, :sample_agent_string => 'Slurp'},
|
@@ -21,11 +22,11 @@ module Ajax
|
|
21
22
|
end
|
22
23
|
nil
|
23
24
|
end
|
24
|
-
|
25
|
+
|
25
26
|
# Call with a User Agent string
|
26
27
|
def is_robot?(user_agent)
|
27
28
|
!!self.robot_for(user_agent)
|
28
|
-
end
|
29
|
+
end
|
29
30
|
end
|
30
31
|
end
|
31
32
|
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Ajax
|
2
|
+
module Helpers
|
3
|
+
module TaskHelper
|
4
|
+
INSTALL_FILES = %w[
|
5
|
+
app/controllers/ajax_controller.rb
|
6
|
+
app/views/ajax/framework.html.erb
|
7
|
+
config/initializers/ajax.rb
|
8
|
+
public/javascripts/ajax.js
|
9
|
+
public/javascripts/jquery.address-1.2rc.js
|
10
|
+
public/javascripts/jquery.address-1.2rc.min.js
|
11
|
+
public/javascripts/jquery.json-2.2.min.js
|
12
|
+
public/images/ajax-loading.gif]
|
13
|
+
|
14
|
+
UPDATE_JAVASCRIPT_FILES = %w[
|
15
|
+
public/javascripts/ajax.js]
|
16
|
+
|
17
|
+
def copy_unless_exists(file, from_dir=Ajax.root, to_dir=Rails.root)
|
18
|
+
from_file, to_file = File.join(from_dir, file), File.join(to_dir, file)
|
19
|
+
if File.exist?(to_file)
|
20
|
+
return false
|
21
|
+
else
|
22
|
+
FileUtils.mkdir_p(File.dirname(to_file)) unless File.directory?(File.dirname(to_file))
|
23
|
+
FileUtils.cp(from_file, to_file)
|
24
|
+
return true
|
25
|
+
end
|
26
|
+
rescue Exception => e
|
27
|
+
e
|
28
|
+
end
|
29
|
+
|
30
|
+
def copy_and_overwrite(file, from_dir=Ajax.root, to_dir=Rails.root)
|
31
|
+
from_file, to_file = File.join(from_dir, file), File.join(to_dir, file)
|
32
|
+
FileUtils.mkdir_p(File.dirname(to_file)) unless File.directory?(File.dirname(to_file))
|
33
|
+
FileUtils.cp(from_file, to_file)
|
34
|
+
return true
|
35
|
+
rescue Exception => e
|
36
|
+
e
|
37
|
+
end
|
38
|
+
|
39
|
+
def show_result(file)
|
40
|
+
result = yield file
|
41
|
+
return unless verbose
|
42
|
+
|
43
|
+
case result
|
44
|
+
when Exception
|
45
|
+
puts "skipped: #{file} #{result.message}"
|
46
|
+
when true
|
47
|
+
puts "created: #{file}"
|
48
|
+
else
|
49
|
+
puts "skipped: #{file}"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -14,7 +14,7 @@ module Ajax
|
|
14
14
|
def is_hashed_url?(url)
|
15
15
|
!!(encode_and_parse_url(url).fragment=~ %r[^\/])
|
16
16
|
end
|
17
|
-
|
17
|
+
|
18
18
|
# Return a hashed URL using the fragment of <tt>url</tt>
|
19
19
|
def hashed_url_from_fragment(url)
|
20
20
|
url_host(url) + ('/#/' + (encode_and_parse_url(url).fragment || '')).gsub(/\/\//, '/')
|
@@ -34,11 +34,11 @@ module Ajax
|
|
34
34
|
end
|
35
35
|
|
36
36
|
protected
|
37
|
-
|
37
|
+
|
38
38
|
def encode_and_parse_url(url)
|
39
39
|
URI.parse(URI.encode(url).gsub("%23", "#"))
|
40
40
|
end
|
41
|
-
|
41
|
+
|
42
42
|
def url_host(url)
|
43
43
|
if url.match(/^(\w+\:\/\/[^\/]+)\/?/)
|
44
44
|
$1
|
data/lib/ajax/routes.rb
CHANGED
@@ -1,12 +1,15 @@
|
|
1
|
+
require 'ajax'
|
2
|
+
|
1
3
|
module Ajax
|
2
4
|
module Routes
|
3
5
|
# In your <tt>config/routes.rb</tt> file call:
|
4
6
|
# Ajax::Routes.draw(map)
|
5
7
|
# Passing in the routing <tt>map</tt> object.
|
6
8
|
#
|
7
|
-
# Adds an <tt>ajax_framework_path</tt> pointing to <tt
|
9
|
+
# Adds an <tt>ajax_framework_path</tt> pointing to <tt>Ajax.framework_path</tt>
|
10
|
+
# which is <tt>/ajax/framework</tt> by default.
|
8
11
|
def self.draw(map)
|
9
|
-
map.ajax_framework
|
12
|
+
map.ajax_framework Ajax.framework_path, :controller => 'ajax', :action => 'framework'
|
10
13
|
end
|
11
14
|
end
|
12
15
|
end
|
data/lib/ajax/spec/extension.rb
CHANGED
data/lib/ajax/spec/helpers.rb
CHANGED
data/lib/rack-ajax.rb
CHANGED
@@ -5,7 +5,7 @@ require 'json'
|
|
5
5
|
module Rack
|
6
6
|
class Ajax
|
7
7
|
extend Rack::Ajax::DecisionTree
|
8
|
-
|
8
|
+
|
9
9
|
cattr_accessor :decision_tree
|
10
10
|
attr_accessor :user, :request, :params
|
11
11
|
|
@@ -13,8 +13,9 @@ module Rack
|
|
13
13
|
# This is useful when testing.
|
14
14
|
#
|
15
15
|
# To integrate Rack::Ajax into your app you should store the decision
|
16
|
-
# tree in a class-attribute <tt>decision_tree</tt>.
|
17
|
-
#
|
16
|
+
# tree in a class-attribute <tt>decision_tree</tt>.
|
17
|
+
#
|
18
|
+
# The <tt>default_decision_tree</tt> is used if no other is provided.
|
18
19
|
def initialize(app)
|
19
20
|
@app = app
|
20
21
|
@decision_tree = block_given? ? Proc.new : (self.class.decision_tree || self.class.default_decision_tree)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module Rack
|
2
2
|
class Ajax
|
3
3
|
module DecisionTree
|
4
|
-
|
4
|
+
|
5
5
|
# Decision tree for Rack rewrites and redirects.
|
6
6
|
#
|
7
7
|
# To use your own decision tree set it on the <tt>Ajax</tt> instance with:
|
@@ -17,7 +17,7 @@ module Rack
|
|
17
17
|
@@default_decision_tree ||= Proc.new do
|
18
18
|
::Ajax.logger.debug("[ajax] rack session #{@env['rack.session'].inspect}")
|
19
19
|
::Ajax.logger.debug("[ajax] Ajax-Info #{@env['Ajax-Info'].inspect}")
|
20
|
-
|
20
|
+
|
21
21
|
if !::Ajax.exclude_path?(@env['PATH_INFO'] || @env['REQUEST_URI'])
|
22
22
|
if ajax_request?
|
23
23
|
if hashed_url? # the browser never sends the hashed part
|
@@ -52,7 +52,7 @@ module Rack
|
|
52
52
|
end
|
53
53
|
end
|
54
54
|
end
|
55
|
-
end
|
55
|
+
end
|
56
56
|
end
|
57
57
|
end
|
58
58
|
end
|
data/lib/rack-ajax/parser.rb
CHANGED
@@ -40,7 +40,7 @@ module Rack
|
|
40
40
|
# Inspect the headers first - if there are any - so we don't
|
41
41
|
# look in the database unneccessarily.
|
42
42
|
#
|
43
|
-
# Sets the result in a header {Ajax-Info}[user_is_robot] so we
|
43
|
+
# Sets the result in a header {Ajax-Info}[user_is_robot] so we
|
44
44
|
# don't have to repeat this check in the application.
|
45
45
|
def user_is_robot?
|
46
46
|
return @user_is_robot if instance_variable_defined?(:@user_is_robot)
|
@@ -53,7 +53,7 @@ module Rack
|
|
53
53
|
::Ajax.set_header(@env, :robot, @user_is_robot)
|
54
54
|
@user_is_robot
|
55
55
|
end
|
56
|
-
|
56
|
+
|
57
57
|
def rewrite_to_traditional_url_from_fragment
|
58
58
|
rewrite(::Ajax.traditional_url_from_fragment(@env['REQUEST_URI']))
|
59
59
|
end
|
@@ -73,7 +73,7 @@ module Rack
|
|
73
73
|
end
|
74
74
|
|
75
75
|
def rewrite_to_render_ajax_framework
|
76
|
-
rewrite(
|
76
|
+
rewrite(::Ajax.framework_path)
|
77
77
|
end
|
78
78
|
|
79
79
|
private
|
@@ -106,7 +106,7 @@ module Rack
|
|
106
106
|
headers.reverse_merge!({'Content-Type' => 'text/html'})
|
107
107
|
[code, headers, [msg.to_s]]
|
108
108
|
end
|
109
|
-
|
109
|
+
|
110
110
|
def rack_response(*args)
|
111
111
|
self.class.rack_response(*args)
|
112
112
|
end
|
data/public/javascripts/ajax.js
CHANGED
@@ -172,9 +172,13 @@ var AjaxAssets = function(array, type) {
|
|
172
172
|
};
|
173
173
|
|
174
174
|
/**
|
175
|
-
* Class Ajax
|
175
|
+
* = Class Ajax
|
176
|
+
*
|
177
|
+
* == Options
|
178
|
+
*
|
179
|
+
* We extend self with the <tt>options</tt> array. This allows you to set
|
180
|
+
* any instance variable on the instance.
|
176
181
|
*
|
177
|
-
* Options:
|
178
182
|
* <tt>enabled</tt> boolean indicating whether the plugin is enabled.
|
179
183
|
* Callbacks that you set in the Ajax-Info header or directly on
|
180
184
|
* this instance will still be executed. They will not be queued,
|
@@ -217,6 +221,9 @@ var AjaxAssets = function(array, type) {
|
|
217
221
|
*
|
218
222
|
* window.ajax.prependOnLoad(function() { doSomething(args); });
|
219
223
|
*
|
224
|
+
* We also trigger a global 'ajax.onload' event that you can bind to to execute
|
225
|
+
* a callback on every page load.
|
226
|
+
*
|
220
227
|
* KJV 2010-04-22: I've experienced problems with Safari using String callbacks. YMMV.
|
221
228
|
* Browser support for callbacks is patchy at this time so lazy-loading is
|
222
229
|
* not recommended.
|
@@ -226,12 +233,16 @@ var Ajax = function(options) {
|
|
226
233
|
|
227
234
|
/**
|
228
235
|
* Options
|
236
|
+
*
|
237
|
+
* Extend self with the options Array. This allows you to set any instance
|
238
|
+
* variable on the Ajax instance.
|
229
239
|
*/
|
230
240
|
self.options = {
|
231
241
|
enabled: true,
|
232
242
|
default_container: undefined,
|
233
243
|
loaded_by_framework: false,
|
234
244
|
show_loading_image: true,
|
245
|
+
disable_next_address_intercept: false,
|
235
246
|
loading_image: 'img#ajax-loading',
|
236
247
|
loading_image_path: '/images/ajax-loading.gif',
|
237
248
|
javascripts: undefined,
|
@@ -239,7 +250,8 @@ var Ajax = function(options) {
|
|
239
250
|
callbacks: [],
|
240
251
|
loaded: false,
|
241
252
|
lazy_load_assets: false,
|
242
|
-
|
253
|
+
current_request: undefined,
|
254
|
+
|
243
255
|
// For initial position of the loading icon. Often the mouse does not
|
244
256
|
// move so position it by the link that was clicked.
|
245
257
|
last_click_coords: undefined
|
@@ -307,18 +319,44 @@ var Ajax = function(options) {
|
|
307
319
|
|
308
320
|
/**
|
309
321
|
* jQuery Address callback triggered when the address changes.
|
322
|
+
* Loads the current address using AJAX.
|
323
|
+
*
|
324
|
+
* If the inner content was pre-rendered (as in after a redirect),
|
325
|
+
* then <tt>loaded_by_framwork</tt> should be false.
|
326
|
+
*
|
327
|
+
* jQuery Address will still fire a request when the page loads,
|
328
|
+
* so we ignore that request if <tt>loaded_by_framwork</tt> is false.
|
310
329
|
*/
|
311
330
|
self.addressChanged = function() {
|
312
331
|
if (document.location.pathname != '/') { return false; }
|
313
|
-
if (
|
314
|
-
|
332
|
+
if (self.disable_next_address_intercept) {
|
333
|
+
self.disable_next_address_intercept = false;
|
334
|
+
return false;
|
335
|
+
}
|
336
|
+
if (!self.loaded_by_framework) {
|
315
337
|
self.loaded_by_framework = true;
|
316
338
|
return false;
|
317
339
|
}
|
318
340
|
|
319
|
-
|
320
|
-
|
321
|
-
|
341
|
+
// Ensure that the URL ends with '#' if we are on root. This
|
342
|
+
// will not trigger addressChanged().
|
343
|
+
if (document.location.pathname == '/'
|
344
|
+
&& document.location.href.indexOf('#') == -1) {
|
345
|
+
document.location.href = document.location.href + '#';
|
346
|
+
}
|
347
|
+
|
348
|
+
// Clean up the URL before making the request. If the URL changes
|
349
|
+
// as a result of this, update it, which will trigger this
|
350
|
+
// callback again.
|
351
|
+
var url = encodeURI($.address.value()).replace(/\/\//, '/');
|
352
|
+
if (url != $.address.value()) {
|
353
|
+
$.address.value(url);
|
354
|
+
return false;
|
355
|
+
} else {
|
356
|
+
self.loadPage({
|
357
|
+
url: url
|
358
|
+
});
|
359
|
+
}
|
322
360
|
return true;
|
323
361
|
};
|
324
362
|
|
@@ -339,22 +377,34 @@ var Ajax = function(options) {
|
|
339
377
|
*/
|
340
378
|
self.loadPage = function(options) {
|
341
379
|
if (!self.enabled) {
|
342
|
-
document.location = options.url;
|
380
|
+
document.location.href = options.url;
|
343
381
|
return true;
|
344
382
|
}
|
345
383
|
self.loaded = false;
|
346
384
|
self.showLoadingImage();
|
347
385
|
|
348
|
-
|
386
|
+
if (self.current_request !== undefined) {
|
387
|
+
try {
|
388
|
+
self.current_request.abort();
|
389
|
+
console.log('[ajax] aborting current request');
|
390
|
+
} catch(e) {
|
391
|
+
console.log('[ajax] abort failed!', e);
|
392
|
+
}
|
393
|
+
}
|
394
|
+
|
395
|
+
self.current_request = jQuery.ajax({
|
396
|
+
cache: false,
|
349
397
|
url: options.url,
|
350
398
|
method: options.method || 'GET',
|
351
399
|
beforeSend: self.setRequestHeaders,
|
352
400
|
success: self.responseHandler,
|
401
|
+
dataType: 'html',
|
353
402
|
complete: function(XMLHttpRequest, responseText) {
|
354
403
|
// Scroll to the top of the page.
|
355
404
|
$(document).scrollTop(0);
|
356
405
|
self.hideLoadingImage();
|
357
406
|
self.loaded = true;
|
407
|
+
self.current_request = undefined;
|
358
408
|
},
|
359
409
|
error: function(XMLHttpRequest, textStatus, errorThrown) {
|
360
410
|
var responseText = XMLHttpRequest.responseText;
|
@@ -380,6 +430,15 @@ var Ajax = function(options) {
|
|
380
430
|
XMLHttpRequest.setRequestHeader('AJAX_INFO', $.toJSON(data));
|
381
431
|
};
|
382
432
|
|
433
|
+
/**
|
434
|
+
* host
|
435
|
+
*
|
436
|
+
* Return the current host with protocol and with no trailing slash.
|
437
|
+
*/
|
438
|
+
self.host = function() {
|
439
|
+
return $.address.baseURL().replace(new RegExp(document.location.pathname), '');
|
440
|
+
};
|
441
|
+
|
383
442
|
/**
|
384
443
|
* linkClicked
|
385
444
|
*
|
@@ -389,10 +448,8 @@ var Ajax = function(options) {
|
|
389
448
|
*/
|
390
449
|
self.linkClicked = function(event) {
|
391
450
|
if (document.location.pathname != '/') {
|
392
|
-
var url =
|
393
|
-
|
394
|
-
url.replace(/\/\//, '/');
|
395
|
-
document.location = url;
|
451
|
+
var url = ('/#/' + $(this).attr('data-deep-link')).replace(/\/\//, '/');
|
452
|
+
document.location.href = url;
|
396
453
|
} else {
|
397
454
|
self.last_click_coords = { pageX: event.pageX, pageY: event.pageY };
|
398
455
|
$.address.value($(this).attr('data-deep-link'));
|
@@ -412,7 +469,10 @@ var Ajax = function(options) {
|
|
412
469
|
var container = data.container === undefined ? $(self.default_container) : $(data.container);
|
413
470
|
|
414
471
|
// Redirect? Let the JS execute. It will set the new window location.
|
415
|
-
if (responseText && responseText.match(/try\s{\swindow\.location\.href/)) {
|
472
|
+
if (responseText && responseText.match(/try\s{\swindow\.location\.href/)) {
|
473
|
+
jQuery.globalEval(responseText);
|
474
|
+
return true;
|
475
|
+
}
|
416
476
|
|
417
477
|
/**
|
418
478
|
* Extract the body
|
data/rails/install.rb
CHANGED
@@ -1,23 +1,5 @@
|
|
1
|
-
|
1
|
+
require 'ajax'
|
2
|
+
require 'rake'
|
2
3
|
|
3
|
-
|
4
|
-
|
5
|
-
app/views/ajax/framework.html.erb
|
6
|
-
config/initializers/ajax.rb
|
7
|
-
public/javascripts/ajax.js
|
8
|
-
public/javascripts/jquery.address-1.2rc.js
|
9
|
-
public/javascripts/jquery.address-1.2rc.min.js
|
10
|
-
public/javascripts/jquery.json-2.2.min.js
|
11
|
-
public/images/ajax-loading.gif
|
12
|
-
].each do |file|
|
13
|
-
if File.exist?(File.join(Rails.root, file))
|
14
|
-
puts "skipped: #{file} exists!"
|
15
|
-
else
|
16
|
-
begin
|
17
|
-
FileUtils.cp(File.join(AJAX_ROOT, file), File.join(Rails.root, file))
|
18
|
-
puts "created: #{file}"
|
19
|
-
rescue Exception => e
|
20
|
-
puts "skipped: #{file} #{e.message}"
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
4
|
+
load(File.join(Ajax.root, 'tasks', 'ajax_tasks.rake'))
|
5
|
+
Rake::Task['ajax:install'].invoke
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
context Ajax do
|
4
|
+
context "framework path" do
|
5
|
+
it "should be /ajax/framework by default" do
|
6
|
+
Ajax.framework_path.should_not be_nil
|
7
|
+
Ajax.framework_path.should == '/ajax/framework'
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should be able to be changed" do
|
11
|
+
Ajax.framework_path = '/my/path'
|
12
|
+
Ajax.framework_path.should == '/my/path'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should have a root method" do
|
17
|
+
Ajax.root.should == File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
|
18
|
+
end
|
19
|
+
end
|
data/spec/ajax/helpers_spec.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# coding: utf-8
|
1
2
|
require 'spec_helper'
|
2
3
|
|
3
4
|
context 'Ajax::UrlHelpers' do
|
@@ -20,7 +21,7 @@ context 'Ajax::UrlHelpers' do
|
|
20
21
|
Ajax.hashed_url_from_traditional('/beyoncé').should == '/#/beyonc%C3%A9'
|
21
22
|
Ajax.hashed_url_from_traditional('/red hot').should == '/#/red%20hot'
|
22
23
|
end
|
23
|
-
|
24
|
+
|
24
25
|
DOMAINS.each do |domain|
|
25
26
|
it "should work for domain #{domain}" do
|
26
27
|
Ajax.hashed_url_from_traditional("http://#{domain}/playlists").should == "http://#{domain}/#/playlists"
|
@@ -33,7 +34,7 @@ context 'Ajax::UrlHelpers' do
|
|
33
34
|
Ajax.hashed_url_from_fragment('/Beyonce#/Akon').should == '/#/Akon'
|
34
35
|
Ajax.hashed_url_from_fragment('/Beyonce#Akon').should == '/#/Akon'
|
35
36
|
end
|
36
|
-
|
37
|
+
|
37
38
|
it "should handle special characters" do
|
38
39
|
Ajax.hashed_url_from_fragment('/#/beyoncé').should == '/#/beyonc%C3%A9'
|
39
40
|
Ajax.hashed_url_from_fragment('/#/red hot').should == '/#/red%20hot'
|
@@ -66,7 +67,7 @@ context 'Ajax::UrlHelpers' do
|
|
66
67
|
it "should support full URLs" do
|
67
68
|
Ajax.is_hashed_url?('http://musicsocial.com.local/#/playlists').should be(true)
|
68
69
|
end
|
69
|
-
|
70
|
+
|
70
71
|
it "should support special characters" do
|
71
72
|
Ajax.is_hashed_url?('http://musicsocial.com.local/#/beyoncé').should be(true)
|
72
73
|
end
|
@@ -106,7 +107,7 @@ context 'Ajax::UrlHelpers' do
|
|
106
107
|
Ajax.traditional_url_from_fragment('/Beyonce#/beyoncé').should == '/beyonc%C3%A9'
|
107
108
|
Ajax.traditional_url_from_fragment('/#/red hot').should == '/red%20hot'
|
108
109
|
end
|
109
|
-
|
110
|
+
|
110
111
|
it "should handle special characters" do
|
111
112
|
Ajax.traditional_url_from_fragment('/Beyonce#/Akon').should == '/Akon'
|
112
113
|
end
|
@@ -8,26 +8,45 @@ describe 'Ajax::Helpers::RequestHelpers' do
|
|
8
8
|
@headers = {}
|
9
9
|
end
|
10
10
|
|
11
|
+
it "should store headers as JSON" do
|
12
|
+
set_header @headers, :tab, '#main .home_tab'
|
13
|
+
get_header(@headers, :tab).should == '#main .home_tab'
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should not fail on bad JSON" do
|
17
|
+
@headers['Ajax-Info'] = 'invalid JSON!'
|
18
|
+
get_header(@headers, :layout).should be_nil
|
19
|
+
|
20
|
+
set_header(@headers, :layout, '')
|
21
|
+
get_header(@headers, :layout).should == ''
|
22
|
+
end
|
23
|
+
|
11
24
|
it "should add headers" do
|
12
25
|
set_header @headers, :tab, '#main .home_tab'
|
13
|
-
@headers
|
26
|
+
get_header(@headers, :tab).should == '#main .home_tab'
|
14
27
|
end
|
15
28
|
|
16
29
|
it "should add assets" do
|
17
30
|
set_header @headers, :assets, { :key => ['value'] }
|
18
|
-
@headers
|
31
|
+
get_header(@headers, :assets).should == { 'key' => ['value'] }
|
19
32
|
end
|
20
33
|
|
21
34
|
it "should merge hashes" do
|
22
|
-
set_header @headers, :assets, { :
|
23
|
-
set_header @headers, :assets, { :
|
24
|
-
@headers
|
35
|
+
set_header @headers, :assets, { :key1 => 'value1' }
|
36
|
+
set_header @headers, :assets, { :key2 => 'value2' }
|
37
|
+
get_header(@headers, :assets).should == { 'key1' => 'value1', 'key2' => 'value2' }
|
25
38
|
end
|
26
|
-
|
39
|
+
|
27
40
|
it "should concat arrays" do
|
28
41
|
set_header @headers, :callbacks, ['one']
|
29
42
|
set_header @headers, :callbacks, ['two']
|
30
|
-
@headers
|
31
|
-
end
|
43
|
+
get_header(@headers, :callbacks).should == ['one', 'two']
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should deep merge" do
|
47
|
+
set_header @headers, :assets, { :key => ['value1'] }
|
48
|
+
set_header @headers, :assets, { :key => ['value2'] }
|
49
|
+
get_header(@headers, :assets).should == { 'key' => ['value1', 'value2'] }
|
50
|
+
end
|
32
51
|
end
|
33
52
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
# Dummy Rails.root to tmp/
|
5
|
+
TMP_DIR = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'tmp'))
|
6
|
+
Rails = Class.new do
|
7
|
+
def self.root
|
8
|
+
TMP_DIR
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def verbose # silence the tasks
|
13
|
+
false
|
14
|
+
end
|
15
|
+
|
16
|
+
require 'rake'
|
17
|
+
load(File.join(Ajax.root, 'tasks', 'ajax_tasks.rake'))
|
18
|
+
|
19
|
+
context 'task' do
|
20
|
+
before :each do
|
21
|
+
FileUtils.rm_r(TMP_DIR) if File.exists?(TMP_DIR)
|
22
|
+
FileUtils.mkdir(TMP_DIR)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "rails root should be tmp/" do
|
26
|
+
Rails.root.should == TMP_DIR
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'install' do
|
30
|
+
it "should install files" do
|
31
|
+
Rake::Task['ajax:install'].invoke
|
32
|
+
INSTALL_FILES.each do |file|
|
33
|
+
file_should_exist(File.join(Rails.root, file))
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context 'update' do
|
39
|
+
context 'javascript' do
|
40
|
+
it "should update files" do
|
41
|
+
Rake::Task['ajax:update:javascript'].invoke
|
42
|
+
UPDATE_JAVASCRIPT_FILES.each do |file|
|
43
|
+
file_should_exist(File.join(Rails.root, file))
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -17,26 +17,26 @@ context 'Rack::Ajax' do
|
|
17
17
|
after :all do
|
18
18
|
unmock_ajax
|
19
19
|
end
|
20
|
-
|
20
|
+
|
21
21
|
context "XMLHttpRequest" do
|
22
22
|
context "hashed url" do
|
23
23
|
it "should rewrite GET request" do
|
24
24
|
xhr(:get, '/?query1#/Beyonce?query2')
|
25
25
|
should_rewrite_to('/Beyonce?query2')
|
26
26
|
end
|
27
|
-
|
27
|
+
|
28
28
|
it "should not modify POST" do
|
29
29
|
xhr(:post, '/#/user_session/new?param1=1¶m2=2')
|
30
30
|
should_not_modify_request
|
31
31
|
end
|
32
32
|
end
|
33
|
-
|
33
|
+
|
34
34
|
context "traditional url" do
|
35
35
|
it "should not be modified" do
|
36
36
|
xhr(:get, '/Beyonce')
|
37
37
|
should_not_modify_request
|
38
38
|
end
|
39
|
-
|
39
|
+
|
40
40
|
it "should not be modified" do
|
41
41
|
xhr(:post, '/user_session/new?param1=1¶m2=2')
|
42
42
|
should_not_modify_request
|
@@ -49,7 +49,7 @@ context 'Rack::Ajax' do
|
|
49
49
|
get('/')
|
50
50
|
should_not_modify_request
|
51
51
|
end
|
52
|
-
|
52
|
+
|
53
53
|
it "should not be modified" do
|
54
54
|
get('/?query_string')
|
55
55
|
should_not_modify_request
|
@@ -65,17 +65,17 @@ context 'Rack::Ajax' do
|
|
65
65
|
before :each do
|
66
66
|
@user = login_robot_user
|
67
67
|
end
|
68
|
-
|
68
|
+
|
69
69
|
it "should not modify request for root" do
|
70
70
|
get('/')
|
71
71
|
should_not_modify_request
|
72
72
|
end
|
73
|
-
|
73
|
+
|
74
74
|
it "should not modify traditional requests" do
|
75
75
|
get('/Beyonce')
|
76
76
|
should_not_modify_request
|
77
77
|
end
|
78
|
-
|
78
|
+
|
79
79
|
context "request hashed" do
|
80
80
|
context "non-root url" do
|
81
81
|
it "should not modify the request" do
|
@@ -83,7 +83,7 @@ context 'Rack::Ajax' do
|
|
83
83
|
should_not_modify_request
|
84
84
|
end
|
85
85
|
end
|
86
|
-
|
86
|
+
|
87
87
|
context "root url" do
|
88
88
|
it "should rewrite to traditional url" do
|
89
89
|
get('/#/Beyonce?query2')
|
@@ -92,18 +92,18 @@ context 'Rack::Ajax' do
|
|
92
92
|
end
|
93
93
|
end
|
94
94
|
end
|
95
|
-
|
95
|
+
|
96
96
|
context "regular user" do
|
97
97
|
it "should not modify request for root" do
|
98
98
|
get('/')
|
99
99
|
should_not_modify_request
|
100
100
|
end
|
101
|
-
|
101
|
+
|
102
102
|
it "should ignore query string on root url" do
|
103
103
|
get('/?query1#/Beyonce?query2')
|
104
104
|
should_rewrite_to('/Beyonce?query2')
|
105
105
|
end
|
106
|
-
|
106
|
+
|
107
107
|
context "request hashed" do
|
108
108
|
context "non-root url" do
|
109
109
|
it "should redirect to hashed part at root" do
|
@@ -111,7 +111,7 @@ context 'Rack::Ajax' do
|
|
111
111
|
should_redirect_to('/#/Beyonce?query2')
|
112
112
|
end
|
113
113
|
end
|
114
|
-
|
114
|
+
|
115
115
|
context "root url" do
|
116
116
|
it "should rewrite to traditional url" do
|
117
117
|
get('/#/Beyonce?query2')
|
@@ -119,7 +119,7 @@ context 'Rack::Ajax' do
|
|
119
119
|
end
|
120
120
|
end
|
121
121
|
end
|
122
|
-
|
122
|
+
|
123
123
|
context "request traditional url" do
|
124
124
|
it "should not be modified" do
|
125
125
|
get('/')
|
@@ -129,12 +129,12 @@ context 'Rack::Ajax' do
|
|
129
129
|
get('/?query_string')
|
130
130
|
should_not_modify_request
|
131
131
|
end
|
132
|
-
|
132
|
+
|
133
133
|
it "should redirect GET request" do
|
134
134
|
get('/Beyonce')
|
135
135
|
should_redirect_to('/#/Beyonce')
|
136
136
|
end
|
137
|
-
|
137
|
+
|
138
138
|
it "should not modify non-GET request" do
|
139
139
|
%w[post put delete].each do |method|
|
140
140
|
send(method, '/')
|
@@ -22,11 +22,11 @@ describe Rack::Ajax::Parser, :type => :integration do
|
|
22
22
|
end
|
23
23
|
should_set_ajax_request_header('robot', true)
|
24
24
|
end
|
25
|
-
|
25
|
+
|
26
26
|
it "should recognize regular users" do
|
27
27
|
call_rack('/', { 'HTTP_USER_AGENT' => 'Safari' }) do
|
28
28
|
rack_response(user_is_robot?)
|
29
|
-
end
|
29
|
+
end
|
30
30
|
should_set_ajax_request_header('robot', false)
|
31
31
|
end
|
32
32
|
|
data/spec/spec_helper.rb
CHANGED
@@ -7,12 +7,18 @@ require 'ajax/spec/extension'
|
|
7
7
|
require 'ruby-debug'
|
8
8
|
|
9
9
|
# Rails dependencies
|
10
|
+
gem 'actionpack', '2.3.5'
|
10
11
|
require 'action_controller'
|
11
12
|
require 'active_support/core_ext'
|
12
13
|
|
13
14
|
$: << File.join(File.dirname(__FILE__), '..', 'lib')
|
14
15
|
require File.join(File.dirname(__FILE__), '..', 'rails', 'init')
|
15
16
|
|
17
|
+
# Requires supporting files with custom matchers and macros, etc,
|
18
|
+
# in ./support/ and its subdirectories.
|
19
|
+
Dir[File.expand_path(File.join(File.dirname(__FILE__),'support','**','*.rb'))].each {|f| require f}
|
20
|
+
|
16
21
|
Spec::Runner.configure do |config|
|
17
22
|
include Ajax::Spec::Extension
|
23
|
+
config.include(FileMacros)
|
18
24
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module FileMacros
|
2
|
+
module ExampleMethods
|
3
|
+
|
4
|
+
def files_should_be_identical(first, second)
|
5
|
+
identical_files?(first, second).should be(true)
|
6
|
+
end
|
7
|
+
|
8
|
+
def files_should_not_be_identical(first, second)
|
9
|
+
identical_files?(first, second).should be(false)
|
10
|
+
end
|
11
|
+
|
12
|
+
def (file)
|
13
|
+
File.exists?(file).should be(true)
|
14
|
+
end
|
15
|
+
|
16
|
+
def file_should_not_exist(file)
|
17
|
+
File.exists?(file).should be(false)
|
18
|
+
end
|
19
|
+
|
20
|
+
def identical_files?(first, second)
|
21
|
+
open(second, 'r').read.should == open(first, 'r').read
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.included(receiver)
|
26
|
+
receiver.send :include, ExampleMethods
|
27
|
+
end
|
28
|
+
end
|
data/tasks/ajax_tasks.rake
CHANGED
@@ -1,15 +1,28 @@
|
|
1
1
|
require 'ajax'
|
2
2
|
|
3
|
+
include Ajax::Helpers::TaskHelper
|
4
|
+
|
3
5
|
namespace :ajax do
|
4
|
-
desc "Install required Ajax files."
|
6
|
+
desc "Install required Ajax files. Existing files will not be overwritten."
|
5
7
|
task :install do
|
6
|
-
|
8
|
+
INSTALL_FILES.map do |file|
|
9
|
+
show_result(file) { |file| copy_unless_exists(file) }
|
10
|
+
end
|
7
11
|
end
|
8
12
|
|
9
13
|
namespace :install do
|
10
|
-
desc "
|
14
|
+
desc "Copy Ajax integration spec tests into spec/integration."
|
11
15
|
task :specs do
|
12
16
|
puts "Coming soon..."
|
13
17
|
end
|
14
18
|
end
|
19
|
+
|
20
|
+
namespace :update do
|
21
|
+
desc "Overwrite public/javascripts/ajax.js with the latest version."
|
22
|
+
task :javascript do
|
23
|
+
UPDATE_JAVASCRIPT_FILES.map do |file|
|
24
|
+
show_result(file) { |file| copy_and_overwrite(file) }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
15
28
|
end
|
metadata
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ajax
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 5
|
9
|
+
version: 0.1.5
|
5
10
|
platform: ruby
|
6
11
|
authors:
|
7
12
|
- Karl Varga
|
@@ -9,19 +14,21 @@ autorequire:
|
|
9
14
|
bindir: bin
|
10
15
|
cert_chain: []
|
11
16
|
|
12
|
-
date: 2010-04-
|
17
|
+
date: 2010-04-27 00:00:00 -07:00
|
13
18
|
default_executable:
|
14
19
|
dependencies:
|
15
20
|
- !ruby/object:Gem::Dependency
|
16
21
|
name: rspec
|
17
|
-
|
18
|
-
|
19
|
-
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
20
24
|
requirements:
|
21
25
|
- - ">="
|
22
26
|
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 0
|
23
29
|
version: "0"
|
24
|
-
|
30
|
+
type: :development
|
31
|
+
version_requirements: *id001
|
25
32
|
description: Augment a traditional Rails application with a completely AJAX frontend, while transparently handling issues important to both the enterprise and end users, such as testing, SEO and browser history.
|
26
33
|
email: kjvarga@gmail.com
|
27
34
|
executables: []
|
@@ -44,6 +51,7 @@ files:
|
|
44
51
|
- lib/ajax/helpers.rb
|
45
52
|
- lib/ajax/helpers/request_helper.rb
|
46
53
|
- lib/ajax/helpers/robot_helper.rb
|
54
|
+
- lib/ajax/helpers/task_helper.rb
|
47
55
|
- lib/ajax/helpers/url_helper.rb
|
48
56
|
- lib/ajax/railtie.rb
|
49
57
|
- lib/ajax/routes.rb
|
@@ -66,12 +74,15 @@ files:
|
|
66
74
|
- rails/init.rb
|
67
75
|
- rails/install.rb
|
68
76
|
- rails/uninstall.rb
|
77
|
+
- spec/ajax/ajax_spec.rb
|
69
78
|
- spec/ajax/helpers_spec.rb
|
70
79
|
- spec/ajax/request_helper_spec.rb
|
80
|
+
- spec/ajax/tasks_spec.rb
|
71
81
|
- spec/integration/ajax_spec.rb
|
72
82
|
- spec/rack-ajax/parser_spec.rb
|
73
83
|
- spec/spec.opts
|
74
84
|
- spec/spec_helper.rb
|
85
|
+
- spec/support/file_macros.rb
|
75
86
|
- tasks/ajax_tasks.rake
|
76
87
|
has_rdoc: true
|
77
88
|
homepage: http://github.com/kjvarga/ajax
|
@@ -86,18 +97,20 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
86
97
|
requirements:
|
87
98
|
- - ">="
|
88
99
|
- !ruby/object:Gem::Version
|
100
|
+
segments:
|
101
|
+
- 0
|
89
102
|
version: "0"
|
90
|
-
version:
|
91
103
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
92
104
|
requirements:
|
93
105
|
- - ">="
|
94
106
|
- !ruby/object:Gem::Version
|
107
|
+
segments:
|
108
|
+
- 0
|
95
109
|
version: "0"
|
96
|
-
version:
|
97
110
|
requirements: []
|
98
111
|
|
99
112
|
rubyforge_project:
|
100
|
-
rubygems_version: 1.3.
|
113
|
+
rubygems_version: 1.3.6
|
101
114
|
signing_key:
|
102
115
|
specification_version: 3
|
103
116
|
summary: A framework to augment a traditional Rails application with a completely AJAX frontend.
|