coderrr-monkey_shield 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt ADDED
@@ -0,0 +1,3 @@
1
+ == 0.1.0
2
+
3
+ * New version
data/Manifest.txt ADDED
@@ -0,0 +1,9 @@
1
+ History.txt
2
+ Manifest.txt
3
+ lib/monkey_shield.rb
4
+ lib/monkeyshield.rb
5
+ Rakefile
6
+ README.txt
7
+ TODO
8
+ spec/monkey_shield_spec.rb
9
+ spec/real_libs_spec_explicit.rb
data/README.txt ADDED
@@ -0,0 +1,409 @@
1
+ MonkeyShield
2
+ by coderrr
3
+
4
+ == DESCRIPTION:
5
+
6
+ Protects you from monkey patching!!
7
+
8
+ I actually successfully wrapped all of Rails in a context. This shit actually works!.... kindof
9
+
10
+ == USAGE:
11
+
12
+ Protect.wrap_with_context :lib1 do
13
+ class Object
14
+ def to_xml
15
+ "<lib1/>"
16
+ end
17
+ end
18
+
19
+ class Lib1
20
+ def self.xml_for(o)
21
+ o.to_xml
22
+ end
23
+ end
24
+ end
25
+
26
+ Protect.wrap_with_context :lib2 do
27
+ class Object
28
+ def to_xml
29
+ "<lib2/>"
30
+ end
31
+ end
32
+
33
+ class Lib2
34
+ def self.xml_for(o)
35
+ o.to_xml
36
+ end
37
+ end
38
+ end
39
+
40
+ # or
41
+
42
+ Protect.wrap_with_context(:lib1) { require 'lib1' }
43
+ Protect.wrap_with_context(:lib2) { require 'lib2' }
44
+
45
+ # now you can...
46
+
47
+ Protect.context_switch_for Object, :to_xml
48
+
49
+ o = Object.new
50
+ Lib1.xml_for o # => "<lib1/>"
51
+ Lib2.xml_for o # => "<lib2/>"
52
+
53
+ o.to_xml # => raises Protect::NoContextError
54
+
55
+ Protect.in_context(:lib2) { o.to_xml } # => "<lib2/>
56
+
57
+ Protect.set_default_context_for Object, :to_xml, :lib1
58
+
59
+ o.to_xml => "<lib1/>"
60
+
61
+ == SYNOPSIS:
62
+
63
+ == REQUIREMENTS:
64
+
65
+ == INSTALL:
66
+
67
+ gem install monkeyshield
68
+
69
+ == LICENSE:
70
+
71
+ GNU GENERAL PUBLIC LICENSE
72
+ Version 2, June 1991
73
+
74
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
75
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
76
+ Everyone is permitted to copy and distribute verbatim copies
77
+ of this license document, but changing it is not allowed.
78
+
79
+ Preamble
80
+
81
+ The licenses for most software are designed to take away your
82
+ freedom to share and change it. By contrast, the GNU General Public
83
+ License is intended to guarantee your freedom to share and change free
84
+ software--to make sure the software is free for all its users. This
85
+ General Public License applies to most of the Free Software
86
+ Foundation's software and to any other program whose authors commit to
87
+ using it. (Some other Free Software Foundation software is covered by
88
+ the GNU Lesser General Public License instead.) You can apply it to
89
+ your programs, too.
90
+
91
+ When we speak of free software, we are referring to freedom, not
92
+ price. Our General Public Licenses are designed to make sure that you
93
+ have the freedom to distribute copies of free software (and charge for
94
+ this service if you wish), that you receive source code or can get it
95
+ if you want it, that you can change the software or use pieces of it
96
+ in new free programs; and that you know you can do these things.
97
+
98
+ To protect your rights, we need to make restrictions that forbid
99
+ anyone to deny you these rights or to ask you to surrender the rights.
100
+ These restrictions translate to certain responsibilities for you if you
101
+ distribute copies of the software, or if you modify it.
102
+
103
+ For example, if you distribute copies of such a program, whether
104
+ gratis or for a fee, you must give the recipients all the rights that
105
+ you have. You must make sure that they, too, receive or can get the
106
+ source code. And you must show them these terms so they know their
107
+ rights.
108
+
109
+ We protect your rights with two steps: (1) copyright the software, and
110
+ (2) offer you this license which gives you legal permission to copy,
111
+ distribute and/or modify the software.
112
+
113
+ Also, for each author's protection and ours, we want to make certain
114
+ that everyone understands that there is no warranty for this free
115
+ software. If the software is modified by someone else and passed on, we
116
+ want its recipients to know that what they have is not the original, so
117
+ that any problems introduced by others will not reflect on the original
118
+ authors' reputations.
119
+
120
+ Finally, any free program is threatened constantly by software
121
+ patents. We wish to avoid the danger that redistributors of a free
122
+ program will individually obtain patent licenses, in effect making the
123
+ program proprietary. To prevent this, we have made it clear that any
124
+ patent must be licensed for everyone's free use or not licensed at all.
125
+
126
+ The precise terms and conditions for copying, distribution and
127
+ modification follow.
128
+
129
+ GNU GENERAL PUBLIC LICENSE
130
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
131
+
132
+ 0. This License applies to any program or other work which contains
133
+ a notice placed by the copyright holder saying it may be distributed
134
+ under the terms of this General Public License. The "Program", below,
135
+ refers to any such program or work, and a "work based on the Program"
136
+ means either the Program or any derivative work under copyright law:
137
+ that is to say, a work containing the Program or a portion of it,
138
+ either verbatim or with modifications and/or translated into another
139
+ language. (Hereinafter, translation is included without limitation in
140
+ the term "modification".) Each licensee is addressed as "you".
141
+
142
+ Activities other than copying, distribution and modification are not
143
+ covered by this License; they are outside its scope. The act of
144
+ running the Program is not restricted, and the output from the Program
145
+ is covered only if its contents constitute a work based on the
146
+ Program (independent of having been made by running the Program).
147
+ Whether that is true depends on what the Program does.
148
+
149
+ 1. You may copy and distribute verbatim copies of the Program's
150
+ source code as you receive it, in any medium, provided that you
151
+ conspicuously and appropriately publish on each copy an appropriate
152
+ copyright notice and disclaimer of warranty; keep intact all the
153
+ notices that refer to this License and to the absence of any warranty;
154
+ and give any other recipients of the Program a copy of this License
155
+ along with the Program.
156
+
157
+ You may charge a fee for the physical act of transferring a copy, and
158
+ you may at your option offer warranty protection in exchange for a fee.
159
+
160
+ 2. You may modify your copy or copies of the Program or any portion
161
+ of it, thus forming a work based on the Program, and copy and
162
+ distribute such modifications or work under the terms of Section 1
163
+ above, provided that you also meet all of these conditions:
164
+
165
+ a) You must cause the modified files to carry prominent notices
166
+ stating that you changed the files and the date of any change.
167
+
168
+ b) You must cause any work that you distribute or publish, that in
169
+ whole or in part contains or is derived from the Program or any
170
+ part thereof, to be licensed as a whole at no charge to all third
171
+ parties under the terms of this License.
172
+
173
+ c) If the modified program normally reads commands interactively
174
+ when run, you must cause it, when started running for such
175
+ interactive use in the most ordinary way, to print or display an
176
+ announcement including an appropriate copyright notice and a
177
+ notice that there is no warranty (or else, saying that you provide
178
+ a warranty) and that users may redistribute the program under
179
+ these conditions, and telling the user how to view a copy of this
180
+ License. (Exception: if the Program itself is interactive but
181
+ does not normally print such an announcement, your work based on
182
+ the Program is not required to print an announcement.)
183
+
184
+ These requirements apply to the modified work as a whole. If
185
+ identifiable sections of that work are not derived from the Program,
186
+ and can be reasonably considered independent and separate works in
187
+ themselves, then this License, and its terms, do not apply to those
188
+ sections when you distribute them as separate works. But when you
189
+ distribute the same sections as part of a whole which is a work based
190
+ on the Program, the distribution of the whole must be on the terms of
191
+ this License, whose permissions for other licensees extend to the
192
+ entire whole, and thus to each and every part regardless of who wrote it.
193
+
194
+ Thus, it is not the intent of this section to claim rights or contest
195
+ your rights to work written entirely by you; rather, the intent is to
196
+ exercise the right to control the distribution of derivative or
197
+ collective works based on the Program.
198
+
199
+ In addition, mere aggregation of another work not based on the Program
200
+ with the Program (or with a work based on the Program) on a volume of
201
+ a storage or distribution medium does not bring the other work under
202
+ the scope of this License.
203
+
204
+ 3. You may copy and distribute the Program (or a work based on it,
205
+ under Section 2) in object code or executable form under the terms of
206
+ Sections 1 and 2 above provided that you also do one of the following:
207
+
208
+ a) Accompany it with the complete corresponding machine-readable
209
+ source code, which must be distributed under the terms of Sections
210
+ 1 and 2 above on a medium customarily used for software interchange; or,
211
+
212
+ b) Accompany it with a written offer, valid for at least three
213
+ years, to give any third party, for a charge no more than your
214
+ cost of physically performing source distribution, a complete
215
+ machine-readable copy of the corresponding source code, to be
216
+ distributed under the terms of Sections 1 and 2 above on a medium
217
+ customarily used for software interchange; or,
218
+
219
+ c) Accompany it with the information you received as to the offer
220
+ to distribute corresponding source code. (This alternative is
221
+ allowed only for noncommercial distribution and only if you
222
+ received the program in object code or executable form with such
223
+ an offer, in accord with Subsection b above.)
224
+
225
+ The source code for a work means the preferred form of the work for
226
+ making modifications to it. For an executable work, complete source
227
+ code means all the source code for all modules it contains, plus any
228
+ associated interface definition files, plus the scripts used to
229
+ control compilation and installation of the executable. However, as a
230
+ special exception, the source code distributed need not include
231
+ anything that is normally distributed (in either source or binary
232
+ form) with the major components (compiler, kernel, and so on) of the
233
+ operating system on which the executable runs, unless that component
234
+ itself accompanies the executable.
235
+
236
+ If distribution of executable or object code is made by offering
237
+ access to copy from a designated place, then offering equivalent
238
+ access to copy the source code from the same place counts as
239
+ distribution of the source code, even though third parties are not
240
+ compelled to copy the source along with the object code.
241
+
242
+ 4. You may not copy, modify, sublicense, or distribute the Program
243
+ except as expressly provided under this License. Any attempt
244
+ otherwise to copy, modify, sublicense or distribute the Program is
245
+ void, and will automatically terminate your rights under this License.
246
+ However, parties who have received copies, or rights, from you under
247
+ this License will not have their licenses terminated so long as such
248
+ parties remain in full compliance.
249
+
250
+ 5. You are not required to accept this License, since you have not
251
+ signed it. However, nothing else grants you permission to modify or
252
+ distribute the Program or its derivative works. These actions are
253
+ prohibited by law if you do not accept this License. Therefore, by
254
+ modifying or distributing the Program (or any work based on the
255
+ Program), you indicate your acceptance of this License to do so, and
256
+ all its terms and conditions for copying, distributing or modifying
257
+ the Program or works based on it.
258
+
259
+ 6. Each time you redistribute the Program (or any work based on the
260
+ Program), the recipient automatically receives a license from the
261
+ original licensor to copy, distribute or modify the Program subject to
262
+ these terms and conditions. You may not impose any further
263
+ restrictions on the recipients' exercise of the rights granted herein.
264
+ You are not responsible for enforcing compliance by third parties to
265
+ this License.
266
+
267
+ 7. If, as a consequence of a court judgment or allegation of patent
268
+ infringement or for any other reason (not limited to patent issues),
269
+ conditions are imposed on you (whether by court order, agreement or
270
+ otherwise) that contradict the conditions of this License, they do not
271
+ excuse you from the conditions of this License. If you cannot
272
+ distribute so as to satisfy simultaneously your obligations under this
273
+ License and any other pertinent obligations, then as a consequence you
274
+ may not distribute the Program at all. For example, if a patent
275
+ license would not permit royalty-free redistribution of the Program by
276
+ all those who receive copies directly or indirectly through you, then
277
+ the only way you could satisfy both it and this License would be to
278
+ refrain entirely from distribution of the Program.
279
+
280
+ If any portion of this section is held invalid or unenforceable under
281
+ any particular circumstance, the balance of the section is intended to
282
+ apply and the section as a whole is intended to apply in other
283
+ circumstances.
284
+
285
+ It is not the purpose of this section to induce you to infringe any
286
+ patents or other property right claims or to contest validity of any
287
+ such claims; this section has the sole purpose of protecting the
288
+ integrity of the free software distribution system, which is
289
+ implemented by public license practices. Many people have made
290
+ generous contributions to the wide range of software distributed
291
+ through that system in reliance on consistent application of that
292
+ system; it is up to the author/donor to decide if he or she is willing
293
+ to distribute software through any other system and a licensee cannot
294
+ impose that choice.
295
+
296
+ This section is intended to make thoroughly clear what is believed to
297
+ be a consequence of the rest of this License.
298
+
299
+ 8. If the distribution and/or use of the Program is restricted in
300
+ certain countries either by patents or by copyrighted interfaces, the
301
+ original copyright holder who places the Program under this License
302
+ may add an explicit geographical distribution limitation excluding
303
+ those countries, so that distribution is permitted only in or among
304
+ countries not thus excluded. In such case, this License incorporates
305
+ the limitation as if written in the body of this License.
306
+
307
+ 9. The Free Software Foundation may publish revised and/or new versions
308
+ of the General Public License from time to time. Such new versions will
309
+ be similar in spirit to the present version, but may differ in detail to
310
+ address new problems or concerns.
311
+
312
+ Each version is given a distinguishing version number. If the Program
313
+ specifies a version number of this License which applies to it and "any
314
+ later version", you have the option of following the terms and conditions
315
+ either of that version or of any later version published by the Free
316
+ Software Foundation. If the Program does not specify a version number of
317
+ this License, you may choose any version ever published by the Free Software
318
+ Foundation.
319
+
320
+ 10. If you wish to incorporate parts of the Program into other free
321
+ programs whose distribution conditions are different, write to the author
322
+ to ask for permission. For software which is copyrighted by the Free
323
+ Software Foundation, write to the Free Software Foundation; we sometimes
324
+ make exceptions for this. Our decision will be guided by the two goals
325
+ of preserving the free status of all derivatives of our free software and
326
+ of promoting the sharing and reuse of software generally.
327
+
328
+ NO WARRANTY
329
+
330
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
331
+ FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
332
+ OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
333
+ PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
334
+ OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
335
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
336
+ TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
337
+ PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
338
+ REPAIR OR CORRECTION.
339
+
340
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
341
+ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
342
+ REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
343
+ INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
344
+ OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
345
+ TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
346
+ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
347
+ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
348
+ POSSIBILITY OF SUCH DAMAGES.
349
+
350
+ END OF TERMS AND CONDITIONS
351
+
352
+ How to Apply These Terms to Your New Programs
353
+
354
+ If you develop a new program, and you want it to be of the greatest
355
+ possible use to the public, the best way to achieve this is to make it
356
+ free software which everyone can redistribute and change under these terms.
357
+
358
+ To do so, attach the following notices to the program. It is safest
359
+ to attach them to the start of each source file to most effectively
360
+ convey the exclusion of warranty; and each file should have at least
361
+ the "copyright" line and a pointer to where the full notice is found.
362
+
363
+ <one line to give the program's name and a brief idea of what it does.>
364
+ Copyright (C) <year> <name of author>
365
+
366
+ This program is free software; you can redistribute it and/or modify
367
+ it under the terms of the GNU General Public License as published by
368
+ the Free Software Foundation; either version 2 of the License, or
369
+ (at your option) any later version.
370
+
371
+ This program is distributed in the hope that it will be useful,
372
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
373
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
374
+ GNU General Public License for more details.
375
+
376
+ You should have received a copy of the GNU General Public License along
377
+ with this program; if not, write to the Free Software Foundation, Inc.,
378
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
379
+
380
+ Also add information on how to contact you by electronic and paper mail.
381
+
382
+ If the program is interactive, make it output a short notice like this
383
+ when it starts in an interactive mode:
384
+
385
+ Gnomovision version 69, Copyright (C) year name of author
386
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
387
+ This is free software, and you are welcome to redistribute it
388
+ under certain conditions; type `show c' for details.
389
+
390
+ The hypothetical commands `show w' and `show c' should show the appropriate
391
+ parts of the General Public License. Of course, the commands you use may
392
+ be called something other than `show w' and `show c'; they could even be
393
+ mouse-clicks or menu items--whatever suits your program.
394
+
395
+ You should also get your employer (if you work as a programmer) or your
396
+ school, if any, to sign a "copyright disclaimer" for the program, if
397
+ necessary. Here is a sample; alter the names:
398
+
399
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
400
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
401
+
402
+ <signature of Ty Coon>, 1 April 1989
403
+ Ty Coon, President of Vice
404
+
405
+ This General Public License does not permit incorporating your program into
406
+ proprietary programs. If your program is a subroutine library, you may
407
+ consider it more useful to permit linking proprietary applications with the
408
+ library. If this is what you want to do, use the GNU Lesser General
409
+ Public License instead of this License.
data/Rakefile ADDED
@@ -0,0 +1,23 @@
1
+ require 'lib/monkey_shield'
2
+ require 'rake'
3
+ require 'spec/rake/spectask'
4
+
5
+ desc "Run all examples"
6
+ Spec::Rake::SpecTask.new('spec') do |t|
7
+ t.spec_files = FileList['spec/**/*.rb']
8
+ end
9
+
10
+ require 'rubygems'
11
+ require 'hoe'
12
+
13
+ Hoe.new('monkeyshield', MonkeyShield::VERSION) do |p|
14
+ p.rubyforge_name = 'coderrr'
15
+ p.author = 'coderrr'
16
+ p.email = 'coderrr.contact@gmail.com'
17
+ # p.summary = 'FIX'
18
+ p.description = p.paragraphs_of('README.txt', 0..1).join("\n\n")
19
+ # p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[1..-1]
20
+ p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
21
+ p.remote_rdoc_dir = '' # Release to root
22
+ p.extra_deps << ["RubyInline", ">= 3.6.7"]
23
+ end
@@ -0,0 +1,260 @@
1
+ require 'inline'
2
+
3
+ class Module
4
+ # the singleton method which module_function creates should point to the original
5
+ # method, not the context wrapped one
6
+ def __MONKEY__module_function__with_args(*methods)
7
+ methods.each do |method|
8
+ if unique_method_name = MonkeyShield::UNIQUE_METHOD_NAMES[ [self, method] ]
9
+ __module_function__(unique_method_name)
10
+ (class << self; self; end).class_eval { alias_method method, unique_method_name }
11
+ else
12
+ __module_function__(method)
13
+ end
14
+ end
15
+ end
16
+
17
+ alias_method :__module_function__, :module_function # store original module_function
18
+ # this has to be a C function so that it can modify the module's scope
19
+ inline { |builder| builder.c_raw %q{
20
+ static VALUE __MONKEY__module_function(int argc, VALUE *argv, VALUE self) {
21
+ if (argc == 0)
22
+ return rb_funcall(self, rb_intern("__module_function__"), 0);
23
+ else
24
+ return rb_funcall2(self, rb_intern("__MONKEY__module_function__with_args"), argc, argv);
25
+ }
26
+ } }
27
+
28
+ def instance_method_visibility(method_name)
29
+ if public_method_defined? method_name
30
+ :public
31
+ elsif protected_method_defined? method_name
32
+ :protected
33
+ elsif private_method_defined? method_name
34
+ :private
35
+ end
36
+ end
37
+ end
38
+
39
+ class MonkeyShield
40
+ VERSION = "0.1.0"
41
+
42
+ UNIQUE_METHOD_NAMES = {}
43
+ CONTEXT_WRAPPED_METHODS = Hash.new{|h,k| h[k] = [] }
44
+
45
+ class NoContextError < StandardError; end
46
+ class MethodDefinedInModuleCallsSuper < StandardError; end
47
+
48
+ class << self
49
+ def wrap_with_context(context, exceptions = [], &blk)
50
+ Module.class_eval do
51
+ define_method :__MONKEY__method_added do |klass, method_name|
52
+ return unless MonkeyShield.hook_method_added?
53
+ return if exceptions.include? method_name or exceptions.include? "#{klass.name}##{method_name}" or
54
+ exceptions.any? {|ex| ex.is_a? Regexp and ex =~ method_name.to_s }
55
+
56
+ MonkeyShield.ignore_method_added { MonkeyShield.wrap_method_with_context(klass, method_name, context) }
57
+ end
58
+ end
59
+
60
+ MonkeyShield.alias_method_added_hooks
61
+
62
+ MonkeyShield.hook_module_function do
63
+ MonkeyShield.hook_method_added do
64
+ yield
65
+ end
66
+ end
67
+
68
+ MonkeyShield.warnings
69
+ end
70
+
71
+ def wrap_method_with_context(klass, method_name, context)
72
+ klass.class_eval do
73
+ visibility = instance_method_visibility method_name
74
+ return if ! visibility # something else removed the method already, wth!
75
+
76
+ method_name_with_context = MonkeyShield.prefix_with_context(method_name, context)
77
+ unique_method_name = MonkeyShield.unique_method_name(method_name)
78
+
79
+ alias_method method_name_with_context, method_name
80
+ alias_method unique_method_name, method_name
81
+ private method_name_with_context, unique_method_name
82
+
83
+ UNIQUE_METHOD_NAMES[ [self, method_name] ] = unique_method_name
84
+ CONTEXT_WRAPPED_METHODS[ [self, method_name] ] << context
85
+
86
+ class_eval <<-EOF, __FILE__, __LINE__
87
+ def #{tmp_name = MonkeyShield.temp_method_name} *args, &blk
88
+ MonkeyShield.in_context #{context.inspect} do
89
+ __send__(#{unique_method_name.inspect}, *args, &blk)
90
+ end
91
+ rescue NoMethodError
92
+ if $!.message =~ /super: no superclass method `(.+?)'/
93
+ raise MonkeyShield::MethodDefinedInModuleCallsSuper, "Please add #{self.name}##{method_name} to the exceptions list!"
94
+ end
95
+
96
+ raise
97
+ end
98
+ EOF
99
+ alias_method method_name, tmp_name
100
+ remove_method tmp_name
101
+
102
+ send visibility, method_name
103
+ end
104
+ rescue
105
+ puts "failed to wrap #{klass.name}##{method_name}: #{$!}"
106
+ puts $!.backtrace.join("\n")
107
+ end
108
+
109
+ def reset!
110
+ CONTEXT_WRAPPED_METHODS.clear
111
+ UNIQUE_METHOD_NAMES.clear
112
+ end
113
+
114
+ def context_switch_for klass, method_name
115
+ klass.class_eval do
116
+ class_eval <<-EOF, __FILE__, __LINE__
117
+ def #{tmp_name = MonkeyShield.temp_method_name} *args, &blk
118
+ raise NoContextError if ! MonkeyShield.current_context
119
+ __send__(MonkeyShield.prefix_with_context(#{method_name.inspect}, MonkeyShield.current_context), *args, &blk)
120
+ end
121
+ EOF
122
+ alias_method method_name, tmp_name
123
+ remove_method tmp_name
124
+ end
125
+ end
126
+
127
+ def set_default_context_for klass, method_name, context
128
+ context_switched_name = "__context_switch__#{MonkeyShield.unique}__#{method_name}"
129
+ klass.class_eval do
130
+ alias_method context_switched_name, method_name
131
+ class_eval <<-EOF, __FILE__, __LINE__
132
+ def #{tmp_name = MonkeyShield.temp_method_name} *args, &blk
133
+ if ! MonkeyShield.current_context
134
+ need_pop = true
135
+ MonkeyShield.push_context #{context.inspect}
136
+ end
137
+
138
+ __send__(#{context_switched_name.inspect}, *args, &blk)
139
+ ensure
140
+ MonkeyShield.pop_context if need_pop
141
+ end
142
+ EOF
143
+ alias_method method_name, tmp_name
144
+ remove_method tmp_name
145
+ end
146
+ end
147
+
148
+ def alias_method_added_hooks
149
+ return if @method_added_hooks_aliased
150
+
151
+ # TODO; catch method_added being added and automatically wrap it
152
+ # these list must contain all classes any other library might override method_added on (eg. BlankSlate classes)
153
+ klasses = [Module, (class << Object; self; end), (class << Kernel; self; end)]
154
+ klasses.each do |klass|
155
+ klass.class_eval do
156
+ if method_defined? :method_added
157
+ old_method_added = MonkeyShield.unique_method_name(:method_added)
158
+ alias_method old_method_added, :method_added
159
+ end
160
+
161
+ if method_defined? :singleton_method_added
162
+ old_singleton_method_added = MonkeyShield.unique_method_name(:singleton_method_added)
163
+ alias_method old_singleton_method_added, :singleton_method_added
164
+ end
165
+
166
+ class_eval <<-EOF, __FILE__, __LINE__
167
+ def __MONKEY__method_added__proxy method_name
168
+ __MONKEY__method_added(self, method_name)
169
+ #{old_method_added && "#{old_method_added} method_name"}
170
+ end
171
+
172
+ def __MONKEY__singleton_method_added__proxy method_name
173
+ __MONKEY__method_added((class<<self;self;end), method_name)
174
+ #{old_singleton_method_added && "#{old_singleton_method_added} method_name"}
175
+ end
176
+ EOF
177
+
178
+ alias_method :method_added, :__MONKEY__method_added__proxy
179
+ alias_method :singleton_method_added, :__MONKEY__singleton_method_added__proxy
180
+ end
181
+ end
182
+
183
+
184
+ @method_added_hooks_aliased = true
185
+ end
186
+
187
+ def hook_module_function(&blk)
188
+ old_module_function = MonkeyShield.unique_method_name(:module_function)
189
+ Module.class_eval do
190
+ alias_method old_module_function, :module_function
191
+ alias_method :module_function, :__MONKEY__module_function
192
+ end
193
+
194
+ yield
195
+ ensure
196
+ Module.class_eval { alias_method :module_function, old_module_function }
197
+ end
198
+
199
+ def in_context(context, &blk)
200
+ push_context context
201
+ yield
202
+ ensure
203
+ pop_context
204
+ end
205
+
206
+ def context_stack
207
+ s = Thread.current[:__MONKEY__method_context] and s.dup
208
+ end
209
+
210
+ def current_context
211
+ s = Thread.current[:__MONKEY__method_context] and s.last
212
+ end
213
+
214
+ def push_context(context)
215
+ (Thread.current[:__MONKEY__method_context] ||= []).push context
216
+ end
217
+
218
+ def pop_context
219
+ s = Thread.current[:__MONKEY__method_context] and s.pop
220
+ end
221
+
222
+ def unique
223
+ $unique_counter ||= 0
224
+ $unique_counter += 1
225
+ end
226
+
227
+ def prefix_with_context(method_name, context)
228
+ "__MONKEY__context__#{context}__#{method_name}"
229
+ end
230
+
231
+ def unique_method_name(method_name)
232
+ "__MONKEY__unique_method__#{unique}__#{method_name}"
233
+ end
234
+
235
+ def temp_method_name
236
+ "__MONKEY__temp_method__#{unique}"
237
+ end
238
+
239
+ def hook_method_added(hook = true, &blk)
240
+ orig, @hook_method_added = @hook_method_added, hook
241
+ yield
242
+ ensure
243
+ @hook_method_added = orig
244
+ end
245
+
246
+ def ignore_method_added(&blk)
247
+ hook_method_added false, &blk
248
+ end
249
+
250
+ def hook_method_added?
251
+ @hook_method_added
252
+ end
253
+
254
+ def warnings
255
+ if Object.const_defined? :BasicObject and ! $LOADED_FEATURES.grep(/facets.+basic.?object/).empty?
256
+ raise "BasicObject on Facets <= 2.4.1 will BREAK this library, use alternative blankslate/basicobject class"
257
+ end
258
+ end
259
+ end
260
+ end
@@ -0,0 +1 @@
1
+ require 'monkey_shield'
@@ -0,0 +1,23 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "monkey_shield"
3
+ s.version = "0.1.0"
4
+ s.summary = "protects you from monkey patching!"
5
+ s.email = "coderrr.contact@gmail.com"
6
+ s.homepage = "http://github.com/coderrr/monkey_shieldgrit"
7
+ s.description = "gets around the method collision problem of monkey patching by allowing you to define methods in contexts"
8
+ s.has_rdoc = true
9
+ s.authors = ["coderrr"]
10
+ s.files = [
11
+ "History.txt",
12
+ "Manifest.txt",
13
+ "README.txt",
14
+ "Rakefile",
15
+ "monkey_shield.gemspec",
16
+ "lib/monkey_shield.rb",
17
+ "lib/monkeyshield.rb"]
18
+ s.test_files = ["spec/monkey_shield_spec.rb",
19
+ "spec/real_libs_spec_explicit.rb"]
20
+ s.rdoc_options = ["--main", "README.txt"]
21
+ s.extra_rdoc_files = ["History.txt", "Manifest.txt", "README.txt"]
22
+ s.add_dependency("RubyInline", [">= 3.6.7"])
23
+ end
@@ -0,0 +1,304 @@
1
+ require 'spec'
2
+ require File.dirname(__FILE__) + '/../lib/monkey_shield'
3
+
4
+ $GLOBAL_SCOPE_BINDING = binding
5
+
6
+ describe MonkeyShield do
7
+ def get_klasses
8
+ klasses = []; ObjectSpace.each_object(Module) {|k| klasses << k }; klasses
9
+ end
10
+
11
+ before do
12
+ MonkeyShield.reset!
13
+ @current_klasses = get_klasses
14
+ end
15
+
16
+ after do
17
+ (get_klasses - @current_klasses).each {|k| Object.send :remove_const, k.name }
18
+ end
19
+
20
+ it "aliasing a method then redefining it with the same name should not create an infinite loop" do
21
+ MonkeyShield.wrap_with_context(:ggg) do
22
+ module X
23
+ def encoding= e
24
+ @encoding = e
25
+ end
26
+ end
27
+
28
+ class Y
29
+ include X
30
+
31
+ alias :old_enc= :encoding=
32
+ def encoding= e
33
+ if e.nil?
34
+ self.old_enc = 'UTF-8'
35
+ else
36
+ self.old_enc = e
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ proc { Y.new.encoding = 'abc' }.should_not raise_error
43
+ end
44
+
45
+ it "should be work in the global scope" do
46
+ eval(<<-EOF, $GLOBAL_SCOPE_BINDING)
47
+ MonkeyShield.wrap_with_context :lib1 do
48
+ def to_xml
49
+ "lib1 xml"
50
+ end
51
+ end
52
+
53
+ MonkeyShield.wrap_with_context :lib2 do
54
+ def to_xml
55
+ "lib2 xml"
56
+ end
57
+ end
58
+ EOF
59
+
60
+ MonkeyShield.context_switch_for Object, :to_xml
61
+
62
+ o = Object.new
63
+ proc { o.to_xml }.should raise_error(MonkeyShield::NoContextError)
64
+
65
+ MonkeyShield.in_context(:lib1) { o.to_xml }.should == "lib1 xml"
66
+ MonkeyShield.in_context(:lib2) { o.to_xml }.should == "lib2 xml"
67
+ end
68
+
69
+ it "methods of the same name should be able to exist peacefully in different contexts" do
70
+ MonkeyShield.wrap_with_context :lib1 do
71
+ class Object
72
+ def to_xml
73
+ "lib1 xml"
74
+ end
75
+ end
76
+
77
+ class Lib1
78
+ def x(o)
79
+ o.to_xml
80
+ end
81
+
82
+ def mycontext
83
+ MonkeyShield.current_context
84
+ end
85
+ end
86
+ end
87
+
88
+ MonkeyShield.wrap_with_context :lib2 do
89
+ class Object
90
+ def to_xml
91
+ "lib2 xml"
92
+ end
93
+ end
94
+
95
+ class Lib2
96
+ def x(o)
97
+ o.to_xml
98
+ end
99
+
100
+ def mycontext
101
+ MonkeyShield.current_context
102
+ end
103
+ end
104
+ end
105
+
106
+ Lib1.new.mycontext.should == :lib1
107
+ Lib2.new.mycontext.should == :lib2
108
+ MonkeyShield.current_context.should be_nil
109
+
110
+ MonkeyShield.context_switch_for Object, :to_xml
111
+
112
+ o = Object.new
113
+ Lib1.new.x(o).should == "lib1 xml"
114
+ Lib2.new.x(o).should == "lib2 xml"
115
+
116
+ proc { o.to_xml }.should raise_error(MonkeyShield::NoContextError)
117
+
118
+ MonkeyShield.set_default_context_for Object, :to_xml, :lib1
119
+ o.to_xml.should == "lib1 xml"
120
+
121
+ MonkeyShield.set_default_context_for Object, :to_xml, :lib2
122
+ o.to_xml.should == "lib2 xml"
123
+
124
+ # still works after setting default?
125
+ Lib1.new.x(o).should == "lib1 xml"
126
+ Lib2.new.x(o).should == "lib2 xml"
127
+ end
128
+
129
+ it "ignored method should not be wrapped in context" do
130
+ MonkeyShield.wrap_with_context(:ggg, ['M#ggg']) do
131
+ class A
132
+ def ggg
133
+ "ggg"
134
+ end
135
+
136
+ def hhh
137
+ "hhh"
138
+ end
139
+ end
140
+
141
+ module M
142
+ def ggg
143
+ super
144
+ end
145
+ end
146
+
147
+ module H
148
+ def hhh
149
+ super
150
+ end
151
+ end
152
+
153
+ class B < A
154
+ include M
155
+ include H
156
+ end
157
+ end
158
+
159
+ B.new.ggg.should == "ggg"
160
+ proc { B.new.hhh }.should raise_error(MonkeyShield::MethodDefinedInModuleCallsSuper)
161
+ end
162
+
163
+ it "oo visibility should be preserved" do
164
+ MonkeyShield.wrap_with_context :lib1 do
165
+ class Object
166
+ public
167
+ def pub; end
168
+ protected
169
+ def prot; end
170
+ private
171
+ def priv; end
172
+ end
173
+ end
174
+
175
+ Object.public_instance_methods.index('pub').should_not be_nil
176
+ Object.protected_instance_methods.index('prot').should_not be_nil
177
+ Object.private_instance_methods.index('priv').should_not be_nil
178
+ end
179
+
180
+ it "context switched methods calling context switched methods should work" do
181
+ MonkeyShield.wrap_with_context(:lib1) do
182
+ class Object
183
+ def to_xml
184
+ "lib1 xml"
185
+ end
186
+ end
187
+
188
+ class Array
189
+ def to_g
190
+ map { |o| o.to_xml }
191
+ end
192
+ end
193
+
194
+ class Lib1
195
+ def x(o)
196
+ o.to_g
197
+ end
198
+ end
199
+ end
200
+
201
+ MonkeyShield.wrap_with_context(:lib2) do
202
+ class Object
203
+ def to_xml
204
+ "lib2 xml"
205
+ end
206
+ end
207
+
208
+ class Array
209
+ def to_g
210
+ map { |o| o.to_xml + "huh" }
211
+ end
212
+ end
213
+
214
+ class Lib2
215
+ def x(o)
216
+ o.to_g
217
+ end
218
+ end
219
+ end
220
+
221
+ MonkeyShield.context_switch_for Array, :to_g
222
+ MonkeyShield.context_switch_for Object, :to_xml
223
+
224
+ o = Array.new(1)
225
+ Lib1.new.x(o).should == ["lib1 xml"]
226
+ Lib2.new.x(o).should == ["lib2 xmlhuh"]
227
+ MonkeyShield.current_context.should be_nil
228
+
229
+ MonkeyShield.set_default_context_for Array, :to_g, :lib1
230
+ o.to_g.should == ["lib1 xml"]
231
+ MonkeyShield.set_default_context_for Array, :to_g, :lib2
232
+ o.to_g.should == ["lib2 xmlhuh"]
233
+
234
+ Lib1.new.x(o).should == ["lib1 xml"]
235
+ Lib2.new.x(o).should == ["lib2 xmlhuh"]
236
+ end
237
+
238
+ it "the behavior of module_function should be preserved" do
239
+ MonkeyShield.wrap_with_context :fileutils do
240
+ module G
241
+ def x
242
+ :x
243
+ end
244
+ module_function :x
245
+ end
246
+
247
+ module H
248
+ module_function
249
+
250
+ def a
251
+ :a
252
+ end
253
+ end
254
+
255
+ class X
256
+ include G
257
+ include H
258
+ end
259
+ end
260
+
261
+ G.x.should == :x
262
+ H.a.should == :a
263
+
264
+ X.new.send(:x).should == :x
265
+ X.new.send(:a).should == :a
266
+ end
267
+
268
+ it "singleton methods should be context switchable" do
269
+ MonkeyShield.wrap_with_context :fileutils do
270
+ module FileUtils
271
+ def self.mkdir_p
272
+ mkdir
273
+ end
274
+
275
+ def self.mkdir
276
+ "blah!"
277
+ end
278
+ end
279
+ end
280
+
281
+ MonkeyShield.wrap_with_context :my_fileutils do
282
+ module FileUtils
283
+ def self.mkdir
284
+ "not blah!"
285
+ end
286
+ end
287
+ end
288
+
289
+ MonkeyShield.context_switch_for((class << FileUtils; self; end), :mkdir)
290
+ FileUtils.mkdir_p.should == "blah!"
291
+ end
292
+
293
+ it "irregular method names should be handled" do
294
+ MonkeyShield.wrap_with_context :lib1 do
295
+ class Object
296
+ define_method "abc[def]abc" do
297
+ :hehe
298
+ end
299
+ end
300
+ end
301
+
302
+ Object.send("abc[def]abc").should == :hehe
303
+ end
304
+ end
@@ -0,0 +1,42 @@
1
+ require 'spec'
2
+ require File.dirname(__FILE__) + '/../lib/monkey_shield'
3
+
4
+ describe "MonkeyShield with some real libs" do
5
+ MonkeyShield.wrap_with_context :rails, ['ActiveSupport::CoreExtensions::LoadErrorExtensions::LoadErrorClassMethods#new'] do
6
+ require 'activesupport' # defines String#each_char
7
+ require 'activerecord' # load just to see if we get any errors
8
+
9
+ class String
10
+ def sum
11
+ sum = ""
12
+ each_char {|c| sum += "#{c}+" }
13
+ sum.sub(/\+$/,'')
14
+ end
15
+ end
16
+ end
17
+
18
+ MonkeyShield.wrap_with_context :hpricot do
19
+ require 'hpricot' # load just to see if we get any errors
20
+ end
21
+
22
+ MonkeyShield.wrap_with_context :my_libs do
23
+ class String
24
+ alias each_char each_byte
25
+
26
+ def sum
27
+ sum = 0
28
+ each_char {|c| sum += c }
29
+ sum
30
+ end
31
+ end
32
+ end
33
+
34
+ it "should work" do
35
+ MonkeyShield.context_switch_for String, :sum
36
+ MonkeyShield.context_switch_for String, :each_char
37
+ MonkeyShield.set_default_context_for String, :each_char, :my_libs
38
+
39
+ MonkeyShield.in_context(:my_libs) { "abc".sum }.should == 294
40
+ MonkeyShield.in_context(:rails) { "abc".sum }.should == "a+b+c"
41
+ end
42
+ end
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: coderrr-monkey_shield
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - coderrr
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-10-09 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: RubyInline
17
+ version_requirement:
18
+ version_requirements: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 3.6.7
23
+ version:
24
+ description: gets around the method collision problem of monkey patching by allowing you to define methods in contexts
25
+ email: coderrr.contact@gmail.com
26
+ executables: []
27
+
28
+ extensions: []
29
+
30
+ extra_rdoc_files:
31
+ - History.txt
32
+ - Manifest.txt
33
+ - README.txt
34
+ files:
35
+ - History.txt
36
+ - Manifest.txt
37
+ - README.txt
38
+ - Rakefile
39
+ - monkey_shield.gemspec
40
+ - lib/monkey_shield.rb
41
+ - lib/monkeyshield.rb
42
+ has_rdoc: true
43
+ homepage: http://github.com/coderrr/monkey_shieldgrit
44
+ post_install_message:
45
+ rdoc_options:
46
+ - --main
47
+ - README.txt
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: "0"
55
+ version:
56
+ required_rubygems_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: "0"
61
+ version:
62
+ requirements: []
63
+
64
+ rubyforge_project:
65
+ rubygems_version: 1.2.0
66
+ signing_key:
67
+ specification_version: 2
68
+ summary: protects you from monkey patching!
69
+ test_files:
70
+ - spec/monkey_shield_spec.rb
71
+ - spec/real_libs_spec_explicit.rb