gimme 0.2.0 → 0.3.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 (45) hide show
  1. data/Gemfile +3 -2
  2. data/Guardfile +1 -2
  3. data/LICENSE.txt +1 -1
  4. data/README.markdown +1 -1
  5. data/VERSION +1 -1
  6. data/features/basics.feature +66 -0
  7. data/features/matchers.feature +126 -0
  8. data/features/{argument_captors.feature → old/argument_captors.feature} +0 -0
  9. data/features/{gimme_next.feature → old/gimme_next.feature} +0 -0
  10. data/features/{stub_basic.feature → old/stub_basic.feature} +0 -0
  11. data/features/{stub_class_methods.feature → old/stub_class_methods.feature} +0 -1
  12. data/features/{stub_matchers.feature → old/stub_matchers.feature} +0 -0
  13. data/features/{stub_sensible_defaults.feature → old/stub_sensible_defaults.feature} +0 -0
  14. data/features/{unknown_methods.feature → old/unknown_methods.feature} +0 -0
  15. data/features/{verify_matcher_anything.feature → old/verify_matcher_anything.feature} +0 -0
  16. data/features/old/verify_no_args.feature +23 -0
  17. data/features/{verify_with_args.feature → old/verify_with_args.feature} +0 -0
  18. data/features/readme.md +3 -0
  19. data/features/step_definitions/doc_steps.rb +12 -0
  20. data/features/step_definitions/gimme_steps.rb +17 -0
  21. data/features/support/animals.rb +9 -0
  22. data/gimme.gemspec +35 -18
  23. data/lib/gimme.rb +8 -1
  24. data/lib/gimme/dsl.rb +19 -5
  25. data/lib/gimme/ensures_class_method_restoration.rb +18 -0
  26. data/lib/gimme/gives.rb +1 -2
  27. data/lib/gimme/gives_class_methods.rb +5 -29
  28. data/lib/gimme/invocation_store.rb +14 -0
  29. data/lib/gimme/invokes_satisfied_stubbing.rb +18 -10
  30. data/lib/gimme/method_store.rb +21 -0
  31. data/lib/gimme/reset.rb +10 -3
  32. data/lib/gimme/resolves_methods.rb +1 -1
  33. data/lib/gimme/spies_on_class_methods.rb +28 -0
  34. data/lib/gimme/store.rb +38 -0
  35. data/lib/gimme/stubbing_store.rb +17 -0
  36. data/lib/gimme/test_double.rb +13 -10
  37. data/lib/gimme/verifies.rb +10 -6
  38. data/lib/gimme/verifies_class_methods.rb +8 -0
  39. data/spec/gimme/shared_examples/shared_gives_examples.rb +28 -26
  40. data/spec/gimme/shared_examples/shared_verifies_examples.rb +91 -0
  41. data/spec/gimme/spies_on_class_method_spec.rb +58 -0
  42. data/spec/gimme/verifies_class_methods_spec.rb +46 -0
  43. data/spec/gimme/verifies_spec.rb +7 -94
  44. metadata +59 -42
  45. data/features/verify_no_args.feature +0 -18
@@ -1,14 +1,21 @@
1
1
  require 'gimme/test_double'
2
2
  require 'gimme/resolves_methods'
3
3
  require 'gimme/errors'
4
- require 'gimme/gives_class_methods'
5
4
  require 'gimme/gives'
5
+ require 'gimme/gives_class_methods'
6
+ require 'gimme/ensures_class_method_restoration'
6
7
  require 'gimme/verifies'
8
+ require 'gimme/spies_on_class_methods'
9
+ require 'gimme/verifies_class_methods'
7
10
  require 'gimme/matchers'
8
11
  require 'gimme/captor'
9
12
  require 'gimme/invokes_satisfied_stubbing'
10
13
  require 'gimme/reset'
11
14
  require 'gimme/dsl'
15
+ require 'gimme/store'
16
+ require 'gimme/stubbing_store'
17
+ require 'gimme/invocation_store'
18
+ require 'gimme/method_store'
12
19
 
13
20
  require 'gimme/rspec_adapter'
14
21
 
@@ -1,8 +1,8 @@
1
1
  module Gimme
2
2
  module DSL
3
-
3
+
4
4
  # Instantiation
5
-
5
+
6
6
  def gimme(cls=nil)
7
7
  Gimme::TestDouble.new(cls)
8
8
  end
@@ -19,7 +19,7 @@ module Gimme
19
19
  double
20
20
  end
21
21
 
22
-
22
+
23
23
  # Stubbing
