rails_4_session_flash_backport 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rails_4_session_flash_backport.gemspec
4
+ gemspec
5
+
6
+ group :development do
7
+ gem "rspec"
8
+ gem "actionpack", ENV["ACTIONPACK_VERSION"] || "3.2.9"
9
+ gem "pry"
10
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Envato, Lucas Parry, Jack Chen (chendo)
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,49 @@
1
+ # Rails4SessionFlashBackport
2
+
3
+ Different versions of Rails have stored flash messages in different objects in
4
+ the session, making it a pain to upgrade without nuking everyones session. The
5
+ good ol' `ActionDispatch::Session::SessionRestoreError` making life difficult.
6
+
7
+ This gem was created because we wanted to be able to keep our users Rails 2
8
+ sessions working on Rails 3, and we figured as long as we're going to be doing
9
+ crazy stuff we might as well go and use the far more sensible practice from
10
+ Rails 4 of storing the flash as basic ruby types.
11
+
12
+ When using this gem on a Rails 2 or 3 app:
13
+
14
+ - Flash messages are stored as basic objects in the Rails 4 style.
15
+ - Flash messages in the Rails 2 format can be successfully decoded.
16
+ - Flash messages in the Rails 3 format can be successfully decoded.
17
+ - Flash messages in the Rails 4 format can be successfully decoded.
18
+
19
+ Additionally, on Rails 2 we include some patches for the SessionHash and
20
+ CookieStore, in order to make them act more like their
21
+ HashWithIndifferentAccess versions on Rails 3, so that your session_id can
22
+ survive a trip to Rails 3 and back.
23
+
24
+ This actually makes it possible to bounce requests from a Rails 2 server, to a
25
+ Rails 3 server and back again so long as both servers are using this gem. Very
26
+ helpful when you're doing a big Rails 2 => 3 upgrade and want to run a few
27
+ Rails 3 servers concurrently with your Rails 2 cluster to verify everything is
28
+ fine and performance is acceptable without
29
+ having to do the all-in switch.
30
+
31
+ ## Installation
32
+
33
+ Add this line to your application's Gemfile:
34
+
35
+ gem 'rails_4_session_flash_backport'
36
+
37
+ And then execute:
38
+
39
+ $ bundle
40
+
41
+ Or install it yourself as:
42
+
43
+ $ gem install rails_4_session_flash_backport
44
+
45
+ Copyright
46
+ ---------
47
+
48
+ Copyright (c) 2012 [Envato](http://envato.com), [Lucas Parry](http://github.com/lparry), [chendo](http://github.com/chendo). See LICENSE.txt for further details.
49
+
data/Rakefile ADDED
@@ -0,0 +1,19 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ namespace :spec do
4
+ task :rails3 do
5
+ system "export ACTIONPACK_VERSION=3.2.9 && bundle check > /dev/null || bundle update"
6
+ puts "Running Rails 3 specs"
7
+ system "export ACTIONPACK_VERSION=3.2.9 && bundle exec rspec --color spec/rails3"
8
+ puts ""
9
+ end
10
+
11
+ task :rails2 do
12
+ system "export ACTIONPACK_VERSION=2.3.14 && bundle check > /dev/null || bundle update"
13
+ puts "Running Rails 2 specs"
14
+ system "export ACTIONPACK_VERSION=2.3.14 && bundle exec rspec --color spec/rails2"
15
+ puts ""
16
+ end
17
+ end
18
+
19
+ task :spec => ["spec:rails2", "spec:rails3"]
@@ -0,0 +1,80 @@
1
+ # encoding: utf-8
2
+ require 'action_controller/flash'
3
+ require 'action_controller/test_process'
4
+ # Backport Rails 4 style storing the flash as basic ruby types to Rails 2
5
+ module ActionController #:nodoc:
6
+ module Flash
7
+ class FlashHash
8
+ def self.from_session_value(value)
9
+ flash = case value
10
+ when FlashHash # Rails 2.3
11
+ value
12
+ when ::ActionDispatch::Flash::FlashHash # Rails 3.2
13
+ flashes = value.instance_variable_get(:@flashes) || {}
14
+ used_set = value.instance_variable_get(:@used) || []
15
+ used = Hash[flashes.keys.map{|k| [k, used_set.include?(k)] }]
16
+ new_from_values(flashes, used)
17
+ when Hash # Rails 4.0
18
+ flashes = value['flashes'] || {}
19
+ discard = value['discard']
20
+ used = Hash[flashes.keys.map{|k| [k, discard.include?(k)] }]
21
+
22
+ new_from_values(flashes, used)
23
+ else
24
+ new
25
+ end
26
+ flash
27
+ end
28
+
29
+ def to_session_value
30
+ return nil if empty?
31
+ rails_3_discard_list = @used.map{|k,v| k if v}.compact
32
+ {'discard' => rails_3_discard_list, 'flashes' => Hash[to_a]}
33
+ end
34
+
35
+ def store(session, key = "flash")
36
+ session[key] = to_session_value
37
+ end
38
+
39
+ private
40
+
41
+ def self.new_from_values(flashes, used)
42
+ new.tap do |flash_hash|
43
+ flashes.each do |k, v|
44
+ flash_hash[k] = v
45
+ end
46
+ flash_hash.instance_variable_set("@used", used)
47
+ end
48
+ end
49
+ end
50
+
51
+ module InstanceMethods #:nodoc:
52
+ protected
53
+ def flash #:doc:
54
+ if !defined?(@_flash)
55
+ @_flash = Flash::FlashHash.from_session_value(session["flash"])
56
+ @_flash.sweep
57
+ end
58
+
59
+ @_flash
60
+ end
61
+ end
62
+ end
63
+ module TestResponseBehavior #:nodoc:
64
+ # A shortcut to the flash. Returns an empty hash if no session flash exists.
65
+ def flash
66
+ ActionController::Flash::FlashHash.from_session_value(session["flash"]) || {}
67
+ end
68
+ end
69
+ end
70
+
71
+ # This magic here allows us to unmarshal a Rails 3.2 ActionDispatch::Flash::FlashHash
72
+ module ActionDispatch
73
+ class Flash
74
+ class FlashHash
75
+ def self._load(args)
76
+ {}
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,65 @@
1
+ # The SessionHash is a HashWithIndifferentAccess on Rails 3, which causes
2
+ # problems with the session_id among other things. On Rails 2 the session_id is
3
+ # stored as session[:session_id], if we take that session to Rails 3 it becomes
4
+ # session["session_id"], and if we bring it back to Rails 2 it's still
5
+ # session["session_id"], but Rails checks session[:session_id], finds nothing
6
+ # and generates a new one. This is unacceptable.
7
+ #
8
+ # We've patched the SessionHash to #to_s keys before it stores them, and to
9
+ # fall back from the string key to the symbol if needed.
10
+ module ActionController::Session
11
+ class AbstractStore
12
+ class SessionHash < Hash
13
+ def [](key)
14
+ load! unless @loaded
15
+ super(key.to_s) || super(key)
16
+ end
17
+
18
+ def has_key?(key)
19
+ load! unless @loaded
20
+ super(key.to_s) || super(key)
21
+ end
22
+
23
+ def []=(key, value)
24
+ load! unless @loaded
25
+ super(key.to_s, value)
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ # We've also had to patch the cookie store, to make it use for the string
32
+ # version of "session_id", again falling back to the symbol if needed.
33
+ module ActionController
34
+ module Session
35
+ class CookieStore
36
+ def load_session(env)
37
+ data = unpacked_cookie_data(env)
38
+ data = persistent_session_id!(data)
39
+ [data["session_id"] || data[:session_id], data]
40
+ end
41
+
42
+ def extract_session_id(env)
43
+ if data = unpacked_cookie_data(env)
44
+ persistent_session_id!(data) unless data.empty?
45
+ data["session_id"] || data[:session_id]
46
+ else
47
+ nil
48
+ end
49
+ end
50
+
51
+ def inject_persistent_session_id(data)
52
+ requires_session_id?(data) ? { "session_id" => generate_sid } : {}
53
+ end
54
+
55
+ def requires_session_id?(data)
56
+ if data
57
+ data.respond_to?(:key?) && !(data.key?("session_id") || data.key?(:session_id))
58
+ else
59
+ true
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+
@@ -0,0 +1,74 @@
1
+ # encoding: utf-8
2
+ require 'action_dispatch/middleware/flash'
3
+ # Backport Rails 4 style storing the flash as basic ruby types to Rails 3
4
+ module ActionDispatch
5
+ class Request < Rack::Request
6
+ def flash
7
+ @env[Flash::KEY] ||= Flash::FlashHash.from_session_value(session["flash"]).tap(&:sweep)
8
+ end
9
+ end
10
+ class Flash
11
+ class FlashHash
12
+
13
+ def self.from_session_value(value)
14
+ case value
15
+ when ::ActionController::Flash::FlashHash # Rails 2.x
16
+ new(value, value.instance_variable_get(:@used).select{|a,b| b}.keys)
17
+ when ::ActionDispatch::Flash::FlashHash # Rails 3.1, 3.2
18
+ new(value.instance_variable_get(:@flashes), value.instance_variable_get(:@used))
19
+ when Hash # Rails 4.0, we backported to 2.3 too
20
+ new(value['flashes'], value['discard'])
21
+ else
22
+ new
23
+ end
24
+ end
25
+
26
+ def to_session_value
27
+ return nil if empty?
28
+ {'discard' => @used.to_a, 'flashes' => @flashes}
29
+ end
30
+
31
+ def initialize(flashes = {}, discard = []) #:nodoc:
32
+ @used = Set.new(discard)
33
+ @closed = false
34
+ @flashes = flashes
35
+ @now = nil
36
+ end
37
+
38
+ end
39
+
40
+ def call(env)
41
+ @app.call(env)
42
+ ensure
43
+ session = env['rack.session'] || {}
44
+ flash_hash = env[KEY]
45
+
46
+ if flash_hash
47
+ if !flash_hash.empty? || session.key?('flash')
48
+ session["flash"] = flash_hash.to_session_value
49
+ new_hash = flash_hash.dup
50
+ else
51
+ new_hash = flash_hash
52
+ end
53
+
54
+ env[KEY] = new_hash
55
+ end
56
+
57
+ if session.key?('flash') && session['flash'].nil?
58
+ session.delete('flash')
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ # This magic here allows us to unmarshal the old Rails 2.x ActionController::Flash::FlashHash
65
+ module ActionController
66
+ module Flash
67
+ class FlashHash < Hash
68
+
69
+ def self._load(args)
70
+ {}
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,3 @@
1
+ module Rails4SessionFlashBackport
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,14 @@
1
+ # encoding: utf-8
2
+ require "rails_4_session_flash_backport/version"
3
+
4
+ case Rails.version.to_i
5
+ when 2
6
+ require 'rails_4_session_flash_backport/rails2/flash_hash'
7
+ require 'rails_4_session_flash_backport/rails2/session_with_indifferent_access'
8
+ when 3
9
+ require 'rails_4_session_flash_backport/rails3/flash_hash'
10
+ when 4
11
+ Rails.logger.warn "You're on Rails 4 so it's probably safe to remove the rails_4_session_flash_backport gem!"
12
+ else
13
+ Rails.logger.warn "rails_4_session_flash_backport doesnt yet do anything on Rails #{Rails.version}"
14
+ end
@@ -0,0 +1,19 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'rails_4_session_flash_backport/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "rails_4_session_flash_backport"
8
+ gem.version = Rails4SessionFlashBackport::VERSION
9
+ gem.authors = ["Lucas Parry"]
10
+ gem.email = ["lparry@gmail.com"]
11
+ gem.description = %q{Store flash in the session in Rails 4 style on Rails 2/3}
12
+ gem.summary = %q{Backport of the way Rails 4 stores flash messages in the session to Rails 2 & 3, so you can safely take a session betweens Rails versions without things exploding.}
13
+ gem.homepage = ""
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+ end
@@ -0,0 +1,45 @@
1
+ require 'action_controller'
2
+ require 'rails_4_session_flash_backport/rails2/flash_hash'
3
+
4
+ describe ActionController::Flash::FlashHash, "hax" do
5
+
6
+ let(:rails_2_marshaled) { "\x04\bIC:'ActionController::Flash::FlashHash{\a:\vnoticeI\"\x11I'm a notice\x06:\x06ET:\nerrorI\"\x11I'm an error\x06;\aT\x06:\n@used{\a;\x06T;\bF" }
7
+ let(:rails_3_marshaled) { "\x04\bo:%ActionDispatch::Flash::FlashHash\t:\n@usedo:\bSet\x06:\n@hash{\x06:\vnoticeT:\f@closedF:\r@flashes{\a;\tI\"\x11I'm a notice\x06:\x06EF:\nerrorI\"\x11I'm an error\x06;\fF:\t@now0" }
8
+ let(:rails_2_vanilla) { Marshal.load(rails_2_marshaled) }
9
+ let(:rails_3_vanilla) { Marshal.load(rails_3_marshaled) }
10
+ let(:rails_4_style) { {'flashes' => {:notice => "I'm a notice", :error => "I'm an error"}, 'discard' => [:notice]} }
11
+
12
+ it "happily unmarshals a Rails 3 session without exploding" do
13
+ Marshal.load(rails_3_marshaled).should be_a(ActionDispatch::Flash::FlashHash)
14
+ end
15
+
16
+ context "#to_session_value" do
17
+ it "dumps to basic objects like rails 4" do
18
+ rails_2_vanilla.to_session_value.should be_a(Hash)
19
+ rails_2_vanilla.to_session_value.should == rails_4_style
20
+ end
21
+ end
22
+
23
+ context "#from_session_value" do
24
+ def this_is_the_flash_hash_were_looking_for(flash_hash)
25
+ flash_hash.should be_a(described_class)
26
+ flash_hash[:notice].should == "I'm a notice"
27
+ flash_hash[:error].should == "I'm an error"
28
+ flash_hash.sweep
29
+ flash_hash[:notice].should be_nil
30
+ flash_hash[:error].should == "I'm an error"
31
+ end
32
+
33
+ it "decodes rails 2 style to an empty FlashHash" do
34
+ this_is_the_flash_hash_were_looking_for(ActionController::Flash::FlashHash.from_session_value(rails_2_vanilla))
35
+ end
36
+
37
+ it "decodes rails 3 style to a FlashHash" do
38
+ this_is_the_flash_hash_were_looking_for(ActionController::Flash::FlashHash.from_session_value(rails_3_vanilla))
39
+ end
40
+
41
+ it "decodes rails 4 style to a FlashHash" do
42
+ this_is_the_flash_hash_were_looking_for(ActionController::Flash::FlashHash.from_session_value(rails_4_style))
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,48 @@
1
+ require 'action_dispatch'
2
+ require 'rails_4_session_flash_backport/rails3/flash_hash'
3
+
4
+ describe ActionDispatch::Flash::FlashHash, "hax" do
5
+
6
+ let(:rails_2_marshaled) { "\x04\bIC:'ActionController::Flash::FlashHash{\a:\vnoticeI\"\x11I'm a notice\x06:\x06ET:\nerrorI\"\x11I'm an error\x06;\aT\x06:\n@used{\a;\x06T;\bF" }
7
+ let(:rails_3_marshaled) { "\x04\bo:%ActionDispatch::Flash::FlashHash\t:\n@usedo:\bSet\x06:\n@hash{\x06:\vnoticeT:\f@closedF:\r@flashes{\a;\tI\"\x11I'm a notice\x06:\x06EF:\nerrorI\"\x11I'm an error\x06;\fF:\t@now0" }
8
+ let(:rails_2_vanilla) { Marshal.load(rails_2_marshaled) }
9
+ let(:rails_3_vanilla) { Marshal.load(rails_3_marshaled) }
10
+ let(:rails_4_style) { {'flashes' => {:notice => "I'm a notice", :error => "I'm an error"}, 'discard' => [:notice]} }
11
+ let(:rails_2_marshaled) { "\x04\bIC:'ActionController::Flash::FlashHash{\a:\vnoticeI\"\x11I'm a notice\x06:\x06ET:\nerrorI\"\x11I'm an error\x06;\aT\x06:\n@used{\a;\x06T;\bF" }
12
+ let(:rails_2_vanilla) { Marshal.load(rails_2_marshaled) }
13
+
14
+ it "happily unmarshals a Rails 2 session without exploding" do
15
+ Marshal.load(rails_2_marshaled).should be_a(ActionController::Flash::FlashHash)
16
+ end
17
+
18
+ context "#from_session_value" do
19
+ def this_is_the_flash_hash_were_looking_for(flash_hash)
20
+ flash_hash.should be_a(described_class)
21
+ flash_hash[:notice].should == "I'm a notice"
22
+ flash_hash[:error].should == "I'm an error"
23
+ flash_hash.sweep
24
+ flash_hash[:notice].should be_nil
25
+ flash_hash[:error].should == "I'm an error"
26
+ end
27
+
28
+ it "decodes rails 2 style to an empty FlashHash" do
29
+ this_is_the_flash_hash_were_looking_for(ActionDispatch::Flash::FlashHash.from_session_value(rails_2_vanilla))
30
+ end
31
+
32
+ it "decodes rails 3 style to a FlashHash" do
33
+ this_is_the_flash_hash_were_looking_for(ActionDispatch::Flash::FlashHash.from_session_value(rails_3_vanilla))
34
+ end
35
+
36
+ it "decodes rails 4 style to a FlashHash" do
37
+ this_is_the_flash_hash_were_looking_for(ActionDispatch::Flash::FlashHash.from_session_value(rails_4_style))
38
+ end
39
+ end
40
+
41
+ context "#to_session_value" do
42
+ it "dumps to basic objects like rails 4" do
43
+ rails_3_vanilla.to_session_value.should be_a(Hash)
44
+ rails_3_vanilla.to_session_value.should == rails_4_style
45
+ end
46
+ end
47
+
48
+ end
metadata ADDED
@@ -0,0 +1,61 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rails_4_session_flash_backport
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Lucas Parry
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-12-16 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: Store flash in the session in Rails 4 style on Rails 2/3
15
+ email:
16
+ - lparry@gmail.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - .gitignore
22
+ - Gemfile
23
+ - LICENSE.txt
24
+ - README.md
25
+ - Rakefile
26
+ - lib/rails_4_session_flash_backport.rb
27
+ - lib/rails_4_session_flash_backport/rails2/flash_hash.rb
28
+ - lib/rails_4_session_flash_backport/rails2/session_with_indifferent_access.rb
29
+ - lib/rails_4_session_flash_backport/rails3/flash_hash.rb
30
+ - lib/rails_4_session_flash_backport/version.rb
31
+ - rails_4_session_flash_backport.gemspec
32
+ - spec/rails2/flash_hash_spec.rb
33
+ - spec/rails3/flash_hash_spec.rb
34
+ homepage: ''
35
+ licenses: []
36
+ post_install_message:
37
+ rdoc_options: []
38
+ require_paths:
39
+ - lib
40
+ required_ruby_version: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ required_rubygems_version: !ruby/object:Gem::Requirement
47
+ none: false
48
+ requirements:
49
+ - - ! '>='
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ requirements: []
53
+ rubyforge_project:
54
+ rubygems_version: 1.8.23
55
+ signing_key:
56
+ specification_version: 3
57
+ summary: Backport of the way Rails 4 stores flash messages in the session to Rails
58
+ 2 & 3, so you can safely take a session betweens Rails versions without things exploding.
59
+ test_files:
60
+ - spec/rails2/flash_hash_spec.rb
61
+ - spec/rails3/flash_hash_spec.rb