ajax 0.1.4 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,12 +1,12 @@
1
1
  = Ajax
2
2
 
3
- <b>A Ruby on Rails plugin to 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.</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: Your code shouldn't change; your tests shouldn't change; and the way Google sees your site shouldn't change.
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], if you would like to try it out (as of May 1st 2010 or thereabouts).
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
- Ajax handles these common problems:
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 converts a traditional Rails application to use a completely AJAX frontend.
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 robot sees the traditional Rails application.
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.4
1
+ 0.1.5
@@ -1,3 +1,14 @@
1
1
  class AjaxController < ApplicationController
2
- unloadable
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
@@ -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
@@ -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) && !Ajax.is_hashed_url?(url)
72
- url = Ajax.hashed_url_from_traditional(url)
73
- Ajax.logger.info("[ajax] rewrote redirect to #{url}")
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
 
@@ -1,6 +1,6 @@
1
- require 'ajax/helpers/request_helper'
2
- require 'ajax/helpers/robot_helper'
3
- require 'ajax/helpers/url_helper'
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
- # Hash and/or Array values are merged so you can set multiple values
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.to_s) &&
40
+ if info.has_key?(key) &&
32
41
  value.is_a?(Hash) &&
33
- info[key.to_s].is_a?(Hash)
34
- value = info[key.to_s].merge(value, &DEEP_MERGE)
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.to_s) &&
48
+ if info.has_key?(key) &&
39
49
  value.is_a?(Array) &&
40
- info[key.to_s].is_a?(Array)
41
- value = info[key.to_s].concat(value)
50
+ info[key].is_a?(Array)
51
+ value = info[key].concat(value)
42
52
  end
43
-
44
- info[key.to_s] = value
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.to_s]
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
@@ -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>/ajax/framework</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 "/ajax/framework", :controller => 'ajax', :action => 'framework'
12
+ map.ajax_framework Ajax.framework_path, :controller => 'ajax', :action => 'framework'
10
13
  end
11
14
  end
12
15
  end
@@ -9,16 +9,16 @@ module Ajax
9
9
  def disable_ajax
10
10
  Ajax.enabled = false
11
11
  end
12
-
12
+
13
13
  def mock_ajax
14
14
  integrate_ajax
15
15
  Ajax.mocked = true
16
16
  end
17
-
17
+
18
18
  def unmock_ajax
19
19
  disable_ajax
20
20
  Ajax.mocked = false
21
- end
21
+ end
22
22
  end
23
23
  end
24
24
  end
@@ -30,9 +30,9 @@ module Ajax
30
30
  end
31
31
 
32
32
  def should_set_ajax_request_header(key, value)
33
- @env['Ajax-Info'][key].should == value
33
+ Ajax.get_header(@env, key).should == value
34
34
  end
35
-
35
+
36
36
  def should_rewrite_to(url)
37
37
  should_be_a_valid_response
38
38
 
@@ -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>. This
17
- # decision tree will be used unless a block is provided.
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
@@ -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('/ajax/framework')
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
@@ -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 (window.ajax.disable_address_intercept == true) {return false;}
314
- if (typeof(self.loaded_by_framework) == 'undefined' || self.loaded_by_framework != true) {
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
- self.loadPage({
320
- url: $.address.value().replace(/\/\//, '/')
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
- jQuery.ajax({
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 = $.address.baseURL().replace(new RegExp(document.location.pathname), '')
393
- url += '/#/' + $(this).attr('data-deep-link');
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/)) { return true; }
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
@@ -1,23 +1,5 @@
1
- AJAX_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
1
+ require 'ajax'
2
+ require 'rake'
2
3
 
3
- %w[
4
- app/controllers/ajax_controller.rb
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
@@ -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['Ajax-Info']['tab'].should == '#main .home_tab'
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['Ajax-Info']['assets'].should == { :key => ['value'] }
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, { :key => ['value1'] }
23
- set_header @headers, :assets, { :key => ['value2'] }
24
- @headers['Ajax-Info']['assets'].should == { :key => ['value1', 'value2'] }
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['Ajax-Info']['callbacks'].should == ['one', 'two']
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&param2=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&param2=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
 
@@ -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
@@ -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
- load(File.join(File.dirname(__FILE__), '..', 'rails', 'install.rb'))
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 "Install Ajax integration spec tests into spec/integration."
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
- version: 0.1.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-22 00:00:00 -07:00
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
- type: :development
18
- version_requirement:
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
- version:
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.5
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.