24
24
  def give(double)
25
25
  if double.kind_of? Class
@@ -37,7 +37,11 @@ module Gimme
37
37
 
38
38
  # Verification
39
39
  def verify(double,times=1)
40
- Gimme::Verifies.new(double,times)
40
+ if double.kind_of? Class
41
+ Gimme::VerifiesClassMethods.new(double, times)
42
+ else
43
+ Gimme::Verifies.new(double,times)
44
+ end
41
45
  end
42
46
 
43
47
  def verify!(double,times=1)
@@ -45,6 +49,16 @@ module Gimme
45
49
  verify.raises_no_method_error = false
46
50
  verify
47
51
  end
48
-
52
+
53
+ # Spying on class methods
54
+ def spy_on(cls, method)
55
+ SpiesOnClassMethod.new(cls).spy(method)
56
+ end
57
+
58
+ def spy_on!(cls, method)
59
+ spies_on = SpiesOnClassMethod.new(cls)
60
+ spies_on.raises_no_method_error = false
61
+ spies_on.spy(method)
62
+ end
49
63
  end
50
64
  end
@@ -0,0 +1,18 @@
1
+ module Gimme
2
+ class EnsuresClassMethodRestoration
3
+ def initialize(cls)
4
+ @cls = cls
5
+ end
6
+
7
+ def ensure(method)
8
+ meta_class = (class << @cls; self; end)
9
+ Gimme.on_reset do
10
+ if real_method = Gimme.class_methods.get(@cls, method)
11
+ meta_class.instance_eval { define_method method, real_method }
12
+ else
13
+ meta_class.send(:remove_method, method)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -10,8 +10,7 @@ module Gimme
10
10
  def method_missing(sym, *args, &block)
11
11
  sym = ResolvesMethods.new(@double.cls,sym,args).resolve(@raises_no_method_error)
12
12
 
13
- @double.stubbings[sym] ||= {}
14
- @double.stubbings[sym][args] = block if block
13
+ Gimme.stubbings.set(@double, sym, args, block)
15
14
  end
16
15
  end
17
16
 
@@ -5,50 +5,26 @@ module Gimme
5
5
  def initialize(cls)
6
6
  @cls = cls
7
7
  @raises_no_method_error = true
8
- @@stubbings ||= {}
9
8
  end
10
9
 
11
10
  def method_missing(method, *args, &block)
12
11
  cls = @cls
13
- meta_class = meta_for(@cls)
12
+ meta_class = (class << cls; self; end)
14
13
  method = ResolvesMethods.new(meta_class,method,args).resolve(@raises_no_method_error)
15
- hidden_method_name = hidden_name_for(method)
16
14
 
17
- if @cls.respond_to?(method) && !@cls.respond_to?(hidden_method_name)
18
- meta_class.send(:alias_method, hidden_method_name, method)
19
- end
20
-
21
- @@stubbings[cls] ||= {}
22
- @@stubbings[cls][method] ||= {}
23
- @@stubbings[cls][method][args] = block if block
15
+ Gimme.class_methods.set(cls, method)
16
+ Gimme.stubbings.set(cls, method, args, block)
24
17
 
25
18
  #TODO this will be redundantly overwritten
26
19
  meta_class.instance_eval do
27
20
  define_method method do |*actual_args|
28
- InvokesSatisfiedStubbing.new(@@stubbings[cls]).invoke(method, actual_args)
29
- end
30
- end
31
-
32
- Gimme.on_reset do
33
- if cls.respond_to?(hidden_method_name)
34
- meta_class.instance_eval { define_method method, cls.method(hidden_method_name) }
35
- meta_class.send(:remove_method, hidden_method_name)
36
- else
37
- meta_class.send(:remove_method, method)
21
+ InvokesSatisfiedStubbing.new(Gimme.stubbings.get(cls)).invoke(method, actual_args)
38
22
  end
39
- @@stubbings.clear
40
23
  end
41
- end
42
24
 
43
- private
44
-
45
- def meta_for(cls)
46
- (class << cls; self; end)
25
+ EnsuresClassMethodRestoration.new(@cls).ensure(method)
47
26
  end
48
27
 
49
- def hidden_name_for(method)
50
- "__gimme_#{method}"
51
- end
52
28
  end
53
29
 
54
30
  end
@@ -0,0 +1,14 @@
1
+ module Gimme
2
+
3
+ class InvocationStore < Store
4
+
5
+ def increment(double, method, args)
6
+ set(double, method, args, get(double, method, args).to_i.succ)
7
+ end
8
+ end
9
+
10
+ def self.invocations
11
+ @@invocations ||= InvocationStore.new
12
+ end
13
+
14
+ end
@@ -1,11 +1,24 @@
1
1
  module Gimme
