pachinko 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/lib/pachinko.rb +151 -0
- metadata +96 -0
data/lib/pachinko.rb
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
require 'ansi'
|
2
|
+
|
3
|
+
# A little helper to manage patch application and warn if it looks no longer necessary
|
4
|
+
|
5
|
+
# Example format of a patch:
|
6
|
+
# class YourPatch < Pachinko
|
7
|
+
# # name: any string naming this patch
|
8
|
+
# def name
|
9
|
+
# 'Example Patch Creating FabricatedClass'
|
10
|
+
# end
|
11
|
+
# # relevant?: a method that returns true only if the patch needs to be applied
|
12
|
+
# def relevant?
|
13
|
+
# !defined?(FabricatedClass)
|
14
|
+
# end
|
15
|
+
# # PATCH: A constant which contains a lambda which applies the patch
|
16
|
+
# # Reason for this is that you can't reopen classes in methods...
|
17
|
+
# PATCH = ->{
|
18
|
+
# class ::FabricatedClass; end
|
19
|
+
# }
|
20
|
+
# end
|
21
|
+
|
22
|
+
class Pachinko
|
23
|
+
|
24
|
+
attr_reader :results
|
25
|
+
|
26
|
+
def success?
|
27
|
+
@success
|
28
|
+
end
|
29
|
+
|
30
|
+
def applied?
|
31
|
+
@applied
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.mute?
|
35
|
+
ENV['MUTE_PACHINKO']
|
36
|
+
end
|
37
|
+
|
38
|
+
class << self
|
39
|
+
attr_accessor :last_patch
|
40
|
+
|
41
|
+
def run(*args)
|
42
|
+
new.run(*args)
|
43
|
+
end
|
44
|
+
alias_method :run_if_needed, :run
|
45
|
+
def development_mode?
|
46
|
+
defined?(Rails) && defined?(::Rails.env) && ::Rails.env.development?
|
47
|
+
end
|
48
|
+
def test_mode?
|
49
|
+
if defined?(Rails) && defined?(::Rails.env)
|
50
|
+
::Rails.env.test?
|
51
|
+
else
|
52
|
+
true
|
53
|
+
end
|
54
|
+
end
|
55
|
+
def last_results
|
56
|
+
last_patch.results
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Allows you to prioritize certain patch files first, such as core object patches
|
61
|
+
PATCH_PRIORITY = lambda { |p| p =~ /\/(hash|string|array|object)/i ? ' ' : p }
|
62
|
+
|
63
|
+
def name
|
64
|
+
raise NotImplementedError, "Your patch doesn't define a 'name' method... please override in your patch class"
|
65
|
+
end
|
66
|
+
|
67
|
+
# This method returns a boolean that actually checks the mechanics of whatever the patch is supposed to fix.
|
68
|
+
# It should return true if the patch still needs to be applied, and false after it has been applied or if it doesn't need to be applied.
|
69
|
+
def relevant?
|
70
|
+
raise NotImplementedError, "Your patch doesn't define a 'relevant?' method which tests whether it is necessary... please override in your patch class"
|
71
|
+
end
|
72
|
+
|
73
|
+
# Overridable in subclasses, although not recommended
|
74
|
+
# Should return true if the patch was applied (which in most cases means it's also no longer applicable)
|
75
|
+
def irrelevant?
|
76
|
+
!relevant?
|
77
|
+
end
|
78
|
+
|
79
|
+
def run(force_plain_output = false)
|
80
|
+
@force_plain_output = force_plain_output
|
81
|
+
@success = false
|
82
|
+
@applied = false
|
83
|
+
output_msgs = []
|
84
|
+
output_msgs_warning = []
|
85
|
+
if relevant?
|
86
|
+
apply
|
87
|
+
@applied = true
|
88
|
+
# Check to see if the patch is no longer needed (i.e., the test is valid, and the patch worked)
|
89
|
+
if irrelevant?
|
90
|
+
output_msgs << success_message
|
91
|
+
@success = true
|
92
|
+
else
|
93
|
+
output_msgs_warning << relevancy_assertion_wrong_message
|
94
|
+
end
|
95
|
+
else
|
96
|
+
output_msgs_warning << patch_not_applied_message
|
97
|
+
end
|
98
|
+
if !output_msgs.empty? && (Pachinko.development_mode? || force_plain_output)
|
99
|
+
log_success output_msgs.join("\n") unless Pachinko.mute?
|
100
|
+
end
|
101
|
+
if !output_msgs_warning.empty?
|
102
|
+
log_warning output_msgs_warning.join("\n")
|
103
|
+
end
|
104
|
+
Pachinko.last_patch = self
|
105
|
+
@results = output_msgs_warning | output_msgs
|
106
|
+
self
|
107
|
+
end
|
108
|
+
|
109
|
+
private
|
110
|
+
def apply
|
111
|
+
begin
|
112
|
+
self.class::PATCH.call
|
113
|
+
rescue NameError => e
|
114
|
+
e.message << "\nNOTE TO PATCH DEVS: If you are writing a patch, it is possible that you just have to root-namespace any relevant classes in your PATCH block with a double colon (::) in front, to avoid this error!"
|
115
|
+
raise e
|
116
|
+
end
|
117
|
+
end
|
118
|
+
def log(out)
|
119
|
+
unless out.empty?
|
120
|
+
Rails.logger.info(out) if defined?(Rails) && defined?(Rails.logger) rescue nil
|
121
|
+
puts out
|
122
|
+
end
|
123
|
+
end
|
124
|
+
alias log_success log
|
125
|
+
alias log_warning log
|
126
|
+
def ansi(color, text)
|
127
|
+
if @force_plain_output # don't colorize
|
128
|
+
text
|
129
|
+
else
|
130
|
+
ANSI.send(color){ text }
|
131
|
+
end
|
132
|
+
end
|
133
|
+
def success_message
|
134
|
+
"#{ansi :yellow, 'Patch'} #{ansi :bold, self.class.to_s} applied"
|
135
|
+
end
|
136
|
+
def caution_prelude
|
137
|
+
"#{ansi :red, 'CAUTION'}: #{ansi :yellow, 'Patch'} #{ansi :bold, self.class.to_s} "
|
138
|
+
end
|
139
|
+
def relevancy_assertion_wrong_message
|
140
|
+
caution_prelude << "either wasn't applied correctly or the test code for its necessity is wrong!"
|
141
|
+
end
|
142
|
+
def patch_not_applied_message
|
143
|
+
caution_prelude << "NOT applied! (test for necessity was false)"
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
147
|
+
|
148
|
+
########## inline test running
|
149
|
+
if __FILE__==$PROGRAM_NAME
|
150
|
+
require_relative '../test/pachinko_test'
|
151
|
+
end
|
metadata
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pachinko
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Peter Marreck
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-10-03 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: ansi
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: mocha
|
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
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: active_support
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
description: Pachinko is a Ruby monkeypatch manager tool attempting to control the
|
63
|
+
unpredictable and difficult-to-troubleshoot regressions possible when monkeypatches
|
64
|
+
run amok and unchecked in a medium to large Ruby codebase.
|
65
|
+
email: peter@marreck.com
|
66
|
+
executables: []
|
67
|
+
extensions: []
|
68
|
+
extra_rdoc_files: []
|
69
|
+
files:
|
70
|
+
- lib/pachinko.rb
|
71
|
+
homepage: http://rubygems.org/gems/pachinko
|
72
|
+
licenses:
|
73
|
+
- BSD 3-Clause
|
74
|
+
post_install_message:
|
75
|
+
rdoc_options: []
|
76
|
+
require_paths:
|
77
|
+
- lib
|
78
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
79
|
+
none: false
|
80
|
+
requirements:
|
81
|
+
- - ! '>='
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
85
|
+
none: false
|
86
|
+
requirements:
|
87
|
+
- - ! '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
requirements: []
|
91
|
+
rubyforge_project:
|
92
|
+
rubygems_version: 1.8.25
|
93
|
+
signing_key:
|
94
|
+
specification_version: 3
|
95
|
+
summary: A Ruby monkeypatch manager.
|
96
|
+
test_files: []
|