devise_paypal 0.0.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.
Files changed (82) hide show
  1. data/.gitignore +3 -0
  2. data/Gemfile +4 -0
  3. data/MIT-LICENSE +21 -0
  4. data/README.markdown +125 -0
  5. data/Rakefile +2 -0
  6. data/app/controllers/devise/paypal_authable_controller.rb +24 -0
  7. data/app/controllers/devise/paypal_permissions_authable_controller.rb +25 -0
  8. data/config/locales/en.yml +7 -0
  9. data/devise_paypal.gemspec +23 -0
  10. data/features/paypal_authable.feature +30 -0
  11. data/features/paypal_permissions_authable.feature +40 -0
  12. data/features/step_definitions/extended_web_steps.rb +12 -0
  13. data/features/step_definitions/paypal_authable_steps.rb +11 -0
  14. data/features/step_definitions/paypal_authentication_steps.rb +25 -0
  15. data/features/step_definitions/paypal_permissions_authable_steps.rb +22 -0
  16. data/features/step_definitions/pickle_steps.rb +100 -0
  17. data/features/step_definitions/web_steps.rb +219 -0
  18. data/features/support/env.rb +58 -0
  19. data/features/support/fakeweb.rb +3 -0
  20. data/features/support/hooks.rb +10 -0
  21. data/features/support/paths.rb +46 -0
  22. data/features/support/paypal_response_helpers.rb +14 -0
  23. data/features/support/pickle.rb +24 -0
  24. data/lib/devise_paypal.rb +18 -0
  25. data/lib/devise_paypal/controllers/internal_helpers.rb +58 -0
  26. data/lib/devise_paypal/controllers/url_helpers.rb +16 -0
  27. data/lib/devise_paypal/models/paypal_authable.rb +22 -0
  28. data/lib/devise_paypal/models/paypal_permissions_authable.rb +22 -0
  29. data/lib/devise_paypal/rails.rb +21 -0
  30. data/lib/devise_paypal/rails/routes.rb +39 -0
  31. data/lib/devise_paypal/version.rb +3 -0
  32. data/lib/generators/devise_paypal/install_generator.rb +18 -0
  33. data/lib/generators/templates/README +20 -0
  34. data/test/rails_app/.gitignore +4 -0
  35. data/test/rails_app/Gemfile +21 -0
  36. data/test/rails_app/Gemfile.lock +169 -0
  37. data/test/rails_app/Rakefile +7 -0
  38. data/test/rails_app/app/controllers/application_controller.rb +3 -0
  39. data/test/rails_app/app/controllers/welcome_controller.rb +5 -0
  40. data/test/rails_app/app/models/user.rb +26 -0
  41. data/test/rails_app/app/views/layouts/application.html.erb +16 -0
  42. data/test/rails_app/app/views/welcome/index.html.erb +11 -0
  43. data/test/rails_app/config.ru +4 -0
  44. data/test/rails_app/config/application.rb +42 -0
  45. data/test/rails_app/config/boot.rb +13 -0
  46. data/test/rails_app/config/cucumber.yml +8 -0
  47. data/test/rails_app/config/database.yml +25 -0
  48. data/test/rails_app/config/environment.rb +5 -0
  49. data/test/rails_app/config/environments/development.rb +28 -0
  50. data/test/rails_app/config/environments/production.rb +49 -0
  51. data/test/rails_app/config/environments/test.rb +35 -0
  52. data/test/rails_app/config/initializers/backtrace_silencers.rb +7 -0
  53. data/test/rails_app/config/initializers/devise.rb +168 -0
  54. data/test/rails_app/config/initializers/inflections.rb +10 -0
  55. data/test/rails_app/config/initializers/mime_types.rb +5 -0
  56. data/test/rails_app/config/initializers/paypal.rb +7 -0
  57. data/test/rails_app/config/initializers/secret_token.rb +7 -0
  58. data/test/rails_app/config/initializers/session_store.rb +8 -0
  59. data/test/rails_app/config/locales/devise.en.yml +43 -0
  60. data/test/rails_app/config/locales/en.yml +5 -0
  61. data/test/rails_app/config/routes.rb +5 -0
  62. data/test/rails_app/db/migrate/20101102105924_devise_create_users.rb +26 -0
  63. data/test/rails_app/db/schema.rb +34 -0
  64. data/test/rails_app/db/seeds.rb +7 -0
  65. data/test/rails_app/lib/tasks/.gitkeep +0 -0
  66. data/test/rails_app/lib/tasks/cucumber.rake +53 -0
  67. data/test/rails_app/public/404.html +26 -0
  68. data/test/rails_app/public/422.html +26 -0
  69. data/test/rails_app/public/500.html +26 -0
  70. data/test/rails_app/public/favicon.ico +0 -0
  71. data/test/rails_app/public/images/rails.png +0 -0
  72. data/test/rails_app/public/javascripts/application.js +2 -0
  73. data/test/rails_app/public/javascripts/controls.js +965 -0
  74. data/test/rails_app/public/javascripts/dragdrop.js +974 -0
  75. data/test/rails_app/public/javascripts/effects.js +1123 -0
  76. data/test/rails_app/public/javascripts/prototype.js +6001 -0
  77. data/test/rails_app/public/javascripts/rails.js +175 -0
  78. data/test/rails_app/public/robots.txt +5 -0
  79. data/test/rails_app/public/stylesheets/.gitkeep +0 -0
  80. data/test/rails_app/script/cucumber +10 -0
  81. data/test/rails_app/script/rails +6 -0
  82. metadata +159 -0