2
2
  class InvokesSatisfiedStubbing
3
3
  def initialize(stubbings)
4
- @stubbings = stubbings
4
+ @stubbings = stubbings || {}
5
5
  end
6
6
 
7
7
  def invoke(method, args)
8
- matching_stub_block = nil
8
+ if @stubbings[method] && blk = find_matching_stubbing(method, args)
9
+ blk.call
10
+ elsif method.to_s[-1,1] == '?'
11
+ false
12
+ else
13
+ nil
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def find_matching_stubbing(method, args)
20
+ match = nil
21
+
9
22
  @stubbings[method].each do |stub_args,stub_block|
10
23
  matching = args.size == stub_args.size
11
24
  args.each_index do |i|
@@ -14,16 +27,11 @@ module Gimme
14
27
  break
15
28
  end
16
29
  end
17
- matching_stub_block = stub_block if matching
30
+ match = stub_block if matching
18
31
  end
19
32
 
20
- if matching_stub_block
21
- matching_stub_block.call
22
- elsif method.to_s[-1,1] == '?'
23
- false
24
- else
25
- nil
26
- end
33
+ match
27
34
  end
35
+
28
36
  end
29
37
  end
@@ -0,0 +1,21 @@
1
+ module Gimme
2
+
3
+ class ClassMethodStore < Store
4
+
5
+ def set(cls, method_name)
6
+ id = cls.__id__
7
+ meta = (class << cls; self; end)
8
+ @store[id] ||= {}
9
+
10
+ if meta.method_defined?(method_name) && !@store[id][method_name]
11
+ @store[id][method_name] = cls.method(method_name)
12
+ end
13
+ end
14
+
15
+ end
16
+
17
+ def self.class_methods
18
+ @@class_methods ||= ClassMethodStore.new
19
+ end
20
+
21
+ end
@@ -1,14 +1,21 @@
1
1
  module Gimme
2
2
  @@stuff_to_do_on_reset = []
3
+ @@stuff_to_do_on_every_reset = []
3
4
 
4
- def self.on_reset (&blk)
5
- @@stuff_to_do_on_reset << blk
5
+ def self.on_reset (situation = :once, &blk)
6
+ if situation == :once
7
+ @@stuff_to_do_on_reset << blk
8
+ else
9
+ @@stuff_to_do_on_every_reset << blk
10
+ end
6
11
  end
7
12
 
8
13
  def self.reset
9
14
  @@stuff_to_do_on_reset.delete_if do |stuff|
10
15
  stuff.call
11
- true
16
+ end
17
+ @@stuff_to_do_on_every_reset.each do |stuff|
18
+ stuff.call
12
19
  end
13
20
  end
14
21
  end
@@ -1,7 +1,7 @@
1
1
  module Gimme
2
2
 
3
3
  class ResolvesMethods
4
- def initialize(cls,sym,args)
4
+ def initialize(cls, sym, args=[])
5
5
  @cls = cls
6
6
  @sym = sym
7
7
  @args = args
@@ -0,0 +1,28 @@
1
+ module Gimme
2
+ class SpiesOnClassMethod
3
+ attr_accessor :raises_no_method_error
4
+ def initialize(cls)
5
+ @cls = cls
6
+ @raises_no_method_error = true
7
+ end
8
+
9
+ def spy(method)
10
+ meta_class = (class << @cls; self; end)
11
+ method = ResolvesMethods.new(meta_class, method).resolve(@raises_no_method_error)
12
+
13
+ if meta_class.method_defined? method
14
+ Gimme.class_methods.set(@cls, method)
15
+ meta_class.send(:remove_method, method)
16
+ end
17
+
18
+ cls = @cls
19
+ meta_class.instance_eval do
20
+ define_method method do |*args|
21
+ Gimme.invocations.increment(cls, method, args)
22
+ end
23
+ end
24
+
25
+ EnsuresClassMethodRestoration.new(@cls).ensure(method)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,38 @@
1
+ module Gimme
2
+ class Store
3
+ @store = {}
4
+
5
+ def initialize
6
+ @store = {}
7
+ Gimme.on_reset(:each) do
8
+ @store.clear
9
+ end
10
+ end
11
+
12
+ def reset
13
+ @store = {}
14
+ end
15
+
16
+ def set(double, method, args, content)
17
+ id = double.__id__
18
+ @store[id] ||= {}
19
+ @store[id][method] ||= {}
20
+ @store[id][method][args] = content
21
+ end
22
+
23
+ def get(double, method=nil, args=nil)
24
+ id = double.__id__
25
+ if !method
26
+ @store[id]
27
+ elsif @store[id] && !args
28
+ @store[id][method]
29
+ elsif @store[id] && @store[id][method]
30
+ @store[id][method][args]
31
+ end
32
+ end
33
+
34
+ def clear
35
+ @store.clear
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,17 @@
1
+ module Gimme
2
+
3
+ class StubbingStore < Store
4
+
5
+ def invoke(double, method, args)
6
+ if stubbing = get(double, method, args)
7
+ stubbing.call
8
+ end
9
+ end
10
+
11
+ end
12
+
13
+ def self.stubbings
14
+ @@stubbings ||= StubbingStore.new
15
+ end
16
+
17
+ end
@@ -8,24 +8,27 @@ module Gimme
8
8
 
