onesie 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ require "autotest/restart"
2
+
3
+ Autotest.add_hook :initialize do |at|
4
+ at.testlib = "minitest/autorun"
5
+ end
@@ -0,0 +1,3 @@
1
+ === 1.0.0 / 2010-08-23
2
+
3
+ * Birthday!
@@ -0,0 +1,7 @@
1
+ .autotest
2
+ CHANGELOG.rdoc
3
+ Manifest.txt
4
+ README.rdoc
5
+ Rakefile
6
+ lib/onesie.rb
7
+ test/test_onesie.rb
@@ -0,0 +1,88 @@
1
+ = Onesie
2
+
3
+ * http://github.com/jbarnette/onesie
4
+
5
+ == Description
6
+
7
+ A Rack middleware to make URLs in one-page webapps easier.
8
+
9
+ In a couple of recent projects, I've needed to avoid full page
10
+ refreshes as much as possible. In the first, I wanted to keep an
11
+ embedded music player active while the user was browsing. In the
12
+ second, I just wanted fancier transitions between pages.
13
+
14
+ It's possible to do this in an ad-hoc way, but I very quickly got
15
+ tired of hacking things together. Enter Onesie.
16
+
17
+ Onesie congealed from these requirements:
18
+
19
+ * I want a one-page web app,
20
+ * But I want the back button to work,
21
+ * And I want search engines to still index some stuff,
22
+ * And I (mostly) don't want to change the way I write a Rails/Sinatra app.
23
+
24
+ If someone visits <tt>http://example.org/meta/contact</tt>, I want
25
+ them to be redirected to <tt>http://example.org/blah/#/meta/contact</tt>,
26
+ but after the redirection I still want the original route to be
27
+ rendered for search engine indexing, etc.
28
+
29
+ When Onesie gets a request, it looks to see if under your preferred
30
+ one-page app path ("blah" in the example above). If it's not, Onesie
31
+ sets the current request's path in the session and redirects to your
32
+ app path.
33
+
34
+ If a request is under the one-page app path, the "real" request's path
35
+ is retrieved from the session and used for subsequent routing and
36
+ rendering. This means that, as above, a request for
37
+
38
+ http://example.org/meta/contact
39
+
40
+ Will be redirected to
41
+
42
+ http://example.org/blah/#/meta/contact
43
+
44
+ But still render the correct action in the wrapped app, even though
45
+ URL fragments aren't passed to the server.
46
+
47
+ This is a terrible explanation. I'll write a sample app or something
48
+ soon.
49
+
50
+ == Examples
51
+
52
+ require "onesie"
53
+
54
+ # in config.ru, after any session support
55
+ use Onesie
56
+
57
+ # in Rails
58
+
59
+ unless Rails.env.test?
60
+ config.middleware.insert_before ActionDispatch::Flash, "Onesie"
61
+ end
62
+
63
+ == Installation
64
+
65
+ $ gem install onesie
66
+
67
+ == License
68
+
69
+ Copyright 2010 John Barnette (code@jbarnette.com)
70
+
71
+ Permission is hereby granted, free of charge, to any person obtaining
72
+ a copy of this software and associated documentation files (the
73
+ 'Software'), to deal in the Software without restriction, including
74
+ without limitation the rights to use, copy, modify, merge, publish,
75
+ distribute, sublicense, and/or sell copies of the Software, and to
76
+ permit persons to whom the Software is furnished to do so, subject to
77
+ the following conditions:
78
+
79
+ The above copyright notice and this permission notice shall be
80
+ included in all copies or substantial portions of the Software.
81
+
82
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
83
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
84
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
85
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
86
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
87
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
88
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,15 @@
1
+ require "hoe"
2
+
3
+ Hoe.plugins.delete :rubyforge
4
+ Hoe.plugin :doofus, :isolate, :git
5
+
6
+ Hoe.spec "onesie" do
7
+ developer "John Barnette", "code@jbarnette.com"
8
+
9
+ self.extra_rdoc_files = Dir["*.rdoc"]
10
+ self.history_file = "CHANGELOG.rdoc"
11
+ self.readme_file = "README.rdoc"
12
+ self.testlib = :minitest
13
+
14
+ extra_deps << ["rack", ">= 1.2"]
15
+ end
@@ -0,0 +1,81 @@
1
+ require "rack/request"
2
+
3
+ # For any request path that doesn't match <tt>options[:path]</tt>,
4
+ # immediately redirect to <tt>#{options[:path]}/#/#{request-path}</tt>
5
+ # after dropping the original request path in the session. For any
6
+ # request that comes in, matches <tt>options[:path]</tt>, and has a
7
+ # request-path in the session, rewrite the request to match the path
8
+ # info in the session, remove the session copy, and allow the app to
9
+ # route/handle as normal.
10
+
11
+ class Onesie
12
+
13
+ # Duh.
14
+ VERSION = "1.0.0"
15
+
16
+ attr_reader :except, :only, :path
17
+
18
+ # Create a new instance of this middleware. +app+ (required) is the
19
+ # Rack application we're wrapping. Valid +options+:
20
+ #
21
+ # :except # An Array of prefix strings or Regexps to ignore.
22
+ # :log # A lambda for logging, takes one message arg.
23
+ # :only # An array of prefix strings or Regexps to require.
24
+ # :path # The correct path for the one-page app. Default: "/"
25
+
26
+ def initialize app, options = {}
27
+ @app = app
28
+ @except = Array(options[:except]).map { |e| regexpify e }
29
+ @log = options[:log]
30
+ @only = Array(options[:only]).map { |o| regexpify o }
31
+ @path = options[:path] || "/"
32
+
33
+ if FalseClass === @log
34
+ @log = lambda { |m| }
35
+ elsif !@log
36
+ if defined? Rails
37
+ @log = lambda { |m| Rails.logger.info " [Onesie] #{m}" }
38
+ else
39
+ @log = lambda { |m| puts "[Onesie] #{m}" }
40
+ end
41
+ end
42
+ end
43
+
44
+ def call env
45
+ request = Rack::Request.new env
46
+ session = env["rack.session"]
47
+
48
+ session.delete "onesie.path" if /onesie.clear/ =~ env["REQUEST_URI"]
49
+
50
+ allowed = only.empty? || only.any? { |o| o =~ request.path_info }
51
+ denied = except.any? { |e| e =~ request.path_info }
52
+
53
+ return @app.call env if request.xhr? || !allowed || denied
54
+
55
+ if request.path_info == @path
56
+ path = session.delete("onesie.path") || "/"
57
+ old_path_info = request.path_info
58
+
59
+ env["PATH_INFO"] = env["onesie.path"] = path
60
+ env["REQUEST_URI"].sub!(/^#{old_path_info}/, path)
61
+
62
+ @log.call "actual path is #{path}"
63
+ return @app.call env
64
+ end
65
+
66
+ session["onesie.path"] = dest = request.path_info
67
+ request.path_info = @path
68
+ request.path_info << "/" unless "/" == request.path_info[-1, 1]
69
+
70
+ dest = request.url + "##{dest}"
71
+ @log.call "redirecting to #{dest}"
72
+
73
+ [302, { "Location" => dest }, "Redirecting."]
74
+ end
75
+
76
+ protected
77
+
78
+ def regexpify thing
79
+ Regexp === thing ? thing : /^#{Regexp.escape thing}/
80
+ end
81
+ end
@@ -0,0 +1,81 @@
1
+ require "minitest/autorun"
2
+ require "onesie"
3
+
4
+ class TestOnesie < MiniTest::Unit::TestCase
5
+ def test_initialize
6
+ app = Onesie.new nil
7
+
8
+ assert_equal [], app.except
9
+ assert_equal [], app.only
10
+ assert_equal "/", app.path
11
+ end
12
+
13
+ def test_initialize_except
14
+ app = Onesie.new nil, :except => "/foo"
15
+ refute_nil app.except.first
16
+ end
17
+
18
+ def test_initialize_only
19
+ app = Onesie.new nil, :only => "/foo"
20
+ refute_nil app.only.first
21
+ end
22
+
23
+ def test_call_onesie_clear
24
+ app = make_app
25
+ env = make_env "REQUEST_URI" => "onesie.clear"
26
+ session = env["rack.session"]
27
+
28
+ session["onesie.path"] = "foo"
29
+
30
+ app.call env
31
+ refute_equal "foo", session["onesie.path"]
32
+ end
33
+
34
+ def test_call_except
35
+ called = false
36
+ app = make_app(:except => "/foo") { called = true }
37
+ env = make_env
38
+
39
+ env["REQUEST_URI"] = env["PATH_INFO"] = "/bar"
40
+ status, headers, body = app.call env
41
+
42
+ refute called
43
+ assert_equal 302, status
44
+ assert_match(/\/bar$/, headers["Location"])
45
+
46
+ env["REQUEST_URI"] = env["PATH_INFO"] = "/foo"
47
+ app.call env
48
+ assert called
49
+ end
50
+
51
+ def test_call_only
52
+ called = false
53
+ app = make_app(:only => "/foo") { called = true }
54
+ env = make_env
55
+
56
+ env["REQUEST_URI"] = env["PATH_INFO"] = "/bar"
57
+ app.call env
58
+ assert called
59
+
60
+ called = false
61
+ env["REQUEST_URI"] = env["PATH_INFO"] = "/foo"
62
+ status, headers, body = app.call env
63
+
64
+ refute called
65
+ assert_equal 302, status
66
+ assert_match(/\/foo$/, headers["Location"])
67
+ end
68
+
69
+ def make_app *args, &block
70
+ options = Hash === args.last ? args.pop : {}
71
+ app = args.first || block
72
+ Onesie.new app, { :log => false }.merge(options)
73
+ end
74
+
75
+ def make_env extras = {}
76
+ {
77
+ "rack.session" => {},
78
+ "rack.url_scheme" => "http",
79
+ }.merge extras
80
+ end
81
+ end
metadata ADDED
@@ -0,0 +1,149 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: onesie
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease: false
6
+ segments:
7
+ - 1
8
+ - 0
9
+ - 0
10
+ version: 1.0.0
11
+ platform: ruby
12
+ authors:
13
+ - John Barnette
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-08-24 00:00:00 -07:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: rack
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 11
30
+ segments:
31
+ - 1
32
+ - 2
33
+ version: "1.2"
34
+ type: :runtime
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: hoe
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ hash: 21
45
+ segments:
46
+ - 2
47
+ - 6
48
+ - 1
49
+ version: 2.6.1
50
+ type: :development
51
+ version_requirements: *id002
52
+ description: |-
53
+ A Rack middleware to make URLs in one-page webapps easier.
54
+
55
+ In a couple of recent projects, I've needed to avoid full page
56
+ refreshes as much as possible. In the first, I wanted to keep an
57
+ embedded music player active while the user was browsing. In the
58
+ second, I just wanted fancier transitions between pages.
59
+
60
+ It's possible to do this in an ad-hoc way, but I very quickly got
61
+ tired of hacking things together. Enter Onesie.
62
+
63
+ Onesie congealed from these requirements:
64
+
65
+ * I want a one-page web app,
66
+ * But I want the back button to work,
67
+ * And I want search engines to still index some stuff,
68
+ * And I (mostly) don't want to change the way I write a Rails/Sinatra app.
69
+
70
+ If someone visits <tt>http://example.org/meta/contact</tt>, I want
71
+ them to be redirected to <tt>http://example.org/blah/#/meta/contact</tt>,
72
+ but after the redirection I still want the original route to be
73
+ rendered for search engine indexing, etc.
74
+
75
+ When Onesie gets a request, it looks to see if under your preferred
76
+ one-page app path ("blah" in the example above). If it's not, Onesie
77
+ sets the current request's path in the session and redirects to your
78
+ app path.
79
+
80
+ If a request is under the one-page app path, the "real" request's path
81
+ is retrieved from the session and used for subsequent routing and
82
+ rendering. This means that, as above, a request for
83
+
84
+ http://example.org/meta/contact
85
+
86
+ Will be redirected to
87
+
88
+ http://example.org/blah/#/meta/contact
89
+
90
+ But still render the correct action in the wrapped app, even though
91
+ URL fragments aren't passed to the server.
92
+
93
+ This is a terrible explanation. I'll write a sample app or something
94
+ soon.
95
+ email:
96
+ - code@jbarnette.com
97
+ executables: []
98
+
99
+ extensions: []
100
+
101
+ extra_rdoc_files:
102
+ - Manifest.txt
103
+ - CHANGELOG.rdoc
104
+ - README.rdoc
105
+ files:
106
+ - .autotest
107
+ - CHANGELOG.rdoc
108
+ - Manifest.txt
109
+ - README.rdoc
110
+ - Rakefile
111
+ - lib/onesie.rb
112
+ - test/test_onesie.rb
113
+ has_rdoc: true
114
+ homepage: http://github.com/jbarnette/onesie
115
+ licenses: []
116
+
117
+ post_install_message:
118
+ rdoc_options:
119
+ - --main
120
+ - README.rdoc
121
+ require_paths:
122
+ - lib
123
+ required_ruby_version: !ruby/object:Gem::Requirement
124
+ none: false
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ hash: 3
129
+ segments:
130
+ - 0
131
+ version: "0"
132
+ required_rubygems_version: !ruby/object:Gem::Requirement
133
+ none: false
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ hash: 3
138
+ segments:
139
+ - 0
140
+ version: "0"
141
+ requirements: []
142
+
143
+ rubyforge_project: onesie
144
+ rubygems_version: 1.3.7
145
+ signing_key:
146
+ specification_version: 3
147
+ summary: A Rack middleware to make URLs in one-page webapps easier
148
+ test_files:
149
+ - test/test_onesie.rb