tennpipes-base 3.6.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +20 -0
  3. data/README.rdoc +294 -0
  4. data/Rakefile +1 -0
  5. data/bin/tennpipes +8 -0
  6. data/lib/tennpipes-base.rb +196 -0
  7. data/lib/tennpipes-base/application.rb +175 -0
  8. data/lib/tennpipes-base/application/application_setup.rb +202 -0
  9. data/lib/tennpipes-base/application/authenticity_token.rb +25 -0
  10. data/lib/tennpipes-base/application/flash.rb +229 -0
  11. data/lib/tennpipes-base/application/params_protection.rb +129 -0
  12. data/lib/tennpipes-base/application/routing.rb +1002 -0
  13. data/lib/tennpipes-base/application/show_exceptions.rb +50 -0
  14. data/lib/tennpipes-base/caller.rb +53 -0
  15. data/lib/tennpipes-base/cli/adapter.rb +33 -0
  16. data/lib/tennpipes-base/cli/base.rb +105 -0
  17. data/lib/tennpipes-base/cli/console.rb +20 -0
  18. data/lib/tennpipes-base/cli/launcher.rb +103 -0
  19. data/lib/tennpipes-base/cli/rake.rb +50 -0
  20. data/lib/tennpipes-base/cli/rake_tasks.rb +72 -0
  21. data/lib/tennpipes-base/command.rb +38 -0
  22. data/lib/tennpipes-base/ext/sinatra.rb +29 -0
  23. data/lib/tennpipes-base/filter.rb +52 -0
  24. data/lib/tennpipes-base/images/404.png +0 -0
  25. data/lib/tennpipes-base/images/500.png +0 -0
  26. data/lib/tennpipes-base/loader.rb +202 -0
  27. data/lib/tennpipes-base/logger.rb +492 -0
  28. data/lib/tennpipes-base/module.rb +58 -0
  29. data/lib/tennpipes-base/mounter.rb +308 -0
  30. data/lib/tennpipes-base/path_router.rb +119 -0
  31. data/lib/tennpipes-base/path_router/compiler.rb +110 -0
  32. data/lib/tennpipes-base/path_router/error_handler.rb +8 -0
  33. data/lib/tennpipes-base/path_router/matcher.rb +123 -0
  34. data/lib/tennpipes-base/path_router/route.rb +169 -0
  35. data/lib/tennpipes-base/reloader.rb +309 -0
  36. data/lib/tennpipes-base/reloader/rack.rb +26 -0
  37. data/lib/tennpipes-base/reloader/storage.rb +55 -0
  38. data/lib/tennpipes-base/router.rb +98 -0
  39. data/lib/tennpipes-base/server.rb +119 -0
  40. data/lib/tennpipes-base/tasks.rb +21 -0
  41. data/lib/tennpipes-base/version.rb +20 -0
  42. data/lib/tennpipes-base/version.rb~ +20 -0
  43. data/test/fixtures/app_gem/Gemfile +4 -0
  44. data/test/fixtures/app_gem/app/app.rb +3 -0
  45. data/test/fixtures/app_gem/app_gem.gemspec +17 -0
  46. data/test/fixtures/app_gem/lib/app_gem.rb +7 -0
  47. data/test/fixtures/app_gem/lib/app_gem/version.rb +3 -0
  48. data/test/fixtures/apps/complex.rb +32 -0
  49. data/test/fixtures/apps/demo_app.rb +7 -0
  50. data/test/fixtures/apps/demo_demo.rb +7 -0
  51. data/test/fixtures/apps/demo_project/api/app.rb +7 -0
  52. data/test/fixtures/apps/demo_project/api/lib/api_lib.rb +3 -0
  53. data/test/fixtures/apps/demo_project/app.rb +7 -0
  54. data/test/fixtures/apps/external_apps/fake_lib.rb +1 -0
  55. data/test/fixtures/apps/external_apps/fake_root.rb +2 -0
  56. data/test/fixtures/apps/helpers/class_methods_helpers.rb +4 -0
  57. data/test/fixtures/apps/helpers/instance_methods_helpers.rb +4 -0
  58. data/test/fixtures/apps/helpers/support.rb +1 -0
  59. data/test/fixtures/apps/helpers/system_helpers.rb +8 -0
  60. data/test/fixtures/apps/kiq.rb +3 -0
  61. data/test/fixtures/apps/lib/myklass.rb +2 -0
  62. data/test/fixtures/apps/lib/myklass/mysubklass.rb +4 -0
  63. data/test/fixtures/apps/models/child.rb +2 -0
  64. data/test/fixtures/apps/models/parent.rb +5 -0
  65. data/test/fixtures/apps/mountable_apps/rack_apps.rb +15 -0
  66. data/test/fixtures/apps/mountable_apps/static.html +1 -0
  67. data/test/fixtures/apps/precompiled_app.rb +19 -0
  68. data/test/fixtures/apps/simple.rb +32 -0
  69. data/test/fixtures/apps/static.rb +10 -0
  70. data/test/fixtures/apps/system.rb +13 -0
  71. data/test/fixtures/apps/system_class_methods_demo.rb +7 -0
  72. data/test/fixtures/apps/system_instance_methods_demo.rb +7 -0
  73. data/test/fixtures/dependencies/a.rb +9 -0
  74. data/test/fixtures/dependencies/b.rb +4 -0
  75. data/test/fixtures/dependencies/c.rb +1 -0
  76. data/test/fixtures/dependencies/circular/e.rb +13 -0
  77. data/test/fixtures/dependencies/circular/f.rb +2 -0
  78. data/test/fixtures/dependencies/circular/g.rb +2 -0
  79. data/test/fixtures/dependencies/d.rb +4 -0
  80. data/test/fixtures/reloadable_apps/external/app/app.rb +6 -0
  81. data/test/fixtures/reloadable_apps/external/app/controllers/base.rb +6 -0
  82. data/test/fixtures/reloadable_apps/main/app.rb +10 -0
  83. data/test/helper.rb +30 -0
  84. data/test/test_application.rb +185 -0
  85. data/test/test_core.rb +93 -0
  86. data/test/test_csrf_protection.rb +208 -0
  87. data/test/test_dependencies.rb +57 -0
  88. data/test/test_filters.rb +389 -0
  89. data/test/test_flash.rb +168 -0
  90. data/test/test_locale.rb +21 -0
  91. data/test/test_logger.rb +295 -0
  92. data/test/test_mounter.rb +302 -0
  93. data/test/test_params_protection.rb +195 -0
  94. data/test/test_reloader_complex.rb +74 -0
  95. data/test/test_reloader_external.rb +21 -0
  96. data/test/test_reloader_simple.rb +101 -0
  97. data/test/test_reloader_system.rb +113 -0
  98. data/test/test_restful_routing.rb +33 -0
  99. data/test/test_router.rb +281 -0
  100. data/test/test_routing.rb +2328 -0
  101. metadata +301 -0
