cross-stub 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/README.rdoc +21 -5
- data/Rakefile +27 -1
- data/VERSION +1 -1
- data/cross-stub.gemspec +11 -6
- data/lib/cross-stub/pseudo_class.rb +28 -8
- data/lib/cross-stub/stub_helpers.rb +5 -5
- data/rails_generators/cross_stub/cross_stub_generator.rb +1 -1
- data/spec/cross-stub/clearing_stubs_spec.rb +8 -0
- data/spec/cross-stub/creating_stubs_spec.rb +6 -0
- data/spec/cross-stub/getting_along_with_mocha_spec.rb +27 -0
- data/spec/helpers.rb +30 -12
- data/spec/spec_helper.rb +3 -2
- metadata +15 -3
data/.gitignore
CHANGED
data/README.rdoc
CHANGED
@@ -14,11 +14,23 @@ It's hosted on gemcutter.org.
|
|
14
14
|
|
15
15
|
== Setting Up
|
16
16
|
|
17
|
-
If you are using rails, you are in luck:
|
17
|
+
#1. If you are using rails, you are in luck:
|
18
18
|
|
19
19
|
$ ./script/generate cucumber
|
20
20
|
$ ./script/generate cross_stub
|
21
21
|
|
22
|
+
#2. Even if you are using something else, no worries:
|
23
|
+
|
24
|
+
# In the test setup method:
|
25
|
+
CrossStub.setup :file => <CACHE_FILE_PATH>
|
26
|
+
|
27
|
+
# In the test teardown method:
|
28
|
+
CrossStub.clear
|
29
|
+
|
30
|
+
# Find an entry point in your target application, eg. in a server, the
|
31
|
+
# point where all request handling starts:
|
32
|
+
CrossStub.refresh :file => <CACHE_FILE_PATH>
|
33
|
+
|
22
34
|
== Using It
|
23
35
|
|
24
36
|
Using cross-stub is simple:
|
@@ -70,16 +82,20 @@ The above fails as a result of undefined variable/method 'something', to workaro
|
|
70
82
|
|
71
83
|
== Caveats
|
72
84
|
|
73
|
-
1. Cross-stub uses ruby's Marshal class to dump & load the stubs, thus it has the same limitations as Marshal
|
85
|
+
#1. Cross-stub uses ruby's Marshal class to dump & load the stubs, thus it has the same limitations as Marshal
|
86
|
+
|
87
|
+
#2. Cross-stub only supports stubbing of class methods, since it makes no sense to do cross process stubbing of instances
|
88
|
+
|
89
|
+
== TODO(s)
|
74
90
|
|
75
|
-
|
91
|
+
#1. We can possibly use Hijack (http://github.com/ileitch/hijack) within the current test process to trigger refreshing of stubs in the other process, thus using cross-stub no longer requires changing of any existing application code to insert CrossStub.refresh(...). This has in fact been our original intended strategy, however, we have not been able to do so as Hijack currently hangs on starting up on our development machines.
|
76
92
|
|
77
93
|
== Contacts
|
78
94
|
|
79
95
|
Written 2009 by:
|
80
96
|
|
81
|
-
1. NgTzeYang, contact ngty77[at]gmail.com or http://github.com/ngty
|
97
|
+
#1. NgTzeYang, contact ngty77[at]gmail.com or http://github.com/ngty
|
82
98
|
|
83
|
-
2. WongLiangZan, contact liangzan[at]gmail.com or http://github.com/liangzan
|
99
|
+
#2. WongLiangZan, contact liangzan[at]gmail.com or http://github.com/liangzan
|
84
100
|
|
85
101
|
Released under the MIT license
|
data/Rakefile
CHANGED
@@ -10,7 +10,8 @@ begin
|
|
10
10
|
gem.email = "ngty77@gmail.com"
|
11
11
|
gem.homepage = "http://github.com/ngty/cross-stub"
|
12
12
|
gem.authors = ["NgTzeYang"]
|
13
|
-
gem.add_development_dependency "bacon", ">= 0"
|
13
|
+
gem.add_development_dependency "bacon", ">= 0.0.0"
|
14
|
+
gem.add_development_dependency "eventmachine", ">= 0.0.0"
|
14
15
|
gem.add_dependency "ParseTree", "= 3.0.4"
|
15
16
|
gem.add_dependency "ruby2ruby", "= 1.2.4"
|
16
17
|
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
@@ -41,6 +42,31 @@ end
|
|
41
42
|
|
42
43
|
task :spec => :check_dependencies
|
43
44
|
|
45
|
+
begin
|
46
|
+
require 'reek/adapters/rake_task'
|
47
|
+
Reek::RakeTask.new do |t|
|
48
|
+
t.fail_on_error = true
|
49
|
+
t.verbose = false
|
50
|
+
t.source_files = 'lib/**/*.rb'
|
51
|
+
end
|
52
|
+
rescue LoadError
|
53
|
+
task :reek do
|
54
|
+
abort "Reek is not available. In order to run reek, you must: sudo gem install reek"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
begin
|
59
|
+
require 'roodi'
|
60
|
+
require 'roodi_task'
|
61
|
+
RoodiTask.new do |t|
|
62
|
+
t.verbose = false
|
63
|
+
end
|
64
|
+
rescue LoadError
|
65
|
+
task :roodi do
|
66
|
+
abort "Roodi is not available. In order to run roodi, you must: sudo gem install roodi"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
44
70
|
task :default => :spec
|
45
71
|
|
46
72
|
require 'rake/rdoctask'
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.2
|
data/cross-stub.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{cross-stub}
|
8
|
-
s.version = "0.1.
|
8
|
+
s.version = "0.1.2"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["NgTzeYang"]
|
12
|
-
s.date = %q{2009-12-
|
12
|
+
s.date = %q{2009-12-16}
|
13
13
|
s.description = %q{}
|
14
14
|
s.email = %q{ngty77@gmail.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -37,7 +37,8 @@ Gem::Specification.new do |s|
|
|
37
37
|
"spec/cross-stub/creating_stubs_spec.rb",
|
38
38
|
"spec/cross-stub/stubbing_error_spec.rb",
|
39
39
|
"spec/helpers.rb",
|
40
|
-
"spec/spec_helper.rb"
|
40
|
+
"spec/spec_helper.rb",
|
41
|
+
"tmp/.dummy"
|
41
42
|
]
|
42
43
|
s.homepage = %q{http://github.com/ngty/cross-stub}
|
43
44
|
s.rdoc_options = ["--charset=UTF-8"]
|
@@ -46,6 +47,7 @@ Gem::Specification.new do |s|
|
|
46
47
|
s.summary = %q{Simple cross process stubbing}
|
47
48
|
s.test_files = [
|
48
49
|
"spec/spec_helper.rb",
|
50
|
+
"spec/cross-stub/getting_along_with_mocha_spec.rb",
|
49
51
|
"spec/cross-stub/clearing_stubs_spec.rb",
|
50
52
|
"spec/cross-stub/stubbing_error_spec.rb",
|
51
53
|
"spec/cross-stub/creating_stubs_spec.rb",
|
@@ -57,16 +59,19 @@ Gem::Specification.new do |s|
|
|
57
59
|
s.specification_version = 3
|
58
60
|
|
59
61
|
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
60
|
-
s.add_development_dependency(%q<bacon>, [">= 0"])
|
62
|
+
s.add_development_dependency(%q<bacon>, [">= 0.0.0"])
|
63
|
+
s.add_development_dependency(%q<eventmachine>, [">= 0.0.0"])
|
61
64
|
s.add_runtime_dependency(%q<ParseTree>, ["= 3.0.4"])
|
62
65
|
s.add_runtime_dependency(%q<ruby2ruby>, ["= 1.2.4"])
|
63
66
|
else
|
64
|
-
s.add_dependency(%q<bacon>, [">= 0"])
|
67
|
+
s.add_dependency(%q<bacon>, [">= 0.0.0"])
|
68
|
+
s.add_dependency(%q<eventmachine>, [">= 0.0.0"])
|
65
69
|
s.add_dependency(%q<ParseTree>, ["= 3.0.4"])
|
66
70
|
s.add_dependency(%q<ruby2ruby>, ["= 1.2.4"])
|
67
71
|
end
|
68
72
|
else
|
69
|
-
s.add_dependency(%q<bacon>, [">= 0"])
|
73
|
+
s.add_dependency(%q<bacon>, [">= 0.0.0"])
|
74
|
+
s.add_dependency(%q<eventmachine>, [">= 0.0.0"])
|
70
75
|
s.add_dependency(%q<ParseTree>, ["= 3.0.4"])
|
71
76
|
s.add_dependency(%q<ruby2ruby>, ["= 1.2.4"])
|
72
77
|
end
|
@@ -23,11 +23,27 @@ module CrossStub
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def replace_method(method, value_or_code)
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
26
|
+
status = backup_method(method)
|
27
|
+
@klass.instance_eval \
|
28
|
+
"#{value_or_code}" =~ /^def / ? value_or_code :
|
29
|
+
%\def #{method}; Marshal.load(%|#{Marshal.dump(value_or_code)}|) ; end\
|
30
|
+
status
|
31
|
+
end
|
32
|
+
|
33
|
+
def revert_method(method)
|
34
|
+
new_name = before_stubbing_method_name(method)
|
35
|
+
@metaclass.instance_eval("alias_method :#{method}, :#{new_name}") rescue nil
|
36
|
+
remove_method(new_name)
|
37
|
+
end
|
38
|
+
|
39
|
+
def backup_method(method)
|
40
|
+
if @klass.respond_to?(method)
|
41
|
+
!@klass.respond_to?(new_name = before_stubbing_method_name(method)) &&
|
42
|
+
@metaclass.instance_eval("alias_method :#{new_name}, :#{method}")
|
43
|
+
true
|
44
|
+
else
|
45
|
+
false
|
46
|
+
end
|
31
47
|
end
|
32
48
|
|
33
49
|
def remove_method(method)
|
@@ -37,11 +53,15 @@ module CrossStub
|
|
37
53
|
def replace_methods(&blk)
|
38
54
|
(tmp = BlankObject.new).__instance_eval__(&blk)
|
39
55
|
methods_in_block = tmp.__methods__ - BlankObject.new.__methods__
|
40
|
-
|
41
|
-
memo.merge(method =>
|
56
|
+
is_method_implemented_flags = methods_in_block.inject({}) do |memo, method|
|
57
|
+
memo.merge(method => backup_method(method))
|
42
58
|
end
|
43
59
|
@klass.instance_eval(&blk)
|
44
|
-
|
60
|
+
is_method_implemented_flags
|
61
|
+
end
|
62
|
+
|
63
|
+
def before_stubbing_method_name(method)
|
64
|
+
:"__#{method}_before_xstubbing"
|
45
65
|
end
|
46
66
|
|
47
67
|
end
|
@@ -36,7 +36,7 @@ module CrossStub
|
|
36
36
|
cache.each do |klass, hash|
|
37
37
|
pk = PseudoClass.new(klass)
|
38
38
|
hash.each do |method, codes|
|
39
|
-
codes[:before] ? pk.
|
39
|
+
codes[:before] ? pk.revert_method(method) : pk.remove_method(method)
|
40
40
|
end
|
41
41
|
end
|
42
42
|
end
|
@@ -44,8 +44,8 @@ module CrossStub
|
|
44
44
|
def create_stub_from_hash(pk, cache, hash)
|
45
45
|
hash.inject(cache) do |cache, args|
|
46
46
|
method, value = args
|
47
|
-
|
48
|
-
cache[method] ||= {:before =>
|
47
|
+
is_method_implemented = pk.replace_method(method, value)
|
48
|
+
cache[method] ||= {:before => is_method_implemented}
|
49
49
|
cache[method][:after] = pk.method_code(method)
|
50
50
|
cache
|
51
51
|
end
|
@@ -53,8 +53,8 @@ module CrossStub
|
|
53
53
|
|
54
54
|
def create_stub_from_block(pk, cache, &blk)
|
55
55
|
pk.replace_methods(&blk).inject(cache) do |cache, args|
|
56
|
-
method,
|
57
|
-
cache[method] ||= {:before =>
|
56
|
+
method, is_method_implemented = args
|
57
|
+
cache[method] ||= {:before => is_method_implemented}
|
58
58
|
cache[method][:after] = pk.method_code(method)
|
59
59
|
cache
|
60
60
|
end
|
@@ -4,7 +4,7 @@ class CrossStubGenerator < Rails::Generator::Base
|
|
4
4
|
record do |m|
|
5
5
|
m.file 'config/initializers/cross-stub.rb', 'config/initializers/cross-stub.rb'
|
6
6
|
m.file 'features/support/cross-stub.rb', 'features/support/cross-stub.rb'
|
7
|
-
m.gsub_file 'config/environments/cucumber.rb', /\z/, "config.gem 'cross-stub', :version => '>=0.1.
|
7
|
+
m.gsub_file 'config/environments/cucumber.rb', /\z/, "config.gem 'cross-stub', :version => '>=0.1.2'\n"
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
@@ -93,6 +93,14 @@ describe 'Clearing Stubs' do
|
|
93
93
|
should.raise(NoMethodError) { @get_value['AnyClass.say_hello'] }
|
94
94
|
end
|
95
95
|
|
96
|
+
it "should clear for method not implemented in ruby and return original value for #{mode} process" do
|
97
|
+
Time.xstub(:now => 'abc')
|
98
|
+
CrossStub.clear
|
99
|
+
value = nil
|
100
|
+
should.not.raise(NoMethodError) { value = @get_value['Time.now'] }
|
101
|
+
value.should.not.equal 'abc'
|
102
|
+
end
|
103
|
+
|
96
104
|
end
|
97
105
|
|
98
106
|
end
|
@@ -72,6 +72,12 @@ describe 'Creating Stubs' do
|
|
72
72
|
@get_value['AnyClass.do_action.i.say'].should.equal 'i say hello'
|
73
73
|
end
|
74
74
|
|
75
|
+
it "should create for method not implemented in ruby for #{mode} process" do
|
76
|
+
now = Time.now - 365*60*60*24
|
77
|
+
Time.xstub(:now => now)
|
78
|
+
@get_value['Time.now'].should.equal now
|
79
|
+
end
|
80
|
+
|
75
81
|
# it "should create with block that takes argument(s) for #{mode} process" do
|
76
82
|
# # a, b = 1, 2
|
77
83
|
# # AnyClass.xstub do |a, b|
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper.rb'
|
2
|
+
|
3
|
+
describe 'Getting Along with MOCHA' do
|
4
|
+
|
5
|
+
behaves_like 'has standard setup'
|
6
|
+
|
7
|
+
%w{current other}.each do |mode|
|
8
|
+
|
9
|
+
behaves_like "has #{mode} process setup"
|
10
|
+
|
11
|
+
it "should work for :stubbing_instance_methods_on_real_objects in #{mode} process" do
|
12
|
+
AnyClass.xstub(:something => stub(:to_s => 'hello')) do
|
13
|
+
def say_hello ; %\i say #{something}\ ; end
|
14
|
+
end
|
15
|
+
@get_value['AnyClass.say_hello'].should.equal 'i say hello'
|
16
|
+
end
|
17
|
+
|
18
|
+
# it "should work for :traditional_mocking in #{mode} process" do
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# it "should work for :shortcuts in #{mode} process" do
|
22
|
+
# end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
data/spec/helpers.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'eventmachine'
|
3
3
|
|
4
|
-
$cache_file = '
|
4
|
+
$cache_file = File.join(File.dirname(__FILE__), '..', 'tmp', 'stubbing.cache')
|
5
|
+
$log_file = File.join(File.dirname(__FILE__), '..', 'tmp', 'echoserver.log')
|
6
|
+
$sleep_time = 1.5 # may need to increase this depending on ur machine's prowess
|
5
7
|
|
6
8
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
7
9
|
require 'cross-stub'
|
@@ -28,9 +30,9 @@ module EchoClient
|
|
28
30
|
address, port = EchoServer::ADDRESS, EchoServer::PORT
|
29
31
|
EventMachine::run do
|
30
32
|
(EventMachine::connect(address, port, EM)).
|
31
|
-
execute(klass_and_method) {|data| self.result = data }
|
33
|
+
execute(klass_and_method) {|data| self.result = Marshal.load(data) }
|
32
34
|
end
|
33
|
-
|
35
|
+
self.result
|
34
36
|
end
|
35
37
|
|
36
38
|
end
|
@@ -63,7 +65,7 @@ module EchoServer
|
|
63
65
|
def start(other_process=false)
|
64
66
|
unless other_process
|
65
67
|
@process = IO.popen("ruby #{__FILE__}")
|
66
|
-
sleep
|
68
|
+
sleep $sleep_time
|
67
69
|
else
|
68
70
|
EventMachine::run { EventMachine::start_server(ADDRESS, PORT, EM) }
|
69
71
|
end
|
@@ -79,18 +81,34 @@ module EchoServer
|
|
79
81
|
|
80
82
|
module EM
|
81
83
|
def receive_data(klass_and_method)
|
84
|
+
log "(1) EchoServer::EM#receive_data ... receives: #{klass_and_method}"
|
82
85
|
CrossStub.refresh(:file => $cache_file)
|
86
|
+
log "(2) EchoServer::EM#receive_data ... completes stubs refresh"
|
83
87
|
klass, method, *args = klass_and_method.split('.')
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
88
|
+
log "(3) EchoServer::EM#receive_data ... parses arguments to:",
|
89
|
+
" * klass ... #{klass}",
|
90
|
+
" * method ... #{method}",
|
91
|
+
" * args ... #{args.inspect}"
|
92
|
+
value = args.empty? ? Object.const_get(klass).send(method) :
|
93
|
+
Object.const_get(klass).send(method, *args) rescue $!.message
|
94
|
+
log "(4) EchoServer::EM#receive_data ... returns: #{value.inspect}"
|
95
|
+
send_data(Marshal.dump(value))
|
96
|
+
log "(5) EchoServer::EM#receive_data ... end"
|
97
|
+
end
|
98
|
+
|
99
|
+
def log(*msg)
|
100
|
+
$logger << [msg, ""].flatten.join("\n")
|
91
101
|
end
|
92
102
|
end
|
93
103
|
|
94
104
|
end
|
95
105
|
|
96
|
-
|
106
|
+
if $0 == __FILE__
|
107
|
+
begin
|
108
|
+
require 'logger'
|
109
|
+
$logger = Logger.new($log_file)
|
110
|
+
EchoServer.start(true)
|
111
|
+
ensure
|
112
|
+
$logger.close
|
113
|
+
end
|
114
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'bacon'
|
3
|
+
require 'mocha'
|
3
4
|
|
4
5
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
5
6
|
require 'helpers'
|
@@ -27,14 +28,14 @@ end
|
|
27
28
|
|
28
29
|
shared 'has other process setup' do
|
29
30
|
before do
|
30
|
-
EchoServer.start
|
31
|
+
EchoServer.start unless ENV['ECHO_SERVER'] == 'false'
|
31
32
|
@get_value = lambda do |klass_and_method_and_args|
|
32
33
|
(value = EchoClient.get(klass_and_method_and_args)) !~ /^undefined method/ ? value :
|
33
34
|
Object.we_just_wanna_trigger_a_no_method_error_with_this_very_long_and_weird_method!
|
34
35
|
end
|
35
36
|
end
|
36
37
|
after do
|
37
|
-
EchoServer.stop
|
38
|
+
EchoServer.stop unless ENV['ECHO_SERVER'] == 'false'
|
38
39
|
end
|
39
40
|
end
|
40
41
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cross-stub
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- NgTzeYang
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-12-
|
12
|
+
date: 2009-12-16 00:00:00 +08:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -20,7 +20,17 @@ dependencies:
|
|
20
20
|
requirements:
|
21
21
|
- - ">="
|
22
22
|
- !ruby/object:Gem::Version
|
23
|
-
version:
|
23
|
+
version: 0.0.0
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: eventmachine
|
27
|
+
type: :development
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.0.0
|
24
34
|
version:
|
25
35
|
- !ruby/object:Gem::Dependency
|
26
36
|
name: ParseTree
|
@@ -73,6 +83,7 @@ files:
|
|
73
83
|
- spec/cross-stub/stubbing_error_spec.rb
|
74
84
|
- spec/helpers.rb
|
75
85
|
- spec/spec_helper.rb
|
86
|
+
- tmp/.dummy
|
76
87
|
has_rdoc: true
|
77
88
|
homepage: http://github.com/ngty/cross-stub
|
78
89
|
licenses: []
|
@@ -103,6 +114,7 @@ specification_version: 3
|
|
103
114
|
summary: Simple cross process stubbing
|
104
115
|
test_files:
|
105
116
|
- spec/spec_helper.rb
|
117
|
+
- spec/cross-stub/getting_along_with_mocha_spec.rb
|
106
118
|
- spec/cross-stub/clearing_stubs_spec.rb
|
107
119
|
- spec/cross-stub/stubbing_error_spec.rb
|
108
120
|
- spec/cross-stub/creating_stubs_spec.rb
|