Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: rails 4 Flash fixes #55

Merged
merged 1 commit into from
Mar 20, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions lib/apress/utils/engine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ class Engine < ::Rails::Engine
require cd + '/extensions/readthis/cache'

Readthis::Cache.send(:include, ::Apress::Utils::Extensions::Readthis::Cache)

if Rails::VERSION::MAJOR == 4 && Rails::VERSION::MINOR < 2
require 'apress/utils/extensions/action_dispatch/flash'
end
end
end
end
Expand Down
278 changes: 278 additions & 0 deletions lib/apress/utils/extensions/action_dispatch/flash.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,278 @@
# В рельсах 4.0 есть проблемы с флешем - пишем символы, читаем строки и тп.
# https://github.com/rails/rails/pull/13945/
#
# Забрал несколько коммитов:
# https://github.com/rails/rails/commit/a6ce984b49519de7701aa13d04300c9d03cf8f72
# https://github.com/rails/rails/commit/a668beffd64106a1e1fedb71cc25eaaa11baf0c1
# https://github.com/rails/rails/commit/3c60fb429d9e4271bce3a71b0f5fb4bb2f7fbe2d

require 'active_support/core_ext/hash/keys'

module ActionDispatch
class Request < Rack::Request
# Access the contents of the flash. Use <tt>flash["notice"]</tt> to
# read a notice you put there or <tt>flash["notice"] = "hello"</tt>
# to put a new one.
def flash
@env[Flash::KEY] ||= Flash::FlashHash.from_session_value(session["flash"])
end
end

# The flash provides a way to pass temporary objects between actions. Anything you place in the flash will be exposed
# to the very next action and then cleared out. This is a great way of doing notices and alerts, such as a create
# action that sets <tt>flash[:notice] = "Post successfully created"</tt> before redirecting to a display action that can

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [122/120]

# then expose the flash to its template. Actually, that exposure is automatically done.
#
# class PostsController < ActionController::Base
# def create
# # save post
# flash[:notice] = "Post successfully created"
# redirect_to @post
# end
#
# def show
# # doesn't need to assign the flash notice to the template, that's done automatically
# end
# end
#
# show.html.erb
# <% if flash[:notice] %>
# <div class="notice"><%= flash[:notice] %></div>
# <% end %>
#
# Since the +notice+ and +alert+ keys are a common idiom, convenience accessors are available:
#
# flash.alert = "You must be logged in"
# flash.notice = "Post successfully created"
#
# This example just places a string in the flash, but you can put any object in there. And of course, you can put as
# many as you like at a time too. Just remember: They'll be gone by the time the next action has been performed.
#
# See docs on the FlashHash class for more details about the flash.
class Flash
KEY = 'action_dispatch.request.flash_hash'.freeze

class FlashNow #:nodoc:
attr_accessor :flash

def initialize(flash)
@flash = flash
end

def []=(k, v)
k = k.to_s
@flash[k] = v
@flash.discard(k)
v

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Variable v used in void context.

end

def [](k)
@flash[k.to_s]
end

# Convenience accessor for <tt>flash.now[:alert]=</tt>.
def alert=(message)
self[:alert] = message
end

# Convenience accessor for <tt>flash.now[:notice]=</tt>.
def notice=(message)
self[:notice] = message
end
end

class FlashHash
include Enumerable

def self.from_session_value(value)
flash = case value
when FlashHash # Rails 3.1, 3.2

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indent when as deep as case.

new(value.instance_variable_get(:@flashes), value.instance_variable_get(:@used))
when Hash # Rails 4.0

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indent when as deep as case.

new(value['flashes'], value['discard'])
else
new
end

flash.tap(&:sweep)
end

def to_session_value
return nil if empty?
{'discard' => @discard.to_a, 'flashes' => @flashes}
end

def initialize(flashes = {}, discard = []) #:nodoc:
@discard = Set.new(stringify_array(discard))
@flashes = flashes.stringify_keys
@now = nil
end

def initialize_copy(other)
if other.now_is_loaded?
@now = other.now.dup
@now.flash = self
end
super
end

def []=(k, v)
k = k.to_s
@discard.delete k
@flashes[k] = v
end

