stubborn 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -63,13 +63,16 @@ You can control which methods to check with the options :except and :only. Both
63
63
  Stubborn.should_be_stubbed(Api.new, :except => :safe_method)
64
64
  Stubborn.should_be_stubbed(Api.new, :only => [:slow_method, :external_service])
65
65
 
66
+ You can also filter on instance methods. All you need to do is using the :instance_methods key to scope your :only or :except methods like this:
67
+
68
+ Stubborn.should_be_stubbed(User, :instance_methods => {:only => :save})
69
+
66
70
  == Installation
67
71
 
68
- sudo gem install stubborn -s http://gemcutter.org
72
+ sudo gem install stubborn
69
73
 
70
74
  == To Do
71
75
 
72
- * Filters so that you can choose which methods to include or exclude
73
76
  * Give stub suggestions for more test frameworks. It currently shows only rspec syntax
74
77
 
75
78
  == Collaborate
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.0
1
+ 0.3.0
@@ -1,70 +1,21 @@
1
- require 'suggesters/rspec_suggester'
2
- require 'missed_stub_exception'
1
+ require 'stubborn/suggesters/rspec_suggester'
2
+ require 'stubborn/proxy_for_instance'
3
+ require 'stubborn/proxy_for_module'
3
4
 
4
5
  module Stubborn
5
6
  def self.should_be_stubbed(object, options = {})
6
- if object.is_a?(Class)
7
- ProxyForClass.new(object, options)
7
+ if object.is_a?(Module)
8
+ ProxyForModule.new(object, options)
8
9
  else
9
10
  ProxyForInstance.new(object, options)
10
11
  end
11
12
  end
12
13
 
13
- class ProxyForInstance
14
- instance_methods.each do |sym|
15
- undef_method(sym) unless sym.to_s =~ /__/ || sym.to_s == "send"
16
- end
17
-
18
- def initialize(proxy_target, options = {})
19
- @proxy_target = proxy_target
20
-
21
- options[:except] = [options[:except]].flatten.compact.map{|m| m.to_s}
22
- options[:only] = [options[:only]].flatten.compact.map{|m| m.to_s}
23
- options = {:class => proxy_target.class}.merge(options)
24
- @label = options[:label]
25
- @class = options[:class]
26
- @methods_to_skip = ["respond_to?", "is_a?", "kind_of?", "equal?", "eql?", "==", "==="] + options[:except]
27
- @only_methods = options[:only]
28
- end
29
-
30
- def class
31
- @class
32
- end
33
-
34
- def method_missing(method_name, *args, &block)
35
- were_we_already_processing_a_missed_stub = Thread.current["inside_missed_stub"]
36
- Thread.current["inside_missed_stub"] = true
37
- result = @proxy_target.send(method_name, *args, &block)
38
- return result if !@only_methods.include?(method_name.to_s) && !@only_methods.empty? || @methods_to_skip.include?(method_name.to_s) || were_we_already_processing_a_missed_stub
39
- raise MissedStubException.new(@label || @proxy_target, method_name, args, result, Suggesters::RSpecSuggester)
40
- ensure
41
- Thread.current["inside_missed_stub"] = false unless were_we_already_processing_a_missed_stub
42
- end
14
+ def self.suggester=(suggester)
15
+ @suggester = suggester
43
16
  end
44
17
 
45
- class ProxyForClass < ProxyForInstance
46
- def initialize(proxy_target, options = {})
47
- super
48
- redefine_const(proxy_target, self) unless proxy_target.name.strip.empty?
49
- end
50
-
51
- def name
52
- @proxy_target.name
53
- end
54
-
55
- def new(*args, &block)
56
- new_instance = @proxy_target.new(*args, &block)
57
- ProxyForInstance.new(new_instance, :class => self)
58
- end
59
-
60
- private
61
- def redefine_const(const, value)
62
- const_parts = const.name.split('::')
63
- const_name = const_parts.pop
64
- parent_const = const_parts.inject(Object){|a, c| a.const_get(c) }
65
-
66
- parent_const.__send__(:remove_const, const_name) if parent_const.const_defined?(const_name)
67
- parent_const.const_set(const_name, value)
68
- end
18
+ def self.suggester
19
+ @suggester ||= Suggesters::RSpecSuggester
69
20
  end
