ice_nine 0.5.0 → 0.6.0
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/.travis.yml +2 -1
- data/Gemfile +42 -23
- data/Rakefile +0 -1
- data/ice_nine.gemspec +9 -8
- data/lib/ice_nine.rb +0 -1
- data/lib/ice_nine/freezer/object.rb +2 -0
- data/lib/ice_nine/version.rb +1 -1
- data/spec/integration/ice_nine/class_methods/deep_freeze_spec.rb +77 -0
- data/spec/spec_helper.rb +1 -1
- data/spec/support/config_alias.rb +1 -1
- data/tasks/metrics/ci.rake +5 -3
- data/tasks/metrics/flay.rake +25 -23
- data/tasks/metrics/flog.rake +25 -22
- data/tasks/metrics/heckle.rake +123 -126
- data/tasks/metrics/metric_fu.rake +1 -1
- data/tasks/metrics/reek.rake +12 -2
- data/tasks/metrics/roodi.rake +7 -5
- data/tasks/metrics/yardstick.rake +8 -8
- data/tasks/spec.rake +37 -25
- data/tasks/yard.rake +1 -1
- metadata +74 -41
- data/lib/ice_nine/freezer/string.rb +0 -10
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
@@ -4,36 +4,55 @@ source 'https://rubygems.org'
|
|
4
4
|
|
5
5
|
gemspec
|
6
6
|
|
7
|
-
group :
|
8
|
-
gem '
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
gem '
|
13
|
-
gem '
|
14
|
-
gem '
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
7
|
+
group :yard do
|
8
|
+
gem 'redcarpet', '~> 2.2.2', :platforms => [ :mri, :rbx ]
|
9
|
+
end
|
10
|
+
|
11
|
+
group :guard do
|
12
|
+
gem 'guard', '~> 1.5.4'
|
13
|
+
gem 'guard-bundler', '~> 1.0.0'
|
14
|
+
gem 'guard-rspec', '~> 1.2.1'
|
15
|
+
end
|
16
|
+
|
17
|
+
group :benchmarks do
|
18
|
+
gem 'rbench', '~> 0.2.3'
|
19
|
+
end
|
20
|
+
|
21
|
+
platform :jruby do
|
22
|
+
group :jruby do
|
23
|
+
gem 'jruby-openssl', '~> 0.8.2'
|
22
24
|
end
|
25
|
+
end
|
26
|
+
|
27
|
+
group :metrics do
|
28
|
+
gem 'flay', '~> 1.4.3'
|
29
|
+
gem 'flog', '~> 2.5.3'
|
30
|
+
gem 'reek', '~> 1.2.8', :github => 'dkubb/reek'
|
31
|
+
gem 'roodi', '~> 2.1.0'
|
32
|
+
gem 'yardstick', '~> 0.8.0'
|
23
33
|
|
24
|
-
platforms :
|
25
|
-
|
26
|
-
gem '
|
27
|
-
gem 'mspec', '~> 1.5.17'
|
28
|
-
gem 'ruby2ruby', '= 1.2.2'
|
34
|
+
platforms :ruby_18, :ruby_19 do
|
35
|
+
# this indirectly depends on ffi which does not build on ruby-head
|
36
|
+
gem 'yard-spellcheck', '~> 0.1.5'
|
29
37
|
end
|
30
38
|
|
31
39
|
platforms :mri_18 do
|
32
|
-
gem '
|
33
|
-
gem '
|
40
|
+
gem 'arrayfields', '~> 4.7.4' # for metric_fu
|
41
|
+
gem 'fattr', '~> 2.2.0' # for metric_fu
|
42
|
+
gem 'heckle', '~> 1.4.3'
|
43
|
+
gem 'json', '~> 1.7.3' # for metric_fu rake task
|
44
|
+
gem 'map', '~> 6.2.0' # for metric_fu
|
45
|
+
gem 'metric_fu', '~> 2.1.1'
|
46
|
+
gem 'mspec', '~> 1.5.17'
|
47
|
+
gem 'rcov', '~> 1.0.0'
|
48
|
+
gem 'ruby2ruby', '= 1.2.2' # for heckle
|
49
|
+
end
|
50
|
+
|
51
|
+
platforms :ruby_19 do
|
52
|
+
gem 'simplecov', '~> 0.7.1'
|
34
53
|
end
|
35
54
|
|
36
55
|
platforms :rbx do
|
37
|
-
gem 'pelusa', '~> 0.2.
|
56
|
+
gem 'pelusa', '~> 0.2.2'
|
38
57
|
end
|
39
58
|
end
|
data/Rakefile
CHANGED
data/ice_nine.gemspec
CHANGED
@@ -4,18 +4,19 @@ require File.expand_path('../lib/ice_nine/version', __FILE__)
|
|
4
4
|
|
5
5
|
Gem::Specification.new do |gem|
|
6
6
|
gem.name = 'ice_nine'
|
7
|
-
gem.version = IceNine::VERSION
|
8
|
-
gem.authors = [
|
9
|
-
gem.email = [
|
7
|
+
gem.version = IceNine::VERSION.dup
|
8
|
+
gem.authors = ['Dan Kubb']
|
9
|
+
gem.email = %w[dan.kubb@gmail.com]
|
10
10
|
gem.description = 'Deep Freeze Ruby Objects'
|
11
11
|
gem.summary = gem.description
|
12
12
|
gem.homepage = 'https://github.com/dkubb/ice_nine'
|
13
13
|
|
14
|
-
gem.require_paths = %w[
|
15
|
-
gem.files = `git ls-files`.split(
|
16
|
-
gem.test_files = `git ls-files -- {spec}
|
17
|
-
gem.extra_rdoc_files = %w[
|
14
|
+
gem.require_paths = %w[lib]
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.test_files = `git ls-files -- {spec}/{unit,integration}`.split($/)
|
17
|
+
gem.extra_rdoc_files = %w[LICENSE README.md TODO]
|
18
18
|
|
19
|
-
gem.add_development_dependency('rake', '~> 0.
|
19
|
+
gem.add_development_dependency('rake', '~> 10.0.3')
|
20
20
|
gem.add_development_dependency('rspec', '~> 1.3.2')
|
21
|
+
gem.add_development_dependency('yard', '~> 0.8.3')
|
21
22
|
end
|
data/lib/ice_nine.rb
CHANGED
@@ -16,7 +16,6 @@ require 'ice_nine/freezer/nil_class'
|
|
16
16
|
require 'ice_nine/freezer/numeric'
|
17
17
|
require 'ice_nine/freezer/range'
|
18
18
|
require 'ice_nine/freezer/rubinius'
|
19
|
-
require 'ice_nine/freezer/string'
|
20
19
|
require 'ice_nine/freezer/struct'
|
21
20
|
require 'ice_nine/freezer/symbol'
|
22
21
|
require 'ice_nine/freezer/true_class'
|
data/lib/ice_nine/version.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'spec_helper'
|
4
4
|
require 'ice_nine'
|
5
|
+
require 'delegate'
|
5
6
|
|
6
7
|
describe IceNine, '.deep_freeze' do
|
7
8
|
subject { object.deep_freeze(value) }
|
@@ -142,6 +143,44 @@ describe IceNine, '.deep_freeze' do
|
|
142
143
|
end
|
143
144
|
end
|
144
145
|
|
146
|
+
context 'with a String' do
|
147
|
+
let(:value) { '' }
|
148
|
+
|
149
|
+
before do
|
150
|
+
value.instance_eval { @a = '1' }
|
151
|
+
end
|
152
|
+
|
153
|
+
it 'returns the object' do
|
154
|
+
should be(value)
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'freezes the object' do
|
158
|
+
expect { subject }.should change(value, :frozen?).from(false).to(true)
|
159
|
+
end
|
160
|
+
|
161
|
+
it 'freezes the instance variables in the String' do
|
162
|
+
subject.instance_variable_get(:@a).should be_frozen
|
163
|
+
end
|
164
|
+
|
165
|
+
context 'with a circular reference' do
|
166
|
+
before do
|
167
|
+
value.instance_eval { @self = self }
|
168
|
+
end
|
169
|
+
|
170
|
+
it 'returns the object' do
|
171
|
+
should be(value)
|
172
|
+
end
|
173
|
+
|
174
|
+
it 'freezes the object' do
|
175
|
+
expect { subject }.should change(value, :frozen?).from(false).to(true)
|
176
|
+
end
|
177
|
+
|
178
|
+
it 'freezes the instance variables in the String' do
|
179
|
+
subject.instance_variable_get(:@a).should be_frozen
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
145
184
|
context 'with a Struct' do
|
146
185
|
let(:value) { klass.new(%w[ 1 2 ]) }
|
147
186
|
let(:klass) { Struct.new(:a) }
|
@@ -177,6 +216,44 @@ describe IceNine, '.deep_freeze' do
|
|
177
216
|
end
|
178
217
|
end
|
179
218
|
|
219
|
+
context 'with an SimpleDelegator' do
|
220
|
+
let(:value) { SimpleDelegator.new(nil) }
|
221
|
+
|
222
|
+
before do
|
223
|
+
value.instance_eval { @a = '1' }
|
224
|
+
end
|
225
|
+
|
226
|
+
it 'returns the object' do
|
227
|
+
should be(value)
|
228
|
+
end
|
229
|
+
|
230
|
+
it 'freezes the object' do
|
231
|
+
expect { subject }.should change(value, :frozen?).from(false).to(true)
|
232
|
+
end
|
233
|
+
|
234
|
+
it 'freezes the instance variables in the SimpleDelegator' do
|
235
|
+
subject.instance_variable_get(:@a).should be_frozen
|
236
|
+
end
|
237
|
+
|
238
|
+
context 'with a circular reference' do
|
239
|
+
before do
|
240
|
+
value.instance_eval { @self = self }
|
241
|
+
end
|
242
|
+
|
243
|
+
it 'returns the object' do
|
244
|
+
should be(value)
|
245
|
+
end
|
246
|
+
|
247
|
+
it 'freezes the object' do
|
248
|
+
expect { subject }.should change(value, :frozen?).from(false).to(true)
|
249
|
+
end
|
250
|
+
|
251
|
+
it 'freezes the instance variables in the SimpleDelegator' do
|
252
|
+
subject.instance_variable_get(:@a).should be_frozen
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
180
257
|
[0.0, 0, 0x7fffffffffffffff, true, false, nil, :symbol].each do |value|
|
181
258
|
context "with a #{value.class}" do
|
182
259
|
let(:value) { value }
|
data/spec/spec_helper.rb
CHANGED
data/tasks/metrics/ci.rake
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
desc 'Run metrics with Heckle'
|
2
|
-
task :ci => %w[ ci:metrics heckle ]
|
4
|
+
task :ci => %w[ ci:metrics metrics:heckle ]
|
3
5
|
|
4
6
|
namespace :ci do
|
5
|
-
desc 'Run metrics'
|
6
|
-
task :metrics => %w[ verify_measurements flog flay reek roodi metrics:all ]
|
7
|
+
desc 'Run metrics (except heckle) and spec'
|
8
|
+
task :metrics => %w[ spec metrics:verify_measurements metrics:flog metrics:flay metrics:reek metrics:roodi metrics:all ]
|
7
9
|
end
|
data/tasks/metrics/flay.rake
CHANGED
@@ -7,37 +7,39 @@ begin
|
|
7
7
|
config = YAML.load_file(File.expand_path('../../../config/flay.yml', __FILE__)).freeze
|
8
8
|
threshold = config.fetch('threshold').to_i
|
9
9
|
total_score = config.fetch('total_score').to_f
|
10
|
-
files = Flay.expand_dirs_to_files(config.fetch('path', 'lib'))
|
10
|
+
files = Flay.expand_dirs_to_files(config.fetch('path', 'lib')).sort
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
12
|
+
namespace :metrics do
|
13
|
+
# original code by Marty Andrews:
|
14
|
+
# http://blog.martyandrews.net/2009/05/enforcing-ruby-code-quality.html
|
15
|
+
desc 'Analyze for code duplication'
|
16
|
+
task :flay do
|
17
|
+
# run flay once without a threshold to ensure the max mass matches the threshold
|
18
|
+
flay = Flay.new(:fuzzy => false, :verbose => false, :mass => 0)
|
19
|
+
flay.process(*files)
|
19
20
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
21
|
+
max = (flay.masses.map { |hash, mass| mass.to_f / flay.hashes[hash].size }.max) || 0
|
22
|
+
unless max >= threshold
|
23
|
+
raise "Adjust flay threshold down to #{max}"
|
24
|
+
end
|
24
25
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
26
|
+
total = flay.masses.reduce(0.0) { |total, (hash, mass)| total + (mass.to_f / flay.hashes[hash].size) }
|
27
|
+
unless total == total_score
|
28
|
+
raise "Flay total is now #{total}, but expected #{total_score}"
|
29
|
+
end
|
29
30
|
|
30
|
-
|
31
|
-
|
32
|
-
|
31
|
+
# run flay a second time with the threshold set
|
32
|
+
flay = Flay.new(:fuzzy => false, :verbose => false, :mass => threshold.succ)
|
33
|
+
flay.process(*files)
|
33
34
|
|
34
|
-
|
35
|
-
|
36
|
-
|
35
|
+
if flay.masses.any?
|
36
|
+
flay.report
|
37
|
+
raise "#{flay.masses.size} chunks of code have a duplicate mass > #{threshold}"
|
38
|
+
end
|
37
39
|
end
|
38
40
|
end
|
39
41
|
rescue LoadError
|
40
42
|
task :flay do
|
41
|
-
|
43
|
+
$stderr.puts 'Flay is not available. In order to run flay, you must: gem install flay'
|
42
44
|
end
|
43
45
|
end
|
data/tasks/metrics/flog.rake
CHANGED
@@ -13,34 +13,37 @@ begin
|
|
13
13
|
config = YAML.load_file(File.expand_path('../../../config/flog.yml', __FILE__)).freeze
|
14
14
|
threshold = config.fetch('threshold').to_f.round_to(1)
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
flog
|
21
|
-
|
16
|
+
namespace :metrics do
|
17
|
+
# original code by Marty Andrews:
|
18
|
+
# http://blog.martyandrews.net/2009/05/enforcing-ruby-code-quality.html
|
19
|
+
desc 'Analyze for code complexity'
|
20
|
+
task :flog do
|
21
|
+
flog = Flog.new
|
22
|
+
flog.flog Array(config.fetch('path', 'lib'))
|
23
|
+
|
24
|
+
totals = flog.totals.select { |name, score| name[-5, 5] != '#none' }.
|
25
|
+
map { |name, score| [ name, score.round_to(1) ] }.
|
26
|
+
sort_by { |name, score| score }
|
27
|
+
|
28
|
+
if totals.any?
|
29
|
+
max = totals.last[1]
|
30
|
+
unless max >= threshold
|
31
|
+
raise "Adjust flog score down to #{max}"
|
32
|
+
end
|
33
|
+
end
|
22
34
|
|
23
|
-
|
24
|
-
|
25
|
-
|
35
|
+
bad_methods = totals.select { |name, score| score > threshold }
|
36
|
+
if bad_methods.any?
|
37
|
+
bad_methods.reverse_each do |name, score|
|
38
|
+
puts '%8.1f: %s' % [ score, name ]
|
39
|
+
end
|
26
40
|
|
27
|
-
|
28
|
-
max = last[1] if last
|
29
|
-
unless max.nil? || max >= threshold
|
30
|
-
raise "Adjust flog score down to #{max}"
|
31
|
-
end
|
32
|
-
|
33
|
-
bad_methods = totals.select { |name, score| score > threshold }
|
34
|
-
if bad_methods.any?
|
35
|
-
bad_methods.reverse_each do |name, score|
|
36
|
-
puts '%8.1f: %s' % [ score, name ]
|
41
|
+
raise "#{bad_methods.size} methods have a flog complexity > #{threshold}"
|
37
42
|
end
|
38
|
-
|
39
|
-
raise "#{bad_methods.size} methods have a flog complexity > #{threshold}"
|
40
43
|
end
|
41
44
|
end
|
42
45
|
rescue LoadError
|
43
46
|
task :flog do
|
44
|
-
|
47
|
+
$stderr.puts 'Flog is not available. In order to run flog, you must: gem install flog'
|
45
48
|
end
|
46
49
|
end
|
data/tasks/metrics/heckle.rake
CHANGED
@@ -7,7 +7,6 @@ $LOAD_PATH.unshift(File.expand_path('../../../lib', __FILE__))
|
|
7
7
|
|
8
8
|
begin
|
9
9
|
require 'pathname'
|
10
|
-
require 'active_support/inflector'
|
11
10
|
require 'heckle'
|
12
11
|
require 'mspec'
|
13
12
|
require 'mspec/utils/name_map'
|
@@ -26,185 +25,183 @@ begin
|
|
26
25
|
end
|
27
26
|
end
|
28
27
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
28
|
+
namespace :metrics do
|
29
|
+
desc 'Heckle each module and class'
|
30
|
+
task :heckle => :coverage do
|
31
|
+
unless Ruby2Ruby::VERSION == '1.2.2'
|
32
|
+
raise "ruby2ruby version #{Ruby2Ruby::VERSION} may not work properly, 1.2.2 *only* is recommended for use with heckle"
|
33
|
+
end
|
34
34
|
|
35
|
-
|
35
|
+
require 'ice_nine'
|
36
36
|
|
37
|
-
|
38
|
-
'IceNine'
|
39
|
-
)
|
37
|
+
root_module_regexp = Regexp.union('IceNine')
|
40
38
|
|
41
|
-
|
39
|
+
spec_dir = Pathname('spec/unit')
|
42
40
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
41
|
+
NameMap::MAP.each do |op, method|
|
42
|
+
next if method.kind_of?(Hash)
|
43
|
+
NameMap::MAP[op] = { :default => method }
|
44
|
+
end
|
47
45
|
|
48
|
-
|
49
|
-
|
46
|
+
aliases = Hash.new { |h,mod| h[mod] = Hash.new { |h,method| h[method] = method } }
|
47
|
+
map = NameMap.new
|
50
48
|
|
51
|
-
|
52
|
-
|
49
|
+
heckle_caught_modules = Hash.new { |hash, key| hash[key] = [] }
|
50
|
+
unhandled_mutations = 0
|
53
51
|
|
54
|
-
|
55
|
-
|
52
|
+
ObjectSpace.each_object(Module) do |mod|
|
53
|
+
next unless mod.name =~ /\A#{root_module_regexp}(?::|\z)/
|
56
54
|
|
57
|
-
|
55
|
+
spec_prefix = spec_dir.join(mod.name.underscore)
|
58
56
|
|
59
|
-
|
57
|
+
specs = []
|
60
58
|
|
61
|
-
|
62
|
-
|
63
|
-
|
59
|
+
# get the public class methods
|
60
|
+
metaclass = class << mod; self end
|
61
|
+
ancestors = metaclass.ancestors
|
64
62
|
|
65
|
-
|
63
|
+
spec_class_methods = mod.singleton_methods(false)
|
66
64
|
|
67
|
-
|
68
|
-
|
69
|
-
|
65
|
+
spec_class_methods.reject! do |method|
|
66
|
+
%w[ yaml_new yaml_tag_subclasses? included nesting constants ].include?(method.to_s)
|
67
|
+
end
|
70
68
|
|
71
|
-
|
72
|
-
|
73
|
-
|
69
|
+
if mod.ancestors.include?(Singleton)
|
70
|
+
spec_class_methods.reject! { |method| method.to_s == 'instance' }
|
71
|
+
end
|
74
72
|
|
75
|
-
|
76
|
-
|
77
|
-
|
73
|
+
# get the protected and private class methods
|
74
|
+
other_class_methods = metaclass.protected_instance_methods(false) |
|
75
|
+
metaclass.private_instance_methods(false)
|
78
76
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
77
|
+
ancestors.each do |ancestor|
|
78
|
+
other_class_methods -= ancestor.protected_instance_methods(false) |
|
79
|
+
ancestor.private_instance_methods(false)
|
80
|
+
end
|
83
81
|
|
84
|
-
|
85
|
-
|
86
|
-
|
82
|
+
other_class_methods.reject! do |method|
|
83
|
+
method.to_s == 'allocate' || SKIP_METHODS.include?(method.to_s)
|
84
|
+
end
|
87
85
|
|
88
|
-
|
89
|
-
|
86
|
+
other_class_methods.reject! do |method|
|
87
|
+
next unless spec_class_methods.any? { |specced| specced.to_s == $1 }
|
90
88
|
|
91
|
-
|
92
|
-
|
89
|
+
spec_class_methods << method
|
90
|
+
end
|
93
91
|
|
94
|
-
|
92
|
+
spec_class_methods -= other_class_methods
|
95
93
|
|
96
|
-
|
97
|
-
|
94
|
+
# get the instances methods
|
95
|
+
spec_methods = mod.public_instance_methods(false)
|
98
96
|
|
99
|
-
|
100
|
-
|
97
|
+
other_methods = mod.protected_instance_methods(false) |
|
98
|
+
mod.private_instance_methods(false)
|
101
99
|
|
102
|
-
|
103
|
-
|
100
|
+
other_methods.reject! do |method|
|
101
|
+
next unless spec_methods.any? { |specced| specced.to_s == $1 }
|
104
102
|
|
105
|
-
|
106
|
-
|
103
|
+
spec_methods << method
|
104
|
+
end
|
107
105
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
106
|
+
# map the class methods to spec files
|
107
|
+
spec_class_methods.each do |method|
|
108
|
+
method = aliases[mod.name][method]
|
109
|
+
next if SKIP_METHODS.include?(method.to_s)
|
112
110
|
|
113
|
-
|
111
|
+
spec_file = spec_prefix.join('class_methods').join(map.file_name(method, mod.name))
|
114
112
|
|
115
|
-
|
116
|
-
|
117
|
-
|
113
|
+
unless spec_file.file?
|
114
|
+
raise "No spec file #{spec_file} for #{mod}.#{method}"
|
115
|
+
end
|
116
|
+
|
117
|
+
specs << [ ".#{method}", [ spec_file ] ]
|
118
118
|
end
|
119
119
|
|
120
|
-
|
121
|
-
|
120
|
+
# map the instance methods to spec files
|
121
|
+
spec_methods.each do |method|
|
122
|
+
method = aliases[mod.name][method]
|
123
|
+
next if SKIP_METHODS.include?(method.to_s)
|
122
124
|
|
123
|
-
|
124
|
-
spec_methods.each do |method|
|
125
|
-
method = aliases[mod.name][method]
|
126
|
-
next if SKIP_METHODS.include?(method.to_s)
|
125
|
+
spec_file = spec_prefix.join(map.file_name(method, mod.name))
|
127
126
|
|
128
|
-
|
127
|
+
unless spec_file.file?
|
128
|
+
raise "No spec file #{spec_file} for #{mod}##{method}"
|
129
|
+
end
|
129
130
|
|
130
|
-
|
131
|
-
raise "No spec file #{spec_file} for #{mod}##{method}"
|
132
|
-
next
|
131
|
+
specs << [ "##{method}", [ spec_file ] ]
|
133
132
|
end
|
134
133
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
# and any spec fails for the current or descendant modules
|
140
|
-
other_methods.each do |method|
|
141
|
-
descedant_specs = []
|
134
|
+
# non-public methods are considered covered if they can be mutated
|
135
|
+
# and any spec fails for the current or descendant modules
|
136
|
+
other_methods.each do |method|
|
137
|
+
descedant_specs = []
|
142
138
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
139
|
+
ObjectSpace.each_object(Module) do |descedant|
|
140
|
+
next unless descedant.name =~ /\A#{root_module_regexp}(?::|\z)/ && mod >= descedant
|
141
|
+
descedant_spec_prefix = spec_dir.join(descedant.name.underscore)
|
142
|
+
descedant_specs << descedant_spec_prefix
|
147
143
|
|
148
|
-
|
149
|
-
|
144
|
+
if method.to_s == 'initialize'
|
145
|
+
descedant_specs.concat(Pathname.glob(descedant_spec_prefix.join('class_methods/new_spec.rb')))
|
146
|
+
end
|
150
147
|
end
|
148
|
+
|
149
|
+
specs << [ "##{method}", descedant_specs ]
|
151
150
|
end
|
152
151
|
|
153
|
-
|
154
|
-
|
152
|
+
other_class_methods.each do |method|
|
153
|
+
descedant_specs = []
|
155
154
|
|
156
|
-
|
157
|
-
|
155
|
+
ObjectSpace.each_object(Module) do |descedant|
|
156
|
+
next unless descedant.name =~ /\A#{root_module_regexp}(?::|\z)/ && mod >= descedant
|
157
|
+
descedant_specs << spec_dir.join(descedant.name.underscore).join('class_methods')
|
158
|
+
end
|
158
159
|
|
159
|
-
|
160
|
-
next unless descedant.name =~ /\A#{root_module_regexp}(?::|\z)/ && mod >= descedant
|
161
|
-
descedant_specs << spec_dir.join(descedant.name.underscore).join('class_methods')
|
160
|
+
specs << [ ".#{method}", descedant_specs ]
|
162
161
|
end
|
163
162
|
|
164
|
-
specs
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
when '+++ mutation'
|
175
|
-
unhandled_mutations += 1
|
163
|
+
specs.sort.each do |(method, spec_files)|
|
164
|
+
puts "Heckling #{mod}#{method}"
|
165
|
+
IO.popen("spec #{spec_files.join(' ')} --heckle '#{mod}#{method}'") do |pipe|
|
166
|
+
while line = pipe.gets
|
167
|
+
case line = line.chomp
|
168
|
+
when "The following mutations didn't cause test failures:"
|
169
|
+
heckle_caught_modules[mod.name] << method
|
170
|
+
when '+++ mutation'
|
171
|
+
unhandled_mutations += 1
|
172
|
+
end
|
176
173
|
end
|
177
174
|
end
|
178
175
|
end
|
179
176
|
end
|
180
|
-
end
|
181
177
|
|
182
|
-
|
183
|
-
|
178
|
+
if unhandled_mutations > 0
|
179
|
+
error_message_lines = [ "*************\n" ]
|
184
180
|
|
185
|
-
|
186
|
-
|
187
|
-
|
181
|
+
error_message_lines << "Heckle found #{unhandled_mutations} " \
|
182
|
+
"mutation#{"s" unless unhandled_mutations == 1} " \
|
183
|
+
"that didn't cause spec violations\n"
|
188
184
|
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
185
|
+
heckle_caught_modules.each do |mod, methods|
|
186
|
+
error_message_lines << "#{mod} contains the following " \
|
187
|
+
'poorly-specified methods:'
|
188
|
+
methods.each do |method|
|
189
|
+
error_message_lines << " - #{method}"
|
190
|
+
end
|
191
|
+
error_message_lines << ''
|
194
192
|
end
|
195
|
-
error_message_lines << ''
|
196
|
-
end
|
197
193
|
|
198
|
-
|
199
|
-
|
194
|
+
error_message_lines << 'Get your act together and come back ' \
|
195
|
+
'when your specs are doing their job!'
|
200
196
|
|
201
|
-
|
202
|
-
|
203
|
-
|
197
|
+
raise error_message_lines.join("\n")
|
198
|
+
else
|
199
|
+
puts 'Well done! Your code withstood a heckling.'
|
200
|
+
end
|
204
201
|
end
|
205
202
|
end
|
206
203
|
rescue LoadError
|
207
|
-
task :heckle do
|
208
|
-
|
204
|
+
task :heckle => :coverage do
|
205
|
+
$stderr.puts 'Heckle or mspec is not available. In order to run heckle, you must: gem install heckle mspec'
|
209
206
|
end
|
210
207
|
end
|
@@ -25,7 +25,7 @@ begin
|
|
25
25
|
rescue LoadError
|
26
26
|
namespace :metrics do
|
27
27
|
task :all do
|
28
|
-
|
28
|
+
$stderr.puts 'metric_fu is not available. In order to run metrics:all, you must: gem install metric_fu'
|
29
29
|
end
|
30
30
|
end
|
31
31
|
end
|
data/tasks/metrics/reek.rake
CHANGED
@@ -3,9 +3,19 @@
|
|
3
3
|
begin
|
4
4
|
require 'reek/rake/task'
|
5
5
|
|
6
|
-
|
6
|
+
RBX_18_MODE = RUBY_VERSION < '1.9' && defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx'
|
7
|
+
|
8
|
+
namespace :metrics do
|
9
|
+
Reek::Rake::Task.new do |t|
|
10
|
+
# reek has some problems under rbx in 1.8 mode that cause the underlying
|
11
|
+
# script to raise an exception. Rather than halt the "rake ci" process due
|
12
|
+
# to one bug, we choose to ignore it in this specific case until reek can be
|
13
|
+
# fixed.
|
14
|
+
t.fail_on_error = ! RBX_18_MODE # always true, except under rbx 18 mode
|
15
|
+
end
|
16
|
+
end
|
7
17
|
rescue LoadError
|
8
18
|
task :reek do
|
9
|
-
|
19
|
+
$stderr.puts 'Reek is not available. In order to run reek, you must: gem install reek'
|
10
20
|
end
|
11
21
|
end
|
data/tasks/metrics/roodi.rake
CHANGED
@@ -5,13 +5,15 @@ begin
|
|
5
5
|
require 'rake/tasklib'
|
6
6
|
require 'roodi_task'
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
namespace :metrics do
|
9
|
+
RoodiTask.new do |t|
|
10
|
+
t.verbose = false
|
11
|
+
t.config = File.expand_path('../../../config/roodi.yml', __FILE__)
|
12
|
+
t.patterns = %w[ lib/**/*.rb ]
|
13
|
+
end
|
12
14
|
end
|
13
15
|
rescue LoadError
|
14
16
|
task :roodi do
|
15
|
-
|
17
|
+
$stderr.puts 'Roodi is not available. In order to run roodi, you must: gem install roodi'
|
16
18
|
end
|
17
19
|
end
|
@@ -1,25 +1,25 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
begin
|
4
|
-
require 'pathname'
|
5
|
-
require 'yardstick'
|
6
4
|
require 'yardstick/rake/measurement'
|
7
5
|
require 'yardstick/rake/verify'
|
8
6
|
require 'yaml'
|
9
7
|
|
10
8
|
config = YAML.load_file(File.expand_path('../../../config/yardstick.yml', __FILE__))
|
11
9
|
|
12
|
-
|
13
|
-
|
10
|
+
namespace :metrics do
|
11
|
+
# yardstick_measure task
|
12
|
+
Yardstick::Rake::Measurement.new
|
14
13
|
|
15
|
-
|
16
|
-
|
17
|
-
|
14
|
+
# verify_measurements task
|
15
|
+
Yardstick::Rake::Verify.new do |verify|
|
16
|
+
verify.threshold = config.fetch('threshold')
|
17
|
+
end
|
18
18
|
end
|
19
19
|
rescue LoadError
|
20
20
|
%w[ yardstick_measure verify_measurements ].each do |name|
|
21
21
|
task name.to_s do
|
22
|
-
|
22
|
+
$stderr.puts "Yardstick is not available. In order to run #{name}, you must: gem install yardstick"
|
23
23
|
end
|
24
24
|
end
|
25
25
|
end
|
data/tasks/spec.rake
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
+
spec_defaults = lambda do |spec|
|
4
|
+
spec.ruby_opts = %w[ -r./spec/support/config_alias ]
|
5
|
+
spec.spec_opts << '--options' << 'spec/spec.opts'
|
6
|
+
end
|
7
|
+
|
3
8
|
begin
|
4
9
|
require 'spec/rake/spectask'
|
5
10
|
|
@@ -7,42 +12,49 @@ begin
|
|
7
12
|
task :spec => %w[ spec:unit spec:integration ]
|
8
13
|
|
9
14
|
namespace :spec do
|
10
|
-
|
11
|
-
|
12
|
-
|
15
|
+
desc 'Run unit specs'
|
16
|
+
Spec::Rake::SpecTask.new(:unit) do |unit|
|
17
|
+
spec_defaults.call(unit)
|
18
|
+
unit.pattern = 'spec/unit/**/*_spec.rb'
|
13
19
|
end
|
14
20
|
|
15
|
-
|
16
|
-
|
17
|
-
|
21
|
+
desc 'Run integration specs'
|
22
|
+
Spec::Rake::SpecTask.new(:integration) do |integration|
|
23
|
+
spec_defaults.call(integration)
|
24
|
+
integration.pattern = 'spec/integration/**/*_spec.rb'
|
18
25
|
end
|
19
26
|
end
|
20
27
|
rescue LoadError
|
21
|
-
|
22
|
-
|
28
|
+
%w[ spec spec:unit spec:integration ].each do |name|
|
29
|
+
task name do
|
30
|
+
$stderr.puts "rspec is not available. In order to run #{name}, you must: gem install rspec"
|
31
|
+
end
|
23
32
|
end
|
24
33
|
end
|
25
34
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
35
|
+
namespace :metrics do
|
36
|
+
begin
|
37
|
+
if RUBY_VERSION < '1.9'
|
38
|
+
desc 'Generate code coverage'
|
39
|
+
Spec::Rake::SpecTask.new(:coverage) do |rcov|
|
40
|
+
spec_defaults.call(rcov)
|
41
|
+
rcov.rcov = true
|
42
|
+
rcov.pattern = 'spec/unit/**/*_spec.rb'
|
43
|
+
rcov.rcov_opts = File.read('spec/rcov.opts').split(/\s+/)
|
44
|
+
end
|
45
|
+
else
|
46
|
+
desc 'Generate code coverage'
|
47
|
+
task :coverage do
|
48
|
+
ENV['COVERAGE'] = 'true'
|
49
|
+
Rake::Task['spec:unit'].execute
|
50
|
+
end
|
33
51
|
end
|
34
|
-
|
35
|
-
desc 'Generate code coverage'
|
52
|
+
rescue LoadError
|
36
53
|
task :coverage do
|
37
|
-
|
38
|
-
|
54
|
+
lib = RUBY_VERSION < '1.9' ? 'rcov' : 'simplecov'
|
55
|
+
$stderr.puts "coverage is not available. In order to run #{lib}, you must: gem install #{lib}"
|
39
56
|
end
|
40
57
|
end
|
41
|
-
rescue LoadError
|
42
|
-
task :coverage do
|
43
|
-
lib = RUBY_VERSION < '1.9' ? 'rcov' : 'simplecov'
|
44
|
-
abort "coverage is not available. In order to run #{lib}, you must: gem install #{lib}"
|
45
|
-
end
|
46
58
|
end
|
47
59
|
|
48
|
-
task :test =>
|
60
|
+
task :test => :spec
|
data/tasks/yard.rake
CHANGED
metadata
CHANGED
@@ -1,58 +1,82 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: ice_nine
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 7
|
5
5
|
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 6
|
9
|
+
- 0
|
10
|
+
version: 0.6.0
|
6
11
|
platform: ruby
|
7
|
-
authors:
|
12
|
+
authors:
|
8
13
|
- Dan Kubb
|
9
14
|
autorequire:
|
10
15
|
bindir: bin
|
11
16
|
cert_chain: []
|
12
|
-
|
13
|
-
|
14
|
-
|
17
|
+
|
18
|
+
date: 2012-12-20 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
15
21
|
name: rake
|
16
|
-
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
|
-
requirements:
|
19
|
-
- - ~>
|
20
|
-
- !ruby/object:Gem::Version
|
21
|
-
version: 0.9.2.2
|
22
|
-
type: :development
|
23
22
|
prerelease: false
|
24
|
-
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
24
|
none: false
|
26
|
-
requirements:
|
25
|
+
requirements:
|
27
26
|
- - ~>
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
|
30
|
-
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 73
|
29
|
+
segments:
|
30
|
+
- 10
|
31
|
+
- 0
|
32
|
+
- 3
|
33
|
+
version: 10.0.3
|
34
|
+
type: :development
|
35
|
+
version_requirements: *id001
|
36
|
+
- !ruby/object:Gem::Dependency
|
31
37
|
name: rspec
|
32
|
-
|
38
|
+
prerelease: false
|
39
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
33
40
|
none: false
|
34
|
-
requirements:
|
41
|
+
requirements:
|
35
42
|
- - ~>
|
36
|
-
- !ruby/object:Gem::Version
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
hash: 31
|
45
|
+
segments:
|
46
|
+
- 1
|
47
|
+
- 3
|
48
|
+
- 2
|
37
49
|
version: 1.3.2
|
38
50
|
type: :development
|
51
|
+
version_requirements: *id002
|
52
|
+
- !ruby/object:Gem::Dependency
|
53
|
+
name: yard
|
39
54
|
prerelease: false
|
40
|
-
|
55
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
41
56
|
none: false
|
42
|
-
requirements:
|
57
|
+
requirements:
|
43
58
|
- - ~>
|
44
|
-
- !ruby/object:Gem::Version
|
45
|
-
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
hash: 57
|
61
|
+
segments:
|
62
|
+
- 0
|
63
|
+
- 8
|
64
|
+
- 3
|
65
|
+
version: 0.8.3
|
66
|
+
type: :development
|
67
|
+
version_requirements: *id003
|
46
68
|
description: Deep Freeze Ruby Objects
|
47
|
-
email:
|
69
|
+
email:
|
48
70
|
- dan.kubb@gmail.com
|
49
71
|
executables: []
|
72
|
+
|
50
73
|
extensions: []
|
51
|
-
|
74
|
+
|
75
|
+
extra_rdoc_files:
|
52
76
|
- LICENSE
|
53
77
|
- README.md
|
54
78
|
- TODO
|
55
|
-
files:
|
79
|
+
files:
|
56
80
|
- .gitignore
|
57
81
|
- .pelusa.yml
|
58
82
|
- .rvmrc
|
@@ -82,7 +106,6 @@ files:
|
|
82
106
|
- lib/ice_nine/freezer/object.rb
|
83
107
|
- lib/ice_nine/freezer/range.rb
|
84
108
|
- lib/ice_nine/freezer/rubinius.rb
|
85
|
-
- lib/ice_nine/freezer/string.rb
|
86
109
|
- lib/ice_nine/freezer/struct.rb
|
87
110
|
- lib/ice_nine/freezer/symbol.rb
|
88
111
|
- lib/ice_nine/freezer/true_class.rb
|
@@ -120,27 +143,37 @@ files:
|
|
120
143
|
- tasks/yard.rake
|
121
144
|
homepage: https://github.com/dkubb/ice_nine
|
122
145
|
licenses: []
|
146
|
+
|
123
147
|
post_install_message:
|
124
148
|
rdoc_options: []
|
125
|
-
|
149
|
+
|
150
|
+
require_paths:
|
126
151
|
- lib
|
127
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
152
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
128
153
|
none: false
|
129
|
-
requirements:
|
130
|
-
- -
|
131
|
-
- !ruby/object:Gem::Version
|
132
|
-
|
133
|
-
|
154
|
+
requirements:
|
155
|
+
- - ">="
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
hash: 3
|
158
|
+
segments:
|
159
|
+
- 0
|
160
|
+
version: "0"
|
161
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
134
162
|
none: false
|
135
|
-
requirements:
|
136
|
-
- -
|
137
|
-
- !ruby/object:Gem::Version
|
138
|
-
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
hash: 3
|
167
|
+
segments:
|
168
|
+
- 0
|
169
|
+
version: "0"
|
139
170
|
requirements: []
|
171
|
+
|
140
172
|
rubyforge_project:
|
141
173
|
rubygems_version: 1.8.24
|
142
174
|
signing_key:
|
143
175
|
specification_version: 3
|
144
176
|
summary: Deep Freeze Ruby Objects
|
145
177
|
test_files: []
|
178
|
+
|
146
179
|
has_rdoc:
|