def [](k)
@flashes[k.to_s]
end

def update(h) #:nodoc:
@discard.subtract stringify_array(h.keys)
@flashes.update h
self
end

def keys
@flashes.keys
end

def key?(name)
@flashes.key? name.to_s
end

def delete(key)
key = key.to_s
@discard.delete key
@flashes.delete key
self
end

def to_hash
@flashes.dup
end

def empty?
@flashes.empty?
end

def clear
@discard.clear
@flashes.clear
end

def each(&block)
@flashes.each(&block)
end

alias :merge! :update

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use alias merge! update instead of alias :merge! :update.


def replace(h) #:nodoc:
@discard.clear
@flashes.replace h
self
end

# Sets a flash that will not be available to the next action, only to the current.
#
# flash.now[:message] = "Hello current action"
#
# This method enables you to use the flash as a central messaging system in your app.
# When you need to pass an object to the next action, you use the standard flash assign (<tt>[]=</tt>).
# When you need to pass an object to the current action, you use <tt>now</tt>, and your object will
# vanish when the current action is done.
#
# Entries set via <tt>now</tt> are accessed the same way as standard entries: <tt>flash['my-key']</tt>.
#
# Also, brings two convenience accessors:
#
# flash.now.alert = "Beware now!"
# # Equivalent to flash.now[:alert] = "Beware now!"
#
# flash.now.notice = "Good luck now!"
# # Equivalent to flash.now[:notice] = "Good luck now!"
def now
@now ||= FlashNow.new(self)
end

# Keeps either the entire current flash or a specific flash entry available for the next action:
#
# flash.keep # keeps the entire flash
# flash.keep(:notice) # keeps only the "notice" entry, the rest of the flash is discarded
def keep(k = nil)
k = k.to_s if k
@discard.subtract Array(k || keys)
k ? self[k] : self
end

# Marks the entire flash or a single flash entry to be discarded by the end of the current action:
#
# flash.discard # discard the entire flash at the end of the current action
# flash.discard(:warning) # discard only the "warning" entry at the end of the current action
def discard(k = nil)
k = k.to_s if k
@discard.merge Array(k || keys)
k ? self[k] : self
end

# Mark for removal entries that were kept, and delete unkept ones.
#
# This method is called automatically by filters, so you generally don't need to care about it.
def sweep #:nodoc:
@discard.each { |k| @flashes.delete k }
@discard.replace @flashes.keys
end

# Convenience accessor for <tt>flash[:alert]</tt>.
def alert
self[:alert]
end

# Convenience accessor for <tt>flash[:alert]=</tt>.
def alert=(message)
self[:alert] = message
end

# Convenience accessor for <tt>flash[:notice]</tt>.
def notice
self[:notice]
end

# Convenience accessor for <tt>flash[:notice]=</tt>.
def notice=(message)
self[:notice] = message
end

protected

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keep a blank line before and after protected.

def now_is_loaded?
@now
end

def stringify_array(array)
array.map do |item|
item.kind_of?(Symbol) ? item.to_s : item

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer Object#is_a? over Object#kind_of?.

end
end
end

def initialize(app)
@app = app
end

def call(env)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cyclomatic complexity for call is too high. [9/6]
Perceived complexity for call is too high. [9/7]

@app.call(env)
ensure
session = Request::Session.find(env) || {}
flash_hash = env[KEY]

if flash_hash && (flash_hash.present? || session.key?('flash'))
session["flash"] = flash_hash.to_session_value
env[KEY] = flash_hash.dup
end

if (!session.respond_to?(:loaded?) || session.loaded?) && # (reset_session uses {}, which doesn't implement #loaded?)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [123/120]

session.key?('flash') && session['flash'].nil?
session.delete('flash')
end
end
end
end
13 changes: 13 additions & 0 deletions spec/lib/apress/utils/extensions/flash_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
require 'spec_helper'

class ApplicationController < ActionController::Base; end

describe ApplicationController, type: :controller do

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra empty line detected at block body beginning.

it do
controller.flash[:error] = 'error'

expect(controller.flash[:error]).to eq 'error'
expect(controller.flash.key?(:error)).to be true
end
end