70
21
  end
@@ -0,0 +1,11 @@
1
+ module Stubborn
2
+ class MissedStubException < RuntimeError
3
+ def initialize(object_or_label, method_name, args, result, suggester)
4
+ @suggestions = suggester.suggestions(object_or_label, method_name, args, result)
5
+ end
6
+
7
+ def message
8
+ "You've missed adding a stub. Consider this suggestion#{@suggestions.size > 1 ? "s" : ""}:\n#{@suggestions.join("\n")}"
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,46 @@
1
+ require 'stubborn/missed_stub_exception'
2
+
3
+ module Stubborn
4
+ class ProxyForInstance
5
+ instance_methods.each do |sym|
6
+ undef_method(sym) unless sym.to_s =~ /__/ || sym.to_s == "send"
7
+ end
8
+
9
+ def initialize(proxy_target, options = {})
10
+ @proxy_target = proxy_target
11
+
12
+ options[:except] = [options[:except]].flatten.compact.map{|m| m.to_s}
13
+ options[:only] = [options[:only]].flatten.compact.map{|m| m.to_s}
14
+ options = {:class => proxy_target.class}.merge(options)
15
+ @label = options[:label]
16
+ @class = options[:class]
17
+ @methods_to_skip = ["respond_to?", "is_a?", "kind_of?", "equal?", "eql?", "==", "==="] + options[:except]
18
+ @only_methods = options[:only]
19
+ @instance_methods = options[:instance_methods]
20
+ end
21
+
22
+ def class
23
+ @class
24
+ end
25
+
26
+ def method_missing(method_name, *args, &block)
27
+ were_we_already_processing_a_missed_stub = Thread.current["inside_missed_stub"]
28
+ Thread.current["inside_missed_stub"] = true
29
+ result = begin
30
+ @proxy_target.send(method_name, *args, &block)
31
+ rescue => e
32
+ e
33
+ end
34
+
35
+ return result if !@only_methods.include?(method_name.to_s) && !@only_methods.empty? || @methods_to_skip.include?(method_name.to_s) || were_we_already_processing_a_missed_stub
36
+ raise_missed_stub_exception(method_name, args, result)
37
+ ensure
38
+ Thread.current["inside_missed_stub"] = false unless were_we_already_processing_a_missed_stub
39
+ end
40
+
41
+ private
42
+ def raise_missed_stub_exception(method_name, args, result)
43
+ raise MissedStubException.new(@label || @proxy_target, method_name, args, result, Stubborn.suggester)
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,35 @@
1
+ module Stubborn
2
+ class ProxyForModule < ProxyForInstance
3
+ def initialize(proxy_target, options = {})
4
+ super
5
+ redefine_const(proxy_target, self) unless proxy_target.name.strip.empty?
6
+ end
7
+
8
+ def name
9
+ @proxy_target.name
10
+ end
11
+
12
+ def new(*args, &block)
13
+ new_instance = @proxy_target.new(*args, &block)
14
+ raise_missed_stub_exception("new", args, new_instance) if @only_methods.include?("new") && !@methods_to_skip.include?("new")
15
+ options = {:class => self}
16
+
17
+ if @instance_methods
18
+ options[:only] = @instance_methods[:only]
19
+ options[:except] = @instance_methods[:except]
20
+ end
21
+
22
+ ProxyForInstance.new(new_instance, options)
23
+ end
24
+
25
+ private
26
+ def redefine_const(const, value)
27
+ const_parts = const.name.split('::')
28
+ const_name = const_parts.pop
29
+ parent_const = const_parts.inject(Object){|a, c| a.const_get(c) }
30
+
31
+ parent_const.__send__(:remove_const, const_name) if parent_const.const_defined?(const_name)
32
+ parent_const.const_set(const_name, value)
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,38 @@
1
+ module Stubborn
2
+ module Suggesters
3
+ module RSpecSuggester
4
+ def self.suggestions(object_or_label, method_name, args, result_object)
5
+ object_label = object_or_label.respond_to?(:to_str) ? object_or_label : friendly_name(object_or_label)
6
+ args = args.map{|a| friendly_name(a)}.join(", ")
7
+ result = friendly_name(result_object)
8
+
9
+ with = args.strip.empty? ? nil : ".with(#{args})"
10
+
11
+ return_method = if result_object.is_a?(Exception)
12
+ ".and_raise(#{result_object.class})"
13
+ elsif result_object.nil?
14
+ nil
15
+ else
16
+ ".and_return(#{result})"
17
+ end
18
+
19
+ suggestions = []
20
+ suggestions << "#{object_label}.stub!(:#{method_name})#{with}#{return_method}"
21
+ suggestions << "#{object_label}.stub!(:#{method_name})#{return_method}"
22
+ suggestions.uniq
23
+ end
24
+
25
+ private
26
+ def self.friendly_name(object)
27
+ return "\"#{object}\"" if object.respond_to?(:to_str)
28
+ return object.inspect if object.respond_to?(:to_int) || object.is_a?(Hash) || object.nil? || object == true || object == false
29
+
30
+ if object.is_a?(Class)
31
+ object.name
32
+ else
33
+ "#{object.class.name.downcase.gsub(/\W+/, '_')}_instance"
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -1,12 +1,15 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
1
4
  # -*- encoding: utf-8 -*-
