ggoodale-restful-authentication 1.1.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/CHANGELOG +68 -0
- data/README.textile +224 -0
- data/Rakefile +32 -0
- data/TODO +15 -0
- data/generators/authenticated/USAGE +1 -0
- data/generators/authenticated/authenticated_generator.rb +478 -0
- data/generators/authenticated/lib/insert_routes.rb +54 -0
- data/generators/authenticated/templates/_model_partial.html.erb +8 -0
- data/generators/authenticated/templates/activation.erb +3 -0
- data/generators/authenticated/templates/authenticated_system.rb +189 -0
- data/generators/authenticated/templates/authenticated_test_helper.rb +22 -0
- data/generators/authenticated/templates/controller.rb +43 -0
- data/generators/authenticated/templates/helper.rb +2 -0
- data/generators/authenticated/templates/login.html.erb +16 -0
- data/generators/authenticated/templates/mailer.rb +25 -0
- data/generators/authenticated/templates/migration.rb +26 -0
- data/generators/authenticated/templates/model.rb +83 -0
- data/generators/authenticated/templates/model_controller.rb +85 -0
- data/generators/authenticated/templates/model_helper.rb +93 -0
- data/generators/authenticated/templates/model_helper_spec.rb +158 -0
- data/generators/authenticated/templates/observer.rb +11 -0
- data/generators/authenticated/templates/signup.html.erb +19 -0
- data/generators/authenticated/templates/signup_notification.erb +8 -0
- data/generators/authenticated/templates/site_keys.rb +38 -0
- data/generators/authenticated/templates/spec/controllers/access_control_spec.rb +90 -0
- data/generators/authenticated/templates/spec/controllers/authenticated_system_spec.rb +102 -0
- data/generators/authenticated/templates/spec/controllers/sessions_controller_spec.rb +139 -0
- data/generators/authenticated/templates/spec/controllers/users_controller_spec.rb +198 -0
- data/generators/authenticated/templates/spec/fixtures/users.yml +60 -0
- data/generators/authenticated/templates/spec/helpers/users_helper_spec.rb +141 -0
- data/generators/authenticated/templates/spec/models/user_spec.rb +290 -0
- data/generators/authenticated/templates/stories/rest_auth_stories.rb +22 -0
- data/generators/authenticated/templates/stories/rest_auth_stories_helper.rb +81 -0
- data/generators/authenticated/templates/stories/steps/ra_navigation_steps.rb +49 -0
- data/generators/authenticated/templates/stories/steps/ra_resource_steps.rb +179 -0
- data/generators/authenticated/templates/stories/steps/ra_response_steps.rb +171 -0
- data/generators/authenticated/templates/stories/steps/user_steps.rb +153 -0
- data/generators/authenticated/templates/stories/users/accounts.story +186 -0
- data/generators/authenticated/templates/stories/users/sessions.story +134 -0
- data/generators/authenticated/templates/test/functional_test.rb +82 -0
- data/generators/authenticated/templates/test/mailer_test.rb +31 -0
- data/generators/authenticated/templates/test/model_functional_test.rb +93 -0
- data/generators/authenticated/templates/test/unit_test.rb +164 -0
- data/init.rb +1 -0
- data/lib/authentication.rb +40 -0
- data/lib/authentication/by_cookie_token.rb +82 -0
- data/lib/authentication/by_password.rb +64 -0
- data/lib/authorization.rb +14 -0
- data/lib/authorization/aasm_roles.rb +63 -0
- data/lib/authorization/stateful_roles.rb +62 -0
- data/lib/trustification.rb +14 -0
- data/lib/trustification/email_validation.rb +20 -0
- data/rails/init.rb +3 -0
- metadata +115 -0
@@ -0,0 +1,81 @@
|
|
1
|
+
# If you have a global stories helper, move this line there:
|
2
|
+
include AuthenticatedTestHelper
|
3
|
+
|
4
|
+
# Most of the below came out of code from Ben Mabey
|
5
|
+
# http://www.benmabey.com/2008/02/04/rspec-plain-text-stories-webrat-chunky-bacon/
|
6
|
+
|
7
|
+
# These allow exceptions to come through as opposed to being caught and having non-helpful responses returned.
|
8
|
+
ActionController::Base.class_eval do
|
9
|
+
def perform_action
|
10
|
+
perform_action_without_rescue
|
11
|
+
end
|
12
|
+
end
|
13
|
+
Dispatcher.class_eval do
|
14
|
+
def self.failsafe_response(output, status, exception = nil)
|
15
|
+
raise exception
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
#
|
20
|
+
# Sugar for turning a story's attribute list into list, array, etc.
|
21
|
+
#
|
22
|
+
module ToFooFromStory
|
23
|
+
def ToFooFromStory.fix_key key
|
24
|
+
key.downcase.gsub(/\s+/, '_')
|
25
|
+
end
|
26
|
+
def ToFooFromStory.fix_value value
|
27
|
+
return '' if !value
|
28
|
+
value.strip!
|
29
|
+
case
|
30
|
+
when value =~ /^'(.*)'$/ then value = $1
|
31
|
+
when value =~ /^"(.*)"$/ then value = $1
|
32
|
+
when value == 'nil!' then value = nil
|
33
|
+
when value == 'non-nil!' then value = be_nil
|
34
|
+
when value =~ /^#\{(.*)\}$/ then value = eval($1)
|
35
|
+
end
|
36
|
+
value
|
37
|
+
end
|
38
|
+
# Converts a key: value list found in the steps into a hash.
|
39
|
+
# Example:
|
40
|
+
# ISBN: '0967539854' and comment: 'I love this book' and Quality rating: '4'
|
41
|
+
# # => {"quality_rating"=>"4", "isbn"=>"0967539854", "comment"=>"I love this book"}
|
42
|
+
def to_hash_from_story
|
43
|
+
hsh = self.split(/,? and |, /).inject({}) do |hash_so_far, key_value|
|
44
|
+
key, value = key_value.split(":")
|
45
|
+
if !value then warn "Couldn't understand story '#{self}': only understood up to the part '#{hash_so_far.to_yaml}'" end
|
46
|
+
hash_so_far.merge(ToFooFromStory::fix_key(key) => ToFooFromStory::fix_value(value))
|
47
|
+
end
|
48
|
+
end
|
49
|
+
# Coverts an attribute list found in the steps into an array
|
50
|
+
# Example:
|
51
|
+
# login, email, updated_at, and gravatar
|
52
|
+
# # => ['login', 'email', 'updated_at', 'gravatar']
|
53
|
+
def to_array_from_story
|
54
|
+
self.split(/,? and |, /).map do |value|
|
55
|
+
ToFooFromStory::fix_value(value)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
class String
|
60
|
+
include ToFooFromStory
|
61
|
+
end
|
62
|
+
|
63
|
+
def instantize(string)
|
64
|
+
instance_variable_get("@#{string}")
|
65
|
+
end
|
66
|
+
|
67
|
+
#
|
68
|
+
# Spew response onto screen -- painful but scrolling >> debugger
|
69
|
+
#
|
70
|
+
def dump_response
|
71
|
+
# note that @request and @template won't to_yaml and that @session includes @cgi
|
72
|
+
response_methods = response.instance_variables - ['@request', '@template', '@cgi']
|
73
|
+
request_methods = response.request.instance_variables - ['@session_options_with_string_keys', '@cgi', '@session']
|
74
|
+
response_methods.map!{|attr| attr.gsub(/^@/,'')}.sort!
|
75
|
+
request_methods.map!{ |attr| attr.gsub(/^@/,'')}.sort!
|
76
|
+
puts '', '*' * 75,
|
77
|
+
response.instance_values.slice(*response_methods).to_yaml,
|
78
|
+
"*" * 75, '',
|
79
|
+
response.request.instance_values.slice(*request_methods).to_yaml,
|
80
|
+
"*" * 75, ''
|
81
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
#
|
2
|
+
# Where to go
|
3
|
+
#
|
4
|
+
steps_for(:ra_navigation) do
|
5
|
+
#
|
6
|
+
# GET
|
7
|
+
# Go to a given page.
|
8
|
+
When "$actor goes to $path" do |actor, path|
|
9
|
+
case path
|
10
|
+
when 'the home page' then get '/'
|
11
|
+
else get path
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# POST -- Ex:
|
16
|
+
# When she creates a book with ISBN: '0967539854' and comment: 'I love this book' and rating: '4'
|
17
|
+
# When she creates a singular session with login: 'reggie' and password: 'i_haxxor_joo'
|
18
|
+
# Since I'm not smrt enough to do it right, explicitly specify singular resources
|
19
|
+
When %r{$actor creates an? $resource with $attributes} do |actor, resource, attributes|
|
20
|
+
attributes = attributes.to_hash_from_story
|
21
|
+
if resource =~ %r{singular ([\w/]+)}
|
22
|
+
resource = $1.downcase.singularize
|
23
|
+
post "/#{resource}", attributes
|
24
|
+
else
|
25
|
+
post "/#{resource.downcase.pluralize}", { resource.downcase.singularize => attributes }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# PUT
|
30
|
+
When %r{$actor asks to update '$resource' with $attributes} do |_, resource, attributes|
|
31
|
+
attributes = attributes.to_hash_from_story
|
32
|
+
put "#{resource}", attributes
|
33
|
+
dump_response
|
34
|
+
end
|
35
|
+
|
36
|
+
# DELETE -- Slap together the POST-form-as-fake-HTTP-DELETE submission
|
37
|
+
When %r{$actor asks to delete '$resource'} do |_, resource|
|
38
|
+
post "/#{resource.downcase.pluralize}", { :_method => :delete }
|
39
|
+
dump_response
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
# Redirect --
|
44
|
+
# Rather than coding in get/get_via_redirect's and past/p_v_r's,
|
45
|
+
# let's just demand that in the story itself.
|
46
|
+
When "$actor follows that redirect!" do |actor|
|
47
|
+
follow_redirect!
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,179 @@
|
|
1
|
+
# The flexible code for resource testing came out of code from Ben Mabey
|
2
|
+
# http://www.benmabey.com/2008/02/04/rspec-plain-text-stories-webrat-chunky-bacon/
|
3
|
+
steps_for(:ra_resource) do
|
4
|
+
#
|
5
|
+
# Construct resources
|
6
|
+
#
|
7
|
+
|
8
|
+
#
|
9
|
+
# Build a resource as described, store it as an @instance variable. Ex:
|
10
|
+
# "Given a <%= file_name %> with login: 'mojojojo'"
|
11
|
+
# produces a <%= class_name %> instance stored in @<%= file_name %> with 'mojojojo' as its login
|
12
|
+
# attribute.
|
13
|
+
#
|
14
|
+
Given "a $resource instance with $attributes" do |resource, attributes|
|
15
|
+
klass, instance, attributes = parse_resource_args resource, attributes
|
16
|
+
instance = klass.new(attributes)
|
17
|
+
instance.save!
|
18
|
+
find_resource(resource, attributes).should_not be_nil
|
19
|
+
keep_instance! resource, instance
|
20
|
+
end
|
21
|
+
|
22
|
+
#
|
23
|
+
# Stuff attributes into a preexisting @resource
|
24
|
+
# "And the <%= file_name %> has thac0: 3"
|
25
|
+
# takes the earlier-defined @<%= file_name %> instance and sets its thac0 to '3'.
|
26
|
+
#
|
27
|
+
Given "the $resource has $attributes" do |resource, attributes|
|
28
|
+
klass, instance, attributes = parse_resource_args resource, attributes
|
29
|
+
attributes.each do |attr, val|
|
30
|
+
instance.send("#{attr}=", val)
|
31
|
+
end
|
32
|
+
instance.save!
|
33
|
+
find_resource(resource, attributes).should_not be_nil
|
34
|
+
keep_instance! resource, instance
|
35
|
+
end
|
36
|
+
|
37
|
+
#
|
38
|
+
# Destroy all for this resource
|
39
|
+
#
|
40
|
+
Given "no $resource with $attr: '$val' exists" do |resource, attr, val|
|
41
|
+
klass, instance = parse_resource_args resource
|
42
|
+
klass.destroy_all(attr.to_sym => val)
|
43
|
+
instance = find_resource resource, attr.to_sym => val
|
44
|
+
instance.should be_nil
|
45
|
+
keep_instance! resource, instance
|
46
|
+
end
|
47
|
+
|
48
|
+
#
|
49
|
+
# Then's for resources
|
50
|
+
#
|
51
|
+
|
52
|
+
# Resource like this DOES exist
|
53
|
+
Then %r{an? $resource with $attributes should exist} do |resource, attributes|
|
54
|
+
instance = find_resource resource, attributes
|
55
|
+
instance.should_not be_nil
|
56
|
+
keep_instance! resource, instance
|
57
|
+
end
|
58
|
+
# Resource like this DOES NOT exist
|
59
|
+
Then %r{no $resource with $attributes should exist} do |resource, attributes|
|
60
|
+
instance = find_resource resource, attributes
|
61
|
+
instance.should be_nil
|
62
|
+
end
|
63
|
+
|
64
|
+
# Resource has attributes with given values
|
65
|
+
Then "the $resource should have $attributes" do |resource, attributes|
|
66
|
+
klass, instance, attributes = parse_resource_args resource, attributes
|
67
|
+
attributes.each do |attr, val|
|
68
|
+
instance.send(attr).should == val
|
69
|
+
end
|
70
|
+
end
|
71
|
+
# Resource attributes should / should not be nil
|
72
|
+
Then "the $resource's $attr should be nil" do |resource, attr|
|
73
|
+
klass, instance = parse_resource_args resource
|
74
|
+
instance.send(attr).should be_nil
|
75
|
+
end
|
76
|
+
Then "the $resource's $attr should not be nil" do |resource, attr|
|
77
|
+
klass, instance = parse_resource_args resource
|
78
|
+
instance.send(attr).should_not be_nil
|
79
|
+
end
|
80
|
+
|
81
|
+
#
|
82
|
+
# Bank each of the @resource's listed attributes for later.
|
83
|
+
#
|
84
|
+
Given "we try hard to remember the $resource's $attributes" do |resource, attributes|
|
85
|
+
attributes = attributes.to_array_from_story
|
86
|
+
attributes.each do |attr|
|
87
|
+
memorize_resource_value resource, attr
|
88
|
+
end
|
89
|
+
end
|
90
|
+
#
|
91
|
+
# Bank each of the @resource's listed attributes for later.
|
92
|
+
#
|
93
|
+
Given "we don't remember anything about the past" do
|
94
|
+
memorize_forget_all!
|
95
|
+
end
|
96
|
+
|
97
|
+
#
|
98
|
+
# Compare @resource.attr to its earlier-memorized value.
|
99
|
+
# Specify ' using method_name' (abs, to_s, &c) to coerce before comparing.
|
100
|
+
# For important and mysterious reasons, timestamps want to_i or to_s.
|
101
|
+
#
|
102
|
+
Then %r{the $resource\'s $attribute should stay the same(?: under $func)?} do |resource, attr, func|
|
103
|
+
klass, instance = parse_resource_args resource
|
104
|
+
# Get the values
|
105
|
+
old_value = recall_resource_value(resource, attr)
|
106
|
+
new_value = instance.send(attr)
|
107
|
+
# Transform each value, maybe, using value.func
|
108
|
+
if func then new_value = new_value.send(func); old_value = old_value.send(func) end
|
109
|
+
# Compare
|
110
|
+
old_value.should eql(new_value)
|
111
|
+
end
|
112
|
+
|
113
|
+
#
|
114
|
+
# Look for each for the given attributes in the page's text
|
115
|
+
#
|
116
|
+
Then "page should have the $resource's $attributes" do |resource, attributes|
|
117
|
+
actual_resource = instantize(resource)
|
118
|
+
attributes.split(/, and |, /).each do |attribute|
|
119
|
+
response.should have_text(/#{actual_resource.send(attribute.strip.gsub(" ","_"))}/)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
|
125
|
+
#
|
126
|
+
# Turn a resource name and a to_hash_from_story string like
|
127
|
+
# "attr: 'value', attr2: 'value2', ... , and attrN: 'valueN'"
|
128
|
+
# into
|
129
|
+
# * klass -- the class matching that Resource
|
130
|
+
# * instance -- the possibly-preexisting local instance value @resource
|
131
|
+
# * attributes -- a hash matching the given attribute-list string
|
132
|
+
#
|
133
|
+
def parse_resource_args resource, attributes=nil
|
134
|
+
instance = instantize resource
|
135
|
+
klass = resource.classify.constantize
|
136
|
+
attributes = attributes.to_hash_from_story if attributes
|
137
|
+
[klass, instance, attributes]
|
138
|
+
end
|
139
|
+
|
140
|
+
#
|
141
|
+
# Given a class name 'resource' and a hash of conditsion, find a model
|
142
|
+
#
|
143
|
+
def find_resource resource, conditions
|
144
|
+
klass, instance = parse_resource_args resource
|
145
|
+
conditions = conditions.to_hash_from_story unless (conditions.is_a? Hash)
|
146
|
+
klass.find(:first, :conditions => conditions)
|
147
|
+
end
|
148
|
+
|
149
|
+
#
|
150
|
+
# Simple, brittle, useful: store the given resource's attribute
|
151
|
+
# so we can compare it later.
|
152
|
+
#
|
153
|
+
def memorize_resource_value resource, attr
|
154
|
+
klass, instance = parse_resource_args resource
|
155
|
+
value = instance.send(attr)
|
156
|
+
@_memorized ||= {}
|
157
|
+
@_memorized[resource] ||= {}
|
158
|
+
@_memorized[resource][attr] = value
|
159
|
+
value
|
160
|
+
end
|
161
|
+
def recall_resource_value resource, attr
|
162
|
+
@_memorized[resource][attr]
|
163
|
+
end
|
164
|
+
def memorize_forget_all!
|
165
|
+
@_memorized = {}
|
166
|
+
end
|
167
|
+
|
168
|
+
#
|
169
|
+
# Keep the object around in a local instance variable @resource.
|
170
|
+
#
|
171
|
+
# So, for instance,
|
172
|
+
# klass, instance = parse_resource_args '<%= file_name %>'
|
173
|
+
# instance = klass.new({login => 'me', password => 'monkey', ...})
|
174
|
+
# keep_instance! resource, instance
|
175
|
+
# keeps the just-constructed <%= class_name %> model in the @<%= file_name %> instance variable.
|
176
|
+
#
|
177
|
+
def keep_instance! resource, object
|
178
|
+
instance_variable_set("@#{resource}", object)
|
179
|
+
end
|
@@ -0,0 +1,171 @@
|
|
1
|
+
#
|
2
|
+
# What you should see when you get there
|
3
|
+
#
|
4
|
+
|
5
|
+
steps_for(:ra_response) do
|
6
|
+
#
|
7
|
+
# Destinations. Ex:
|
8
|
+
# She should be at the new kids page
|
9
|
+
# Tarkin should be at the destroy alderaan page
|
10
|
+
# The visitor should be at the '/lolcats/download' form
|
11
|
+
# The visitor should be redirected to '/hi/mom'
|
12
|
+
#
|
13
|
+
# It doesn't know anything about actual routes -- it just
|
14
|
+
# feeds its output to render_template or redirect_to
|
15
|
+
#
|
16
|
+
Then "$actor should be at $path" do |_, path|
|
17
|
+
response.should render_template(grok_path(path))
|
18
|
+
end
|
19
|
+
|
20
|
+
Then "$actor should be redirected to $path" do |_, path|
|
21
|
+
response.should redirect_to(grok_path(path))
|
22
|
+
end
|
23
|
+
|
24
|
+
Then "the page should look AWESOME" do
|
25
|
+
response.should have_tag('head>title')
|
26
|
+
response.should have_tag('h1')
|
27
|
+
# response.should be_valid_xhtml
|
28
|
+
end
|
29
|
+
|
30
|
+
#
|
31
|
+
# Tags
|
32
|
+
#
|
33
|
+
|
34
|
+
Then "the page should contain '$text'" do |_, text|
|
35
|
+
response.should have_text(/#{text}/)
|
36
|
+
end
|
37
|
+
|
38
|
+
# please note: this enforces the use of a <label> field
|
39
|
+
Then "$actor should see a <$container> containing a $attributes" do |_, container, attributes|
|
40
|
+
attributes = attributes.to_hash_from_story
|
41
|
+
response.should have_tag(container) do
|
42
|
+
attributes.each do |tag, label|
|
43
|
+
case tag
|
44
|
+
when "textfield" then with_tag "input[type='text']"; with_tag("label", label)
|
45
|
+
when "password" then with_tag "input[type='password']"; with_tag("label", label)
|
46
|
+
when "submit" then with_tag "input[type='submit'][value='#{label}']"
|
47
|
+
else with_tag tag, label
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
#
|
54
|
+
# Session, cookie variables
|
55
|
+
#
|
56
|
+
Then "$actor $token cookie should include $attrlist" do |_, token, attrlist|
|
57
|
+
attrlist = attrlist.to_array_from_story
|
58
|
+
cookies.include?(token).should be_true
|
59
|
+
attrlist.each do |val|
|
60
|
+
cookies[token].include?(val).should be_true
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
Then "$actor $token cookie should exist but not include $attrlist" do |_, token, attrlist|
|
65
|
+
attrlist = attrlist.to_array_from_story
|
66
|
+
cookies.include?(token).should be_true
|
67
|
+
puts [cookies, attrlist, token].to_yaml
|
68
|
+
attrlist.each do |val|
|
69
|
+
cookies[token].include?(val).should_not be_true
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
Then "$actor should have $an $token cookie" do |_, _, token|
|
74
|
+
cookies[token].should_not be_blank
|
75
|
+
end
|
76
|
+
Then "$actor should not have $an $token cookie" do |_, _, token|
|
77
|
+
cookies[token].should be_blank
|
78
|
+
end
|
79
|
+
|
80
|
+
Given "$actor has $an cookie jar with $attributes" do |_, _, attributes|
|
81
|
+
attributes = attributes.to_hash_from_story
|
82
|
+
attributes.each do |attr, val|
|
83
|
+
cookies[attr] = val
|
84
|
+
end
|
85
|
+
end
|
86
|
+
Given "$actor session store has no $attrlist" do |_, attrlist|
|
87
|
+
attrlist = attrlist.to_array_from_story
|
88
|
+
attrlist.each do |attr|
|
89
|
+
# Note that the comparison passes through 'to_s'
|
90
|
+
session[attr.to_sym] = nil
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
Then "$actor session store should have $attributes" do |_, attributes|
|
95
|
+
attributes = attributes.to_hash_from_story
|
96
|
+
attributes.each do |attr, val|
|
97
|
+
# Note that the comparison passes through 'to_s'
|
98
|
+
session[attr.to_sym].to_s.should eql(val)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
Then "$actor session store should not have $attrlist" do |_, attrlist|
|
103
|
+
attrlist = attrlist.to_array_from_story
|
104
|
+
attrlist.each do |attr|
|
105
|
+
session[attr.to_sym].blank?.should be_true
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
#
|
110
|
+
# Flash messages
|
111
|
+
#
|
112
|
+
|
113
|
+
Then "$actor should see $an $notice message '$message'" do |_, _, notice, message|
|
114
|
+
response.should have_flash(notice, %r{#{message}})
|
115
|
+
end
|
116
|
+
|
117
|
+
Then "$actor should not see $an $notice message '$message'" do |_, _, notice, message|
|
118
|
+
response.should_not have_flash(notice, %r{#{message}})
|
119
|
+
end
|
120
|
+
|
121
|
+
Then "$actor should see no messages" do |_|
|
122
|
+
['error', 'warning', 'notice'].each do |notice|
|
123
|
+
response.should_not have_flash(notice)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
RE_POLITENESS = /(?:please|sorry|thank(?:s| you))/i
|
128
|
+
Then %r{we should be polite about it} do
|
129
|
+
response.should have_tag("div.error,div.notice", RE_POLITENESS)
|
130
|
+
end
|
131
|
+
Then %r{we should not even be polite about it} do
|
132
|
+
response.should_not have_tag("div.error,div.notice", RE_POLITENESS)
|
133
|
+
end
|
134
|
+
|
135
|
+
#
|
136
|
+
# Resource's attributes
|
137
|
+
#
|
138
|
+
# "Then page should have the $resource's $attributes" is in resource_steps
|
139
|
+
|
140
|
+
# helpful debug step
|
141
|
+
Then "we dump the response" do
|
142
|
+
dump_response
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
|
147
|
+
def have_flash notice, *args
|
148
|
+
have_tag("div.#{notice}", *args)
|
149
|
+
end
|
150
|
+
|
151
|
+
RE_PRETTY_RESOURCE = /the (index|show|new|create|edit|update|destroy) (\w+) (page|form)/i
|
152
|
+
RE_THE_FOO_PAGE = /the '?([^']*)'? (page|form)/i
|
153
|
+
RE_QUOTED_PATH = /^'([^']*)'$/i
|
154
|
+
def grok_path path
|
155
|
+
path.gsub(/\s+again$/,'') # strip trailing ' again'
|
156
|
+
case
|
157
|
+
when path == 'the home page' then dest = '/'
|
158
|
+
when path =~ RE_PRETTY_RESOURCE then dest = template_for $1, $2
|
159
|
+
when path =~ RE_THE_FOO_PAGE then dest = $1
|
160
|
+
when path =~ RE_QUOTED_PATH then dest = $1
|
161
|
+
else dest = path
|
162
|
+
end
|
163
|
+
dest
|
164
|
+
end
|
165
|
+
|
166
|
+
# turns 'new', 'road bikes' into 'road_bikes/new'
|
167
|
+
# note that it's "action resource"
|
168
|
+
def template_for(action, resource)
|
169
|
+
"#{resource.gsub(" ","_")}/#{action}"
|
170
|
+
end
|
171
|
+
|