@@ -0,0 +1,3 @@
1
+ pkg/*
2
+ *.gem
3
+ .bundle
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in devise_paypal.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2010 David Wilkie
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
@@ -0,0 +1,125 @@
1
+ # devise_paypal
2
+
3
+ devise_paypal is [Devise](http://github.com/plataformatec/devise) extension that allows you to authenticate users using the the [Paypal Permissions API](https://www.x.com/community/ppx/permissions) or the [Paypal Authentication API](https://www.x.com/community/ppx/authentication) through Devise.
4
+
5
+ ## Installation
6
+
7
+ Add devise_paypal to your Gemfile and make sure your using Devise from the git repository or at least version: "1.2.rc"
8
+
9
+ gem "devise", :git => "git://github.com/plataformatec/devise.git" # "1.2.rc"
10
+ gem "devise_paypal" #, :git => "git://github.com/dwilkie/devise_paypal.git" # for the latest and greatest
11
+
12
+ Ensure your bundle is installed and run the generator
13
+ bundle
14
+ rails g devise_paypal:install
15
+
16
+ As the generator instructs, you need to also add paypal-ipn to your gemfile then run its generator
17
+
18
+ gem 'paypal-ipn', :require => 'paypal' #, :git => "git://github.com/dwilkie/paypal.git" # for the latest and greatest
19
+
20
+ bundle
21
+ rails g paypal:initializer
22
+
23
+ This will create a configuration file where you can put your paypal api credentials.
24
+
25
+ Note: to enable the Paypal Permissions API you must file a ticket [here](https://www.paypal.com/mts). See this [page](https://www.x.com/community/ppx/permissions) for further details.
26
+
27
+ ## Usage
28
+
29
+ ### Model Configuration
30
+
31
+ Using the `devise` method, add `:paypal_authable` and/or `:paypal_permissions_authable` to your model.
32
+
33
+ class User < ActiveRecord::Base
34
+ devise paypal_authable, :paypal_permissions_authable
35
+ end
36
+
37
+ ### Views
38
+
39
+ If you have chosen a model named User and `devise_for :users` is already added to your config/routes.rb, devise_paypal will create the following url methods:
40
+
41
+ new_user_paypal_authable
42
+ new_user_paypal_permissions_authable
43
+
44
+ Then you only need to add them to your layouts in order to provide Paypal authentication:
45
+
46
+ <%= link_to "Sign in with Paypal Authable", new_user_paypal_authable_path %>
47
+ <%= link_to "Sign in with Paypal Permissions Authable", new_user_paypal_permissions_authable_path %>
48
+
49
+ By clicking on these links, the user will be redirected to Paypal. Then after entering their credentials, they'll be redirected back to your application.
50
+
51
+ ### Model Callback Method
52
+
53
+ Implement a class method in your model called `find_for_paypal_auth` which accepts a single params hash argument. The params hash contains the information returned from Paypal in the following format:
54
+
55
+ :email => "johnny@example.com",
56
+ :first_name => "Johnny",
57
+ :last_name => "Walker",
58
+ :permissions => {
59
+ :mass_pay => true
60
+ }
61
+
62
+ The method should return a single record which will be used to sign in the user. A simple implementation may look like this:
63
+
64
+ class User < ActiveRecord::Base
65
+ def self.find_for_paypal_auth(params)
66
+ if params
67
+ user = self.find_or_initialize_by_email(params[:email])
68
+ if user.new_record?
69
+ stubbed_password = Devise.friendly_token[0..password_length.max-1]
70
+ user.password = stubbed_password
71
+ user.password_confirmation = stubbed_password
72
+ user.save
73
+ end
74
+ else
75
+ user = self.new
76
+ end
77
+ user
78
+ end
79
+ end
80
+
81
+ See [user.rb](https://github.com/dwilkie/devise_paypal/blob/master/test/rails_app/app/models/user.rb) in the [sample rails app](https://github.com/dwilkie/devise_paypal/tree/master/test/rails_app) for more details.
82
+
83
+ ### Overriding Defaults
84
+
85
+ Say you want to request permission to access a Paypal API on behalf of a user. You can do this by overriding the devise_for call in your routes.rb file.
86
+
87
+ # routes.rb
88
+ devise_for :users, :controllers => {
89
+ :paypal_permissions_authable => "paypal_registrations"
90
+ }
91
+
92
+ Then creating your own controller inheriting from: `Devise::PaypalPermisssionsAuthableController`
93
+
94
+ # app/controllers/paypal_registrations_controller.rb
95
+ class PaypalRegistrationsController < Devise::PaypalPermissionsAuthableController
96
+ def new
97
+ @permissions = {:mass_pay => true}
98
+ super
99
+ end
100
+ end
101
+
102
+ In this case be sure to remember to modify the keys for your locale file:
103
+ # config/locales/devise_paypal.en.yml
104
+ en:
105
+ devise:
106
+ paypal_registrations:
107
+ success: "Successfully authorized from paypal account."
108
+
109
+ By default, if a non-persisted record is returned by your model callback method, the user will be rendered the new registrations page from `devise :registrations`
110
+
111
+ To change this behavior simply override `render_for_paypal` in your controller
112
+
113
+ # app/controllers/paypal_registrations_controller.rb
114
+ class PaypalRegistrationsController < Devise::PaypalPermissionsAuthableController
115
+ private
116
+
117
+ def render_for_paypal
118
+ render "welcome#index"
119
+ end
120
+ end
121
+
122
+ For more details check out the [source](https://github.com/dwilkie/devise_paypal/tree/master/lib/devise_paypal/)
123
+
124
+ Copyright (c) 2010 David Wilkie, released under the MIT license
125
+
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,24 @@
1
+ class Devise::PaypalAuthableController < ApplicationController
2
+ prepend_before_filter :require_no_authentication, :only => :new
3
+ include Devise::Controllers::InternalHelpers
4
+ include DevisePaypal::Controllers::InternalHelpers
5
+ include Paypal::Authentication
6
+
7
+ # GET /resource/paypal_authorize_without_permissions
8
+ def new
9
+ callback_url = paypal_authable_callback_uri
10
+ redirect_to authenticate_with_paypal_url(callback_url)
11
+ end
12
+
13
+ # GET /resource/paypal_authable/callback
14
+ def callback_action
15
+ paypal_user_details = get_auth_details(params[:token]) if params[:token]
16
+ handle_callback_action(paypal_user_details)
17
+ end
18
+
19
+ private
20
+ def paypal_authable_callback_uri #:nodoc:
21
+ paypal_authable_callback_url(resource_name)
22
+ end
23
+ end
24
+
@@ -0,0 +1,25 @@
1
+ class Devise::PaypalPermissionsAuthableController < ApplicationController
2
+ prepend_before_filter :require_no_authentication, :only => :new
3
+ include Devise::Controllers::InternalHelpers
4
+ include DevisePaypal::Controllers::InternalHelpers
5
+ include Paypal::Permissions
6
+
7
+ # GET /resource/paypal_authorize_with_permissions
8
+ def new
9
+ @permissions ||= {}
10
+ callback_url = paypal_permissions_authable_callback_uri
11
+ redirect_to set_paypal_permissions_url(callback_url, @permissions)
12
+ end
13
+
14
+ # GET /resource/paypal_permissions_authable/callback
15
+ def callback_action
16
+ paypal_user_details = get_paypal_permissions(params[:token]) if params[:token]
17
+ handle_callback_action(paypal_user_details)
18
+ end
19
+
20
+ private
21
+ def paypal_permissions_authable_callback_uri #:nodoc:
22
+ paypal_permissions_authable_callback_url(resource_name)
23
+ end
24
+ end
25
+
@@ -0,0 +1,7 @@
1
+ en:
2
+ devise:
3
+ paypal_permissions_authable:
4
+ success: "Successfully authorized from Paypal account."
5
+ paypal_authable:
6
+ success: "Successfully authorized from Paypal account."
7
+
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "devise_paypal/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "devise_paypal"
7
+ s.version = DevisePaypal::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["David Wilkie"]
10
+ s.email = ["dwilkie@gmail.com"]
11
+ s.homepage = "http://github.com/dwilkie/devise_paypal"
12
+ s.summary = %q{Signup or login using Paypal}
13
+ s.description = %q{Signup or login using Paypal's Authorization or Permissions api's}
14
+
15
+ s.rubyforge_project = "devise_paypal"
16
+ s.add_runtime_dependency "paypal-ipn", ">0.0.1"
17
+
18
+ s.files = `git ls-files`.split("\n")
19
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
20
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
21
+ s.require_paths = ["lib"]
22
+ end
23
+
@@ -0,0 +1,30 @@
1
+ @paypal_authentication_request
2
+ Feature: Authenticate using Paypal Authable
3
+ In order to signup or login using by logging into paypal
4
+ As a user
5
+ I want to be able to authenticate using the Paypal authable api
6
+
7
+ Scenario: I authenticate with Paypal authable
8
+ When I go to the authenticate with paypal authable page
9
+
10
+ Then I should be redirected to sign in with paypal
11
+ And I should be requested to grant access to my name and email address
12
+
13
+ Scenario: I sign into paypal
14
+ Given I have a paypal account with email: "mara@example.com"
15
+ And I sign into paypal through paypal authable
16
+
17
+ When I am redirected back to the application from paypal after an authentication request
18
+
19
+ Then a user should exist with email: "mara@example.com"
20
+ And I should be on the home page
21
+ And I should see "Successfully authorized from Paypal account."
22
+
23
+ Scenario: I do not sign into paypal
24
+ Given I have a paypal account with email: "mara@example.com"
25
+ But I do not sign into paypal
26
+
27
+ When I am redirected back to the application from paypal after an authentication request
28
+
29
+ Then a user should not exist with email: "mara@example.com"
30
+
@@ -0,0 +1,40 @@
1
+ @paypal_authentication_request
2
+ Feature: Authenticate using Paypal Permissions Authable
3
+ In order to signup or login using Paypal and grant requested permissions
4
+ As a user
5
+ I want to be able to authenticate using the Paypal permissions api
6
+
7
+ Scenario: I authenticate with Paypal permissions authable
8
+ When I go to the authenticate with paypal permissions page
9
+
10
+ Then I should be redirected to sign in with paypal
11
+ And I should be requested to grant the required permissions
12
+
13
+ Scenario: I grant the required permissions
14
+ Given I have a paypal account with email: "mara@example.com"
15
+ And I sign into paypal through paypal permissions authable
16
+ And I grant the required permissions
17
+
18
+ When I am redirected back to the application from paypal after a permissions request
19
+
20
+ Then a user should exist with email: "mara@example.com"
21
+ And I should be on the home page
22
+ And I should see "Successfully authorized from Paypal account."
23
+
24
+ Scenario: I do not grant the required permissions
25
+ Given I have a paypal account with email: "mara@example.com"
26
+ And I sign into paypal through paypal permissions authable
27
+ But I do not grant the required permissions
28
+
29
+ When I am redirected back to the application from paypal after a permissions request
30
+
31
+ Then a user should not exist with email: "mara@example.com"
32
+
33
+ Scenario: I do not sign into paypal
34
+ Given I have a paypal account with email: "mara@example.com"
35
+ But I do not sign into paypal
36
+
37
+ When I am redirected back to the application from paypal after a permissions request
38
+
39
+ Then a user should not exist with email: "mara@example.com"
40
+
@@ -0,0 +1,12 @@
1
+ Then /^(?:|I )should be at "([^"]*)"$/ do |url|
2
+ current_uri = URI.parse(current_url)
3
+ current_uri.query = nil
4
+ current_uri.path = "" if current_uri.path == "/"
5
+ current_uri = current_uri.to_s
6
+ if current_uri.respond_to? :should
7
+ current_uri.should == url
8
+ else
9
+ assert_equal url, current_uri
10
+ end
11
+ end
12
+
@@ -0,0 +1,11 @@
1
+ Given /^I sign into paypal through paypal authable$/ do
2
+ register_get_user_details_response
3
+ end
4
+
5
+ Then /^I should be requested to grant access to my name and email address$/ do
6
+ Then %{I should have the following query string:}, table(%{
7
+ | cmd | _account-authenticate-login |
8
+ | token | HA-DJW3X5Y99KRR4 |
9
+ })
10
+ end
11
+
@@ -0,0 +1,25 @@
1
+ Given /^I have a paypal account(?: with #{capture_fields})?$/ do |fields|
2
+ @paypal_user_details = parse_fields(fields)
3
+ parsed_paypal_user_details = {}
4
+ @paypal_user_details.each do |key, value|
5
+ parsed_paypal_user_details[key.classify.upcase] = value
6
+ end
7
+ @paypal_user_details = parsed_paypal_user_details
8
+ end
9
+
10
+ Given /^I do not sign into paypal$/ do
11
+ # this step is intentionally blank
12
+ end
13
+
14
+ When /^I am redirected back to the application from paypal after a permissions request$/ do
15
+ When "I go to the paypal permissions callback page"
16
+ end
17
+
18
+ When /^I am redirected back to the application from paypal after an authentication request$/ do
19
+ When "I go to the paypal authable callback page"
20
+ end
21
+
22
+ Then /^I should be redirected to sign in with paypal$/ do
23
+ Then %{I should be at "#{Paypal.uri}"}
24
+ end
25
+
@@ -0,0 +1,22 @@
1
+ Given /^I sign into paypal through paypal permissions authable$/ do
2
+ # this step is intentionally blank
3
+ end
4
+
5
+ Given /^I grant the required permissions$/ do
6
+ register_get_user_details_response
7
+ end
8
+
9
+ Given /^I do not grant the required permissions$/ do
10
+ body = "TIMESTAMP=2010%2d10%2d08T11%3a35%3a41Z&CORRELATIONID=d41ee44136721&ACK=Failure&VERSION=2%2e3&BUILD=1545724&L_ERRORCODE0=11622&L_SHORTMESSAGE0=User%20Does%20Not%20Exist%2e&L_LONGMESSAGE0=User%20may%20not%20have%20logged%20in%20using%20this%20token%2e&L_SEVERITYCODE0=Error"
11
+ FakeWeb.register_uri(
12
+ :post, Paypal.nvp_uri, :body => body
13
+ )
14
+ end
15
+
16
+ Then /^I should be requested to grant the required permissions$/ do
17
+ Then %{I should have the following query string:}, table(%{
18
+ | cmd | _access-permission-login |
19
+ | token | HA-DJW3X5Y99KRR4 |
20
+ })
21
+ end
22
+
@@ -0,0 +1,100 @@
1
+ # this file generated by script/generate pickle
2
+
3
+ # create a model
4
+ Given(/^#{capture_model} exists?(?: with #{capture_fields})?$/) do |name, fields|
5
+ create_model(name, fields)
6
+ end
7
+
8
+ # create n models
9
+ Given(/^(\d+) #{capture_plural_factory} exist(?: with #{capture_fields})?$/) do |count, plural_factory, fields|
10
+ count.to_i.times { create_model(plural_factory.singularize, fields) }
11
+ end
12
+
13
+ # create models from a table
14
+ Given(/^the following #{capture_plural_factory} exists?:?$/) do |plural_factory, table|
15
+ create_models_from_table(plural_factory, table)
16
+ end
17
+
18
+ # find a model
19
+ Then(/^#{capture_model} should exist(?: with #{capture_fields})?$/) do |name, fields|
20
+ find_model!(name, fields)
21
+ end
22
+
23
+ # not find a model
24
+ Then(/^#{capture_model} should not exist(?: with #{capture_fields})?$/) do |name, fields|
25
+ find_model(name, fields).should be_nil
26
+ end
27
+
28
+ # find models with a table
29
+ Then(/^the following #{capture_plural_factory} should exists?:?$/) do |plural_factory, table|
30
+ find_models_from_table(plural_factory, table).should_not be_any(&:nil?)
31
+ end
32
+
33
+ # find exactly n models
34
+ Then(/^(\d+) #{capture_plural_factory} should exist(?: with #{capture_fields})?$/) do |count, plural_factory, fields|
35
+ find_models(plural_factory.singularize, fields).size.should == count.to_i
36
+ end
37
+
38
+ # assert equality of models
39
+ Then(/^#{capture_model} should be #{capture_model}$/) do |a, b|
40
+ model!(a).should == model!(b)
41
+ end
42
+
43
+ # assert model is in another model's has_many assoc
44
+ Then(/^#{capture_model} should be (?:in|one of|amongst) #{capture_model}(?:'s)? (\w+)$/) do |target, owner, association|
45
+ model!(owner).send(association).should include(model!(target))
46
+ end
47
+
48
+ # assert model is not in another model's has_many assoc
49
+ Then(/^#{capture_model} should not be (?:in|one of|amongst) #{capture_model}(?:'s)? (\w+)$/) do |target, owner, association|
50
+ model!(owner).send(association).should_not include(model!(target))
51
+ end
52
+
53
+ # assert model is another model's has_one/belongs_to assoc
54
+ Then(/^#{capture_model} should be #{capture_model}(?:'s)? (\w+)$/) do |target, owner, association|
55
+ model!(owner).send(association).should == model!(target)
56
+ end
57
+
58
+ # assert model is not another model's has_one/belongs_to assoc
59
+ Then(/^#{capture_model} should not be #{capture_model}(?:'s)? (\w+)$/) do |target, owner, association|
60
+ model!(owner).send(association).should_not == model!(target)
61
+ end
62
+
63
+ # assert model.predicate?
64
+ Then(/^#{capture_model} should (?:be|have) (?:an? )?#{capture_predicate}$/) do |name, predicate|
65
+ if model!(name).respond_to?("has_#{predicate.gsub(' ', '_')}")
66
+ model!(name).should send("have_#{predicate.gsub(' ', '_')}")
67
+ else
68
+ model!(name).should send("be_#{predicate.gsub(' ', '_')}")
69
+ end
70
+ end
71
+
72
+ # assert not model.predicate?
73
+ Then(/^#{capture_model} should not (?:be|have) (?:an? )?#{capture_predicate}$/) do |name, predicate|
74
+ if model!(name).respond_to?("has_#{predicate.gsub(' ', '_')}")
75
+ model!(name).should_not send("have_#{predicate.gsub(' ', '_')}")
76
+ else
77
+ model!(name).should_not send("be_#{predicate.gsub(' ', '_')}")
78
+ end
79
+ end
80
+
81
+ # model.attribute.should eql(value)
82
+ # model.attribute.should_not eql(value)
83
+ Then(/^#{capture_model}'s (\w+) (should(?: not)?) be #{capture_value}$/) do |name, attribute, expectation, expected|
84
+ actual_value = model(name).send(attribute)
85
+ expectation = expectation.gsub(' ', '_')
86
+
87
+ case expected
88
+ when 'nil', 'true', 'false'
89
+ actual_value.send(expectation, send("be_#{expected}"))
90
+ when /^[+-]?[0-9_]+(\.\d+)?$/
91
+ actual_value.send(expectation, eql(expected.to_f))
92
+ else
93
+ actual_value.to_s.send(expectation, eql(eval(expected)))
94
+ end
95
+ end
96
+
97
+ # assert size of association
98
+ Then /^#{capture_model} should have (\d+) (\w+)$/ do |name, size, association|
99
+ model!(name).send(association).size.should == size.to_i
100
+ end