pachinko 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (2) hide show
  1. data/lib/pachinko.rb +151 -0
  2. 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: []