2
5
 
3
6
  Gem::Specification.new do |s|
4
7
  s.name = %q{stubborn}
5
- s.version = "0.2.0"
8
+ s.version = "0.3.0"
6
9
 
7
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
11
  s.authors = ["Daniel Cadenas"]
9
- s.date = %q{2009-10-05}
12
+ s.date = %q{2010-02-17}
10
13
  s.email = %q{dcadenas@gmail.com}
11
14
  s.extra_rdoc_files = [
12
15
  "LICENSE",
@@ -19,9 +22,11 @@ Gem::Specification.new do |s|
19
22
  "README.rdoc",
20
23
  "Rakefile",
21
24
  "VERSION",
22
- "lib/missed_stub_exception.rb",
23
25
  "lib/stubborn.rb",
24
- "lib/suggesters/rspec_suggester.rb",
26
+ "lib/stubborn/missed_stub_exception.rb",
27
+ "lib/stubborn/proxy_for_instance.rb",
28
+ "lib/stubborn/proxy_for_module.rb",
29
+ "lib/stubborn/suggesters/rspec_suggester.rb",
25
30
  "stubborn.gemspec",
26
31
  "test/stubborn_method_filtering_test.rb",
27
32
  "test/stubborn_test.rb",
@@ -32,7 +37,7 @@ Gem::Specification.new do |s|
32
37
  s.rdoc_options = ["--charset=UTF-8"]
33
38
  s.require_paths = ["lib"]
34
39
  s.rubyforge_project = %q{stubborn}
35
- s.rubygems_version = %q{1.3.3}
40
+ s.rubygems_version = %q{1.3.5}
36
41
  s.summary = %q{A gem to help you find your missing stubs}
37
42
  s.test_files = [
38
43
  "test/stubborn_method_filtering_test.rb",
@@ -51,3 +56,4 @@ Gem::Specification.new do |s|
51
56
  else
52
57
  end
53
58
  end
59
+
@@ -1,8 +1,10 @@
1
1
  require 'test_helper'
2
2
 
3
- module FilteringTest; end
3
+ module FilteringTest
4
+ class Error < StandardError; end
5
+ end
4
6
 
5
- def reset_FilteringTest_api_class
7
+ def reset_filtering_test_api_class
6
8
  name = "Api"
7
9
  FilteringTest.send(:remove_const, name) if FilteringTest.const_defined?(name)
8
10
  klass = Class.new do
@@ -14,6 +16,10 @@ def reset_FilteringTest_api_class
14
16
  "instance_method_2 called"
15
17
  end
16
18
 
19
+ def instance_method_3
20
+ raise FilteringTest::Error
21
+ end
22
+
17
23
  def self.class_method_1
18
24
  "class_method_1 called"
19
25
  end
@@ -27,51 +33,98 @@ end
27
33
 
28
34
  Expectations do
29
35
  expect "instance_method_1 called" do
30
- reset_FilteringTest_api_class
31
- api = FilteringTest::Api.new
36
+ reset_filtering_test_api_class
37
+ api = FilteringTest::Api.new
32
38
  api = Stubborn.should_be_stubbed(api, :except => [:instance_method_1, :instance_method_2])
33
39
  api.instance_method_1
34
40
  end
35
41
 
36
42
  expect "instance_method_2 called" do
37
- reset_FilteringTest_api_class
38
- api = FilteringTest::Api.new
43
+ reset_filtering_test_api_class
44
+ api = FilteringTest::Api.new
39
45
  api = Stubborn.should_be_stubbed(api, :except => [:instance_method_1, :instance_method_2])
40
46
  api.instance_method_2
41
47
  end
42
48
 
43
49
  expect Stubborn::MissedStubException do
44
- reset_FilteringTest_api_class
45
- api = FilteringTest::Api.new
50
+ reset_filtering_test_api_class
51
+ api = FilteringTest::Api.new
46
52
  api = Stubborn.should_be_stubbed(api, :except => :instance_method_1)
47
53
  api.instance_method_2
48
54
  end
49
55
 
50
56
  expect "instance_method_2 called" do
51
- reset_FilteringTest_api_class
52
- api = FilteringTest::Api.new
57
+ reset_filtering_test_api_class
58
+ api = FilteringTest::Api.new
53
59
  api = Stubborn.should_be_stubbed(api, :only => :instance_method_1)
54
60
  api.instance_method_2
55
61
  end
56
62
 
57
63
  expect Stubborn::MissedStubException do
58
- reset_FilteringTest_api_class
59
- api = FilteringTest::Api.new
64
+ reset_filtering_test_api_class
65
+ api = FilteringTest::Api.new
60
66
  api = Stubborn.should_be_stubbed(api, :only => :instance_method_1)
61
67
  api.instance_method_1
62
68
  end
63
69
 
64
70
  expect Stubborn::MissedStubException do
65
- reset_FilteringTest_api_class
66
- api = FilteringTest::Api.new
71
+ reset_filtering_test_api_class
72
+ api = FilteringTest::Api.new
67
73
  api = Stubborn.should_be_stubbed(api, :only => [:instance_method_1, :instance_method_2])
68
74
  api.instance_method_1
69
75
  end
70
76
 
71
77
  expect Stubborn::MissedStubException do
72
- reset_FilteringTest_api_class
73
- api = FilteringTest::Api.new
78
+ reset_filtering_test_api_class
79
+ api = FilteringTest::Api.new
74
80
  api = Stubborn.should_be_stubbed(api, :only => [:instance_method_1, :instance_method_2])
75
81
  api.instance_method_2
76
82
  end
83
+
84
+ expect Stubborn::MissedStubException do
85
+ reset_filtering_test_api_class
86
+ Stubborn.should_be_stubbed(FilteringTest::Api, :only => :new)
87
+ FilteringTest::Api.new
88
+ end
89
+
90
+ expect Stubborn::MissedStubException do
91
+ reset_filtering_test_api_class
92
+ Stubborn.should_be_stubbed(FilteringTest::Api, :instance_methods => {:only => :instance_method_1})
93
+ api = FilteringTest::Api.new
94
+ api.instance_method_1
95
+ end
96
+
97
+ expect "instance_method_2 called" do
98
+ reset_filtering_test_api_class
99
+ Stubborn.should_be_stubbed(FilteringTest::Api, :instance_methods => {:only => :instance_method_1})
100
+ api = FilteringTest::Api.new
101
+ api.instance_method_2
102
+ end
103
+
104
+ expect Stubborn::MissedStubException do
105
+ reset_filtering_test_api_class
106
+ Stubborn.should_be_stubbed(FilteringTest::Api, :instance_methods => {:except => :instance_method_1})
107
+ api = FilteringTest::Api.new
108
+ api.instance_method_2
109
+ end
110
+
111
+ expect "instance_method_1 called" do
112
+ reset_filtering_test_api_class
113
+ Stubborn.should_be_stubbed(FilteringTest::Api, :instance_methods => {:except => :instance_method_1})
114
+ api = FilteringTest::Api.new
115
+ api.instance_method_1
116
+ end
117
+
118
+ expect Stubborn::MissedStubException do
119
+ reset_filtering_test_api_class
120
+ Stubborn.should_be_stubbed(FilteringTest::Api)
121
+ api = FilteringTest::Api.new
122
+ api.instance_method_1
123
+ end
124
+
125
+ expect Stubborn::MissedStubException do
126
+ reset_filtering_test_api_class
127
+ api = Stubborn.should_be_stubbed(FilteringTest::Api.new)
128
+ api.instance_method_3
129
+ end
77
130
  end
@@ -4,7 +4,3 @@ require 'expectations'
4
4
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
5
5
  $LOAD_PATH.unshift(File.dirname(__FILE__))
6
6
  require 'stubborn'
7
-
8
- class Test::Unit::TestCase
9
- end
10
-
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stubborn
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Cadenas
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-10-05 00:00:00 -02:00
12
+ date: 2010-02-17 00:00:00 -02:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -29,9 +29,11 @@ files:
29
29
  - README.rdoc
30
30
  - Rakefile
31
31
  - VERSION
32
- - lib/missed_stub_exception.rb
33
32
  - lib/stubborn.rb
34
- - lib/suggesters/rspec_suggester.rb
33
+ - lib/stubborn/missed_stub_exception.rb
34
+ - lib/stubborn/proxy_for_instance.rb
35
+ - lib/stubborn/proxy_for_module.rb
36
+ - lib/stubborn/suggesters/rspec_suggester.rb
35
37
  - stubborn.gemspec
36
38
  - test/stubborn_method_filtering_test.rb
37
39
  - test/stubborn_test.rb
@@ -61,7 +63,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
61
63
  requirements: []
62
64
 
63
65
  rubyforge_project: stubborn
64
- rubygems_version: 1.3.3
66
+ rubygems_version: 1.3.5
65
67
  signing_key:
66
68
  specification_version: 3
67
69
  summary: A gem to help you find your missing stubs
@@ -1,27 +0,0 @@
1
- module Stubborn
2
- class MissedStubException < RuntimeError
3
- def initialize(object_or_label, method_name, args, result, suggester)
4
- object_label = object_or_label.respond_to?(:to_str) ? object_or_label : friendly_name(object_or_label)
5
- args = args.map{|a| friendly_name(a)}.join(", ")
6
- result = friendly_name(result)
7
-
8
- @suggestions = suggester.suggestions(object_label, method_name, args, result)
9
- end
10
-
11
- def message
12
- "You've missed adding a stub. Consider this suggestion#{@suggestions.size > 1 ? "s" : ""}:\n#{@suggestions.join("\n")}"
13
- end
14
-
15
- private
16
- def friendly_name(object)
17
- return "\"#{object}\"" if object.respond_to?(:to_str)
18
- return object.inspect if object.respond_to?(:to_int) || object.is_a?(Hash) || object.nil? || object == true || object == false
19
-
20
- if object.is_a?(Class)
21
- object.name
22
- else
23
- "#{object.class.name.downcase.gsub(/\W+/, '_')}_instance"
24
- end
25
- end
26
- end
27
- end
@@ -1,14 +0,0 @@
1
- module Stubborn
2
- module Suggesters
3
- module RSpecSuggester
4
- def self.suggestions(object_label, method_name, args, result)
5
- with = args.strip.empty? ? nil : ".with(#{args})"
6
- and_return = result == "nil" ? nil : ".and_return(#{result})"
7
- suggestions = []
8
- suggestions << "#{object_label}.stub!(:#{method_name})#{with}#{and_return}"
9
- suggestions << "#{object_label}.stub!(:#{method_name})#{and_return}"
10
- suggestions.uniq
11
- end
12
- end
13
- end
14
- end