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.
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: []