@@ -0,0 +1,175 @@
1
+ require 'tennpipes-base/application/flash'
2
+ require 'tennpipes-base/application/routing'
3
+ require 'tennpipes-base/application/show_exceptions'
4
+ require 'tennpipes-base/application/authenticity_token'
5
+ require 'tennpipes-base/application/application_setup'
6
+ require 'tennpipes-base/application/params_protection'
7
+
8
+ module Tennpipes
9
+ ##
10
+ # Subclasses of this become independent Tennpipes applications
11
+ # (stemming from Sinatra::Application).
12
+ # These subclassed applications can be easily mounted into other
13
+ # Tennpipes applications as well.
14
+ #
15
+ class Application < Sinatra::Base
16
+ register Tennpipes::ApplicationSetup
17
+ register Tennpipes::Routing
18
+ register Tennpipes::ParamsProtection
19
+
20
+ ##
21
+ # Returns the logger for this application.
22
+ #
23
+ # @return [Tennpipes::Logger] Logger associated with this app.
24
+ #
25
+ def logger
26
+ Tennpipes.logger
27
+ end
28
+
29
+ class << self
30
+ def inherited(base)
31
+ begun_at = Time.now
32
+ CALLERS_TO_IGNORE.concat(TENNPIPES_IGNORE_CALLERS)
33
+ super(base)
34
+ base.prerequisites.replace(self.prerequisites.dup)
35
+ base.default_configuration!
36
+ logger.devel :setup, begun_at, base
37
+ end
38
+
39
+ ##
40
+ # Reloads the application files from all defined load paths.
41
+ #
42
+ # This method is used from our Tennpipes Reloader during development mode
43
+ # in order to reload the source files.
44
+ #
45
+ # @return [TrueClass]
46
+ #
47
+ # @example
48
+ # MyApp.reload!
49
+ #
50
+ def reload!
51
+ logger.devel "Reloading application #{settings}"
52
+ reset!
53
+ reset_router!
54
+ Tennpipes.require_dependencies(settings.app_file, :force => true)
55
+ require_dependencies
56
+ default_routes
57
+ default_errors
58
+ I18n.reload! if defined?(I18n)
59
+ true
60
+ end
61
+
62
+ ##
63
+ # Resets application routes to only routes not defined by the user.
64
+ #
65
+ # @return [TrueClass]
66
+ #
67
+ # @example
68
+ # MyApp.reset_routes!
69
+ #
70
+ def reset_routes!
71
+ reset_router!
72
+ default_routes
73
+ true
74
+ end
75
+
76
+ ##
77
+ # Returns the routes of our app.
78
+ #
79
+ # @example
80
+ # MyApp.routes
81
+ #
82
+ def routes
83
+ router.routes
84
+ end
85
+
86
+ ##
87
+ # Returns an absolute path of view in application views folder.
88
+ #
89
+ # @example
90
+ # Admin.view_path 'users/index' #=> "/home/user/test/admin/views/users/index"
91
+ #
92
+ def view_path(view)
93
+ File.expand_path(view, views)
94
+ end
95
+
96
+ ##
97
+ # Returns an absolute path of application layout.
98
+ #
99
+ # @example
100
+ # Admin.layout_path :application #=> "/home/user/test/admin/views/layouts/application"
101
+ #
102
+ def layout_path(layout)
103
+ view_path("layouts/#{layout}")
104
+ end
105
+
106
+ ##
107
+ # Run the Tennpipes app as a self-hosted server using
108
+ # Thin, Mongrel or WEBrick (in that order).
109
+ #
110
+ # @see Tennpipes::Server#start
111
+ #
112
+ def run!(options={})
113
+ return unless Tennpipes.load!
114
+ Tennpipes.mount(settings.to_s).to('/')
115
+ Tennpipes.run!(options)
116
+ end
117
+
118
+ ##
119
+ # Returns default list of path globs to load as dependencies.
120
+ # Appends custom dependency patterns to the be loaded for your Application.
121
+ #
122
+ # @return [Array]
123
+ # list of path globs to load as dependencies
124
+ #
125
+ # @example
126
+ # MyApp.dependencies << "#{Tennpipes.root}/uploaders/**/*.rb"
127
+ # MyApp.dependencies << Tennpipes.root('other_app', 'controllers.rb')
128
+ #
129
+ def dependencies
130
+ [
131
+ 'urls.rb',
132
+ 'config/urls.rb',
133
+ 'mailers/*.rb',
134
+ 'mailers.rb',
135
+ 'controllers/**/*.rb',
136
+ 'controllers.rb',
137
+ 'helpers/**/*.rb',
138
+ 'helpers.rb',
139
+ ].flat_map{ |file| Dir.glob(File.join(settings.root, file)) }
140
+ end
141
+
142
+ ##
143
+ # An array of file to load before your app.rb, basically are files
144
+ # which our app depends on.
145
+ #
146
+ # By default we look for files:
147
+ #
148
+ # # List of default files that we are looking for:
149
+ # yourapp/models.rb
150
+ # yourapp/models/**/*.rb
151
+ # yourapp/lib.rb
152
+ # yourapp/lib/**/*.rb
153
+ #
154
+ # @example Adding a custom prerequisite
155
+ # MyApp.prerequisites << Tennpipes.root('my_app', 'custom_model.rb')
156
+ #
157
+ def prerequisites
158
+ @_prerequisites ||= []
159
+ end
160
+
161
+ def default(option, *args, &block)
162
+ set(option, *args, &block) unless respond_to?(option)
163
+ end
164
+
165
+ protected
166
+
167
+ ##
168
+ # Requires all files within the application load paths.
169
+ #
170
+ def require_dependencies
171
+ Tennpipes.require_dependencies(dependencies, :force => true)
172
+ end
173
+ end
174
+ end
175
+ end
@@ -0,0 +1,202 @@
1
+ module Tennpipes
2
+ ##
3
+ # Holds setup-oriented methods for Tennpipes::Application.
4
+ #
5
+ module ApplicationSetup
6
+ def self.registered(app)
7
+ app.extend(ClassMethods)
8
+ end
9
+
10
+ module ClassMethods
11
+ ##
12
+ # Defines default settings for Tennpipes application.
13
+ #
14
+ def default_configuration!
15
+ set :app_file, File.expand_path(caller_files.first || $0)
16
+ set :app_name, settings.to_s.underscore.to_sym
17
+
18
+ set :environment, Tennpipes.env
19
+ set :reload, proc { development? }
20
+ set :logging, proc { development? }
21
+
22
+ set :method_override, true
23
+ set :default_builder, 'StandardFormBuilder'
24
+
25
+ default_paths
26
+ default_security
27
+ global_configuration
28
+ setup_prerequisites
29
+ end
30
+
31
+ ##
32
+ # Setup the application by registering initializers, load paths and logger.
33
+ # Invoked automatically when an application is first instantiated.
34
+ #
35
+ # @return [TrueClass]
36
+ #
37
+ def setup_application!
38
+ return if @_configured
39
+ require_dependencies
40
+ default_routes
41
+ default_errors
42
+ setup_locale
43
+ precompile_routes!
44
+ @_configured = true
45
+ end
46
+
47
+ def precompile_routes?
48
+ settings.respond_to?(:precompile_routes) && settings.precompile_routes?
49
+ end
50
+
51
+ def precompile_routes!
52
+ compiled_router.prepare!
53
+ compiled_router.engine.compile!
54
+ end
55
+
56
+ private
57
+
58
+ def default_paths
59
+ set :locale_path, proc { Dir.glob File.join(root, 'locale/**/*.{rb,yml}') }
60
+ set :views, proc { File.join(root, 'views') }
61
+
62
+ set :uri_root, '/'
63
+ set :public_folder, proc { Tennpipes.root('public', uri_root) }
64
+ set :images_path, proc { File.join(public_folder, 'images') }
65
+ end
66
+
67
+ def default_security
68
+ set :protection, :except => :path_traversal
69
+ set :sessions, false
70
+ set :protect_from_csrf, false
71
+ set :report_csrf_failure, false
72
+ set :allow_disabled_csrf, false
73
+ end
74
+
75
+ ##
76
+ # Applies global tennpipes configuration blocks to current application.
77
+ #
78
+ def global_configuration
79
+ Tennpipes.global_configurations.each do |configuration|
80
+ class_eval(&configuration)
81
+ end
82
+ end
83
+
84
+ def setup_prerequisites
85
+ prerequisites.concat(default_prerequisites).uniq!
86
+ Tennpipes.require_dependencies(prerequisites)
87
+ end
88
+
89
+ ##
90
+ # Returns globs of default paths of application prerequisites.
91
+ #
92
+ def default_prerequisites
93
+ [
94
+ '/models.rb',
95
+ '/models/**/*.rb',
96
+ '/lib.rb',
97
+ '/lib/**/*.rb',
98
+ ].map{ |glob| File.join(settings.root, glob) }
99
+ end
100
+
101
+ # Overrides the default middleware for Sinatra based on Tennpipes conventions.
102
+ # Also initializes the application after setting up the middleware.
103
+ def setup_default_middleware(builder)
104
+ setup_sessions builder
105
+ builder.use Sinatra::ExtendedRack if defined?(EventMachine)
106
+ builder.use Tennpipes::ShowExceptions if show_exceptions?
107
+ builder.use Tennpipes::Logger::Rack, uri_root if Tennpipes.logger && logging?
108
+ builder.use Tennpipes::Reloader::Rack if reload?
109
+ builder.use Rack::MethodOverride if method_override?
110
+ builder.use Rack::Head
111
+ register Tennpipes::Flash
112
+ setup_protection builder
113
+ setup_csrf_protection builder
114
+ setup_application!
115
+ end
116
+
117
+ ##
118
+ # We need to add almost __sinatra__ images.
119
+ #
120
+ def default_routes
121
+ configure :development do
122
+ get '*__sinatra__/:image.png' do
123
+ content_type :png
124
+ send_file(File.dirname(__FILE__) + "/../images/#{params[:image]}.png")
125
+ end
126
+ end
127
+ end
128
+
129
+ ##
130
+ # This log errors for production environments.
131
+ #
132
+ def default_errors
133
+ configure :production do
134
+ error ::Exception do
135
+ logger.exception env['sinatra.error']
136
+ halt(500, { 'Content-Type' => 'text/html' }, ['<h1>Internal Server Error</h1>'])
137
+ end unless errors.has_key?(::Exception)
138
+ end
139
+ end
140
+
141
+ def setup_locale
142
+ return unless defined? I18n
143
+ Reloader.special_files += locale_path
144
+ I18n.load_path << locale_path
145
+ I18n.reload!
146
+ end
147
+
148
+ # allow custome session management
149
+ def setup_sessions(builder)
150
+ if sessions.kind_of?(Hash) && sessions[:use]
151
+ builder.use sessions[:use], sessions[:config] || {}
152
+ else
153
+ super
154
+ end
155
+ end
156
+
157
+ # sets up csrf protection for the app
158
+ def setup_csrf_protection(builder)
159
+ check_csrf_protection_dependency
160
+
161
+ if protect_from_csrf?
162
+ options = options_for_csrf_protection_setup
163
+ options.merge!(protect_from_csrf) if protect_from_csrf.kind_of?(Hash)
164
+ builder.use(options[:except] ? Tennpipes::AuthenticityToken : Rack::Protection::AuthenticityToken, options)
165
+ end
166
+ end
167
+
168
+ # returns the options used in the builder for csrf protection setup
169
+ def options_for_csrf_protection_setup
170
+ options = { :logger => logger }
171
+ if report_csrf_failure? || allow_disabled_csrf?
172
+ options.merge!(
173
+ :reaction => :report,
174
+ :report_key => 'protection.csrf.failed'
175
+ )
176
+ end
177
+ options
178
+ end
179
+
180
+ # warn if the protect_from_csrf is active but sessions are not
181
+ def check_csrf_protection_dependency
182
+ if (protect_from_csrf? && !sessions?) && !defined?(Tennpipes::IGNORE_CSRF_SETUP_WARNING)
183
+ warn(<<-ERROR)
184
+ `protect_from_csrf` is activated, but `sessions` seem to be off. To enable csrf
185
+ protection, use:
186
+
187
+ enable :sessions
188
+
189
+ or deactivate protect_from_csrf:
190
+
191
+ disable :protect_from_csrf
192
+
193
+ If you use a different session store, ignore this warning using:
194
+
195
+ # in boot.rb:
196
+ Tennpipes::IGNORE_CSRF_SETUP_WARNING = true
197
+ ERROR
198
+ end
199
+ end
200
+ end
201
+ end
202
+ end
@@ -0,0 +1,25 @@
1
+ module Tennpipes
2
+ class AuthenticityToken < Rack::Protection::AuthenticityToken
3
+ def initialize(app, options = {})
4
+ @app = app
5
+ @except = options[:except]
6
+ @except = Array(@except) unless @except.is_a?(Proc)
7
+ super
8
+ end
9
+
10
+ def call(env)
11
+ if except?(env)
12
+ @app.call(env)
13
+ else
14
+ super
15
+ end
16
+ end
17
+
18
+ def except?(env)
19
+ return false unless @except
20
+ path_info = env['PATH_INFO']
21
+ @except.is_a?(Proc) ? @except.call(env) : @except.any?{|path|
22
+ path.is_a?(Regexp) ? path.match(path_info) : path == path_info }
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,229 @@
1
+ module Tennpipes
2
+ module Flash
3
+
4
+ class << self
5
+ def registered(app)
6
+ app.helpers Helpers
7
+ app.after do
8
+ session[:_flash] = @_flash.next if @_flash
9
+ end
10
+ end
11
+ end
12
+
13
+ class Storage
14
+ include Enumerable
15
+
16
+ # @private
17
+ def initialize(session=nil)
18
+ @_now = session || {}
19
+ @_next = {}
20
+ end
21
+
22
+ def now
23
+ @_now
24
+ end
25
+
26
+ def next
27
+ @_next
28
+ end
29
+
30
+ # @since 0.10.8
31
+ # @api public
32
+ def [](type)
33
+ @_now[type]
34
+ end
35
+
36
+ # @since 0.10.8
37
+ # @api public
38
+ def []=(type, message)
39
+ @_next[type] = message
40
+ end
41
+
42
+ # @since 0.10.8
43
+ # @api public
44
+ def delete(type)
45
+ @_now.delete(type)
46
+ self
47
+ end
48
+
49
+ # @since 0.10.8
50
+ # @api public
51
+ def keys
52
+ @_now.keys
53
+ end
54
+
55
+ # @since 0.10.8
56
+ # @api public
57
+ def key?(type)
58
+ @_now.key?(type)
59
+ end
60
+
61
+ # @since 0.10.8
62
+ # @api public
63
+ def each(&block)
64
+ @_now.each(&block)
65
+ end
66
+
67
+ # @since 0.10.8
68
+ # @api public
69
+ def replace(hash)
70
+ @_now.replace(hash)
71
+ self
72
+ end
73
+
74
+ # @since 0.10.8
75
+ # @api public
76
+ def update(hash)
77
+ @_now.update(hash)
78
+ self
79
+ end
80
+ alias_method :merge!, :update
81
+
82
+ # @since 0.10.8
83
+ # @api public
84
+ def sweep
85
+ @_now.replace(@_next)
86
+ @_next = {}
87
+ self
88
+ end
89
+
90
+ # @since 0.10.8
91
+ # @api public
92
+ def keep(key = nil)
93
+ if key
94
+ @_next[key] = @_now[key]
95
+ else
96
+ @_next.merge!(@_now)
97
+ end
98
+ self
99
+ end
100
+
101
+ # @since 0.10.8
102
+ # @api public
103
+ def discard(key = nil)
104
+ if key
105
+ @_next.delete(key)
106
+ else
107
+ @_next = {}
108
+ end
109
+ self
110
+ end
111
+
112
+ # @since 0.10.8
113
+ # @api public
114
+ def clear
115
+ @_now.clear
116
+ end
117
+
118
+ # @since 0.10.8
119
+ # @api public
120
+ def empty?
121
+ @_now.empty?
122
+ end
123
+
124
+ # @since 0.10.8
125
+ # @api public
126
+ def to_hash
127
+ @_now.dup
128
+ end
129
+
130
+ def length
131
+ @_now.length
132
+ end
133
+ alias_method :size, :length
134
+
135
+ # @since 0.10.8
136
+ # @api public
137
+ def to_s
138
+ @_now.to_s
139
+ end
140
+
141
+ # @since 0.10.8
142
+ # @api public
143
+ def error=(message)
144
+ self[:error] = message
145
+ end
146
+
147
+ # @since 0.10.8
148
+ # @api public
149
+ def error
150
+ self[:error]
151
+ end
152
+
153
+ # @since 0.10.8
154
+ # @api public
155
+ def notice=(message)
156
+ self[:notice] = message
157
+ end
158
+
159
+ # @since 0.10.8
160
+ # @api public
161
+ def notice
162
+ self[:notice]
163
+ end
164
+
165
+ # @since 0.10.8
166
+ # @api public
167
+ def success=(message)
168
+ self[:success] = message
169
+ end
170
+
171
+ # @since 0.10.8
172
+ # @api public
173
+ def success
174
+ self[:success]
175
+ end
176
+ end # Storage
177
+
178
+ module Helpers
179
+ ###
180
+ # Overloads the existing redirect helper in-order to provide support for
181
+ # flash messages.
182
+ #
183
+ # @overload redirect(url)
184
+ # @param [String] url
185
+ #
186
+ # @overload redirect(url, status_code)
187
+ # @param [String] url
188
+ # @param [Fixnum] status_code
189
+ #
190
+ # @overload redirect(url, status_code, flash_messages)
191
+ # @param [String] url
192
+ # @param [Fixnum] status_code
193
+ # @param [Hash] flash_messages
194
+ #
195
+ # @overload redirect(url, flash_messages)
196
+ # @param [String] url
197
+ # @param [Hash] flash_messages
198
+ #
199
+ # @example
200
+ # redirect(dashboard, success: :user_created)
201
+ # redirect(new_location, 301, notice: 'This page has moved. Please update your bookmarks!!')
202
+ #
203
+ # @since 0.10.8
204
+ # @api public
205
+ def redirect(url, *args)
206
+ flashes = args.extract_options!
207
+
208
+ flashes.each do |type, message|
209
+ message = I18n.translate(message) if message.is_a?(Symbol) && defined?(I18n)
210
+ flash[type] = message
211
+ end
212
+
213
+ super(url, args)
214
+ end
215
+ alias_method :redirect_to, :redirect
216
+
217
+ ###
218
+ # Returns the flash storage object.
219
+ #
220
+ # @return [Storage]
221
+ #
222
+ # @since 0.10.8
223
+ # @api public
224
+ def flash
225
+ @_flash ||= Storage.new(env['rack.session'] ? session[:_flash] : {})
226
+ end
227
+ end
228
+ end
229
+ end