muack 0.7.3 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,17 @@
1
+
2
+ module Muack
3
+ class Block
4
+ attr_accessor :block, :context
5
+ def initialize block, context=nil
6
+ self.block, self.context = block, context
7
+ end
8
+
9
+ def call *args, &actual_block
10
+ if context # ruby: no way to pass actual_block to instance_exec
11
+ context.instance_exec(*args, &block)
12
+ else
13
+ block.call(*args, &actual_block)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -1,6 +1,7 @@
1
1
 
2
2
  module Muack
3
- Definition = Class.new(Struct.new(:msg, :args, :block,
4
- :original_method, :proxy))
3
+ Definition = Class.new(Struct.new(:msg, :args, :returns,
4
+ :peek_args, :peek_return,
5
+ :original_method))
5
6
  WithAnyArgs = Object.new
6
7
  end
data/lib/muack/mock.rb CHANGED
@@ -2,9 +2,12 @@
2
2
  require 'muack/definition'
3
3
  require 'muack/modifier'
4
4
  require 'muack/failure'
5
+ require 'muack/block'
5
6
  require 'muack/error'
6
7
 
7
8
  module Muack
9
+ EmptyBlock = proc{}
10
+
8
11
  class Mock < BasicObject
9
12
  attr_reader :object
10
13
  def initialize object
@@ -70,21 +73,29 @@ module Muack
70
73
  end
71
74
 
72
75
  # used for mocked object to dispatch mocked method
73
- def __mock_dispatch_call disp, actual_args, actual_block
74
- if disp.proxy
75
- value = yield # need the original context for proxy or AnyInstanceOf
76
- if disp.block
77
- disp.block.call(value)
78
- else
79
- value
80
- end
81
- elsif block = disp.block
82
- arity = block.arity
83
- if arity < 0
84
- block.call(*actual_args , &actual_block)
85
- else
86
- block.call(*actual_args.first(arity), &actual_block)
87
- end
76
+ def __mock_dispatch_call context, disp, actual_args, actual_block, &_yield
77
+ args = if disp.peek_args
78
+ __mock_block_call(context, disp.peek_args,
79
+ actual_args, actual_block, true)
80
+ else
81
+ actual_args
82
+ end
83
+
84
+ ret = if disp.returns
85
+ __mock_block_call(context, disp.returns,
86
+ args, actual_block, true)
87
+ elsif disp.original_method # proxies for singleton methods
88
+ context.__send__(disp.original_method, *args, &actual_block)
89
+ else # proxies for instance methods
90
+ # need the original context for calling `super`
91
+ # ruby: can't pass a block to yield, so we name it _yield
92
+ _yield.call(*args, &actual_block)
93
+ end
94
+
95
+ if disp.peek_return
96
+ __mock_block_call(context, disp.peek_return, ret, EmptyBlock, false)
97
+ else
98
+ ret
88
99
  end
89
100
  end
90
101
 
@@ -105,9 +116,10 @@ module Muack
105
116
  object.singleton_class.module_eval do
106
117
  remove_method(defi.msg)
107
118
  # restore original method
108
- if instance_methods(false).include?(defi.original_method)
109
- alias_method defi.msg, defi.original_method
110
- remove_method defi.original_method
119
+ if instance_methods(false).include?(defi.original_method) ||
120
+ private_instance_methods(false).include?(defi.original_method)
121
+ alias_method(defi.msg, defi.original_method)
122
+ remove_method(defi.original_method)
111
123
  end
112
124
  end
113
125
  end
@@ -120,16 +132,23 @@ module Muack
120
132
  def __mock_inject_method defi
121
133
  __mock_injected[defi.msg] = defi
122
134
  target = object.singleton_class # would be the class in AnyInstanceOf
123
- Mock.store_original_method(target, defi)
124
- __mock_inject_mock_method(target, defi)
135
+ privilege = Mock.store_original_method(target, defi)
136
+ __mock_inject_mock_method(target, defi, privilege)
125
137
  end
126
138
 
127
139
  def self.store_original_method klass, defi
128
- return unless klass.instance_methods(false).include?(defi.msg)
140
+ privilege = if klass.instance_methods(false).include?(defi.msg)
141
+ :public # TODO: forget about protected methods?
142
+ elsif klass.private_instance_methods(false).include?(defi.msg)
143
+ :private
144
+ end
145
+
146
+ return :public unless privilege
129
147
  # store original method
130
148
  original_method = find_new_name(klass, defi.msg)
131
149
  klass.__send__(:alias_method, original_method, defi.msg)
132
150
  defi.original_method = original_method
151
+ privilege
133
152
  end
134
153
 
135
154
  def self.find_new_name klass, message, level=0
@@ -137,7 +156,7 @@ module Muack
137
156
  raise CannotFindInjectionName.new(level+1, message)
138
157
  end
139
158
 
140
- new_name = "__muack_mock_#{level}_#{message}".to_sym
159
+ new_name = "__muack_#{name}_#{level}_#{message}".to_sym
141
160
  if klass.instance_methods(false).include?(new_name)
142
161
  find_new_name(klass, message, level+1)
143
162
  else
@@ -145,18 +164,29 @@ module Muack
145
164
  end
146
165
  end
147
166
 
148
- def __mock_inject_mock_method target, defi
167
+ def __mock_inject_mock_method target, defi, privilege=:public
149
168
  mock = self # remember the context
150
- target.__send__(:define_method, defi.msg){|*actual_args, &actual_block|
169
+ target.__send__(:define_method, defi.msg){ |*actual_args, &actual_block|
151
170
  disp = mock.__mock_dispatch(defi.msg, actual_args)
152
- mock.__mock_dispatch_call(disp, actual_args, actual_block){
153
- if disp.original_method
154
- __send__(disp.original_method, *actual_args, &actual_block)
155
- else
156
- super(*actual_args, &actual_block)
157
- end
158
- }
171
+ mock.__mock_dispatch_call(self, disp, actual_args,
172
+ actual_block) do |args, &block|
173
+ super(*args, &block)
174
+ end
159
175
  }
176
+ target.__send__(privilege, defi.msg)
177
+ end
178
+
179
+ # used for __mock_dispatch_call
180
+ def __mock_block_call context, block, actual_args, actual_block, splat
181
+ return unless block
182
+ # for AnyInstanceOf, we don't have actually context at the time
183
+ # we're defining it, so we update it here
184
+ block.context = context if block.kind_of?(Block)
185
+ if splat
186
+ block.call(*actual_args, &actual_block)
187
+ else # peek_return doesn't need splat
188
+ block.call(actual_args, &actual_block)
189
+ end
160
190
  end
161
191
 
162
192
  def __mock_check_args expected_args, actual_args
@@ -1,8 +1,25 @@
1
1
 
2
+ require 'muack/block'
2
3
  require 'muack/error'
3
4
 
4
5
  module Muack
5
6
  class Modifier < Struct.new(:mock, :defi)
7
+ # Public API
8
+ def times number
9
+ if mock.__mock_class == Stub
10
+ raise StubHasNoTimes.new(object, defi, number)
11
+ end
12
+
13
+ if number >= 1
14
+ (number - 1).times{ mock.__mock_defis_push(defi) }
15
+ elsif number == 0
16
+ mock.__mock_defis_pop(defi)
17
+ else
18
+ raise "What would you expect from calling a method #{number} times?"
19
+ end
20
+ self
21
+ end
22
+
6
23
  # Public API
7
24
  def with_any_args
8
25
  defi.args = [WithAnyArgs]
@@ -10,30 +27,20 @@ module Muack
10
27
  end
11
28
 
12
29
  # Public API
13
- def returns val=nil, &block
14
- defi.block = block || lambda{ val }
30
+ def returns opts={}, &block
31
+ defi.returns = create_block(block, opts)
15
32
  self
16
33
  end
17
34
 
18
35
  # Public API
19
- def proxy
20
- defi.proxy = true
36
+ def peek_args opts={}, &block
37
+ defi.peek_args = create_block(block, opts)
21
38
  self
22
39
  end
23
40
 
24
41
  # Public API
25
- def times number
26
- if mock.__mock_class == Stub
27
- raise StubHasNoTimes.new(object, defi, number)
28
- end
29
-
30
- if number >= 1
31
- (number - 1).times{ mock.__mock_defis_push(defi) }
32
- elsif number == 0
33
- mock.__mock_defis_pop(defi)
34
- else
35
- raise "What would you expect from calling a method #{number} times?"
36
- end
42
+ def peek_return opts={}, &block
43
+ defi.peek_return = create_block(block, opts)
37
44
  self
38
45
  end
39
46
 
@@ -41,5 +48,10 @@ module Muack
41
48
  def object
42
49
  mock.object
43
50
  end
51
+
52
+ private
53
+ def create_block block, opts
54
+ if opts[:instance_exec] then Block.new(block) else block end
55
+ end
44
56
  end
45
57
  end
data/lib/muack/satisfy.rb CHANGED
@@ -5,10 +5,10 @@ module Muack
5
5
  !!block.call(actual_arg)
6
6
  end
7
7
 
8
- def | rhs; Satisfy::Union.new(self, rhs); end
9
- def & rhs; Satisfy::Inter.new(self, rhs); end
8
+ def | rhs; Satisfy::Disj.new(self, rhs); end
9
+ def & rhs; Satisfy::Conj.new(self, rhs); end
10
10
 
11
- class Union < Satisfy
11
+ class Disj < Satisfy
12
12
  def initialize lhs, rhs
13
13
  @lhs, @rhs = lhs, rhs
14
14
  super(lambda{ |actual_arg| lhs.match(actual_arg) ||
@@ -19,7 +19,7 @@ module Muack
19
19
  alias_method :inspect, :to_s
20
20
  end
21
21
 
22
- class Inter < Satisfy
22
+ class Conj < Satisfy
23
23
  def initialize lhs, rhs
24
24
  @lhs, @rhs = lhs, rhs
25
25
  super(lambda{ |actual_arg| lhs.match(actual_arg) &&
@@ -36,7 +36,7 @@ module Muack
36
36
  alias_method :inspect, :to_s
37
37
 
38
38
  def api_name
39
- self.class.name[/::(\w+)$/, 1].
39
+ (self.class.name || 'Unknown')[/(::)*(\w+)$/, 2].
40
40
  gsub(/([A-Z][a-z]*)+?(?=[A-Z][a-z]*)/, '\\1_').downcase
41
41
  end
42
42
 
data/lib/muack/session.rb CHANGED
@@ -20,7 +20,7 @@ module Muack
20
20
 
21
21
  def reset
22
22
  instance_variable_defined?(:@others) && @others.clear
23
- each_value(&:__mock_reset)
23
+ reverse_each{ |_, m| m.__mock_reset }
24
24
  clear
25
25
  end
26
26
  end
data/lib/muack/test.rb CHANGED
@@ -10,6 +10,10 @@ Str = 'Moo'
10
10
  def Obj.inspect
11
11
  'obj'
12
12
  end
13
+ def Obj.private
14
+ 'pri'
15
+ end
16
+ Obj.singleton_class.__send__(:private, :private)
13
17
 
14
18
  Muack::EnsureReset = lambda{
15
19
  [Obj, Str].each do |o|
data/lib/muack/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
 
2
2
  module Muack
3
- VERSION = '0.7.3'
3
+ VERSION = '1.0.0'
4
4
  end
data/muack.gemspec CHANGED
@@ -1,14 +1,15 @@
1
1
  # -*- encoding: utf-8 -*-
2
- # stub: muack 0.7.3 ruby lib
2
+ # stub: muack 1.0.0 ruby lib
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "muack"
6
- s.version = "0.7.3"
6
+ s.version = "1.0.0"
7
7
 
8
8
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
9
+ s.require_paths = ["lib"]
9
10
  s.authors = ["Lin Jen-Shin (godfat)"]
10
- s.date = "2013-10-01"
11
- s.description = "Muack -- Yet another mocking library.\n\nBasically it's an [RR][] clone, but much faster under heavy use.\nIt's 32x times faster (750s vs 23s) for running [Rib][] tests.\n\n[RR]: https://github.com/rr/rr\n[Rib]: https://github.com/godfat/rib"
11
+ s.date = "2014-01-06"
12
+ s.description = "Muack -- A fast, small, yet powerful mocking library.\n\nInspired by [RR][], and it's 32x times faster (750s vs 23s) than RR\nfor running [Rib][] tests.\n\n[RR]: https://github.com/rr/rr\n[Rib]: https://github.com/godfat/rib"
12
13
  s.email = ["godfat (XD) godfat.org"]
13
14
  s.files = [
14
15
  ".gitignore",
@@ -21,6 +22,7 @@ Gem::Specification.new do |s|
21
22
  "Rakefile",
22
23
  "lib/muack.rb",
23
24
  "lib/muack/any_instance_of.rb",
25
+ "lib/muack/block.rb",
24
26
  "lib/muack/definition.rb",
25
27
  "lib/muack/error.rb",
26
28
  "lib/muack/failure.rb",
@@ -33,24 +35,25 @@ Gem::Specification.new do |s|
33
35
  "lib/muack/test.rb",
34
36
  "lib/muack/version.rb",
35
37
  "muack.gemspec",
36
- "task/.gitignore",
38
+ "task/README.md",
37
39
  "task/gemgem.rb",
38
40
  "test/test_any_instance_of.rb",
41
+ "test/test_from_readme.rb",
39
42
  "test/test_mock.rb",
43
+ "test/test_modifier.rb",
40
44
  "test/test_proxy.rb",
41
- "test/test_readme.rb",
42
45
  "test/test_satisfy.rb",
43
46
  "test/test_stub.rb"]
44
47
  s.homepage = "https://github.com/godfat/muack"
45
48
  s.licenses = ["Apache License 2.0"]
46
- s.require_paths = ["lib"]
47
- s.rubygems_version = "2.1.5"
48
- s.summary = "Muack -- Yet another mocking library."
49
+ s.rubygems_version = "2.2.0"
50
+ s.summary = "Muack -- A fast, small, yet powerful mocking library."
49
51
  s.test_files = [
50
52
  "test/test_any_instance_of.rb",
53
+ "test/test_from_readme.rb",
51
54
  "test/test_mock.rb",
55
+ "test/test_modifier.rb",
52
56
  "test/test_proxy.rb",
53
- "test/test_readme.rb",
54
57
  "test/test_satisfy.rb",
55
58
  "test/test_stub.rb"]
56
59
  end
data/task/README.md ADDED
@@ -0,0 +1,54 @@
1
+ # Gemgem
2
+
3
+ ## DESCRIPTION:
4
+
5
+ Provided tasks:
6
+
7
+ rake clean # Remove ignored files
8
+ rake gem:build # Build gem
9
+ rake gem:install # Install gem
10
+ rake gem:release # Release gem
11
+ rake gem:spec # Generate gemspec
12
+ rake test # Run tests in memory
13
+
14
+ ## REQUIREMENTS:
15
+
16
+ * Tested with MRI (official CRuby) 1.9.3, 2.0.0, Rubinius and JRuby.
17
+
18
+ ## INSTALLATION:
19
+
20
+ git submodule add git://github.com/godfat/gemgem.git task
21
+
22
+ And in Rakefile:
23
+
24
+ ``` ruby
25
+ begin
26
+ require "#{dir = File.dirname(__FILE__)}/task/gemgem"
27
+ rescue LoadError
28
+ sh 'git submodule update --init'
29
+ exec Gem.ruby, '-S', $PROGRAM_NAME, *ARGV
30
+ end
31
+
32
+ Gemgem.init(dir) do |s|
33
+ s.name = 'your-gem'
34
+ s.version = '0.1.0'
35
+ end
36
+ ```
37
+
38
+ ## LICENSE:
39
+
40
+ Apache License 2.0
41
+
42
+ Copyright (c) 2011-2013, Lin Jen-Shin (godfat)
43
+
44
+ Licensed under the Apache License, Version 2.0 (the "License");
45
+ you may not use this file except in compliance with the License.
46
+ You may obtain a copy of the License at
47
+
48
+ <http://www.apache.org/licenses/LICENSE-2.0>
49
+
50
+ Unless required by applicable law or agreed to in writing, software
51
+ distributed under the License is distributed on an "AS IS" BASIS,
52
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
53
+ See the License for the specific language governing permissions and
54
+ limitations under the License.
data/task/gemgem.rb CHANGED
@@ -144,20 +144,18 @@ module Gemgem
144
144
  end
145
145
 
146
146
  def ignored_pattern
147
- @ignored_pattern ||= Regexp.new(expand_patterns(gitignore).join('|'))
147
+ @ignored_pattern ||= if gitignore.empty?
148
+ /^$/
149
+ else
150
+ Regexp.new(expand_patterns(gitignore).join('|'))
151
+ end
148
152
  end
149
153
 
150
154
  def expand_patterns pathes
151
155
  # http://git-scm.com/docs/gitignore
152
156
  pathes.flat_map{ |path|
153
- case path
154
- when %r{\*}
155
- Regexp.escape(path).gsub(/\\\*/, '[^/]*')
156
- when %r{^/}
157
- "^#{Regexp.escape(path[1..-1])}"
158
- else # we didn't implement negative pattern for now
159
- Regexp.escape(path)
160
- end
157
+ # we didn't implement negative pattern for now
158
+ Regexp.escape(path).sub(%r{^/}, '^').gsub(/\\\*/, '[^/]*')
161
159
  }
162
160
  end
163
161
 
@@ -226,7 +224,7 @@ end
226
224
 
227
225
  end # of gem namespace
228
226
 
229
- desc 'Run tests in memory'
227
+ desc 'Run tests'
230
228
  task :test do
231
229
  next if Gemgem.test_files.empty?
232
230
 
@@ -236,7 +234,7 @@ task :test do
236
234
  Gemgem.test_files.each{ |file| require "#{Gemgem.dir}/#{file[0..-4]}" }
237
235
  end
238
236
 
239
- desc 'Remove ignored files'
237
+ desc 'Trash ignored files'
240
238
  task :clean => ['gem:spec'] do
241
239
  next if Gemgem.ignored_files.empty?
242
240