rspec_candy 0.1.2 → 0.2.0
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/README.md +40 -4
- data/lib/rspec_candy/all.rb +2 -0
- data/lib/rspec_candy/base.rb +2 -0
- data/lib/rspec_candy/helpers.rb +1 -2
- data/lib/rspec_candy/matchers/be_same_number_as.rb +14 -0
- data/lib/rspec_candy/matchers/be_same_second_as.rb +9 -0
- data/lib/rspec_candy/matchers/include_hash.rb +7 -0
- data/lib/rspec_candy/matchers.rb +4 -0
- data/lib/rspec_candy/version.rb +1 -1
- data/lib/rspec_candy.rb +2 -2
- data/spec/rspec1/spec_helper.rb +1 -1
- data/spec/rspec2/spec_helper.rb +1 -1
- data/spec/shared/rspec_candy/matchers/be_same_number_as_spec.rb +31 -0
- data/spec/shared/rspec_candy/matchers/be_same_second_as_spec.rb +29 -0
- data/spec/shared/rspec_candy/matchers/include_hash_spec.rb +30 -0
- metadata +13 -6
- data/template/spec_candy.rails2.rb +0 -171
- data/template/spec_candy.rails3.rb +0 -175
data/README.md
CHANGED
@@ -1,15 +1,51 @@
|
|
1
1
|
#rspec_candy
|
2
2
|
|
3
3
|
|
4
|
-
A collection of nifty helpers for your
|
4
|
+
A collection of nifty helpers and matchers for your RSpec suite.
|
5
5
|
|
6
|
-
Will work for RSpec1 and Rails 2, or RSpec2 and Rails 3, or no Rails at all
|
6
|
+
Will work for RSpec1 and Rails 2, or RSpec2 and Rails 3, or no Rails at all.
|
7
7
|
|
8
8
|
|
9
|
-
##
|
9
|
+
##Installation
|
10
10
|
|
11
11
|
Add `rspec_candy` to your Gemfile.
|
12
|
-
|
12
|
+
|
13
|
+
Now, in your `spec_helper.rb`, add this after your RSpec requires:
|
14
|
+
|
15
|
+
require 'rspec_candy/all'
|
16
|
+
|
17
|
+
If you only care about the matchers or helpers you can also more specifically require:
|
18
|
+
|
19
|
+
require 'rspec_candy/matchers'
|
20
|
+
require 'rspec_candy/helpers'
|
21
|
+
|
22
|
+
|
23
|
+
##Matchers provided
|
24
|
+
|
25
|
+
**be_same_number_as**
|
26
|
+
|
27
|
+
Tests if the given number is the "same" as the receiving number, regardless of whether you're comparing `Fixnums` (integers), `Floats` and `BigDecimals`:
|
28
|
+
|
29
|
+
100.should be_same_number_as(100.0)
|
30
|
+
50.4.should be_same_number_as(BigDecimal('50.4'))
|
31
|
+
|
32
|
+
Note that "same" means "same for your purposes". Internally the matcher compares normalized results of `#to_s`.
|
33
|
+
|
34
|
+
**be_same_second_as**
|
35
|
+
|
36
|
+
Tests if the given `Time` or `DateTime` is the same as the receiving `Time` or `DateTime`, [ignoring sub-second differences](https://makandracards.com/makandra/1057-why-two-ruby-time-objects-are-not-equal-although-they-appear-to-be):
|
37
|
+
|
38
|
+
Time.parse('2012-12-01 14:00:00.5').should == Time.parse('2012-12-01 14:00')
|
39
|
+
|
40
|
+
Note that two times in a different time zones will still be considered different.q
|
41
|
+
|
42
|
+
**include_hash**
|
43
|
+
|
44
|
+
Matches if the given hash is included in the receiving hash:
|
45
|
+
|
46
|
+
{ :foo => 'a', :bar => 'b' }.should include_hash(:foo => 'a') # passes
|
47
|
+
{ :foo => 'a', :bar => 'b' }.should include_hash(:foo => 'b') # fails
|
48
|
+
{ :foo => 'a', :bar => 'b' }.should include_hash(:foo => 'a', :baz => 'c') # fails
|
13
49
|
|
14
50
|
|
15
51
|
##Helpers provided
|
data/lib/rspec_candy/helpers.rb
CHANGED
@@ -0,0 +1,14 @@
|
|
1
|
+
RSpecCandy::Switcher.define_matcher :be_same_number_as do |expected|
|
2
|
+
|
3
|
+
match do |actual|
|
4
|
+
actual.is_a?(Numeric) && expected.is_a?(Numeric) && normalize(expected) == normalize(actual)
|
5
|
+
end
|
6
|
+
|
7
|
+
def normalize(number)
|
8
|
+
number.to_s.tap do |str|
|
9
|
+
str << ".0" unless str.include?('.')
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
data/lib/rspec_candy/version.rb
CHANGED
data/lib/rspec_candy.rb
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
# empty
|
2
|
-
|
1
|
+
# empty so bundler doesn't require a thing
|
2
|
+
|
data/spec/rspec1/spec_helper.rb
CHANGED
data/spec/rspec2/spec_helper.rb
CHANGED
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe RSpecCandy do
|
4
|
+
describe 'matchers' do
|
5
|
+
describe 'be_same_number_as' do
|
6
|
+
|
7
|
+
it 'should correctly compare Fixnums and Floats' do
|
8
|
+
1.should be_same_number_as(1.0)
|
9
|
+
2.should_not be_same_number_as(1.0)
|
10
|
+
-1.should be_same_number_as(-1.0)
|
11
|
+
-2.should_not be_same_number_as(-1.0)
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should correctly compare Fixnums and BigDecimals' do
|
15
|
+
1.should be_same_number_as(BigDecimal('1.0'))
|
16
|
+
2.should_not be_same_number_as(BigDecimal('1.0'))
|
17
|
+
-1.should be_same_number_as(BigDecimal('-1.0'))
|
18
|
+
-2.should_not be_same_number_as(BigDecimal('-1.0'))
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should correctly compare Floats and BigDecimals' do
|
22
|
+
1.1.should be_same_number_as(BigDecimal('1.1'))
|
23
|
+
2.1.should_not be_same_number_as(BigDecimal('1.1'))
|
24
|
+
-1.1.should be_same_number_as(BigDecimal('-1.1'))
|
25
|
+
-2.1.should_not be_same_number_as(BigDecimal('-1.1'))
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe RSpecCandy do
|
4
|
+
describe 'matchers' do
|
5
|
+
describe 'be_same_second_as' do
|
6
|
+
|
7
|
+
it 'should consider equal two Times with the same second' do
|
8
|
+
Time.parse('2012-05-01 14:15:16').should be_same_second_as(Time.parse('2012-05-01 14:15:16'))
|
9
|
+
Time.parse('2012-05-01 14:15:17').should_not be_same_second_as(Time.parse('2012-05-01 14:15:16'))
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should ignore sub-second differences' do
|
13
|
+
Time.parse('2012-05-01 00:00:00.1').should be_same_second_as(Time.parse('2012-05-01 00:00:00.2'))
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should correctly compare Time and DateTime objects' do
|
17
|
+
Time.parse('2012-05-01 14:15:16 +0000').should be_same_second_as(DateTime.parse('2012-05-01 14:15:16 +0000'))
|
18
|
+
Time.parse('2012-05-01 14:15:17 +0000').should_not be_same_second_as(DateTime.parse('2012-05-01 14:15:16 +0000'))
|
19
|
+
Time.parse('2012-05-01 00:00:00.1 +0000').should be_same_second_as(DateTime.parse('2012-05-01 00:00:00.2 +0000'))
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should consider different two times in different zones' do
|
23
|
+
Time.parse('2012-05-01 14:15:16 +0000').should_not be_same_second_as(Time.parse('2012-05-01 14:15:16 +0100'))
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe RSpecCandy do
|
4
|
+
describe 'matchers' do
|
5
|
+
describe 'include_hash' do
|
6
|
+
|
7
|
+
it 'should match if all pairs in the given hash exist in the receiver' do
|
8
|
+
{ :foo => 'a', :bar => 'b' , :bam => 'c'}.should include_hash(:foo => 'a', :bam => 'c')
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should not match if the given hash has keys that do not exist in the receiver' do
|
12
|
+
{ :foo => 'a', :bar => 'b' , :bam => 'c'}.should_not include_hash(:foo => 'a', :baz => 'd')
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should not match if the given hash has values that are different in the receiver' do
|
16
|
+
{ :foo => 'a', :bar => 'b' , :bam => 'c'}.should_not include_hash(:foo => 'a', :bam => 'different-value')
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should not crash and not match if the receiver is nil' do
|
20
|
+
nil.should_not include_hash(:foo => 'a')
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should match if the receiver is empty and the given hash is empty' do
|
24
|
+
{}.should include_hash({})
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rspec_candy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 23
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
- 1
|
9
8
|
- 2
|
10
|
-
|
9
|
+
- 0
|
10
|
+
version: 0.2.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Henning Koch
|
@@ -16,7 +16,7 @@ autorequire:
|
|
16
16
|
bindir: bin
|
17
17
|
cert_chain: []
|
18
18
|
|
19
|
-
date: 2012-
|
19
|
+
date: 2012-08-06 00:00:00 +02:00
|
20
20
|
default_executable:
|
21
21
|
dependencies:
|
22
22
|
- !ruby/object:Gem::Dependency
|
@@ -46,6 +46,8 @@ files:
|
|
46
46
|
- README.md
|
47
47
|
- Rakefile
|
48
48
|
- lib/rspec_candy.rb
|
49
|
+
- lib/rspec_candy/all.rb
|
50
|
+
- lib/rspec_candy/base.rb
|
49
51
|
- lib/rspec_candy/helpers.rb
|
50
52
|
- lib/rspec_candy/helpers/disposable_copy.rb
|
51
53
|
- lib/rspec_candy/helpers/it_should_act_like.rb
|
@@ -58,6 +60,10 @@ files:
|
|
58
60
|
- lib/rspec_candy/helpers/should_receive_chain.rb
|
59
61
|
- lib/rspec_candy/helpers/stub_any_instance.rb
|
60
62
|
- lib/rspec_candy/helpers/stub_existing.rb
|
63
|
+
- lib/rspec_candy/matchers.rb
|
64
|
+
- lib/rspec_candy/matchers/be_same_number_as.rb
|
65
|
+
- lib/rspec_candy/matchers/be_same_second_as.rb
|
66
|
+
- lib/rspec_candy/matchers/include_hash.rb
|
61
67
|
- lib/rspec_candy/switcher.rb
|
62
68
|
- lib/rspec_candy/version.rb
|
63
69
|
- rspec_candy.gemspec
|
@@ -119,10 +125,11 @@ files:
|
|
119
125
|
- spec/shared/rspec_candy/helpers/should_receive_chain_spec.rb
|
120
126
|
- spec/shared/rspec_candy/helpers/stub_any_instance_spec.rb
|
121
127
|
- spec/shared/rspec_candy/helpers/stub_existing_spec.rb
|
128
|
+
- spec/shared/rspec_candy/matchers/be_same_number_as_spec.rb
|
129
|
+
- spec/shared/rspec_candy/matchers/be_same_second_as_spec.rb
|
130
|
+
- spec/shared/rspec_candy/matchers/include_hash_spec.rb
|
122
131
|
- spec/shared/support/matchers/pass_as_describe_block.rb
|
123
132
|
- spec/shared/support/matchers/pass_as_example.rb
|
124
|
-
- template/spec_candy.rails2.rb
|
125
|
-
- template/spec_candy.rails3.rb
|
126
133
|
has_rdoc: true
|
127
134
|
homepage: https://github.com/makandra/rspec_candy
|
128
135
|
licenses: []
|
@@ -1,171 +0,0 @@
|
|
1
|
-
Object.class_eval do
|
2
|
-
|
3
|
-
def should_receive_chain(*parts)
|
4
|
-
setup_expectation_chain(parts)
|
5
|
-
end
|
6
|
-
|
7
|
-
def self.new_with_stubs(attrs)
|
8
|
-
new.tap do |obj|
|
9
|
-
obj.stub_existing attrs
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
def stub_existing(attrs)
|
14
|
-
attrs.each do |method, value|
|
15
|
-
if respond_to?(method, true)
|
16
|
-
stub(method => value)
|
17
|
-
else
|
18
|
-
raise "Attempted to stub non-existing method ##{method} on a #{self.class.name}"
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def should_receive_and_return(methods_and_values)
|
24
|
-
methods_and_values.each do |method, value|
|
25
|
-
should_receive(method).and_return(value)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
def should_receive_all_with(methods_and_values)
|
30
|
-
methods_and_values.each do |method, value|
|
31
|
-
should_receive(method).with(value)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
def should_not_receive_and_execute(method)
|
36
|
-
should_receive_and_execute(method, true)
|
37
|
-
end
|
38
|
-
|
39
|
-
def should_receive_and_execute(method, negate = false)
|
40
|
-
method_base = method.to_s.gsub(/([\?\!\=\[\]]+)$/, '')
|
41
|
-
method_suffix = $1
|
42
|
-
|
43
|
-
method_called = "_#{method_base}_called#{method_suffix}"
|
44
|
-
method_with_spy = "#{method_base}_with_spy#{method_suffix}"
|
45
|
-
method_without_spy = "#{method_base}_without_spy#{method_suffix}"
|
46
|
-
|
47
|
-
prototype = respond_to?(:singleton_class) ? singleton_class : metaclass
|
48
|
-
prototype.class_eval do
|
49
|
-
|
50
|
-
unless method_defined?(method_with_spy)
|
51
|
-
|
52
|
-
define_method method_called do
|
53
|
-
end
|
54
|
-
|
55
|
-
define_method method_with_spy do |*args, &block|
|
56
|
-
send(method_called, *args)
|
57
|
-
send(method_without_spy, *args, &block)
|
58
|
-
end
|
59
|
-
alias_method_chain method, :spy
|
60
|
-
end
|
61
|
-
|
62
|
-
end
|
63
|
-
|
64
|
-
expectation = negate ? :should_not_receive : :should_receive
|
65
|
-
send(expectation, method_called)
|
66
|
-
end
|
67
|
-
|
68
|
-
private
|
69
|
-
|
70
|
-
def setup_expectation_chain(parts)
|
71
|
-
obj = self
|
72
|
-
for part in parts
|
73
|
-
if part == parts.last
|
74
|
-
obj = add_expectation_chain_link(obj, part)
|
75
|
-
else
|
76
|
-
next_obj = Spec::Mocks::Mock.new('chain link')
|
77
|
-
add_expectation_chain_link(obj, part).at_least(:once).and_return(next_obj)
|
78
|
-
obj = next_obj
|
79
|
-
end
|
80
|
-
end
|
81
|
-
obj
|
82
|
-
end
|
83
|
-
|
84
|
-
def add_expectation_chain_link(obj, part)
|
85
|
-
if part.is_a?(Array)
|
86
|
-
obj.should_receive(part.first).with(*part[1..-1])
|
87
|
-
else
|
88
|
-
obj.should_receive(part)
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
end
|
93
|
-
|
94
|
-
Spec::Example::ExampleGroupMethods.class_eval do
|
95
|
-
|
96
|
-
# Improves it_should_behave_like in some ways:
|
97
|
-
# - It scopes the reused examples so #let und #subject does not bleed into the reusing example groups
|
98
|
-
# - It allows to parametrize the reused example group by appending a hash argument.
|
99
|
-
# Every key/value pair in the hash will become a #let variable for the reused example group
|
100
|
-
# - You can call it with a block. It will be available to the reused example group as let(:block)
|
101
|
-
def it_should_act_like(shared_example_group, environment = {}, &block)
|
102
|
-
description = "as #{shared_example_group}"
|
103
|
-
description << " (#{environment.inspect})" if environment.present?
|
104
|
-
describe description do
|
105
|
-
environment.each do |name, value|
|
106
|
-
let(name) { value }
|
107
|
-
end
|
108
|
-
let(:block) { block } if block
|
109
|
-
it_should_behave_like(shared_example_group)
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
end
|
114
|
-
|
115
|
-
Spec::Rails::Example::ModelExampleGroup.class_eval do
|
116
|
-
|
117
|
-
def self.it_should_run_callbacks_in_order(*callbacks)
|
118
|
-
callbacks.push(:ordered => true)
|
119
|
-
it_should_run_callbacks(*callbacks)
|
120
|
-
end
|
121
|
-
|
122
|
-
def self.it_should_run_callbacks(*callbacks)
|
123
|
-
options = callbacks.last.is_a?(Hash) ? callbacks.pop : {}
|
124
|
-
reason = callbacks.pop if callbacks.last.is_a?(String)
|
125
|
-
hook = description_parts.last.sub(/^#/, '')
|
126
|
-
should = ['should run callbacks', callbacks.inspect, ('in order' if options[:ordered]), reason].compact.join ' '
|
127
|
-
send(:it, should) do
|
128
|
-
callbacks.each do |callback|
|
129
|
-
expectation = subject.should_receive(callback).once
|
130
|
-
expectation.ordered if options[:ordered]
|
131
|
-
end
|
132
|
-
run_state_machine_callbacks_from_prose(hook) || subject.run_callbacks(hook)
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
private
|
137
|
-
|
138
|
-
def run_state_machine_callbacks_from_prose(prose)
|
139
|
-
if parts = prose.match(/^(\w+) from ([\:\w]+) to ([\:\w]+)$/)
|
140
|
-
name = parts[1].to_sym
|
141
|
-
from = parts[2].sub(/^:/, '').to_sym
|
142
|
-
to = parts[3].sub(/^:/, '').to_sym
|
143
|
-
transition = StateMachine::Transition.new(subject, subject.class.state_machine, name, from, to)
|
144
|
-
transition.run_callbacks
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
end
|
149
|
-
|
150
|
-
ActiveRecord::Base.class_eval do
|
151
|
-
|
152
|
-
# Prevents the databse from being touched, but still runs all validations.
|
153
|
-
def keep_invalid!
|
154
|
-
errors.stub :empty? => false
|
155
|
-
end
|
156
|
-
|
157
|
-
end
|
158
|
-
|
159
|
-
Class.class_eval do
|
160
|
-
|
161
|
-
define_method :stub_any_instance do |stubs|
|
162
|
-
unstubbed_new = method(:new)
|
163
|
-
stub(:new).and_return do |*args|
|
164
|
-
unstubbed_new.call(*args).tap do |obj|
|
165
|
-
obj.stub stubs
|
166
|
-
end
|
167
|
-
end
|
168
|
-
stubs
|
169
|
-
end
|
170
|
-
|
171
|
-
end
|
@@ -1,175 +0,0 @@
|
|
1
|
-
Object.class_eval do
|
2
|
-
|
3
|
-
# def should_receive_chain(*parts)
|
4
|
-
# setup_expectation_chain(parts)
|
5
|
-
# end
|
6
|
-
#
|
7
|
-
# def should_not_receive_chain(*parts)
|
8
|
-
# setup_expectation_chain(parts, :negate => true)
|
9
|
-
# end
|
10
|
-
#
|
11
|
-
# def self.new_with_stubs(attrs)
|
12
|
-
# new.tap { |obj| obj.stub(attrs) }
|
13
|
-
# end
|
14
|
-
#
|
15
|
-
# def should_receive_and_return(methods_and_values)
|
16
|
-
# methods_and_values.each do |method, value|
|
17
|
-
# should_receive(method).and_return(value)
|
18
|
-
# end
|
19
|
-
# end
|
20
|
-
#
|
21
|
-
# def self.new_and_store(options = {})
|
22
|
-
# new.tap do |record|
|
23
|
-
# record.send(:attributes=, options, false)
|
24
|
-
# record.save!(:validate => false)
|
25
|
-
# end
|
26
|
-
# end
|
27
|
-
#
|
28
|
-
# def stub_existing(attrs)
|
29
|
-
# attrs.each do |method, value|
|
30
|
-
# if respond_to?(method, true)
|
31
|
-
# stub(method => value)
|
32
|
-
# else
|
33
|
-
# raise "Attempted to stub non-existing method ##{method} on a #{self.class.name}"
|
34
|
-
# end
|
35
|
-
# end
|
36
|
-
# end
|
37
|
-
#
|
38
|
-
# def should_receive_and_execute(method, negate = false)
|
39
|
-
# method_base = method.to_s.gsub(/([\?\!\=\[\]]+)$/, '')
|
40
|
-
# method_suffix = $1
|
41
|
-
#
|
42
|
-
# method_called = "_#{method_base}_called#{method_suffix}"
|
43
|
-
# method_with_spy = "#{method_base}_with_spy#{method_suffix}"
|
44
|
-
# method_without_spy = "#{method_base}_without_spy#{method_suffix}"
|
45
|
-
#
|
46
|
-
# prototype = respond_to?(:singleton_class) ? singleton_class : metaclass
|
47
|
-
# prototype.class_eval do
|
48
|
-
#
|
49
|
-
# unless method_defined?(method_with_spy)
|
50
|
-
#
|
51
|
-
# define_method method_called do
|
52
|
-
# end
|
53
|
-
#
|
54
|
-
# define_method method_with_spy do |*args, &block|
|
55
|
-
# send(method_called, *args)
|
56
|
-
# send(method_without_spy, *args, &block)
|
57
|
-
# end
|
58
|
-
# alias_method_chain method, :spy
|
59
|
-
# end
|
60
|
-
#
|
61
|
-
# end
|
62
|
-
#
|
63
|
-
# expectation = negate ? :should_not_receive : :should_receive
|
64
|
-
# send(expectation, method_called)
|
65
|
-
# end
|
66
|
-
#
|
67
|
-
private
|
68
|
-
|
69
|
-
def setup_expectation_chain(parts, options = {})
|
70
|
-
obj = self
|
71
|
-
for part in parts
|
72
|
-
if part == parts.last
|
73
|
-
expectation = options[:negate] ? :should_not_receive : :should_receive
|
74
|
-
obj = add_expectation_chain_link(obj, expectation, part)
|
75
|
-
else
|
76
|
-
next_obj = RSpec::Mocks::Mock.new('chain link')
|
77
|
-
add_expectation_chain_link(obj, :stub, part).and_return(next_obj)
|
78
|
-
obj = next_obj
|
79
|
-
end
|
80
|
-
end
|
81
|
-
obj
|
82
|
-
end
|
83
|
-
|
84
|
-
def add_expectation_chain_link(obj, expectation, part)
|
85
|
-
if part.is_a?(Array)
|
86
|
-
obj.send(expectation, part.first).with(*part[1..-1])
|
87
|
-
else
|
88
|
-
obj.send(expectation, part)
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
end
|
93
|
-
|
94
|
-
RSpec::Core::ExampleGroup.class_eval do
|
95
|
-
|
96
|
-
def self.it_should_run_callbacks_in_order(*callbacks)
|
97
|
-
callbacks.push(:ordered => true)
|
98
|
-
it_should_run_callbacks(*callbacks)
|
99
|
-
end
|
100
|
-
|
101
|
-
|
102
|
-
# Improves it_should_behave_like in some ways:
|
103
|
-
# - It scopes the reused examples so #let und #subject does not bleed into the reusing example groups
|
104
|
-
# - It allows to parametrize the reused example group by appending a hash argument.
|
105
|
-
# Every key/value pair in the hash will become a #let variable for the reused example group
|
106
|
-
# - You can call it with a block. It will be available to the reused example group as let(:block)
|
107
|
-
def self.it_should_act_like(shared_example_group, environment = {}, &block)
|
108
|
-
description = "as #{shared_example_group}"
|
109
|
-
description << " (#{environment.inspect})" if environment.present?
|
110
|
-
describe description do
|
111
|
-
environment.each do |name, value|
|
112
|
-
let(name) { value }
|
113
|
-
end
|
114
|
-
let(:block) { block } if block
|
115
|
-
it_should_behave_like(shared_example_group)
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
def self.it_should_run_callbacks(*callbacks)
|
120
|
-
options = callbacks.last.is_a?(Hash) ? callbacks.pop : {}
|
121
|
-
reason = callbacks.pop if callbacks.last.is_a?(String)
|
122
|
-
example_description = description
|
123
|
-
send(:it, ["should run callbacks", callbacks.inspect, ('in order' if options[:ordered]), reason].compact.join(' ')) do
|
124
|
-
# Set expectations
|
125
|
-
callbacks.each do |callback|
|
126
|
-
expectation = subject.should_receive(callback).once
|
127
|
-
expectation.ordered if options[:ordered]
|
128
|
-
end
|
129
|
-
run_state_machine_callbacks_from_prose(example_description) || run_active_record_callbacks_from_prose(example_description)
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
def run_active_record_callbacks_from_prose(prose)
|
134
|
-
hook = prose.split.last.sub(/^#/, '')
|
135
|
-
if hook.sub!(/_on_(create|update)$/, '')
|
136
|
-
condition = "validation_context == :#{$1}"
|
137
|
-
else
|
138
|
-
condition = nil
|
139
|
-
end
|
140
|
-
if hook == 'validate'
|
141
|
-
kind = 'before'
|
142
|
-
action = 'validate'
|
143
|
-
else
|
144
|
-
hook =~ /^(before|after|around)_(.*)$/
|
145
|
-
kind = $1
|
146
|
-
action = $2
|
147
|
-
end
|
148
|
-
# Run all matching callbacks
|
149
|
-
subject.send("_#{action}_callbacks").each do |callback|
|
150
|
-
if callback.kind.to_s == kind && (condition.nil? || callback.options[:if].include?(condition))
|
151
|
-
subject.send callback.filter.to_s.gsub(/[\(,\)]/, '').to_sym
|
152
|
-
end
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
def run_state_machine_callbacks_from_prose(prose)
|
157
|
-
if parts = prose.match(/^(\#\w+) from ([\:\w]+) to ([\:\w]+)$/)
|
158
|
-
name = parts[1].sub(/^#/, '').to_sym
|
159
|
-
from = parts[2].sub(/^:/, '').to_sym
|
160
|
-
to = parts[3].sub(/^:/, '').to_sym
|
161
|
-
transition = StateMachine::Transition.new(subject, subject.class.state_machine, name, from, to)
|
162
|
-
transition.run_callbacks
|
163
|
-
end
|
164
|
-
end
|
165
|
-
|
166
|
-
end
|
167
|
-
|
168
|
-
ActiveRecord::Base.class_eval do
|
169
|
-
|
170
|
-
# Prevents the database from being touched, but still runs all validations.
|
171
|
-
def keep_invalid!
|
172
|
-
errors.stub :empty? => false
|
173
|
-
end
|
174
|
-
|
175
|
-
end
|