muack 0.7.3 → 1.0.0

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.
@@ -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