dyna_mo 0.0.1

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,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: