substituter 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.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in meta_programming.gemspec
4
+ gemspec
5
+
6
+ group :development do
7
+ gem 'guard-minitest'
8
+ end
data/Guardfile ADDED
@@ -0,0 +1,5 @@
1
+ guard :minitest do
2
+ watch(%r{^spec/(.*)_spec\.rb$})
3
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
4
+ watch(%r{^spec/spec_helper\.rb$}) { 'spec' }
5
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 dvallance
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.
data/README.md ADDED
@@ -0,0 +1,76 @@
1
+ # Substituter
2
+
3
+ Substituter is designed to easily override an existing class instance method with a provided Proc. The existing method is renamed and a reference is kept to this original method so that the new Proc that is executed will be able to call the original method and has access to its parameters.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'substituter'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install substituter
18
+
19
+ ## Usage
20
+
21
+ ```ruby
22
+ my_proc = Proc.new{|original_method,*args|
23
+ "My proc info and original methods value = #{original_method.call(*args)}"
24
+ }
25
+
26
+ Substituter.sub String, :to_s, my_proc
27
+ puts String.new("So Cool!")
28
+
29
+ #returns "My proc info and original methods value = So Cool!"
30
+
31
+ #to restore the original method just clear the substitute
32
+ Substituter.clear String, :to_s
33
+
34
+ ```
35
+
36
+ ## *args gottcha's
37
+
38
+ Generally calling `original_method.call(*args)` will work unless args contains a Proc object, which it will if the orginal method is called with a proc parameter or a block.
39
+
40
+ Note: an _implicit_ _block_ will be turned into a Proc object and appended to the args array.
41
+
42
+ Proc objects must be passed with the _&_ operator prepended manually.
43
+
44
+ ```ruby
45
+ # our sample class with a method to substitute
46
+ class Sample
47
+ def taking_a_param_and_block(param)
48
+ block_given? yield(param) : param
49
+ end
50
+ end
51
+
52
+ # our proc we will substitute in for the original method
53
+ my_proc = Proc.new{|original_method,*args|
54
+ #here we explicitly pass in the arguments, making sure to add '&' to the proc object
55
+ "My proc info and original methods value = #{original_method.call(args[0], &args[1])}"
56
+ }
57
+
58
+ Substituter.sub Sample, :taking_a_param_and_block, my_proc
59
+
60
+ Sample.new.taking_a_param_and_block("Hello") do |param|
61
+ "#{param} World"
62
+ end
63
+
64
+ #returns "My proc info and original methods value = Hello World!"
65
+
66
+ #see the spec/substituter_spec.rb for examples
67
+
68
+ ```
69
+
70
+ ## Contributing
71
+
72
+ 1. Fork it
73
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
74
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
75
+ 4. Push to the branch (`git push origin my-new-feature`)
76
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,69 @@
1
+ require "substituter/version"
2
+ def Hijack(klass, method, &aproc)
3
+ Substituter.sub(klass, method, &aproc)
4
+ end
5
+
6
+ module Substituter
7
+
8
+ @handled_methods = {}
9
+ @stored_procs = {}
10
+ @method_rename_prefix = "substituted_"
11
+
12
+ class << self
13
+ attr_accessor :stored_procs
14
+
15
+ def clear(klass, method)
16
+ klass.class_eval %Q(
17
+ remove_method method
18
+ alias_method :#{method.to_s}, :#{prefix(method)}
19
+ remove_method :#{prefix(method)}
20
+ ), __FILE__, __LINE__
21
+ @handled_methods[klass.to_s].delete(method)
22
+ end
23
+
24
+ def get_proc klass, method
25
+ @stored_procs[klass.to_s + method.to_s]
26
+ end
27
+
28
+ def prefix(method)
29
+ @method_rename_prefix + method.to_s
30
+ end
31
+
32
+ def sub klass, method, &aproc
33
+ if substituted_methods(klass).include? method
34
+ raise StandardError, "Already substituted"
35
+ end
36
+
37
+ klass.class_eval %Q(
38
+ include Substituter unless ancestors.select{|obj| obj.class == Module}.include?(Substituter)
39
+ alias_method :#{prefix(method)}, :#{method.to_s}
40
+ def #{method.to_s}(*args)
41
+ if block_given?
42
+ proc_caller(self.class, __method__, *(args << Proc.new))
43
+ else
44
+ proc_caller(self.class, __method__, *args)
45
+ end
46
+ end
47
+ ), __FILE__, __LINE__
48
+ substituted_method klass, method, &aproc
49
+ end
50
+
51
+ def substituted_method(klass, method, &aproc)
52
+ @handled_methods[klass.to_s] = [] unless @handled_methods[klass.to_s].is_a? Array
53
+ @handled_methods[klass.to_s] << method
54
+ Substituter.stored_procs[klass.to_s + method.to_s] = aproc
55
+ end
56
+
57
+ def substituted_methods(klass)
58
+ @handled_methods[klass.to_s].nil? ? [] : @handled_methods[klass.to_s]
59
+ end
60
+ end
61
+
62
+ #instance methods below
63
+
64
+ def proc_caller(klass, method, *args)
65
+ stored_proc = Substituter.get_proc(klass, method)
66
+ stored_proc.call(method("#{Substituter.prefix(method)}".to_sym), *args)
67
+ end
68
+
69
+ end
@@ -0,0 +1,3 @@
1
+ module Substituter
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,3 @@
1
+ require_relative '../lib/substituter'
2
+ require 'minitest/spec'
3
+ require 'minitest/autorun'
@@ -0,0 +1,151 @@
1
+ require_relative 'spec_helper'
2
+
3
+ class SampleClass
4
+
5
+ def one_param(str)
6
+ str
7
+ end
8
+
9
+ def two_params(one, two)
10
+ return [one,two].join(",")
11
+ end
12
+
13
+ def param_and_splat(one, *values)
14
+ return "#{one.to_s},#{values.join(",")}"
15
+ end
16
+
17
+ def taking_a_proc(&block)
18
+ block.yield
19
+ end
20
+
21
+ def taking_a_param_and_proc(param, &block)
22
+ block_given? ? yield(param) : param
23
+ end
24
+
25
+ def taking_a_param_and_block(param)
26
+ block_given? ? yield(param) : param
27
+ end
28
+
29
+ def to_s
30
+ "from sample class"
31
+ end
32
+ end
33
+
34
+ describe Substituter do
35
+
36
+ it ".sub properly allows us to inject a proc to substitute a class instance method " do
37
+ myproc = Proc.new { |original_method, *args|
38
+ "Proc knows original = #{original_method.call(*args)}"
39
+ }
40
+ Substituter.sub(SampleClass, :one_param, &myproc)
41
+ SampleClass.new.one_param("Cool").must_equal "Proc knows original = Cool"
42
+ Substituter.clear SampleClass, :one_param
43
+ end
44
+
45
+ it ".sub will not allow you to substitute the same method twice" do
46
+ myproc = Proc.new { |original_method, *args|
47
+ "Proc knows original = #{original_method.call(*args)}"
48
+ }
49
+ Substituter.sub SampleClass, :one_param, &myproc
50
+ lambda { Substituter.sub SampleClass, :one_param, &myproc }.must_raise(StandardError)
51
+ Substituter.clear SampleClass, :one_param
52
+ end
53
+
54
+ it ".sub handles two params properly" do
55
+ myproc = Proc.new { |original_method, *args|
56
+ "Proc knows original = #{original_method.call(*args)}"
57
+ }
58
+ Substituter.sub SampleClass, :two_params, &myproc
59
+ SampleClass.new.two_params("one","two").must_equal "Proc knows original = one,two"
60
+ Substituter.clear SampleClass, :two_params
61
+ end
62
+
63
+ it ".sub handles param and splat" do
64
+ myproc = Proc.new { |original_method, *args|
65
+ "Proc knows original = #{original_method.call(*args)}"
66
+ }
67
+ Substituter.sub SampleClass, :param_and_splat, &myproc
68
+ SampleClass.new.param_and_splat(:something,"two","three").must_equal "Proc knows original = something,two,three"
69
+ Substituter.clear SampleClass, :param_and_splat
70
+ end
71
+
72
+ it ".sub handles taking a block" do
73
+ myproc = Proc.new { |original_method, *args|
74
+ "Proc knows original = #{original_method.call(&args[0])}"
75
+ }
76
+ Substituter.sub SampleClass, :taking_a_proc, &myproc
77
+ SampleClass.new.taking_a_proc(&Proc.new{"block value"}).must_equal "Proc knows original = block value"
78
+ Substituter.clear SampleClass, :taking_a_proc
79
+ end
80
+
81
+ it ".sub handles a param and a proc" do
82
+ myproc = Proc.new { |original_method, *args|
83
+ "Proc knows original = #{original_method.call(args[0], &args[1])}"
84
+ }
85
+ Substituter.sub SampleClass, :taking_a_param_and_proc, &myproc
86
+ SampleClass.new.taking_a_param_and_proc("my param"){|p| "block value and #{p}"}.must_equal "Proc knows original = block value and my param"
87
+ Substituter.clear SampleClass, :taking_a_param_and_proc
88
+ end
89
+
90
+ it ".sub handles a param and a block" do
91
+ myproc = Proc.new { |original_method, *args|
92
+ "Proc knows original = #{original_method.call(args[0], &args[1])}"
93
+ }
94
+ Substituter.sub SampleClass, :taking_a_param_and_block, &myproc
95
+ SampleClass.new.taking_a_param_and_block("my param"){|p| "block value and #{p}"}.must_equal "Proc knows original = block value and my param"
96
+ Substituter.clear SampleClass, :taking_a_param_and_block
97
+ end
98
+
99
+ it ".sub String#to_s" do
100
+ myproc = Proc.new { |original_method, *args|
101
+ "Proc knows original = #{original_method.call(*args)}"
102
+ }
103
+ Substituter.sub String, :to_s, &myproc
104
+ String.new("my string").to_s.must_equal "Proc knows original = my string"
105
+ Substituter.clear String, :to_s
106
+ String.new("my string").to_s.must_equal "my string"
107
+ end
108
+
109
+ it ".sub String#index" do
110
+ myproc = Proc.new { |original_method, *args|
111
+ "Proc knows original = #{original_method.call(*args[0])}"
112
+ }
113
+ Substituter.sub String, :index, &myproc
114
+ String.new("my string").index("my").must_equal("Proc knows original = 0")
115
+ Substituter.clear String, :index
116
+ end
117
+
118
+ it ".sub String#gsub" do
119
+ myproc = Proc.new { |original_method, *args|
120
+ "Proc knows original = #{original_method.call(*args)}"
121
+ }
122
+ Substituter.sub String, :gsub, &myproc
123
+ String.new("my string").gsub("my", "our").must_equal("Proc knows original = our string")
124
+ Substituter.clear String, :gsub
125
+ end
126
+
127
+ it ".sub Object.instance_of?" do
128
+ myproc = Proc.new { |original_method, *args|
129
+ "Proc knows original = #{original_method.call(*args)}"
130
+ }
131
+ Substituter.sub Object, :instance_of?, &myproc
132
+ Object.new().instance_of?(Array).must_equal("Proc knows original = false")
133
+ Substituter.clear Object, :instance_of?
134
+ end
135
+
136
+ it ".sub will properly handle the same method name put on different objects" do
137
+ string_proc = Proc.new { |original_method, *args|
138
+ "String proc with original #{original_method.call(*args)}"
139
+ }
140
+ sample_class_proc = Proc.new { |original_method, *args|
141
+ "Sample proc with original #{original_method.call(*args)}"
142
+ }
143
+ Substituter.sub String, :to_s, &string_proc
144
+ Substituter.sub SampleClass, :to_s, &sample_class_proc
145
+ SampleClass.new.to_s.must_equal("Sample proc with original from sample class")
146
+ String.new("test").to_s.must_equal("String proc with original test")
147
+ Substituter.clear String, :to_s
148
+ Substituter.clear SampleClass, :to_s
149
+ end
150
+
151
+ end
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'substituter/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "substituter"
8
+ spec.version = Substituter::VERSION
9
+ spec.authors = ["David Vallance"]
10
+ spec.email = ["davevallance@gmail.com"]
11
+ spec.description = %q{Substitute an existing method with a Proc. The Proc will have access to the replaced method and its parameters.}
12
+ spec.summary = %q{Substitute an existing method with a Proc.}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
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", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ end
metadata ADDED
@@ -0,0 +1,98 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: substituter
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - David Vallance
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-04-04 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '1.3'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '1.3'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ description: Substitute an existing method with a Proc. The Proc will have access
47
+ to the replaced method and its parameters.
48
+ email:
49
+ - davevallance@gmail.com
50
+ executables: []
51
+ extensions: []
52
+ extra_rdoc_files: []
53
+ files:
54
+ - .gitignore
55
+ - Gemfile
56
+ - Guardfile
57
+ - LICENSE.txt
58
+ - README.md
59
+ - Rakefile
60
+ - lib/substituter.rb
61
+ - lib/substituter/version.rb
62
+ - spec/spec_helper.rb
63
+ - spec/substituter_spec.rb
64
+ - substituter.gemspec
65
+ homepage: ''
66
+ licenses:
67
+ - MIT
68
+ post_install_message:
69
+ rdoc_options: []
70
+ require_paths:
71
+ - lib
72
+ required_ruby_version: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ segments:
79
+ - 0
80
+ hash: 768294849277507384
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ! '>='
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ segments:
88
+ - 0
89
+ hash: 768294849277507384
90
+ requirements: []
91
+ rubyforge_project:
92
+ rubygems_version: 1.8.23
93
+ signing_key:
94
+ specification_version: 3
95
+ summary: Substitute an existing method with a Proc.
96
+ test_files:
97
+ - spec/spec_helper.rb
98
+ - spec/substituter_spec.rb