devise 0.5.1 → 0.5.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of devise might be problematic. Click here for more details.

@@ -1,12 +1,15 @@
1
+ == 0.5.2
2
+
1
3
  * enhancements
2
4
  * [#28] Improved sign_in and sign_out helpers to accepts resources
3
5
  * [#28] Added stored_location_for as a helper
6
+ * [#20] Added test helpers
4
7
 
5
8
  == 0.5.1
6
9
 
7
10
  * enhancements
8
- * Added serializers based on Warden ones
9
- * Allow authentication keys to be set
11
+ * Added serializers based on Warden ones
12
+ * Allow authentication keys to be set
10
13
 
11
14
  == 0.5.0
12
15
 
@@ -228,6 +228,18 @@ Devise mailer uses the same pattern to create subject messages:
228
228
 
229
229
  Take a look at our locale file to check all available messages.
230
230
 
231
+ == Test helpers
232
+
233
+ Devise includes some tests helpers for functional specs. To use them, you just
234
+ need to include Devise::TestHelpers in your test class and use the sign_in and
235
+ sign_out method. Such methods have the same signature as in controllers:
236
+
237
+ sign_in :user, @user # sign_in(scope, resource)
238
+ sign_in @user # sign_in(resource)
239
+
240
+ sign_out :user # sign_out(scope)
241
+ sign_out @user # sign_out(resource)
242
+
231
243
  == Migrating from other solutions
232
244
 
233
245
  Devise implements encryption strategies for Clearance, Authlogic and Restful-Authentication. To make use of it set the desired encryptor in the encryptor initializer config option. You might also need to rename your encrypted password and salt columns to match Devises's one (encrypted_password and password_salt).
data/Rakefile CHANGED
@@ -36,7 +36,7 @@ begin
36
36
  s.description = "Flexible authentication solution for Rails with Warden"
37
37
  s.authors = ['José Valim', 'Carlos Antônio']
38
38
  s.files = FileList["[A-Z]*", "{app,config,generators,lib}/**/*", "init.rb"]
39
- s.add_dependency("warden", "~> 0.6.0")
39
+ s.add_dependency("warden", "~> 0.6.1")
40
40
  end
41
41
 
42
42
  Jeweler::GemcutterTasks.new
data/TODO CHANGED
@@ -1,5 +1,4 @@
1
1
  * Devise::Timeoutable
2
- * Devise::TestHelper
3
2
  * Use request_ip in session cookies
4
3
  * Devise::BruteForceProtection
5
4
  * Devise::MagicColumns
@@ -17,7 +17,7 @@ class SessionsController < ApplicationController
17
17
  set_flash_message :success, :signed_in
18
18
  redirect_back_or_to home_or_root_path
19
19
  else
20
- set_now_flash_message :failure, :invalid
20
+ set_now_flash_message :failure, warden.message || :invalid
21
21
  build_resource
22
22
  render :new
23
23
  end
@@ -110,7 +110,7 @@ module Devise
110
110
  def configure_warden_manager(manager) #:nodoc:
111
111
  manager.default_strategies *Devise::STRATEGIES
112
112
  manager.default_serializers *Devise::SERIALIZERS
113
- manager.failure_app = Devise::Failure
113
+ manager.failure_app = Devise::FailureApp
114
114
  manager.silence_missing_strategies!
115
115
  manager.silence_missing_serializers!
116
116
 
@@ -135,4 +135,4 @@ end
135
135
  require 'devise/strategies/base'
136
136
  require 'devise/serializers/base'
137
137
 
138
- require 'devise/rails'
138
+ require 'devise/rails'
@@ -54,11 +54,11 @@ module Devise
54
54
  #
55
55
  # Examples:
56
56
  #
57
- # sign_in :user, @user # sign_in(scope, resource)
58
- # sign_in @user # sign_in(resource)
57
+ # sign_in :user, @user # sign_in(scope, resource)
58
+ # sign_in @user # sign_in(resource)
59
59
  #
60
60
  def sign_in(resource_or_scope, resource=nil)
61
- scope ||= find_devise_scope(resource_or_scope)
61
+ scope ||= Devise::Mapping.find_scope!(resource_or_scope)
62
62
  resource ||= resource_or_scope
63
63
  warden.set_user(resource, :scope => scope)
64
64
  end
@@ -72,7 +72,7 @@ module Devise
72
72
  # sign_out @user # sign_out(resource)
73
73
  #
74
74
  def sign_out(resource_or_scope)
75
- scope = find_devise_scope(resource_or_scope)
75
+ scope = Devise::Mapping.find_scope!(resource_or_scope)
76
76
  warden.user(scope) # Without loading user here, before_logout hook is not called
77
77
  warden.raw_session.inspect # Without this inspect here. The session does not clear.
78
78
  warden.logout(scope)
@@ -86,7 +86,7 @@ module Devise
86
86
  # redirect_to stored_location_for(:user) || root_path
87
87
  #
88
88
  def stored_location_for(resource_or_scope)
89
- scope = find_devise_scope(resource_or_scope)
89
+ scope = Devise::Mapping.find_scope!(resource_or_scope)
90
90
  session.delete(:"#{scope}.return_to")
91
91
  end
92
92
 
@@ -134,16 +134,6 @@ module Devise
134
134
  METHODS
135
135
  end
136
136
 
137
- protected
138
-
139
- def find_devise_scope(resource_or_scope)
140
- if resource_or_scope.is_a?(Symbol)
141
- resource_or_scope
142
- else
143
- Devise::Mapping.find_by_class!(resource_or_scope.class).name
144
- end
145
- end
146
-
147
137
  end
148
138
  end
149
139
  end
@@ -1,5 +1,5 @@
1
1
  module Devise
2
- module Failure
2
+ module FailureApp
3
3
  mattr_accessor :default_url
4
4
 
5
5
  # Failure application that will be called every time :warden is thrown from
@@ -9,13 +9,15 @@ module Devise
9
9
  def self.call(env)
10
10
  options = env['warden.options']
11
11
  scope = options[:scope]
12
- params = case env['warden'].try(:message)
12
+ message = env['warden'].try(:message) || options[:message]
13
+
14
+ params = case message
13
15
  when Symbol
14
- { env['warden'].message => true }
16
+ { message => true }
15
17
  when String
16
- { :message => env['warden'].message }
18
+ { :message => message }
17
19
  else
18
- options[:params]
20
+ {}
19
21
  end
20
22
 
21
23
  redirect_path = if mapping = Devise.mappings[scope]
@@ -23,14 +25,14 @@ module Devise
23
25
  else
24
26
  "/#{default_url}"
25
27
  end
28
+ query_string = Rack::Utils.build_query(params)
26
29
 
27
30
  headers = {}
28
31
  headers["Location"] = redirect_path
29
- headers["Location"] << "?" << Rack::Utils.build_query(params) if params
32
+ headers["Location"] << "?" << query_string unless query_string.empty?
30
33
  headers["Content-Type"] = 'text/plain'
31
34
 
32
- message = options[:message] || "You are being redirected to #{redirect_path}"
33
- [302, headers, [message]]
35
+ [302, headers, ["You are being redirected to #{redirect_path}"]]
34
36
  end
35
37
  end
36
38
  end
@@ -6,6 +6,13 @@ Warden::Manager.after_set_user do |record, warden, options|
6
6
  if record && record.respond_to?(:active?) && !record.active?
7
7
  scope = options[:scope]
8
8
  warden.logout(scope)
9
- throw :warden, :scope => scope, :params => { :unconfirmed => true }
9
+
10
+ if warden.winning_strategy
11
+ # If winning strategy was set, this is being called after authenticate and
12
+ # there is no need to force a redirect.
13
+ warden.winning_strategy.fail!(:unconfirmed)
14
+ else
15
+ throw :warden, :scope => scope, :message => :unconfirmed
16
+ end
10
17
  end
11
18
  end
@@ -39,11 +39,17 @@ module Devise
39
39
  Devise.mappings.values.find { |m| return m if klass <= m.to }
40
40
  end
41
41
 
42
- # Find by class but raising an error in case it can't be found.
43
- def self.find_by_class!(klass)
44
- mapping = find_by_class(klass)
45
- raise "Could not find a valid mapping for #{klass}" unless mapping
46
- mapping
42
+ # Receives an object and find a scope for it. If a scope cannot be found,
43
+ # raises an error. If a symbol is given, it's considered to be the scope.
44
+ def self.find_scope!(duck)
45
+ if duck.is_a?(Symbol)
46
+ duck
47
+ else
48
+ klass = duck.is_a?(Class) ? duck : duck.class
49
+ mapping = Devise::Mapping.find_by_class(klass)
50
+ raise "Could not find a valid mapping for #{duck}" unless mapping
51
+ mapping.name
52
+ end
47
53
  end
48
54
 
49
55
  # Default url options which can be used as prefix.
@@ -6,13 +6,19 @@ module Devise
6
6
  # Creates email, encrypted_password and password_salt.
7
7
  #
8
8
  # == Options
9
- # * :null when true, allow columns to be null
10
- # * :encryptor The encryptor going to be used, necessary for setting the proper encrypter password length
9
+ # * :null - When true, allow columns to be null.
10
+ # * :encryptor - The encryptor going to be used, necessary for setting the proper encrypter password length.
11
+ # * :skip_email - If you want to use another authentication key, you can skip e-mail creation.
12
+ # If you are using an ORM where the devise declaration is in the same class as the schema,
13
+ # as in Datamapper or Mongomapper, the email is skipped automatically if not included in
14
+ # authentication_keys.
11
15
  def authenticatable(options={})
12
16
  null = options[:null] || false
13
- encryptor = options[:encryptor] || :sha1
17
+ encryptor = options[:encryptor] || (respond_to?(:encryptor) ? self.encryptor : :sha1)
18
+ have_email = respond_to?(:authentication_keys) ? self.authentication_keys.include?(:email) : true
19
+ skip_email = options[:skip_email] || !have_email
14
20
 
15
- apply_schema :email, String, :null => null, :limit => 100
21
+ apply_schema :email, String, :null => null, :limit => 100 unless skip_email
16
22
  apply_schema :encrypted_password, String, :null => null, :limit => Devise::ENCRYPTORS_LENGTH[encryptor]
17
23
  apply_schema :password_salt, String, :null => null, :limit => 20
18
24
  end
@@ -13,8 +13,12 @@ module Devise
13
13
  # The first does not perform any action when calling authenticate, just
14
14
  # when authenticate! is invoked. The second always perform the action.
15
15
  def authenticate!
16
- if valid_attributes? && resource = mapping.to.authenticate(params[scope])
17
- success!(resource)
16
+ if valid_attributes?
17
+ if resource = mapping.to.authenticate(params[scope])
18
+ success!(resource)
19
+ else
20
+ fail!(:invalid)
21
+ end
18
22
  else
19
23
  store_location
20
24
  fail!(:unauthenticated)
@@ -33,7 +37,7 @@ module Devise
33
37
  # yet, but we still need to store the uri based on scope, so different scopes
34
38
  # would never use the same uri to redirect.
35
39
  def store_location
36
- session[:"#{mapping.name}.return_to"] = request.request_uri if request.get?
40
+ session[:"#{mapping.name}.return_to"] ||= request.request_uri if request.get?
37
41
  end
38
42
  end
39
43
  end
@@ -5,6 +5,7 @@ module Devise
5
5
  # Validate strategy. By default will raise an error if no scope or an
6
6
  # invalid mapping is found.
7
7
  def valid?
8
+ raise "Could not find mapping for #{scope}" unless mapping
8
9
  mapping.for.include?(klass_type)
9
10
  end
10
11
 
@@ -0,0 +1,85 @@
1
+ module Devise
2
+ module TestHelpers
3
+ def self.included(base)
4
+ base.class_eval do
5
+ setup :setup_controller_for_warden, :warden if respond_to?(:setup)
6
+ end
7
+ end
8
+
9
+ # This is a Warden::Proxy customized for functional tests. It's meant to
10
+ # some of Warden::Manager resposnabilities, as retrieving configuration
11
+ # options and calling the FailureApp.
12
+ class TestWarden < Warden::Proxy #:nodoc:
13
+ attr_reader :controller
14
+
15
+ def initialize(controller)
16
+ @controller = controller
17
+ manager = Warden::Manager.new(nil) do |manager|
18
+ Devise.configure_warden_manager(manager)
19
+ end
20
+ super(controller.request.env, manager.config)
21
+ end
22
+
23
+ def authenticate!(*args)
24
+ catch_with_redirect { super }
25
+ end
26
+
27
+ def catch_with_redirect(&block)
28
+ result = catch(:warden, &block)
29
+
30
+ if result.is_a?(Hash) && !custom_failure? && !@controller.send(:performed?)
31
+ result[:action] ||= :unauthenticated
32
+
33
+ env = @controller.request.env
34
+ env["PATH_INFO"] = "/#{result[:action]}"
35
+ env["warden.options"] = result
36
+ Warden::Manager._before_failure.each{ |hook| hook.call(env, result) }
37
+
38
+ status, headers, body = Devise::FailureApp.call(env).to_a
39
+ @controller.send :redirect_to, headers["Location"]
40
+ else
41
+ result
42
+ end
43
+ end
44
+ end
45
+
46
+ # We need to setup the environment variables and the response in the controller.
47
+ def setup_controller_for_warden #:nodoc:
48
+ @request.env['action_controller.rescue.request'] = @request
49
+ @request.env['action_controller.rescue.response'] = @response
50
+ @request.env['rack.session'] = session
51
+ @controller.response = @response
52
+ end
53
+
54
+ # Quick access to Warden::Proxy.
55
+ def warden #:nodoc:
56
+ @warden ||= (@request.env['warden'] = TestWarden.new(@controller))
57
+ end
58
+
59
+ # sign_in a given resource by storing its keys in the session.
60
+ #
61
+ # Examples:
62
+ #
63
+ # sign_in :user, @user # sign_in(scope, resource)
64
+ # sign_in @user # sign_in(resource)
65
+ #
66
+ def sign_in(resource_or_scope, resource=nil)
67
+ scope ||= Devise::Mapping.find_scope!(resource_or_scope)
68
+ resource ||= resource_or_scope
69
+ session["warden.user.#{scope}.key"] = resource.class.serialize_into_session(resource)
70
+ end
71
+
72
+ # Sign out a given resource or scope by calling logout on Warden.
73
+ #
74
+ # Examples:
75
+ #
76
+ # sign_out :user # sign_out(scope)
77
+ # sign_out @user # sign_out(resource)
78
+ #
79
+ def sign_out(resource_or_scope)
80
+ scope = Devise::Mapping.find_scope!(resource_or_scope)
81
+ warden.logout(scope)
82
+ end
83
+
84
+ end
85
+ end
@@ -1,3 +1,3 @@
1
1
  module Devise
2
- VERSION = "0.5.1".freeze
2
+ VERSION = "0.5.2".freeze
3
3
  end
@@ -61,7 +61,7 @@ class DeviseTest < ActiveSupport::TestCase
61
61
  manager = MockManager.new
62
62
  Devise.configure_warden_manager(manager)
63
63
 
64
- assert_equal Devise::Failure, manager.failure_app
64
+ assert_equal Devise::FailureApp, manager.failure_app
65
65
  assert_equal [:authenticatable], manager.default_strategies
66
66
  assert manager.silence_missing_strategies
67
67
  end
@@ -1,10 +1,11 @@
1
1
  require 'test/test_helper'
2
+ require 'ostruct'
2
3
 
3
4
  class FailureTest < ActiveSupport::TestCase
4
5
 
5
6
  def call_failure(env_params={})
6
- env = {'warden.options' => {:scope => :user}.update(env_params)}
7
- Devise::Failure.call(env)
7
+ env = {'warden.options' => { :scope => :user }}.merge!(env_params)
8
+ Devise::FailureApp.call(env)
8
9
  end
9
10
 
10
11
  test 'return 302 status' do
@@ -15,8 +16,9 @@ class FailureTest < ActiveSupport::TestCase
15
16
  assert_equal '/users/sign_in', call_failure.second['Location']
16
17
  end
17
18
 
18
- test 'add params to redirect location' do
19
- location = call_failure(:params => {:test => true}).second['Location']
19
+ test 'uses the proxy failure message' do
20
+ warden = OpenStruct.new(:message => :test)
21
+ location = call_failure('warden' => warden).second['Location']
20
22
  assert_equal '/users/sign_in?test=true', location
21
23
  end
22
24
 
@@ -27,8 +29,4 @@ class FailureTest < ActiveSupport::TestCase
27
29
  test 'setup a default message' do
28
30
  assert_equal ['You are being redirected to /users/sign_in'], call_failure.last
29
31
  end
30
-
31
- test 'pass in a different message' do
32
- assert_equal ['Hello world'], call_failure(:message => 'Hello world').last
33
- end
34
32
  end
@@ -62,7 +62,7 @@ class ConfirmationTest < ActionController::IntegrationTest
62
62
  Devise.confirm_within = 0
63
63
  user = sign_in_as_user(:confirm => false)
64
64
 
65
- assert_redirected_to new_user_session_path(:unconfirmed => true)
65
+ assert_contain 'You have to confirm your account before continuing'
66
66
  assert_not warden.authenticated?(:user)
67
67
  end
68
68
 
@@ -49,9 +49,15 @@ class MappingTest < ActiveSupport::TestCase
49
49
  assert_equal Devise.mappings[:user], Devise::Mapping.find_by_class(klass)
50
50
  end
51
51
 
52
- test 'find mapping raises an error for invalid class' do
52
+ test 'find scope for a given object' do
53
+ assert_equal :user, Devise::Mapping.find_scope!(User)
54
+ assert_equal :user, Devise::Mapping.find_scope!(:user)
55
+ assert_equal :user, Devise::Mapping.find_scope!(User.new)
56
+ end
57
+
58
+ test 'find scope raises an error if cannot be found' do
53
59
  assert_raise RuntimeError do
54
- Devise::Mapping.find_by_class!(String)
60
+ Devise::Mapping.find_scope!(String)
55
61
  end
56
62
  end
57
63
 
@@ -0,0 +1,44 @@
1
+ require 'test/test_helper'
2
+
3
+ class TestHelpersTest < ActionController::TestCase
4
+ tests UsersController
5
+ include Devise::TestHelpers
6
+
7
+ test "redirects if attempting to access a page unauthenticated" do
8
+ get :index
9
+ assert_redirected_to "/users/sign_in?unauthenticated=true"
10
+ end
11
+
12
+ test "redirects if attempting to access a page with a unconfirmed account" do
13
+ swap Devise, :confirm_within => 0 do
14
+ sign_in create_user
15
+ get :index
16
+ assert_redirected_to "/users/sign_in?unconfirmed=true"
17
+ end
18
+ end
19
+
20
+ test "does not redirect with valid user" do
21
+ user = create_user
22
+ user.confirm!
23
+
24
+ sign_in user
25
+ get :index
26
+ assert_response :success
27
+ end
28
+
29
+ test "redirects if valid user signed out" do
30
+ user = create_user
31
+ user.confirm!
32
+
33
+ sign_in user
34
+ get :index
35
+
36
+ sign_out user
37
+ get :index
38
+ assert_redirected_to "/users/sign_in?unauthenticated=true"
39
+ end
40
+
41
+ def create_user
42
+ User.create!(:email => "jose.valim@plataformatec.com", :password => "123456")
43
+ end
44
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: devise
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.5.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - "Jos\xC3\xA9 Valim"
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2009-11-15 00:00:00 -02:00
13
+ date: 2009-11-17 00:00:00 -02:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
@@ -21,7 +21,7 @@ dependencies:
21
21
  requirements:
22
22
  - - ~>
23
23
  - !ruby/object:Gem::Version
24
- version: 0.6.0
24
+ version: 0.6.1
25
25
  version:
26
26
  description: Flexible authentication solution for Rails with Warden
27
27
  email: contact@plataformatec.com.br
@@ -68,7 +68,7 @@ files:
68
68
  - lib/devise/encryptors/restful_authentication_sha1.rb
69
69
  - lib/devise/encryptors/sha1.rb
70
70
  - lib/devise/encryptors/sha512.rb
71
- - lib/devise/failure.rb
71
+ - lib/devise/failure_app.rb
72
72
  - lib/devise/hooks/confirmable.rb
73
73
  - lib/devise/locales/en.yml
74
74
  - lib/devise/mapping.rb
@@ -89,6 +89,7 @@ files:
89
89
  - lib/devise/serializers/rememberable.rb
90
90
  - lib/devise/strategies/authenticatable.rb
91
91
  - lib/devise/strategies/base.rb
92
+ - lib/devise/test_helpers.rb
92
93
  - lib/devise/version.rb
93
94
  has_rdoc: true
94
95
  homepage: http://github.com/plataformatec/devise
@@ -119,44 +120,45 @@ signing_key:
119
120
  specification_version: 3
120
121
  summary: Flexible authentication solution for Rails with Warden
121
122
  test_files:
122
- - test/controllers/filters_test.rb
123
- - test/controllers/helpers_test.rb
123
+ - test/rails_app/config/boot.rb
124
+ - test/rails_app/config/routes.rb
125
+ - test/rails_app/config/environments/development.rb
126
+ - test/rails_app/config/environments/production.rb
127
+ - test/rails_app/config/environments/test.rb
128
+ - test/rails_app/config/environment.rb
129
+ - test/rails_app/config/initializers/session_store.rb
130
+ - test/rails_app/config/initializers/new_rails_defaults.rb
131
+ - test/rails_app/app/controllers/users_controller.rb
132
+ - test/rails_app/app/controllers/application_controller.rb
133
+ - test/rails_app/app/controllers/admins_controller.rb
134
+ - test/rails_app/app/controllers/home_controller.rb
135
+ - test/rails_app/app/helpers/application_helper.rb
136
+ - test/rails_app/app/models/admin.rb
137
+ - test/rails_app/app/models/organizer.rb
138
+ - test/rails_app/app/models/account.rb
139
+ - test/rails_app/app/models/user.rb
124
140
  - test/controllers/url_helpers_test.rb
125
- - test/devise_test.rb
126
- - test/encryptors_test.rb
127
- - test/failure_test.rb
141
+ - test/controllers/helpers_test.rb
142
+ - test/controllers/filters_test.rb
143
+ - test/models_test.rb
128
144
  - test/integration/authenticatable_test.rb
129
- - test/integration/confirmable_test.rb
130
- - test/integration/recoverable_test.rb
131
145
  - test/integration/rememberable_test.rb
146
+ - test/integration/recoverable_test.rb
147
+ - test/integration/confirmable_test.rb
132
148
  - test/mailers/confirmation_instructions_test.rb
133
149
  - test/mailers/reset_password_instructions_test.rb
134
- - test/mapping_test.rb
135
150
  - test/models/authenticatable_test.rb
136
- - test/models/confirmable_test.rb
137
- - test/models/recoverable_test.rb
138
151
  - test/models/rememberable_test.rb
152
+ - test/models/recoverable_test.rb
139
153
  - test/models/validatable_test.rb
140
- - test/models_test.rb
141
- - test/rails_app/app/controllers/admins_controller.rb
142
- - test/rails_app/app/controllers/application_controller.rb
143
- - test/rails_app/app/controllers/home_controller.rb
144
- - test/rails_app/app/controllers/users_controller.rb
145
- - test/rails_app/app/helpers/application_helper.rb
146
- - test/rails_app/app/models/account.rb
147
- - test/rails_app/app/models/admin.rb
148
- - test/rails_app/app/models/organizer.rb
149
- - test/rails_app/app/models/user.rb
150
- - test/rails_app/config/boot.rb
151
- - test/rails_app/config/environment.rb
152
- - test/rails_app/config/environments/development.rb
153
- - test/rails_app/config/environments/production.rb
154
- - test/rails_app/config/environments/test.rb
155
- - test/rails_app/config/initializers/new_rails_defaults.rb
156
- - test/rails_app/config/initializers/session_store.rb
157
- - test/rails_app/config/routes.rb
158
- - test/routes_test.rb
154
+ - test/models/confirmable_test.rb
155
+ - test/encryptors_test.rb
156
+ - test/support/model_tests_helper.rb
159
157
  - test/support/assertions_helper.rb
160
158
  - test/support/integration_tests_helper.rb
161
- - test/support/model_tests_helper.rb
159
+ - test/failure_app_test.rb
160
+ - test/devise_test.rb
161
+ - test/routes_test.rb
162
162
  - test/test_helper.rb
163
+ - test/test_helpers_test.rb
164
+ - test/mapping_test.rb