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 CHANGED
@@ -19,3 +19,4 @@ rdoc
19
19
  pkg
20
20
 
21
21
  ## PROJECT::SPECIFIC
22
+ tmp
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
- 2. Cross-stub only supports stubbing of class methods, since it makes no sense to do cross process stubbing of instances
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
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.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-07}
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
- old_method_code = method_code(method)
27
- new_method_code = "#{value_or_code}" =~ /^def / ? value_or_code :
28
- %\def #{method}; Marshal.load(%|#{Marshal.dump(value_or_code)}|) ; end\
29
- @klass.instance_eval(new_method_code)
30
- old_method_code
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
- original_method_codes = methods_in_block.inject({}) do |memo, method|
41
- memo.merge(method => method_code(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
- original_method_codes
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.replace_method(method, codes[:before]) : pk.remove_method(method)
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
- original_method_code = pk.replace_method(method, value)
48
- cache[method] ||= {:before => original_method_code}
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, original_method_code = args
57
- cache[method] ||= {:before => original_method_code}
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.0'\n"
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 = '/tmp/crossstub.cache'
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
- (self.result == '<NIL>') ? nil : self.result
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 1
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
- value =
85
- if args.empty?
86
- Object.const_get(klass).send(method) rescue $!
87
- else
88
- Object.const_get(klass).send(method, *args) rescue $!
89
- end
90
- send_data(value.nil? ? '<NIL>' : value)
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
- EchoServer.start(true) if ($0 == __FILE__)
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.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-07 00:00:00 +08:00
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: "0"
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