module_shims 0.1.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,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4fb050764d4610e725df30049551b313d93e388a
4
+ data.tar.gz: 0b71eb057f042f1c79a702c95f08a24d2941de38
5
+ SHA512:
6
+ metadata.gz: 60d80f64dd2bb5c112440276dbadd2cfdef577e502df779246c938f6da12a4406606dbc68aa4527980240d4d618a1b96322791fd120a342deb2d5e3ccfa0b4eb
7
+ data.tar.gz: ec99f0364d5d2de60459a3d11137cb4445234ac394e5bd9b4112ab267bf9fb15b4ff925dc04203a05f81a75f44aef016cf080f5b08cbca87adfbb2018539a459
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.4
4
+ before_install: gem install bundler -v 1.10.6
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in module_shims.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 ypxing
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,124 @@
1
+ # Module::Shims
2
+
3
+ The gem is one tool used for debugging/developing Ruby projects and as one helper for reading/trying source code of Ruby projects (like source code of Ruby on Rails).
4
+ It should NOT be used for production.
5
+
6
+ In one inheritance chain like [A, B, C, D], it can add shim module to anyone of them so that you can "replace" one method's implementation of one specific module/class with your own implementation without impacting other modules/classes.
7
+
8
+ Different from monkey patch, you can freely enable/disable your implementation. You can maitain many of your implementation and enable them whenever you want to debug/develop your projects.
9
+
10
+ It makes use of Module#prepend of Ruby (>= 2.0)
11
+
12
+ See example in Usage section.
13
+
14
+ ## Installation
15
+
16
+ Add this line to your application's Gemfile (for development/test groups):
17
+
18
+ ```ruby
19
+ gem 'module_shims'
20
+ ```
21
+
22
+ And then execute:
23
+
24
+ $ bundle
25
+
26
+ Or install it yourself as:
27
+
28
+ $ gem install module_shims
29
+
30
+ ## Usage
31
+
32
+ For below inheritance chain [BClass, AModule]
33
+ ```ruby
34
+ module AModule
35
+ def target
36
+ 'a_module'
37
+ end
38
+ end
39
+
40
+ class BClass
41
+ include AModule
42
+
43
+ def target
44
+ 'b_class'
45
+ end
46
+
47
+ def irrelevant
48
+ end
49
+ end
50
+ ```
51
+
52
+ Let's hook method "target"
53
+ ```ruby
54
+
55
+ # give BClass one outer namespace.
56
+ module Mine
57
+ module BClass
58
+ extend ModuleShims::Switch
59
+
60
+ def target
61
+ "mine with #{super}"
62
+ end
63
+ end
64
+ end
65
+
66
+ Mine::BClass.enable_shim
67
+ # your target will be used from this line
68
+
69
+ BClass.new.target # "mine with a_module"
70
+
71
+ Mine::BClass.disable_shim
72
+ # original target will be used from this line
73
+
74
+ BClass.new.target # "b_class"
75
+
76
+ ```
77
+
78
+ You can keep BClass's method implementation in the inheritance chain like
79
+
80
+ ```ruby
81
+ Mine::BClass.enable_shim(false)
82
+ BClass.new.target # "mine with b_class"
83
+
84
+ ```
85
+
86
+ ## Note for adding shim to one module
87
+
88
+ For existing module M, below code cannot impact existing classes which have already included M.
89
+ ```ruby
90
+ Fake::M.enable_shim
91
+ ```
92
+ To cover this case, you can put simliar code at the beginning of the boot of your project.
93
+
94
+ It just makes use of TracePoint and inserts the shim when M is defined.
95
+ You can put all your "fake" modules in below code.
96
+
97
+ ```ruby
98
+ trace = TracePoint.new(:class) do |tp|
99
+ src_mod =
100
+ %w(
101
+ Fake::M
102
+ ).find { |src| tp.self.name == src.gsub(/\A[^:]+::/, '') }
103
+
104
+ src_mod.constantize.insert_shim if src_mod
105
+ end
106
+
107
+ trace.enable
108
+ ```
109
+
110
+ ## Development
111
+
112
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
113
+
114
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
115
+
116
+ ## Contributing
117
+
118
+ Bug reports and pull requests are welcome on GitHub at https://github.com/ypxing/module_shims.
119
+
120
+
121
+ ## License
122
+
123
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
124
+
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "module_shims"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,6 @@
1
+ require "module_shims/version"
2
+ require "module_shims/module_ext"
3
+ require "module_shims/switch"
4
+
5
+ module ModuleShims
6
+ end
@@ -0,0 +1,65 @@
1
+ class Module
2
+ def instance_method_inheritance_list(instance_method, location = true)
3
+ ancestors.find_all do |ancestor|
4
+ (ancestor.public_instance_methods(false) +
5
+ ancestor.protected_instance_methods(false) +
6
+ ancestor.private_instance_methods(false)).include?(instance_method)
7
+ end
8
+ .map do |x|
9
+ m = x.instance_method(instance_method)
10
+ m.respond_to?(:super_method) && m.super_method ? m.super_method : m
11
+ end
12
+ .map { |m| location ? m.source_location : m }.uniq
13
+ end
14
+
15
+ # def supermodule(target = self)
16
+ # ancestors[ancestors.index(target) + 1]
17
+ # end
18
+
19
+ def remove_existing_instance_methods(mod)
20
+ methods = Module === mod ?
21
+ (mod.public_instance_methods(false) + mod.private_instance_methods(false) + mod.protected_instance_methods(false)) : Array(mod)
22
+
23
+ methods.each do |m|
24
+ remove_method(m) rescue nil
25
+ end
26
+ end
27
+
28
+ def copy_existing_instance_methods(mod, template = mod)
29
+ ['public', 'private', 'protected'].each do |perm|
30
+ perm_s = "#{perm}_"
31
+ template.send("#{perm_s}instance_methods", false).each do |m|
32
+ define_method(m, mod.instance_method(m))
33
+ send("#{perm}", m)
34
+ end
35
+ end
36
+ end
37
+
38
+ def prefix_existing_instance_methods(prefix, template = self, remove_prefix = false)
39
+ ['public', 'private', 'protected'].each do |perm|
40
+ perm_s = "#{perm}_"
41
+
42
+ template.send("#{perm_s}instance_methods", false).each do |m|
43
+ new_name, old_name =
44
+ if remove_prefix
45
+ template == self ? [m[prefix.size..-1], m] : [m, "#{prefix}#{m}"]
46
+ else
47
+ ["#{prefix}#{m}", m]
48
+ end
49
+
50
+ next unless send("#{perm_s}instance_methods", false).include?(old_name.to_sym)
51
+
52
+ define_method(new_name, instance_method(old_name))
53
+ send("#{perm}", new_name)
54
+ remove_method(old_name)
55
+ end
56
+ end
57
+ end
58
+
59
+ def remove_instance_methods_from_ancestors(anc_name)
60
+ ancestors.each do |anc|
61
+ # just reset all SHIM module
62
+ anc.remove_existing_instance_methods(anc) if anc.name == anc_name
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,39 @@
1
+ module ModuleShims
2
+ module Switch
3
+ def enable_shim(shadow_target = true)
4
+ insert_shim unless @inserted
5
+ target_const.prefix_existing_instance_methods('__module_switch', self) if shadow_target
6
+ shim_const.copy_existing_instance_methods(self)
7
+ end
8
+
9
+ def disable_shim
10
+ target_const.prefix_existing_instance_methods('__module_switch', self, true)
11
+ target_const.remove_instance_methods_from_ancestors("#{name}::SHIM")
12
+ end
13
+
14
+ def insert_shim
15
+ @inserted ||=
16
+ begin
17
+ target_const.send(:prepend, shim_const)
18
+ true
19
+ end
20
+ end
21
+
22
+ def shim_const
23
+ @shim ||= const_defined?('SHIM') ? const_get('SHIM') :
24
+ const_set('SHIM', Module.new{ def self.prepended(mod); mod.remove_instance_methods_from_ancestors(name); super; end })
25
+ end
26
+
27
+ def target_const
28
+ @target ||= default_target
29
+ end
30
+
31
+ def target_const=(target = default_target)
32
+ @target = target
33
+ end
34
+
35
+ def default_target
36
+ const_get(name.gsub(/\A[^:]*/, ''))
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,3 @@
1
+ module ModuleShims
2
+ VERSION = "0.1.0"
3
+ end
@@ -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 'module_shims/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "module_shims"
8
+ spec.version = ModuleShims::VERSION
9
+ spec.authors = ["Yunpeng Xing (Rick)"]
10
+ spec.email = ["ypxing@gmail.com"]
11
+
12
+ spec.summary = %q{Insert shims among inheritance chain for debugging/development and reading source code purpose.}
13
+ spec.description = %q{It helps you prepend modules in the inheritance chain so that you can easily implement your own methods to override existing ones in specific modules/classes without impacting others.}
14
+ spec.homepage = "https://github.com/ypxing/module_shims"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = "exe"
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.10"
23
+ spec.add_development_dependency "rake", "~> 10.0"
24
+ spec.add_development_dependency "rspec", "~> 3.4"
25
+ end
metadata ADDED
@@ -0,0 +1,103 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: module_shims
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Yunpeng Xing (Rick)
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2015-11-20 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: '1.10'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.10'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.4'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.4'
55
+ description: It helps you prepend modules in the inheritance chain so that you can
56
+ easily implement your own methods to override existing ones in specific modules/classes
57
+ without impacting others.
58
+ email:
59
+ - ypxing@gmail.com
60
+ executables: []
61
+ extensions: []
62
+ extra_rdoc_files: []
63
+ files:
64
+ - ".gitignore"
65
+ - ".rspec"
66
+ - ".travis.yml"
67
+ - Gemfile
68
+ - LICENSE.txt
69
+ - README.md
70
+ - Rakefile
71
+ - bin/console
72
+ - bin/setup
73
+ - lib/module_shims.rb
74
+ - lib/module_shims/module_ext.rb
75
+ - lib/module_shims/switch.rb
76
+ - lib/module_shims/version.rb
77
+ - module_shims.gemspec
78
+ homepage: https://github.com/ypxing/module_shims
79
+ licenses:
80
+ - MIT
81
+ metadata: {}
82
+ post_install_message:
83
+ rdoc_options: []
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ required_rubygems_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ requirements: []
97
+ rubyforge_project:
98
+ rubygems_version: 2.2.2
99
+ signing_key:
100
+ specification_version: 4
101
+ summary: Insert shims among inheritance chain for debugging/development and reading
102
+ source code purpose.
103
+ test_files: []