9
9
  class TestDouble < BlankSlate
10
10
  attr_accessor :cls
11
- attr_accessor :stubbings
12
- attr_reader :invocations
13
11
 
14
12
  def initialize(cls=nil)
15
13
  @cls = cls
16
- @stubbings = {}
17
- @invocations = {}
18
14
  end
19
15
 
20
- def method_missing(sym, *args, &block)
21
- sym = ResolvesMethods.new(self.cls,sym,args).resolve(false)
16
+ def method_missing(method, *args, &block)
17
+ method = ResolvesMethods.new(self.cls, method, args).resolve(false)
18
+ Gimme.invocations.increment(self, method, args)
19
+ InvokesSatisfiedStubbing.new(Gimme.stubbings.get(self)).invoke(method, args)
20
+ end
22
21
 
23
- @invocations[sym] ||= {}
24
- @stubbings[sym] ||= {}
22
+ def hash
23
+ __id__
24
+ end
25
25
 
26
- @invocations[sym][args] = 1 + (@invocations[sym][args]||0)
26
+ def eql?(other)
27
+ __id__ == other.__id__
28
+ end
27
29
 
28
- InvokesSatisfiedStubbing.new(@stubbings).invoke(sym, args)
30
+ def ==(other)
31
+ eql?(other)
29
32
  end
30
33
  end
31
34
 
@@ -8,13 +8,17 @@ module Gimme
8
8
  @raises_no_method_error = true
9
9
  end
10
10
 
11
+ def __gimme__cls
12
+ @double.cls
13
+ end
14
+
11
15
  def method_missing(sym, *args, &block)
12
- sym = ResolvesMethods.new(@double.cls,sym,args).resolve(@raises_no_method_error)
16
+ sym = ResolvesMethods.new(__gimme__cls,sym,args).resolve(@raises_no_method_error)
13
17
 
14
18
  #gosh, this loop sure looks familiar. just like another ugly loop I know. TODO.
15
19
  invoked = 0
16
- if @double.invocations[sym]
17
- @double.invocations[sym].each do |invoke_args,count|
20
+ if Gimme.invocations.get(@double, sym)
21
+ Gimme.invocations.get(@double, sym).each do |invoke_args,count|
18
22
  matching = args.size == invoke_args.size
19
23
  invoke_args.each_index do |i|
20
24
  unless invoke_args[i] == args[i] || (args[i].respond_to?(:matches?) && args[i].matches?(invoke_args[i]))
@@ -27,11 +31,11 @@ module Gimme
27
31
  end
28
32
 
29
33
  if invoked != @times
30
- msg = "expected #{@double.cls.to_s}##{sym} to have been called with arguments #{args}"
31
- if !@double.invocations[sym] || @double.invocations[sym].empty?
34
+ msg = "expected #{__gimme__cls.to_s}##{sym} to have been called with arguments #{args}"
35
+ if !Gimme.invocations.get(@double, sym) || Gimme.invocations.get(@double, sym).empty?
32
36
  msg << "\n but was never called"
33
37
  else
34
- msg = @double.invocations[sym].inject msg do |memo, actual|
38
+ msg = Gimme.invocations.get(@double, sym).inject msg do |memo, actual|
35
39
  memo + "\n was actually called #{actual[1]} times with arguments #{actual[0]}"
36
40
  end
37
41
  end
@@ -0,0 +1,8 @@
1
+ module Gimme
2
+
3
+ class VerifiesClassMethods < Verifies
4
+ def __gimme__cls
5
+ (class << @double; self; end)
6
+ end
7
+ end
8
+ end