dyna_mo 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 85938a7bd6853c94b9e45fb70cf3021d36144027
4
+ data.tar.gz: 9b5de78332444ee32c20d8222d5a1aa37fa9c485
5
+ SHA512:
6
+ metadata.gz: 1aec6e1a6b02f58f13962d31191d2f6b755827cb78a64caa67d8093cac80121fdfc2e04567a3c82baa07af31ab41c16687bc09b6549c45a64a02a9ef3bb18a76
7
+ data.tar.gz: 7ce62a173331ceadeb40ac5c27772e3f6e199167adb23b0e8758586505ffa86ebd19e46ae1b87bec2b026a63f7bb928b7584c0a7e129327d3da78259663a5438
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in dyna_mo.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 TAGOMORI Satoshi
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,270 @@
1
+ # DynaMo: Testing module to provide dynamic scope method overriding
2
+
3
+ Dynamic scope implementation for Method Overriding: override methods by specified context, only in current thread.
4
+
5
+ **DON'T USE THIS GEM IN PRODUCTION CODE.**
6
+
7
+ ## What's this?
8
+
9
+ To modify methods' behavior with its dynamic contexts, like this:
10
+
11
+ ```ruby
12
+ require 'dyna_mo'
13
+
14
+ module MyModule
15
+ class MyClass
16
+ attr_accessor :num
17
+ def initialize; @num = 0; end
18
+ def name; "name"; end
19
+ def sum(numbers); @num + numbers.reduce(:+); end
20
+ end
21
+ end
22
+
23
+ dynamo_define('MyModule::MyClass', :mytest_case_default) do
24
+ def_method(:initialize) do
25
+ @num = 1
26
+ end
27
+
28
+ def_method(:name) do
29
+ "dummyname"
30
+ end
31
+
32
+ def_instance_method(:name, :mytest_case1) do
33
+ "dummyname1"
34
+ end
35
+
36
+ def_method(:sum) do |numbers|
37
+ @num + numbers.reduce(:+) + 1
38
+ end
39
+
40
+ def_class_method(:create) do |init_num=0|
41
+ obj = self.new
42
+ obj.num = init_num
43
+ obj
44
+ end
45
+ end
46
+
47
+ class SynopsisTest < Test::Unit::TestCase
48
+ def test_synopsis
49
+ assert { MyModule::MyClass.new.name == "name" }
50
+
51
+ obj = MyModule::MyClass.new
52
+
53
+ assert { obj.num == 0 }
54
+ assert { obj.name == "name" }
55
+
56
+ dynamo_context(:mytest_case_default) do
57
+ assert { obj.num == 0 } # #initialize is not overridden
58
+
59
+ assert { obj.name == "dummyname" }
60
+ assert { MyModule::MyClass.new.name == "dummyname" }
61
+
62
+ assert { MyModule::MyClass.new.num == 1 }
63
+
64
+ assert { MyModule::MyClass.new.sum([1,2,3]) == (1+(1+2+3)+1) }
65
+
66
+ assert { MyModule::MyClass.create(100).num == 100 }
67
+ end
68
+
69
+ dynamo_context(:mytest_case1) do
70
+ assert { obj.name == "dummyname1" }
71
+ end
72
+
73
+ dynamo_define(MyModule::MyClass, :onetime_context) do
74
+ def_method(:name) do
75
+ "onetime"
76
+ end
77
+ end
78
+
79
+ dynamo_context(:onetime_context) do
80
+ assert { obj.name == "onetime" }
81
+ end
82
+ end
83
+ end
84
+
85
+ module MyModule; class MyClass2 < MyClass; end; end
86
+
87
+ class Synopsis2Test < Test::Unit::TestCase
88
+ def test_onece_more
89
+ dynamo_define(MyModule::MyClass, :onetime_context) do
90
+ def_method(:name) do
91
+ "onetime"
92
+ end
93
+ end
94
+
95
+ dynamo_context(:onetime_context) do
96
+ assert { MyModule::MyClass2.new.name == "onetime" }
97
+ end
98
+ end
99
+ end
100
+ ```
101
+
102
+ ## Installation
103
+
104
+ Add this line to your application's Gemfile:
105
+
106
+ ```ruby
107
+ gem 'dyna_mo'
108
+ ```
109
+
110
+ And then execute:
111
+
112
+ $ bundle
113
+
114
+ Or install it yourself as:
115
+
116
+ $ gem install dyna_mo
117
+
118
+ ## Usage
119
+
120
+ Require this module in `helper.rb` or anywhere you want in test code.
121
+
122
+ ```ruby
123
+ require "dyna_mo"
124
+ ```
125
+
126
+ ### Kernel.dynamo_define(name_or_module_instance, default_context_name, &block)
127
+
128
+ Create context to define instance methods and class methods of specified Module/Class.
129
+
130
+ * `name_or_module_instance` accepts both of String or Module/Class instance. But string specification must be name from top-level, like `Net::HTTP` (or `::Net::HTTP`).
131
+
132
+ Given block is evaluated with a receiver instance of `DynaMo::Contexts`.
133
+
134
+ ```ruby
135
+ dynamo_define(MyClass, :test_awesome_situation) do
136
+ # ...
137
+ end
138
+ ```
139
+
140
+ ### DynaMo::Contexts#def_instance_method(method_name, context_name = default_context_name, &block)
141
+
142
+ Define instance method to override existing instance method, only in specified context. Instance variable reference like `@data` is handled correctly for each objects.
143
+
144
+ `super` cannot be used in this block. Use `dynamo_super()` instead.
145
+
146
+ Given block is to be method body, and prepended on specified Module/Class, not rewrite method itself. So we can call original method definition by `dynamo_super()`.
147
+
148
+ ```ruby
149
+ # in dynamo_define
150
+ dynamo_instance_method(:data, :my_test_context) do |num|
151
+ @data * num
152
+ end
153
+ obj.instance_eval{ @data = "abc" }
154
+ obj.data(3) #=> "abcabcabc"
155
+ ```
156
+
157
+ The most recently called `#def_instance_method` have highest priority. It's for ad-hoc definition in test code.
158
+
159
+ With `def_instance_method`, given block can have arguments of arbitrary number, not same with original definition. But it brings very confusing behavior (especially for `dynamo_super`), so it is not recommended for many cases.
160
+
161
+ This method also can add method which does NOT exists in original Module/Class definition.
162
+
163
+ ### DynaMo::Contexts#def_method(...)
164
+
165
+ Alias of `def_instance_method`.
166
+
167
+ ### DynaMo::Contexts#def_class_method(method_name, context_name = default_context_name, &block)
168
+
169
+ Define class method. All other things are same with `define_instance_method`.
170
+
171
+ ### Kernel.dynamo_context(context_name, &block)
172
+
173
+ Create dynamic scope in current thread for specified context name. Given block and lower stack calls run with overridden methods.
174
+
175
+ ```ruby
176
+ require "dyna_mo"
177
+
178
+ class A1; def self.label; "A"; end; end
179
+ class A2 < A1; def self.label; (super) * 2; end; end
180
+
181
+ dynamo_define(A1, :test) do
182
+ def_class_method(:label) do
183
+ "AB"
184
+ end
185
+ end
186
+
187
+ dynamo_context(:test) do
188
+ A2.label #=> "ABAB"
189
+ Thread.new { A2.label }.value #=> "AA"
190
+ end
191
+ ```
192
+
193
+ We can apply 2 or more contexts at the same time. If these contexts have definition for same method, the recent defined one is called at first.
194
+
195
+ ```ruby
196
+ dynamo_define(A1, :test1) do
197
+ def_class_method(:label) do
198
+ "AB"
199
+ end
200
+ def_class_method(:label, :test2) do
201
+ "AC"
202
+ end
203
+ def_class_method(:label, :test3) do
204
+ "BC"
205
+ end
206
+ end
207
+
208
+ dynamo_context(:test2) do
209
+ dynamo_context(:test3) do
210
+ dynamo_context(:test1) do
211
+ A2.label #=> "BC"
212
+ end
213
+ end
214
+ end
215
+ ```
216
+
217
+ ### Kernel.dynamo_super(*args)
218
+
219
+ Use to call original method definition in overriding method body (block). `dyna_mo` method overriding is implemented with `Module.prepend`, so `dynamo_super()` calls original definition, not parent class's definition.
220
+
221
+ With `dynamo_super`, all arguments must be specified explicitly.
222
+
223
+ ```ruby
224
+ dynamo_define(A1, :test_default) do
225
+ def_method(:name) do |prefix|
226
+ prefix + dynamo_super()
227
+ end
228
+ end
229
+ ```
230
+
231
+ If you use this method with applying multi contexts, `dynamo_super()` calls each overriding method bodies, like this:
232
+
233
+ ```ruby
234
+ dynamo_define(A1, :test1) do
235
+ def_class_method(:label) do
236
+ "1" + dynamo_super()
237
+ end
238
+ def_class_method(:label, :test2) do
239
+ "2" + dynamo_super()
240
+ end
241
+ def_class_method(:label, :test3) do
242
+ "3" + dynamo_super()
243
+ end
244
+ end
245
+
246
+ dynamo_context(:test1) do
247
+ dynamo_context(:test2) do
248
+ dynamo_context(:test3) do
249
+ # A1.label/test3 -> A1.label/test2 -> A1.label/test1 -> A1.label/(original)
250
+ A1.label #=> "321A"
251
+ end
252
+ end
253
+ end
254
+ ```
255
+
256
+ ## Contributing
257
+
258
+ 1. Fork it ( https://github.com/[my-github-username]/dyna_mo/fork )
259
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
260
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
261
+ 4. Push to the branch (`git push origin my-new-feature`)
262
+ 5. Create a new Pull Request
263
+
264
+ ## Copyright
265
+
266
+ * License
267
+ * MIT
268
+ * Author
269
+ * @tagomoris
270
+
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rake/testtask'
4
+ Rake::TestTask.new(:test) do |test|
5
+ test.libs << 'lib' << 'test'
6
+ test.pattern = 'test/**/test_*.rb'
7
+ test.verbose = true
8
+ end
9
+
10
+ task :default => :test
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'dyna_mo/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "dyna_mo"
8
+ spec.version = DynaMo::VERSION
9
+ spec.authors = ["TAGOMORI Satoshi"]
10
+ spec.email = ["tagomoris@gmail.com"]
11
+ spec.summary = %q{Testing module to provide dynamic scope method overriding}
12
+ spec.description = %q{Dynamic scope implementation for Method Overriding: override methods by specified context, only in current thread}
13
+ spec.homepage = "https://github.com/tagomoris/dyna_mo"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "test-unit", "~> 3.0"
24
+ spec.add_development_dependency "test-unit-power_assert"
25
+ end
@@ -0,0 +1,84 @@
1
+ require 'dyna_mo'
2
+
3
+ module MyModule # External module definition (other's gem)
4
+ class MyClass
5
+ attr_accessor :num
6
+
7
+ def initialize
8
+ @num = 0
9
+ end
10
+
11
+ def name
12
+ "name"
13
+ end
14
+
15
+ def sum(numbers)
16
+ @num + numbers.reduce(:+)
17
+ end
18
+ end
19
+ end
20
+
21
+ ## use "dynamo_super()" to call super in dynamo overriding
22
+
23
+ # in test helper
24
+ dynamo_define('MyModule::MyClass', default_context = :mytest_case_default) do
25
+ def_method(:initialize) do # == def_instance_method
26
+ @num = 1
27
+ end
28
+
29
+ def_method(:name) do # arity == 0, default context
30
+ "dummyname"
31
+ end
32
+
33
+ def_instance_method(:name, :mytest_case1) do
34
+ "dummyname1"
35
+ end
36
+
37
+ def_method(:sum) do |numbers|
38
+ @num + numbers.reduce(:+)
39
+ end
40
+
41
+ # define method only in :mytest_case_default context
42
+ def_singleton_method(:create) do |init_num=0|
43
+ obj = self.new
44
+ obj.num = init_num
45
+ obj
46
+ end
47
+ end
48
+
49
+ # test code
50
+ class MyTestCase < Test::Unit::TestCase
51
+ def test_name
52
+ assert_equal "name", MyModule::MyClass.new.name
53
+
54
+ obj = MyModule::MyClass.new
55
+
56
+ assert_equal 0, obj.num
57
+ assert_equal "name", obj.name
58
+
59
+ dynamo_context(:mytest_case_default) do
60
+ assert_equal 0, obj.num # not overridden
61
+
62
+ assert_equal "dummyname", obj.name
63
+ assert_equal "dummyname", MyModule::MyClass.new.name
64
+
65
+ assert_equal 1, MyModule::MyClass.new.num
66
+
67
+ assert_equal 100, MyModule::MyClass.create(100).num
68
+ end
69
+
70
+ dynamo_context(:mytest_case1) do
71
+ assert_equal "dummyname1", obj.name
72
+ end
73
+
74
+ dynamo_define(MyModule::MyClass, :onetime_context) do
75
+ def_method(:name) do
76
+ "onetime"
77
+ end
78
+ end
79
+
80
+ dynamo_context(:onetime_context) do
81
+ assert_equal "onetime", obj.name
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,18 @@
1
+ require "dyna_mo/version"
2
+
3
+ require "dyna_mo/module"
4
+
5
+ module DynaMo
6
+ # constants or ...
7
+ end
8
+
9
+ require "dyna_mo/contexts"
10
+ # DynaMo::Contexts
11
+ # #def_instance_method(method_name, context_name=default_context_name, &block)
12
+ # #def_singleton_method(method_name, context_name=default_context_name, &block)
13
+ # #def_method -> #def_instance_method
14
+
15
+ require "dyna_mo/environment"
16
+ # Kernel.dynamo_define(target_name_or_instance, default_context_name_sym, &block)
17
+ # Kernel.dynamo_context(context_name, &block)
18
+ # Kernel.dynamo_super(*args)
@@ -0,0 +1,42 @@
1
+ require "dyna_mo/override_method"
2
+
3
+ module DynaMo
4
+ class Contexts < BasicObject
5
+ attr_reader :module_name
6
+ attr_accessor :default_context_name
7
+
8
+ def initialize(mod_name, default_context_name)
9
+ # mod_name MUST be string here to assure target module_name consistency
10
+ @module_name = (mod_name =~ /^::/ ? mod_name : '::' + mod_name)
11
+ @default_context_name = default_context_name.to_sym
12
+
13
+ @instance_method_mods = []
14
+ @class_method_mods = []
15
+ end
16
+
17
+ def __apply__
18
+ target = ::Kernel.eval(@module_name)
19
+
20
+ # reverse: Last defined context's method priority is highest
21
+ target.prepend( *(@instance_method_mods.reverse.map(&:applied_module)) )
22
+ (class << target; self; end).prepend( *(@class_method_mods.reverse.map(&:applied_module)) )
23
+
24
+ # prepending twice has no effects
25
+ end
26
+
27
+ def def_instance_method(method_name, context_name = @default_context_name, &block)
28
+ raise "block is not given for def_instance_method" unless block # block_given?
29
+
30
+ @instance_method_mods.push OverrideMethod.new(context_name.to_sym, method_name, &block)
31
+ method_name
32
+ end
33
+ alias :def_method :def_instance_method
34
+
35
+ def def_class_method(method_name, context_name = @default_context_name, &block)
36
+ raise "block is not given for def_singleton_method" unless block #_given?
37
+
38
+ @class_method_mods.push OverrideMethod.new(context_name.to_sym, method_name, &block)
39
+ method_name
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,66 @@
1
+ require "dyna_mo/contexts"
2
+
3
+ module DynaMo
4
+ module Environment
5
+ @@contexts_store = {}
6
+ @@contexts_mutex = Mutex.new
7
+
8
+ def self.synchronize
9
+ @@contexts_mutex.synchronize do
10
+ yield
11
+ end
12
+ end
13
+
14
+ def self.contexts(name, default_context_name)
15
+ context = ( @@contexts_store[name] ||= DynaMo::Contexts.new(name, default_context_name) )
16
+ context.default_context_name = default_context_name
17
+ context
18
+ end
19
+
20
+ def self.apply_environment
21
+ @@contexts_store.each do |name, contexts|
22
+ contexts.__apply__
23
+ end
24
+ true
25
+ end
26
+ end
27
+ end
28
+
29
+ module Kernel
30
+ def dynamo_define(target_name_or_instance, default_context_name, &block)
31
+ raise "block is not given for dynamo_define" unless block_given?
32
+ # target_name_or_instance accepts module instance, to help programmer to avoid typo
33
+ target = target_name_or_instance.is_a?(Module) ? target_name_or_instance.name : target_name_or_instance.to_s
34
+ ::DynaMo::Environment.synchronize do
35
+ # get/create context object and evaluate block with it
36
+ context = ::DynaMo::Environment.contexts(target, default_context_name)
37
+ context.instance_exec(&block)
38
+ end
39
+ end
40
+
41
+ def dynamo_context(context_name, &block)
42
+ raise "block is not given for dynamo_context" unless block_given?
43
+ # get context, apply context and yield block
44
+ ::DynaMo::Environment.synchronize do
45
+ ::DynaMo::Environment.apply_environment
46
+ end
47
+ Thread.current[:dynamo_contexts] ||= {}
48
+ Thread.current[:dynamo_contexts][context_name] = true
49
+ begin
50
+ yield
51
+ ensure
52
+ Thread.current[:dynamo_contexts].delete(context_name)
53
+ end
54
+ end
55
+
56
+ def dynamo_super(*args)
57
+ Thread.current[:dynamo_stack].last.call(*args)
58
+ end
59
+
60
+ ## This method is to call `super` just like in original definition,
61
+ ## without calls of any other contexts' definition and original method definition
62
+ # def dynamo_ultra_super(*args)
63
+ # # But, we need Method#super_method and something to get Method object now running...
64
+ # raise NotImplementedError, "We need Kernel.current_method"
65
+ # end
66
+ end
@@ -0,0 +1,5 @@
1
+ class Module
2
+ def prepend?(mod)
3
+ self.ancestors.include?(mod) && self.ancestors.index(mod) < self.ancestors.index(self)
4
+ end
5
+ end
@@ -0,0 +1,42 @@
1
+ module DynaMo
2
+ class OverrideMethod
3
+ attr_reader :context, :name
4
+
5
+ def initialize(context, name, &block)
6
+ @context = context.to_sym
7
+ @name = name.to_sym
8
+ @override = block
9
+
10
+ @mod = nil
11
+ end
12
+
13
+ def applied_module
14
+ return @mod if @mod
15
+
16
+ mod = Module.new
17
+ mod.__send__(:define_method, @name, self.to_proc)
18
+ @mod = mod
19
+ end
20
+
21
+ def to_proc
22
+ context = @context
23
+ override = @override
24
+
25
+ -> (*args) {
26
+ Thread.current[:dynamo_contexts] ||= {}
27
+ Thread.current[:dynamo_stack] ||= []
28
+
29
+ if Thread.current[:dynamo_contexts][context]
30
+ Thread.current[:dynamo_stack].push(-> (*args) { super(*args) })
31
+ begin
32
+ instance_exec(*args, &override)
33
+ ensure
34
+ Thread.current[:dynamo_stack].pop
35
+ end
36
+ else
37
+ super(*args)
38
+ end
39
+ }
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,3 @@
1
+ module DynaMo
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,16 @@
1
+ require 'test/unit'
2
+ require 'test/unit/power_assert'
3
+
4
+ def virtual_dynamo_context(name)
5
+ Thread.current[:dynamo_contexts] ||= {}
6
+ Thread.current[:dynamo_contexts][name] = true
7
+ begin
8
+ yield
9
+ ensure
10
+ Thread.current[:dynamo_contexts].delete(name)
11
+ end
12
+ end
13
+
14
+ def virtual_dynamo_super(*args)
15
+ Thread.current[:dynamo_stack].last.call(*args)
16
+ end
@@ -0,0 +1,89 @@
1
+ require 'helper'
2
+ require 'dyna_mo'
3
+
4
+ module MyModule
5
+ class MyClass
6
+ attr_accessor :num
7
+ def initialize; @num = 0; end
8
+ def name; "name"; end
9
+ def sum(numbers); @num + numbers.reduce(:+); end
10
+ end
11
+ end
12
+
13
+ dynamo_define('MyModule::MyClass', :mytest_case_default) do
14
+ def_method(:initialize) do
15
+ @num = 1
16
+ end
17
+
18
+ def_method(:name) do
19
+ "dummyname"
20
+ end
21
+
22
+ def_instance_method(:name, :mytest_case1) do
23
+ "dummyname1"
24
+ end
25
+
26
+ def_method(:sum) do |numbers|
27
+ @num + numbers.reduce(:+) + 1
28
+ end
29
+
30
+ def_class_method(:create) do |init_num=0|
31
+ obj = self.new
32
+ obj.num = init_num
33
+ obj
34
+ end
35
+ end
36
+
37
+ class SynopsisTest < Test::Unit::TestCase
38
+ def test_synopsis
39
+ assert { MyModule::MyClass.new.name == "name" }
40
+
41
+ obj = MyModule::MyClass.new
42
+
43
+ assert { obj.num == 0 }
44
+ assert { obj.name == "name" }
45
+
46
+ dynamo_context(:mytest_case_default) do
47
+ assert { obj.num == 0 } # #initialize is not overridden
48
+
49
+ assert { obj.name == "dummyname" }
50
+ assert { MyModule::MyClass.new.name == "dummyname" }
51
+
52
+ assert { MyModule::MyClass.new.num == 1 }
53
+
54
+ assert { MyModule::MyClass.new.sum([1,2,3]) == (1+(1+2+3)+1) }
55
+
56
+ assert { MyModule::MyClass.create(100).num == 100 }
57
+ end
58
+
59
+ dynamo_context(:mytest_case1) do
60
+ assert { obj.name == "dummyname1" }
61
+ end
62
+
63
+ dynamo_define(MyModule::MyClass, :onetime_context) do
64
+ def_method(:name) do
65
+ "onetime"
66
+ end
67
+ end
68
+
69
+ dynamo_context(:onetime_context) do
70
+ assert { obj.name == "onetime" }
71
+ end
72
+ end
73
+ end
74
+
75
+ module MyModule; class MyClass2 < MyClass; end; end
76
+
77
+ class Synopsis2Test < Test::Unit::TestCase
78
+ def test_onece_more
79
+ dynamo_define(MyModule::MyClass, :onetime_context) do
80
+ def_method(:name) do
81
+ "onetime"
82
+ end
83
+ end
84
+
85
+ dynamo_context(:onetime_context) do
86
+ assert { MyModule::MyClass2.new.name == "onetime" }
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,136 @@
1
+ require 'helper'
2
+ require 'dyna_mo/contexts'
3
+
4
+ module Foo
5
+ class Bar
6
+ def self.label
7
+ "baz"
8
+ end
9
+ def text
10
+ "foobar"
11
+ end
12
+ def concat(a, b, separator=".")
13
+ a + separator + b
14
+ end
15
+ end
16
+
17
+ class Baz < Bar
18
+ def text
19
+ (super) + "baz"
20
+ end
21
+ end
22
+ end
23
+
24
+ class ContextsTest < Test::Unit::TestCase
25
+ def test_instance_method_contexts
26
+ cxt = DynaMo::Contexts.new('Foo::Bar', :test_i0)
27
+ cxt.def_instance_method(:text) do # default context name
28
+ "foobar0"
29
+ end
30
+ cxt.def_instance_method(:text, :test_i1) do
31
+ "foobar1"
32
+ end
33
+ cxt.def_instance_method(:text, :test_i2) do |x| # special argument for test
34
+ "foobar2:" + x
35
+ end
36
+
37
+ cxt.__apply__
38
+
39
+ assert { Foo::Bar.new.text == "foobar" }
40
+ virtual_dynamo_context(:test_i0) do
41
+ assert { Foo::Bar.new.text == "foobar0" }
42
+ end
43
+ virtual_dynamo_context(:test_i1) do
44
+ assert { Foo::Bar.new.text == "foobar1" }
45
+ end
46
+ virtual_dynamo_context(:test_i2) do
47
+ assert { Foo::Bar.new.text("z") == "foobar2:z" }
48
+ end
49
+
50
+ cxt.def_method(:text, :test_i0) do
51
+ "foobar_zero"
52
+ end
53
+
54
+ cxt.__apply__
55
+
56
+ assert { Foo::Bar.new.text == "foobar" }
57
+ virtual_dynamo_context(:test_i0) do
58
+ assert { Foo::Bar.new.text == "foobar_zero" }
59
+ end
60
+ virtual_dynamo_context(:test_i1) do
61
+ assert { Foo::Bar.new.text == "foobar1" }
62
+ end
63
+ virtual_dynamo_context(:test_i2) do
64
+ assert { Foo::Bar.new.text("z") == "foobar2:z" }
65
+ end
66
+
67
+ # 2 or more dynamo contexts work well: last defined method is prior
68
+ virtual_dynamo_context(:test_i0) do
69
+ virtual_dynamo_context(:test_i1) do
70
+ assert { Foo::Bar.new.text == "foobar_zero" }
71
+ end
72
+ end
73
+ end
74
+
75
+ def test_for_subclass
76
+ cxt1 = DynaMo::Contexts.new('Foo::Bar', :test_s0)
77
+ cxt1.def_instance_method(:text, :test_s1) do
78
+ "foobar1"
79
+ end
80
+ cxt1.def_instance_method(:text) do # default context name
81
+ "foobar0"
82
+ end
83
+ # test_s0 is prior for Foo::Bar
84
+
85
+ cxt1.__apply__
86
+
87
+ # subclass methods are prior, and prepended methods, original methods
88
+ assert { Foo::Baz.new.text == "foobarbaz" }
89
+ virtual_dynamo_context(:test_s0) do
90
+ assert { Foo::Baz.new.text == "foobar0baz" }
91
+ end
92
+ virtual_dynamo_context(:test_s1) do
93
+ assert { Foo::Baz.new.text == "foobar1baz" }
94
+ end
95
+ end
96
+
97
+ def test_multi_context_and_threads
98
+ cxt1 = DynaMo::Contexts.new('Foo::Bar', :test_m0)
99
+ cxt1.def_instance_method(:text, :test_m1) do
100
+ "foobar1"
101
+ end
102
+ cxt1.def_instance_method(:text) do # default context name
103
+ "foobar0"
104
+ end
105
+ # test_m0 is prior for Foo::Bar
106
+
107
+ cxt1.__apply__
108
+
109
+ cxt2 = DynaMo::Contexts.new('Foo::Baz', :test_m0)
110
+ cxt2.def_method(:text) do
111
+ virtual_dynamo_super() + " baz0"
112
+ end
113
+ cxt2.def_method(:text, :test_m1) do
114
+ virtual_dynamo_super() + " baz1"
115
+ end
116
+ # test_m1 is prior for Foo:Baz
117
+
118
+ cxt2.__apply__
119
+
120
+ virtual_dynamo_context(:test_m0) do
121
+ virtual_dynamo_context(:test_m1) do
122
+ # Baz/test_m1 -> Baz/test_m0 -> Baz/origin -> Bar/test_m0
123
+ assert { Foo::Baz.new.text == "foobar0baz baz0 baz1" }
124
+
125
+ # Other thread does NOT accept any effects
126
+ val1 = Thread.new { Foo::Baz.new.text }.value
127
+ assert { val1 == "foobarbaz" }
128
+
129
+ # Other thread can apply other context
130
+ # Baz/test_m1 -> Baz/origin -> Bar/test_m1 (without test_m0)
131
+ val2 = Thread.new { virtual_dynamo_context(:test_m1) { Foo::Baz.new.text } }.value
132
+ assert { val2 == "foobar1baz baz1" }
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,106 @@
1
+ require "helper"
2
+ require "dyna_mo/override_method"
3
+
4
+ class Target1
5
+ def self.label
6
+ "target1"
7
+ end
8
+
9
+ def initialize
10
+ @n = 1
11
+ end
12
+
13
+ def name
14
+ "target#{@n}"
15
+ end
16
+ end
17
+
18
+ class Target2 < Target1
19
+ def self.label
20
+ super
21
+ end
22
+
23
+ def initialize
24
+ @n = 2
25
+ end
26
+ end
27
+
28
+ class OverrideMethodTest < Test::Unit::TestCase
29
+ def test_applied_module_returns_unique_instance
30
+ m1 = DynaMo::OverrideMethod.new(:test_applied_module, :foo) do
31
+ "foo"
32
+ end
33
+ obj1 = m1.applied_module
34
+ assert { obj1.object_id == m1.applied_module.object_id }
35
+ end
36
+
37
+ def test_instance_methods
38
+ t1 = Target1.new
39
+ assert { t1.name == "target1" }
40
+
41
+ m1 = DynaMo::OverrideMethod.new(:test_instance_methods, :name) do
42
+ "p#{@n}:" + virtual_dynamo_super()
43
+ end
44
+
45
+ Target1.send(:prepend, m1.applied_module)
46
+
47
+ assert { t1.name == "target1" }
48
+ virtual_dynamo_context(:test_instance_methods) do
49
+ assert { t1.name == "p1:target1" }
50
+ end
51
+ assert { t1.name == "target1" }
52
+
53
+ m2 = DynaMo::OverrideMethod.new(:test_instance_methods, :initialize) do
54
+ @n = 1111
55
+ end
56
+ Target2.prepend(m2.applied_module)
57
+ t2 = Target2.new
58
+
59
+ assert { t1.name == "target1" }
60
+ virtual_dynamo_context(:test_instance_methods) do
61
+ assert { t1.name == "p1:target1" }
62
+ assert { t2.name == "p2:target2" }
63
+ end
64
+ assert { t1.name == "target1" }
65
+
66
+ virtual_dynamo_context(:test_instance_methods) do
67
+ assert { Target1.new.name == "p1:target1" }
68
+ assert { Target2.new.name == "p1111:target1111" }
69
+ end
70
+ end
71
+
72
+ def test_class_methods
73
+ assert { Target1.label == "target1" }
74
+
75
+ m1 = DynaMo::OverrideMethod.new(:test_class_methods, :label) do
76
+ "p1:target1"
77
+ end
78
+
79
+ mod1 = m1.applied_module
80
+ (class << Target1; self; end).send(:prepend, mod1)
81
+
82
+ assert { Target1.label == "target1" }
83
+ virtual_dynamo_context(:test_class_methods) do
84
+ assert { Target1.label == "p1:target1" }
85
+ end
86
+ assert { Target1.label == "target1" }
87
+
88
+ # Target2.label is equal to Target1.self
89
+ assert { Target2.label == "target1" }
90
+ virtual_dynamo_context(:test_class_methods) do
91
+ assert { Target2.label == "p1:target1" }
92
+ end
93
+ assert { Target2.label == "target1" }
94
+
95
+ m2 = DynaMo::OverrideMethod.new(:test_class_methods, :label) do
96
+ virtual_dynamo_super().gsub(/1/, '2')
97
+ end
98
+ (class << Target2; self; end).send(:prepend, m2.applied_module)
99
+
100
+ assert { Target2.label == "target1" }
101
+ virtual_dynamo_context(:test_class_methods) do
102
+ assert { Target2.label == "p2:target2" }
103
+ end
104
+ assert { Target2.label == "target1" }
105
+ end
106
+ end
@@ -0,0 +1,69 @@
1
+ require 'helper'
2
+ require 'dyna_mo/module'
3
+
4
+ class ModulePrependTest < Test::Unit::TestCase
5
+ module P1
6
+ def name
7
+ val = super
8
+ "p1:" + val
9
+ end
10
+ end
11
+
12
+ module P2
13
+ def name
14
+ val = super
15
+ "p2:" + val
16
+ end
17
+ end
18
+
19
+ class X
20
+ def name
21
+ "x"
22
+ end
23
+ end
24
+
25
+ class Y < X
26
+ def name
27
+ "y"
28
+ end
29
+ end
30
+
31
+ def test_base
32
+ assert { X.new.name == "x" }
33
+ assert { Y.new.name == "y" }
34
+ end
35
+
36
+ def test_normal_prepend
37
+ X.send(:prepend, P1)
38
+
39
+ assert { X.new.name == "p1:x" }
40
+ assert X.include?(P1)
41
+ assert X.prepend?(P1)
42
+
43
+ assert { Y.new.name == "y" } # not prepend
44
+ assert Y.include?(P1)
45
+ assert (not Y.prepend?(P1))
46
+
47
+ Y.send(:prepend, P2)
48
+
49
+ assert { Y.new.name == "p2:y" }
50
+ assert Y.include?(P2)
51
+ assert Y.prepend?(P2)
52
+ end
53
+
54
+ def test_prepend_duplicated_module
55
+ assert X.prepend?(P1)
56
+ assert (not Y.prepend?(P1))
57
+ assert Y.prepend?(P2)
58
+
59
+ assert { Y.new.name == "p2:y" }
60
+ dup_mod = P1.dup
61
+
62
+ Y.send(:prepend, dup_mod)
63
+
64
+ assert Y.prepend?(dup_mod)
65
+ assert { Y.new.name == "p1:p2:y" }
66
+ end
67
+ end
68
+
69
+
metadata ADDED
@@ -0,0 +1,125 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dyna_mo
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - TAGOMORI Satoshi
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-10-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: test-unit
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: test-unit-power_assert
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: 'Dynamic scope implementation for Method Overriding: override methods
70
+ by specified context, only in current thread'
71
+ email:
72
+ - tagomoris@gmail.com
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - ".gitignore"
78
+ - Gemfile
79
+ - LICENSE.txt
80
+ - README.md
81
+ - Rakefile
82
+ - dyna_mo.gemspec
83
+ - example/for_testing.rb
84
+ - lib/dyna_mo.rb
85
+ - lib/dyna_mo/contexts.rb
86
+ - lib/dyna_mo/environment.rb
87
+ - lib/dyna_mo/module.rb
88
+ - lib/dyna_mo/override_method.rb
89
+ - lib/dyna_mo/version.rb
90
+ - test/helper.rb
91
+ - test/test_basic.rb
92
+ - test/test_contexts.rb
93
+ - test/test_override_method.rb
94
+ - test/test_prepend.rb
95
+ homepage: https://github.com/tagomoris/dyna_mo
96
+ licenses:
97
+ - MIT
98
+ metadata: {}
99
+ post_install_message:
100
+ rdoc_options: []
101
+ require_paths:
102
+ - lib
103
+ required_ruby_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ required_rubygems_version: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ requirements: []
114
+ rubyforge_project:
115
+ rubygems_version: 2.2.2
116
+ signing_key:
117
+ specification_version: 4
118
+ summary: Testing module to provide dynamic scope method overriding
119
+ test_files:
120
+ - test/helper.rb
121
+ - test/test_basic.rb
122
+ - test/test_contexts.rb
123
+ - test/test_override_method.rb
124
+ - test/test_prepend.rb
125
+ has_rdoc: