debride 1.4.0 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a6fb60e6abb914cdca253c8f91a87ee9f29b9573
4
- data.tar.gz: c277e27c75a4a571e22cc647fe4d98063f944256
3
+ metadata.gz: 8f2ef4f667b2008a96704a15a62f41bc993f0872
4
+ data.tar.gz: 68ee77d6fd2c8687ef8392cbbd910ae959b251c5
5
5
  SHA512:
6
- metadata.gz: dc8279ae3e71097d6b57ade1054d80c596b76e6b47878b8269fd931abca6b497da3780cf85cff3860563b307a2c01f727fe11f1bc03a11287242e3bd376c75d7
7
- data.tar.gz: 560722e6f9d07d1dff4bab14e912cec2d9a019046ee4a5dcfc7a6394e98c9f933e0ff1c4c9dc9b85f2cdd1ad2093889acd2926fb05246bfc96c3bd89bbf477a7
6
+ metadata.gz: 4f226eb6901be45033201a3ceb29498d253b13fe20c998bdbe8c1200d6401f3785111690437e47f10ecb87919c2c0c73b057a68a8d9a0a630d92ab6e4f067276
7
+ data.tar.gz: ad86dccb33825e6157c33ec72f919dc9f2e3d9730b6a67c35664814fdf4517c27b3f1ca214579c83c4b1a448e4e38a97b1d2282e0215148c21426cdcd57cded0
@@ -1,3 +1,2 @@
1
- qX4R�uDGV���B����gN
2
- SL&�:�;ˑL����Z�ȝ�2��d�=&|�)H��6�KB���V�.�y�^Ӹ�>��2!{H?�K�ųf8t8Z4̀`�h3sMD�������uh��u۔�E<�.�*�(C��F���L|H��"�*�[��K
3
- ��Jc���}����
1
+ ̸H+
2
+ �n:Tၑ��k,!m �h#xk:dd!���L�ȱC���R�8��(�#=��Bsaq�_����(K8����i���B`��;�׳���O�|�vU%�i�&QWwaωޞ�7x�o]��������iiK�4���1B���-/��*���9������:�Ғ��3X��V>0n�>-����3 ���˾bI���Q �5#� �h�meǗ��2�a ,�k:��N,x��9.�4��}�6;~t
data.tar.gz.sig CHANGED
Binary file
@@ -1,3 +1,20 @@
1
+ === 1.5.0 / 2015-06-14
2
+
3
+ * 6 minor enhancements:
4
+
5
+ * Added --focus <path> to filter report on focused path. (phiggins)
6
+ * Added debride_rails_whitelist to help generate emperical whitelists from logs.
7
+ * Added validate to rails methods. (jeremyf)
8
+ * Extended debride to understand attr_* methods. (jeremyf)
9
+ * Extended debride_rails_whitelist to be able to scan compressed logs (gzip/bzip)
10
+ * Improved output of debride_rails_whitelist so you know what's going on. (amerine)
11
+
12
+ * 3 bug fixes:
13
+
14
+ * Fixed handling of uncalled attr_* methods in reports.
15
+ * Fixed improper recording of locations of consts and alias_method_chain.
16
+ * Fixed recording of method names.
17
+
1
18
  === 1.4.0 / 2015-05-27
2
19
 
3
20
  * 1 major enhancement:
@@ -4,5 +4,6 @@ Manifest.txt
4
4
  README.rdoc
5
5
  Rakefile
6
6
  bin/debride
7
+ bin/debride_rails_whitelist
7
8
  lib/debride.rb
8
9
  test/test_debride.rb
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/ruby -w
2
+
3
+ route = ARGV.shift
4
+ routes = {}
5
+ verbs = "(?:GET|POST|PUT|PATCH|DELETE)"
6
+
7
+ unless route then
8
+ cmd = File.basename $0
9
+ warn "error: no files given"
10
+ warn "usage:"
11
+ warn " rake routes > routes.txt"
12
+ warn " #{cmd} routes.txt logfiles... | sort -u > whitelist.txt"
13
+ abort
14
+ end
15
+
16
+ File.foreach route do |line|
17
+ case line.chomp
18
+ when /^\s+Prefix/, "" then
19
+ # ignore
20
+ when /^Routes for /
21
+ break
22
+ when /^\s*(\w+) (#{verbs})\s+(\S+)\s+([\w\/]+#\w+)/ then
23
+ _, verb, path, action = $1, $2, $3, $4
24
+ path.sub!(/..:format./, '\\/?')
25
+ path.sub!(/:id/, '\\d+')
26
+ routes[/^#{verb} #{path}$/] = action.split(/#/).last
27
+ when /^\s*(GET|POST|PUT|PATCH|DELETE)\s+(\S+)\s+([\w\/]+#\w+)/ then
28
+ verb, path, action = $1, $2, $3
29
+ path.sub!(/..:format./, '')
30
+ path.sub!(/:id/, '\\d+')
31
+ routes[/^#{verb} #{path}$/] = action.split(/#/).last
32
+ else
33
+ # warn "unparsed: #{line.chomp}"
34
+ end
35
+ end
36
+
37
+ warn "NOTE: No logs provided. Scanning stdin." if ARGV.empty?
38
+
39
+ ARGV.each do |path|
40
+ warn path
41
+ cmd = case path
42
+ when /\.gz$/ then
43
+ "gzcat"
44
+ when /\.bz2?$/ then
45
+ "bzcat"
46
+ else
47
+ "cat"
48
+ end
49
+
50
+ IO.popen [cmd, path] do |io|
51
+ io.each_line do |line|
52
+ $stderr.print "." if $. % 10_000 == 0
53
+ case line
54
+ when /Processing by (\w+)#(\w+) as/ then
55
+ _, action = $1, $2
56
+ puts action
57
+ when /Started (#{verbs}) \"([^"]+)\"/, /method=(#{verbs}) path="([^"]+)"/ then
58
+ verb, path = $1, $2
59
+
60
+ _, action = routes.find { |k,v| "#{verb} #{path}" =~ k }
61
+
62
+ puts action if action
63
+ else
64
+ # warn "unparsed: #{line.chomp}"
65
+ end
66
+ end
67
+ end
68
+ end
69
+
70
+ warn "done"
@@ -19,7 +19,7 @@ end
19
19
  # A static code analyzer that points out possible dead methods.
20
20
 
21
21
  class Debride < MethodBasedSexpProcessor
22
- VERSION = "1.4.0" # :nodoc:
22
+ VERSION = "1.5.0" # :nodoc:
23
23
  PROJECT = "debride"
24
24
 
25
25
  def self.expand_dirs_to_files *dirs # TODO: push back up to sexp_processor
@@ -100,7 +100,7 @@ class Debride < MethodBasedSexpProcessor
100
100
 
101
101
  def process_rb path_or_io
102
102
  begin
103
- warn "processing: #{path}" if option[:verbose]
103
+ warn "Processing ruby: #{path_or_io}" if option[:verbose]
104
104
 
105
105
  case path_or_io
106
106
  when String then
@@ -147,6 +147,16 @@ class Debride < MethodBasedSexpProcessor
147
147
  options[:whitelist] = File.read(s).split(/\n+/) rescue []
148
148
  end
149
149
 
150
+ opts.on("-f", "--focus PATH", String, "Only report against this path") do |s|
151
+ unless File.exist? s then
152
+ abort "ERROR: --focus path #{s} doesn't exist."
153
+ end
154
+
155
+ s = "#{s.chomp "/"}/*" if File.directory?(s)
156
+
157
+ options[:focus] = s
158
+ end
159
+
150
160
  opts.on("-r", "--rails", "Add some rails call conversions.") do
151
161
  options[:rails] = true
152
162
  end
@@ -154,13 +164,18 @@ class Debride < MethodBasedSexpProcessor
154
164
  opts.on("-v", "--verbose", "Verbose. Show progress processing files.") do
155
165
  options[:verbose] = true
156
166
  end
157
-
158
- opts.parse! args
159
167
  end
160
168
 
169
+ op.parse! args
170
+
161
171
  abort op.to_s if args.empty?
162
172
 
163
173
  options
174
+ rescue OptionParser::InvalidOption => e
175
+ warn op.to_s
176
+ warn ""
177
+ warn e.message
178
+ exit 1
164
179
  end
165
180
 
166
181
  ##
@@ -194,17 +209,111 @@ class Debride < MethodBasedSexpProcessor
194
209
  super.to_s
195
210
  end
196
211
 
197
- def method_name # :nodoc:
198
- super.to_s.sub(/^::|#/, "").to_sym
212
+ def plain_method_name # :nodoc:
213
+ method_name.to_s.sub(/^::|#/, "").to_sym
214
+ end
215
+
216
+ def process_attrasgn(sexp)
217
+ method_name = sexp[2]
218
+ method_name = method_name.last if Sexp === method_name
219
+ called << method_name
220
+ process_until_empty sexp
221
+ sexp
222
+ end
223
+
224
+ def record_method name, file, line
225
+ signature = "#{klass_name}##{name}"
226
+ method_locations[signature] = "#{file}:#{line}"
227
+ map[klass_name][name] = signature
228
+ known[name] << klass_name
229
+ end
230
+
231
+ def process_call sexp # :nodoc:
232
+ method_name = sexp[2]
233
+
234
+ case method_name
235
+ when :new then
236
+ method_name = :initialize
237
+ when :alias_method_chain then
238
+ # s(:call, nil, :alias_method_chain, s(:lit, :royale), s(:lit, :cheese))
239
+ _, _, _, (_, new_name), _ = sexp
240
+ if option[:rails] then
241
+ file, line = sexp.file, sexp.line
242
+ record_method new_name, file, line
243
+ end
244
+ when :attr_accessor then
245
+ # s(:call, nil, :attr_accessor, s(:lit, :a1), ...)
246
+ _, _, _, *args = sexp
247
+ file, line = sexp.file, sexp.line
248
+ args.each do |(_, name)|
249
+ record_method name, file, line
250
+ record_method "#{name}=".to_sym, file, line
251
+ end
252
+ when :attr_writer then
253
+ # s(:call, nil, :attr_writer, s(:lit, :w1), ...)
254
+ _, _, _, *args = sexp
255
+ file, line = sexp.file, sexp.line
256
+ args.each do |(_, name)|
257
+ record_method "#{name}=".to_sym, file, line
258
+ end
259
+ when :attr_reader then
260
+ # s(:call, nil, :attr_reader, s(:lit, :r1), ...)
261
+ _, _, _, *args = sexp
262
+ file, line = sexp.file, sexp.line
263
+ args.each do |(_, name)|
264
+ record_method name, file, line
265
+ end
266
+ when :send, :public_send, :__send__ then
267
+ # s(:call, s(:const, :Seattle), :send, s(:lit, :raining?))
268
+ _, _, _, msg_arg, * = sexp
269
+ if Sexp === msg_arg && [:lit, :str].include?(msg_arg.sexp_type) then
270
+ called << msg_arg.last.to_sym
271
+ end
272
+ when *RAILS_VALIDATION_METHODS then
273
+ if option[:rails]
274
+ possible_hash = sexp.last
275
+ if Sexp === possible_hash && possible_hash.sexp_type == :hash
276
+ possible_hash.sexp_body.each_slice(2) do |key, val|
277
+ called << val.last if val.first == :lit
278
+ called << val.last.to_sym if val.first == :str
279
+ end
280
+ end
281
+ end
282
+ when *RAILS_DSL_METHODS then
283
+ if option[:rails]
284
+ # s(:call, nil, :before_save, s(:lit, :save_callback), s(:hash, ...))
285
+ _, _, _, (_, new_name), possible_hash = sexp
286
+ called << new_name
287
+ if Sexp === possible_hash && possible_hash.sexp_type == :hash
288
+ possible_hash.sexp_body.each_slice(2) do |key, val|
289
+ next unless Sexp === val
290
+ called << val.last if val.first == :lit
291
+ called << val.last.to_sym if val.first == :str
292
+ end
293
+ end
294
+ end
295
+ when /_path$/ then
296
+ method_name = method_name.to_s[0..-6].to_sym if option[:rails]
297
+ end
298
+
299
+ called << method_name
300
+
301
+ process_until_empty sexp
302
+
303
+ sexp
199
304
  end
200
305
 
201
306
  def process_cdecl exp # :nodoc:
202
307
  _, name, val = exp
203
308
  process val
204
309
 
205
- map[klass_name][name] = "#{klass_name}::#{name}"
310
+ signature = "#{klass_name}::#{name}"
311
+ map[klass_name][name] = signature
206
312
  known[name] << klass_name
207
313
 
314
+ file, line = exp.file, exp.line
315
+ method_locations[signature] = "#{file}:#{line}"
316
+
208
317
  exp
209
318
  end
210
319
 
@@ -235,70 +344,20 @@ class Debride < MethodBasedSexpProcessor
235
344
 
236
345
  def process_defn sexp # :nodoc:
237
346
  super do
238
- map[klass_name][method_name] = signature
239
- known[method_name] << klass_name
347
+ map[klass_name][plain_method_name] = signature
348
+ known[plain_method_name] << klass_name
240
349
  process_until_empty sexp
241
350
  end
242
351
  end
243
352
 
244
353
  def process_defs sexp # :nodoc:
245
354
  super do
246
- map[klass_name][method_name] = signature
247
- known[method_name] << klass_name
355
+ map[klass_name][plain_method_name] = signature
356
+ known[plain_method_name] << klass_name
248
357
  process_until_empty sexp
249
358
  end
250
359
  end
251
360
 
252
- def process_call sexp # :nodoc:
253
- method_name = sexp[2]
254
-
255
- case method_name
256
- when :new then
257
- method_name = :initialize
258
- when :alias_method_chain then
259
- new_name = sexp[3]
260
- new_name = new_name.last if Sexp === new_name # when is this NOT the case?
261
- known[new_name] << klass_name if option[:rails]
262
- when :send, :public_send, :__send__ then
263
- sent_method = sexp[3]
264
- if Sexp === sent_method && [:lit, :str].include?(sent_method.first)
265
- called << sent_method.last.to_sym
266
- end
267
- when *RAILS_VALIDATION_METHODS then
268
- if option[:rails]
269
- possible_hash = sexp.last
270
- if Sexp === possible_hash && possible_hash.first == :hash
271
- possible_hash[1..-1].each_slice(2) do |key, val|
272
- called << val.last if val.first == :lit
273
- called << val.last.to_sym if val.first == :str
274
- end
275
- end
276
- end
277
- when *RAILS_DSL_METHODS then
278
- if option[:rails]
279
- new_name = sexp[3]
280
- new_name = new_name.last if Sexp === new_name # when is this NOT the case?
281
- called << new_name
282
- possible_hash = sexp.last
283
- if Sexp === possible_hash && possible_hash.first == :hash
284
- possible_hash[1..-1].each_slice(2) do |key, val|
285
- next unless Sexp === val
286
- called << val.last if val.first == :lit
287
- called << val.last.to_sym if val.first == :str
288
- end
289
- end
290
- end
291
- when /_path$/ then
292
- method_name = method_name.to_s[0..-6].to_sym if option[:rails]
293
- end
294
-
295
- called << method_name
296
-
297
- process_until_empty sexp
298
-
299
- sexp
300
- end
301
-
302
361
  ##
303
362
  # Calculate the difference between known methods and called methods.
304
363
 
@@ -338,14 +397,30 @@ class Debride < MethodBasedSexpProcessor
338
397
  # Print out a report of suspects.
339
398
 
340
399
  def report
400
+ focus = option[:focus]
401
+
402
+ if focus then
403
+ puts "Focusing on #{focus}"
404
+ puts
405
+ end
406
+
341
407
  puts "These methods MIGHT not be called:"
342
408
 
343
409
  missing.each do |klass, meths|
410
+ bad = meths.map { |meth|
411
+ location = method_locations[map[klass][meth]]
412
+ path = location[/(.+):\d+$/, 1]
413
+
414
+ next if focus and not File.fnmatch(focus, path)
415
+
416
+ " %-35s %s" % [meth, location]
417
+ }
418
+ bad.compact!
419
+ next if bad.empty?
420
+
344
421
  puts
345
422
  puts klass
346
- meths.each do |meth|
347
- puts " %-35s %s" % [meth, method_locations[map[klass][meth]]]
348
- end
423
+ puts bad.join "\n"
349
424
  end
350
425
  end
351
426
 
@@ -374,6 +449,9 @@ class Debride < MethodBasedSexpProcessor
374
449
  :before_save,
375
450
  :before_update,
376
451
  :before_validation,
452
+
453
+ # http://api.rubyonrails.org/classes/ActiveModel/Validations/ClassMethods.html#method-i-validate
454
+ :validate,
377
455
  ]
378
456
 
379
457
  # http://api.rubyonrails.org/v4.2.1/classes/ActiveModel/Validations/HelperMethods.html
@@ -23,6 +23,7 @@ class TestDebride < Minitest::Test
23
23
  debride.process debride.process_rb io
24
24
 
25
25
  assert_equal exp, debride.missing
26
+ debride
26
27
  end
27
28
 
28
29
  def test_sanity
@@ -35,7 +36,9 @@ class TestDebride < Minitest::Test
35
36
  end
36
37
 
37
38
  exp = [["Debride",
38
- [:process_call, :process_defn, :process_defs, :process_rb, :report]]]
39
+ [:process_attrasgn, :process_call, :process_cdecl, :process_colon2,
40
+ :process_colon3, :process_const, :process_defn, :process_defs,
41
+ :process_rb, :report]]]
39
42
 
40
43
  assert_equal exp, debride.missing
41
44
  end
@@ -65,6 +68,17 @@ class TestDebride < Minitest::Test
65
68
  assert_option %w[-e moot,moot.rb lib], %w[lib], :exclude => %w[moot moot.rb]
66
69
  end
67
70
 
71
+ def test_parse_options_focus
72
+ assert_option %w[-f lib lib], %w[lib], :focus => "lib/*"
73
+ assert_option %w[--focus lib lib], %w[lib], :focus => "lib/*"
74
+
75
+ e = assert_raises RuntimeError do
76
+ assert_option %w[-f missing lib], %w[], :verbose => true
77
+ end
78
+
79
+ assert_includes e.message, "ERROR: --focus path missing doesn't exist."
80
+ end
81
+
68
82
  def test_parse_options_whitelist
69
83
  exp = File.readlines("Manifest.txt").map(&:chomp) # omg dumb
70
84
  assert_option %w[--whitelist Manifest.txt lib], %w[lib], :whitelist => exp
@@ -76,7 +90,9 @@ class TestDebride < Minitest::Test
76
90
  debride = Debride.run %w[--exclude test lib]
77
91
 
78
92
  exp = [["Debride",
79
- [:process_call, :process_defn, :process_defs, :process_rb, :report]]]
93
+ [:process_attrasgn, :process_call, :process_cdecl, :process_colon2,
94
+ :process_colon3, :process_const, :process_defn, :process_defs,
95
+ :process_rb, :report]]]
80
96
 
81
97
  assert_equal exp, debride.missing
82
98
  end
@@ -199,12 +215,14 @@ class TestDebride < Minitest::Test
199
215
  def action_condition ; 1 ; end
200
216
  def string_condition ; 1 ; end
201
217
  def validation_condition ; 1 ; end
218
+ def some_validation_method; 1 ; end
202
219
 
203
220
  before_save :save_callback, unless: :callback_condition
204
221
  before_save :save_callback, if: 'string_condition'
205
222
  before_action :action_filter, if: :action_condition, only: :new
206
223
  after_save :save_callback, if: lambda {|r| true }
207
224
  validates :database_column, if: :validation_condition
225
+ validate :some_validation_method
208
226
  end
209
227
  RUBY
210
228
 
@@ -230,4 +248,86 @@ class TestDebride < Minitest::Test
230
248
 
231
249
  assert_process [["Constants", [:UNUSED]]], ruby
232
250
  end
251
+
252
+ def test_attr_accessor
253
+ ruby = <<-RUBY.strip
254
+ class AttributeAccessor
255
+ attr_accessor :a1, :a2, :a3
256
+ attr_writer :w1, :w2
257
+ attr_reader :r1, :r2
258
+ def initialize
259
+ self.a2 = 'Bar'
260
+ self.w1 = 'W'
261
+ end
262
+ end
263
+
264
+ object = AttributeAccessor.new
265
+ object.a1
266
+ object.r1
267
+ object.a3 = 'Baz'
268
+ RUBY
269
+
270
+ d = assert_process [["AttributeAccessor", [:a1=, :a2, :a3, :r2, :w2=]]], ruby
271
+
272
+ exp = {
273
+ "AttributeAccessor" => {
274
+ :a1 => "AttributeAccessor#a1",
275
+ :a1= => "AttributeAccessor#a1=",
276
+ :a2 => "AttributeAccessor#a2",
277
+ :a2= => "AttributeAccessor#a2=",
278
+ :a3 => "AttributeAccessor#a3",
279
+ :a3= => "AttributeAccessor#a3=",
280
+ :w1= => "AttributeAccessor#w1=",
281
+ :w2= => "AttributeAccessor#w2=",
282
+ :r1 => "AttributeAccessor#r1",
283
+ :r2 => "AttributeAccessor#r2",
284
+ :initialize => "AttributeAccessor#initialize"
285
+ }
286
+ }
287
+
288
+ assert_equal exp, d.map
289
+
290
+ exp = {
291
+ "AttributeAccessor#a1" => "(io):2",
292
+ "AttributeAccessor#a1=" => "(io):2",
293
+ "AttributeAccessor#a2" => "(io):2",
294
+ "AttributeAccessor#a2=" => "(io):2",
295
+ "AttributeAccessor#a3" => "(io):2",
296
+ "AttributeAccessor#a3=" => "(io):2",
297
+ "AttributeAccessor#w1=" => "(io):3",
298
+ "AttributeAccessor#w2=" => "(io):3",
299
+ "AttributeAccessor#r1" => "(io):4",
300
+ "AttributeAccessor#r2" => "(io):4",
301
+ "AttributeAccessor#initialize" => "(io):5",
302
+ }
303
+
304
+ assert_equal exp, d.method_locations
305
+
306
+ out, err = capture_io do
307
+ d.report
308
+ end
309
+
310
+ assert_match(/AttributeAccessor/, out)
311
+ assert_match(/a1=/, out)
312
+ assert_empty err
313
+ end
314
+
315
+ def test_attr_accessor_with_hash_default_value
316
+ ruby = <<-RUBY.strip
317
+ class AttributeAccessor
318
+ attr_accessor :a1
319
+ def initialize(options = {})
320
+ self.a1 = options.fetch(:a1) { default_a1 }
321
+ end
322
+
323
+ def default_a1
324
+ 'the default_a1'
325
+ end
326
+ end
327
+
328
+ object = AttributeAccessor.new
329
+ RUBY
330
+
331
+ assert_process [["AttributeAccessor", [:a1]]], ruby
332
+ end
233
333
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: debride
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.0
4
+ version: 1.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan Davis
@@ -29,7 +29,7 @@ cert_chain:
29
29
  xJcC6UN6NHMOVMyAXsr2HR0gRRx4ofN1LoP2KhXzSr8UMvQYlwPmE0N5GQv1b5AO
30
30
  VpzF30vNaJK6ZT7xlIsIlwmH
31
31
  -----END CERTIFICATE-----
32
- date: 2015-05-27 00:00:00.000000000 Z
32
+ date: 2015-06-15 00:00:00.000000000 Z
33
33
  dependencies:
34
34
  - !ruby/object:Gem::Dependency
35
35
  name: sexp_processor
@@ -65,14 +65,14 @@ dependencies:
65
65
  requirements:
66
66
  - - ~>
67
67
  - !ruby/object:Gem::Version
68
- version: '5.6'
68
+ version: '5.7'
69
69
  type: :development
70
70
  prerelease: false
71
71
  version_requirements: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - ~>
74
74
  - !ruby/object:Gem::Version
75
- version: '5.6'
75
+ version: '5.7'
76
76
  - !ruby/object:Gem::Dependency
77
77
  name: rdoc
78
78
  requirement: !ruby/object:Gem::Requirement
@@ -106,6 +106,7 @@ email:
106
106
  - ryand-ruby@zenspider.com
107
107
  executables:
108
108
  - debride
109
+ - debride_rails_whitelist
109
110
  extensions: []
110
111
  extra_rdoc_files:
111
112
  - History.rdoc
@@ -119,6 +120,7 @@ files:
119
120
  - README.rdoc
120
121
  - Rakefile
121
122
  - bin/debride
123
+ - bin/debride_rails_whitelist
122
124
  - lib/debride.rb
123
125
  - test/test_debride.rb
124
126
  homepage: https://github.com/seattlerb/debride
metadata.gz.sig CHANGED
@@ -1 +1 @@
1
- ��S��m=X��?t]���<�8R�I��BKC?ʓ�g����ʯ���1
1
+ ��Q����|�u�I;常EE�`��}������,���0�Y_�O���S7a����N=IvaYMB��c/���1��0��l���̻N��oq5u���g�-z���@k6�}