diff --git a/vendor/responders.rb b/vendor/responders.rb new file mode 100644 index 0000000..427685c --- /dev/null +++ b/vendor/responders.rb @@ -0,0 +1,31 @@ +require 'action_controller' + +module Responders + autoload :FlashResponder, 'responders/flash_responder' + autoload :HttpCacheResponder, 'responders/http_cache_responder' + autoload :CollectionResponder, 'responders/collection_responder' + autoload :LocationResponder, 'responders/location_responder' + + require 'responders/controller_method' + + class Railtie < ::Rails::Railtie + config.responders = ActiveSupport::OrderedOptions.new + config.responders.flash_keys = [ :notice, :alert ] + config.responders.namespace_lookup = false + + if config.respond_to?(:app_generators) + config.app_generators.scaffold_controller = :responders_controller + else + config.generators.scaffold_controller = :responders_controller + end + + # Add load paths straight to I18n, so engines and application can overwrite it. + require 'active_support/i18n' + I18n.load_path << File.expand_path('../responders/locales/en.yml', __FILE__) + + initializer "responders.flash_responder" do |app| + Responders::FlashResponder.flash_keys = app.config.responders.flash_keys + Responders::FlashResponder.namespace_lookup = app.config.responders.namespace_lookup + end + end +end diff --git a/vendor/responders/collection_responder.rb b/vendor/responders/collection_responder.rb new file mode 100644 index 0000000..79fa13d --- /dev/null +++ b/vendor/responders/collection_responder.rb @@ -0,0 +1,30 @@ +module Responders + # This responder modifies your current responder to redirect + # to the collection page on POST/PUT/DELETE. + module CollectionResponder + protected + + # Returns the collection location for redirecting after POST/PUT/DELETE. + # This method, converts the following resources array to the following: + # + # [:admin, @post] #=> [:admin, :posts] + # [@user, @post] #=> [@user, :posts] + # + # When these new arrays are given to redirect_to, it will generate the + # proper URL pointing to the index action. + # + # [:admin, @post] #=> admin_posts_url + # [@user, @post] #=> user_posts_url(@user.to_param) + # + def navigation_location + return options[:location] if options[:location] + klass = resources.last.class + + if klass.respond_to?(:model_name) + resources[0...-1] << klass.model_name.route_key.to_sym + else + resources + end + end + end +end diff --git a/vendor/responders/controller_method.rb b/vendor/responders/controller_method.rb new file mode 100644 index 0000000..b9105ee --- /dev/null +++ b/vendor/responders/controller_method.rb @@ -0,0 +1,37 @@ +module Responders + module ControllerMethod + # Adds the given responders to the current controller's responder, allowing you to cherry-pick + # which responders you want per controller. + # + # class InvitationsController < ApplicationController + # responders :flash, :http_cache + # end + # + # Takes symbols and strings and translates them to VariableResponder (eg. :flash becomes FlashResponder). + # Also allows passing in the responders modules in directly, so you could do: + # + # responders FlashResponder, HttpCacheResponder + # + # Or a mix of both methods: + # + # responders :flash, MyCustomResponder + # + def responders(*responders) + self.responder = responders.inject(Class.new(responder)) do |klass, responder| + responder = case responder + when Module + responder + when String, Symbol + Responders.const_get("#{responder.to_s.camelize}Responder") + else + raise "responder has to be a string, a symbol or a module" + end + + klass.send(:include, responder) + klass + end + end + end +end + +ActionController::Base.extend Responders::ControllerMethod diff --git a/vendor/responders/flash_responder.rb b/vendor/responders/flash_responder.rb new file mode 100644 index 0000000..2b297ac --- /dev/null +++ b/vendor/responders/flash_responder.rb @@ -0,0 +1,192 @@ +module Responders + # Responder to automatically set flash messages based on I18n API. It checks for + # message based on the current action, but also allows defaults to be set, using + # the following order: + # + # flash.controller_name.action_name.status + # flash.actions.action_name.status + # + # So, if you have a CarsController, create action, it will check for: + # + # flash.cars.create.status + # flash.actions.create.status + # + # The statuses by default are :notice (when the object can be created, updated + # or destroyed with success) and :alert (when the object cannot be created + # or updated). + # + # On I18n, the resource_name given is available as interpolation option, + # this means you can set: + # + # flash: + # actions: + # create: + # notice: "Hooray! %{resource_name} was successfully created!" + # + # But sometimes, flash messages are not that simple. Going back + # to cars example, you might want to say the brand of the car when it's + # updated. Well, that's easy also: + # + # flash: + # cars: + # update: + # notice: "Hooray! You just tuned your %{car_brand}!" + # + # Since :car_name is not available for interpolation by default, you have + # to overwrite interpolation_options in your controller. + # + # def interpolation_options + # { :car_brand => @car.brand } + # end + # + # Then you will finally have: + # + # 'Hooray! You just tuned your Aston Martin!' + # + # If your controller is namespaced, for example Admin::CarsController, + # the messages will be checked in the following order: + # + # flash.admin.cars.create.status + # flash.admin.actions.create.status + # flash.cars.create.status + # flash.actions.create.status + # + # You can also have flash messages with embedded HTML. Just create a scope that + # ends with _html as the scopes below: + # + # flash.actions.create.notice_html + # flash.cars.create.notice_html + # + # == Options + # + # FlashResponder also accepts some options through respond_with API. + # + # * :flash - When set to false, no flash message is set. + # + # respond_with(@user, :flash => true) + # + # * :notice - Supply the message to be set if the record has no errors. + # * :alert - Supply the message to be set if the record has errors. + # + # respond_with(@user, :notice => "Hooray! Welcome!", :alert => "Woot! You failed.") + # + # * :flash_now - Sets the flash message using flash.now. Accepts true, :on_failure or :on_sucess. + # + # == Configure status keys + # + # As said previously, FlashResponder by default use :notice and :alert + # keys. You can change that by setting the status_keys: + # + # Responders::FlashResponder.flash_keys = [ :success, :failure ] + # + # However, the options :notice and :alert to respond_with are kept :notice + # and :alert. + # + module FlashResponder + class << self + attr_accessor :flash_keys, :namespace_lookup, :helper + end + + self.flash_keys = [ :notice, :alert ] + self.namespace_lookup = false + self.helper = Object.new.extend( + ActionView::Helpers::TranslationHelper, + ActionView::Helpers::TagHelper + ) + + def initialize(controller, resources, options={}) + super + @flash = options.delete(:flash) + @notice = options.delete(:notice) + @alert = options.delete(:alert) + @flash_now = options.delete(:flash_now) { :on_failure } + end + + def to_html + set_flash_message! if set_flash_message? + super + end + + def to_js + set_flash_message! if set_flash_message? + defined?(super) ? super : to_format + end + + protected + + def set_flash_message! + if has_errors? + status = Responders::FlashResponder.flash_keys.last + set_flash(status, @alert) + else + status = Responders::FlashResponder.flash_keys.first + set_flash(status, @notice) + end + + return if controller.flash[status].present? + + options = mount_i18n_options(status) + message = Responders::FlashResponder.helper.t options[:default].shift, options + set_flash(status, message) + end + + def set_flash(key, value) + return if value.blank? + flash = controller.flash + flash = flash.now if set_flash_now? + flash[key] ||= value + end + + def set_flash_now? + @flash_now == true || format == :js || + (default_action && (has_errors? ? @flash_now == :on_failure : @flash_now == :on_success)) + end + + def set_flash_message? #:nodoc: + !get? && @flash != false + end + + def mount_i18n_options(status) #:nodoc: + resource_name = if resource.class.respond_to?(:model_name) + resource.class.model_name.human + else + resource.class.name.underscore.humanize + end + + options = { + :default => flash_defaults_by_namespace(status), + :resource_name => resource_name, + :downcase_resource_name => resource_name.downcase + } + + if controller.respond_to?(:interpolation_options, true) + options.merge!(controller.send(:interpolation_options)) + end + + options + end + + def flash_defaults_by_namespace(status) #:nodoc: + defaults = [] + slices = controller.controller_path.split('/') + lookup = Responders::FlashResponder.namespace_lookup + + begin + controller_scope = :"flash.#{slices.fill(controller.controller_name, -1).join('.')}.#{controller.action_name}.#{status}" + + actions_scope = lookup ? slices.fill('actions', -1).join('.') : :actions + actions_scope = :"flash.#{actions_scope}.#{controller.action_name}.#{status}" + + defaults << :"#{controller_scope}_html" + defaults << controller_scope + + defaults << :"#{actions_scope}_html" + defaults << actions_scope + + slices.shift + end while slices.size > 0 && lookup + + defaults << "" + end + end +end diff --git a/vendor/responders/http_cache_responder.rb b/vendor/responders/http_cache_responder.rb new file mode 100644 index 0000000..6fcace6 --- /dev/null +++ b/vendor/responders/http_cache_responder.rb @@ -0,0 +1,44 @@ +module Responders + # Set HTTP Last-Modified headers based on the given resource. It's used only + # on API behavior (to_format) and is useful for a client to check in the server + # if a resource changed after a specific date or not. + # + # This is not usually not used in html requests because pages contains a lot + # information besides the resource information, as current_user, flash messages, + # widgets... that are better handled with other strategies, as fragment caches and + # the digest of the body. + # + module HttpCacheResponder + def initialize(controller, resources, options={}) + super + @http_cache = options.delete(:http_cache) + end + + def to_format + return if do_http_cache? && do_http_cache! + super + end + + protected + + def do_http_cache! + timestamp = resources.map do |resource| + resource.updated_at.try(:utc) if resource.respond_to?(:updated_at) + end.compact.max + + controller.response.last_modified ||= timestamp if timestamp + + head :not_modified if fresh = request.fresh?(controller.response) + fresh + end + + def do_http_cache? + get? && @http_cache != false && ActionController::Base.perform_caching && + persisted? && resourceful? && resource.respond_to?(:updated_at) + end + + def persisted? + resource.respond_to?(:persisted?) ? resource.persisted? : true + end + end +end \ No newline at end of file diff --git a/vendor/responders/locales/en.yml b/vendor/responders/locales/en.yml new file mode 100644 index 0000000..c3e147a --- /dev/null +++ b/vendor/responders/locales/en.yml @@ -0,0 +1,12 @@ +en: + flash: + actions: + create: + notice: '%{resource_name} was successfully created.' + # alert: '%{resource_name} could not be created.' + update: + notice: '%{resource_name} was successfully updated.' + # alert: '%{resource_name} could not be updated.' + destroy: + notice: '%{resource_name} was successfully destroyed.' + alert: '%{resource_name} could not be destroyed.' diff --git a/vendor/responders/location_responder.rb b/vendor/responders/location_responder.rb new file mode 100644 index 0000000..d555091 --- /dev/null +++ b/vendor/responders/location_responder.rb @@ -0,0 +1,26 @@ +module Responders + # Responder to accept callable objects as the redirect location. + # Useful when you want to use the respond_with method with + # a route that requires persisted objects, but the validation may fail. + # + # class ThingsController < ApplicationController + # responders :location, :flash + # respond_to :html + # + # def create + # @thing = Things.create(params[:thing]) + # respond_with @thing, location: -> { thing_path(@thing) } + # end + # end + # + module LocationResponder + def initialize(controller, resources, options = {}) + super + + if options[:location].respond_to?(:call) + location = options.delete(:location) + options[:location] = location.call unless has_errors? + end + end + end +end diff --git a/vendor/responders/version.rb b/vendor/responders/version.rb new file mode 100644 index 0000000..4965c48 --- /dev/null +++ b/vendor/responders/version.rb @@ -0,0 +1,3 @@ +module Responders + VERSION = "1.1.2".freeze +end