<commit_before># DynaMo: Testing module to provide dynamic scope method overriding

Dynamic scope implementation for Method Overriding: override methods by specified context, only in current thread.

## Installation

Add this line to your application's Gemfile:

```ruby
gem 'dyna_mo'
```

And then execute:

    $ bundle

Or install it yourself as:

    $ gem install dyna_mo

## Usage

TODO: Write usage instructions here

## Contributing

1. Fork it ( https://github.com/[my-github-username]/dyna_mo/fork )
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
5. Create a new Pull Request<commit_msg>write docs<commit_after># DynaMo: Testing module to provide dynamic scope method overriding

Dynamic scope implementation for Method Overriding: override methods by specified context, only in current thread.

**DON'T USE THIS GEM IN PRODUCTION CODE.**

## What's this?

To modify methods' behavior with its dynamic contexts, like this:

```ruby
require 'dyna_mo'
 
module MyModule
  class MyClass
    attr_accessor :num
    def initialize; @num = 0; end
    def name; "name"; end
    def sum(numbers); @num + numbers.reduce(:+); end
  end
end
 
dynamo_define('MyModule::MyClass', :mytest_case_default) do
  def_method(:initialize) do
    @num = 1
  end
  
  def_method(:name) do
    "dummyname"
  end
  
  def_instance_method(:name, :mytest_case1) do
    "dummyname1"
  end
  
  def_method(:sum) do |numbers|
    @num + numbers.reduce(:+) + 1
  end
  
  def_class_method(:create) do |init_num=0|
    obj = self.new
    obj.num = init_num
    obj
  end
end
 
class SynopsisTest < Test::Unit::TestCase
  def test_synopsis
    assert { MyModule::MyClass.new.name == "name" }

    obj = MyModule::MyClass.new
    
    assert { obj.num == 0 }
    assert { obj.name == "name" }
    
    dynamo_context(:mytest_case_default) do
      assert { obj.num == 0 } # #initialize is not overridden
      
      assert { obj.name == "dummyname" }
      assert { MyModule::MyClass.new.name == "dummyname" }
      
      assert { MyModule::MyClass.new.num == 1 }
      
      assert { MyModule::MyClass.new.sum([1,2,3]) == (1+(1+2+3)+1) }
      
      assert { MyModule::MyClass.create(100).num == 100 }
    end
    
    dynamo_context(:mytest_case1) do
      assert { obj.name == "dummyname1" }
    end
    
    dynamo_define(MyModule::MyClass, :onetime_context) do
      def_method(:name) do
        "onetime"
      end
    end
    
    dynamo_context(:onetime_context) do
      assert { obj.name == "onetime" }
    end
  end
end
 
module MyModule; class MyClass2 < MyClass; end; end
 
class Synopsis2Test < Test::Unit::TestCase
  def test_onece_more
    dynamo_define(MyModule::MyClass, :onetime_context) do
      def_method(:name) do
        "onetime"
      end
    end
    
    dynamo_context(:onetime_context) do
      assert { MyModule::MyClass2.new.name == "onetime" }
    end
  end
end
```

## Installation

Add this line to your application's Gemfile:

```ruby
gem 'dyna_mo'
```

And then execute:

    $ bundle

Or install it yourself as:

    $ gem install dyna_mo

## Usage

Require this module in `helper.rb` or anywhere you want in test code.

```ruby
require "dyna_mo"
```

### Kernel.dynamo_define(name_or_module_instance, default_context_name, &block)

Create context to define instance methods and class methods of specified Module/Class.

* `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`).

Given block is evaluated with a receiver instance of `DynaMo::Contexts`.

```ruby
dynamo_define(MyClass, :test_awesome_situation) do
  # ...
end
```

### DynaMo::Contexts#def_instance_method(method_name, context_name = default_context_name, &block)

Define instance method to override existing instance method, only in specified context. Instance variable reference like `@data` is handled correctly for each objects.

`super` cannot be used in this block. Use `dynamo_super()` instead.

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()`.

```ruby
 # in dynamo_define
dynamo_instance_method(:data, :my_test_context) do |num|
  @data * num
end
obj.instance_eval{ @data = "abc" }
obj.data(3) #=> "abcabcabc"
```

The most recently called `#def_instance_method` have highest priority. It's for ad-hoc definition in test code.

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.

This method also can add method which does NOT exists in original Module/Class definition.

### DynaMo::Contexts#def_method(...)

Alias of `def_instance_method`.

### DynaMo::Contexts#def_class_method(method_name, context_name = default_context_name, &block)

Define class method. All other things are same with `define_instance_method`.

### Kernel.dynamo_context(context_name, &block)

Create dynamic scope in current thread for specified context name. Given block and lower stack calls run with overridden methods.

```ruby
require "dyna_mo"
 
class A1; def self.label; "A"; end; end
class A2 < A1; def self.label; (super) * 2; end; end
 
dynamo_define(A1, :test) do
  def_class_method(:label) do
    "AB"
  end
end
 
dynamo_context(:test) do
  A2.label #=> "ABAB"
  Thread.new { A2.label }.value #=> "AA"
end
```

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.

```ruby
dynamo_define(A1, :test1) do
  def_class_method(:label) do
    "AB"
  end
  def_class_method(:label, :test2) do
    "AC"
  end
  def_class_method(:label, :test3) do
    "BC"
  end
end
 
dynamo_context(:test2) do
  dynamo_context(:test3) do
    dynamo_context(:test1) do
      A2.label #=> "BC"
    end
  end
end
```

### Kernel.dynamo_super(*args)

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.

With `dynamo_super`, all arguments must be specified explicitly.

```ruby
dynamo_define(A1, :test_default) do
  def_method(:name) do |prefix|
    prefix + dynamo_super()
  end
end
```

If you use this method with applying multi contexts, `dynamo_super()` calls each overriding method bodies, like this:

```ruby
dynamo_define(A1, :test1) do
  def_class_method(:label) do
    "1" + dynamo_super()
  end
  def_class_method(:label, :test2) do
    "2" + dynamo_super()
  end
  def_class_method(:label, :test3) do
    "3" + dynamo_super()
  end
end

dynamo_context(:test1) do
  dynamo_context(:test2) do
    dynamo_context(:test3) do
      # A1.label/test3 -> A1.label/test2 -> A1.label/test1 -> A1.label/(original)
      A1.label  #=> "321A"
    end
  end
end
```

## Contributing

1. Fork it ( https://github.com/[my-github-username]/dyna_mo/fork )
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
5. Create a new Pull Request

## Copyright

* License
 * MIT
* Author
 * @tagomoris

{'fraction_non_alphanumeric': 0.1072242326765849, 'fraction_numerical': 0.010320332395121297, 'mean_word_length': 3.823529411764706, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 11, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2385064', 'n_tokens_mistral': 2523, 'n_tokens_neox': 2432, 'n_words': 793}