equalizer 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.
@@ -0,0 +1,5 @@
1
+ # encoding: utf-8
2
+
3
+ class Equalizer < Module
4
+ VERSION = '0.0.1'.freeze
5
+ end
data/spec/rcov.opts ADDED
@@ -0,0 +1,7 @@
1
+ --exclude-only "spec/,^/"
2
+ --sort coverage
3
+ --callsites
4
+ --xrefs
5
+ --profile
6
+ --text-summary
7
+ --failure-threshold 100
@@ -0,0 +1,7 @@
1
+ # encoding: utf-8
2
+
3
+ shared_examples_for 'a command method' do
4
+ it 'returns self' do
5
+ should equal(object)
6
+ end
7
+ end
@@ -0,0 +1,15 @@
1
+ # encoding: utf-8
2
+
3
+ shared_examples_for 'an #each method' do
4
+ it_should_behave_like 'a command method'
5
+
6
+ context 'with no block' do
7
+ subject { object.each }
8
+
9
+ it { should be_instance_of(to_enum.class) }
10
+
11
+ it 'yields the expected values' do
12
+ subject.to_a.should eql(object.to_a)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,17 @@
1
+ # encoding: utf-8
2
+
3
+ shared_examples_for 'a hash method' do
4
+ it_should_behave_like 'an idempotent method'
5
+
6
+ specification = proc do
7
+ should be_instance_of(Fixnum)
8
+ end
9
+
10
+ it 'is a fixnum' do
11
+ instance_eval(&specification)
12
+ end
13
+
14
+ it 'memoizes the hash code' do
15
+ subject.should eql(object.memoized(:hash))
16
+ end
17
+ end
@@ -0,0 +1,7 @@
1
+ # encoding: utf-8
2
+
3
+ shared_examples_for 'an idempotent method' do
4
+ it 'is idempotent' do
5
+ should equal(instance_eval(&self.class.subject))
6
+ end
7
+ end
@@ -0,0 +1,9 @@
1
+ # encoding: utf-8
2
+
3
+ shared_examples_for 'an invertible method' do
4
+ it_should_behave_like 'an idempotent method'
5
+
6
+ it 'is invertible' do
7
+ subject.inverse.should equal(object)
8
+ end
9
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ --loadby random
3
+ --format profile
@@ -0,0 +1,11 @@
1
+ # encoding: utf-8
2
+
3
+ require 'equalizer'
4
+ require 'spec'
5
+ require 'spec/autorun'
6
+
7
+ # require spec support files and shared behavior
8
+ Dir[File.expand_path('../{support,shared}/**/*.rb', __FILE__)].each { |f| require f }
9
+
10
+ Spec::Runner.configure do |config|
11
+ end
@@ -0,0 +1,3 @@
1
+ require 'rbconfig'
2
+
3
+ ::Config = RbConfig unless defined?(::Config)
@@ -0,0 +1,134 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Equalizer, '.new' do
6
+ let(:object) { described_class }
7
+ let(:name) { 'User' }
8
+ let(:klass) { ::Class.new }
9
+
10
+ context 'with no keys' do
11
+ subject { object.new }
12
+
13
+ before do
14
+ # specify the class #name method
15
+ klass.stub(:name).and_return(name)
16
+ klass.send(:include, subject)
17
+ end
18
+
19
+ let(:instance) { klass.new }
20
+
21
+ it { should be_instance_of(object) }
22
+
23
+ it { should be_frozen }
24
+
25
+ it 'defines #hash and #inspect methods dynamically' do
26
+ subject.public_instance_methods(false).map(&:to_s).should =~ %w[ hash inspect ]
27
+ end
28
+
29
+ describe '#eql?' do
30
+ context 'when the objects are similar' do
31
+ let(:other) { instance.dup }
32
+
33
+ it { instance.eql?(other).should be(true) }
34
+ end
35
+
36
+ context 'when the objects are different' do
37
+ let(:other) { stub('other') }
38
+
39
+ it { instance.eql?(other).should be(false) }
40
+ end
41
+ end
42
+
43
+ describe '#==' do
44
+ context 'when the objects are similar' do
45
+ let(:other) { instance.dup }
46
+
47
+ it { (instance == other).should be(true) }
48
+ end
49
+
50
+ context 'when the objects are different' do
51
+ let(:other) { stub('other') }
52
+
53
+ it { (instance == other).should be(false) }
54
+ end
55
+ end
56
+
57
+ describe '#hash' do
58
+ it { instance.hash.should eql(klass.hash) }
59
+ end
60
+
61
+ describe '#inspect' do
62
+ it { instance.inspect.should eql('#<User>') }
63
+ end
64
+ end
65
+
66
+ context 'with keys' do
67
+ subject { object.new(*keys) }
68
+
69
+ let(:keys) { [ :first_name ].freeze }
70
+ let(:first_name) { 'John' }
71
+ let(:instance) { klass.new(first_name) }
72
+
73
+ let(:klass) do
74
+ ::Class.new do
75
+ attr_reader :first_name
76
+
77
+ def initialize(first_name)
78
+ @first_name = first_name
79
+ end
80
+ end
81
+ end
82
+
83
+ before do
84
+ # specify the class #inspect method
85
+ klass.stub(:name).and_return(nil)
86
+ klass.stub(:inspect).and_return(name)
87
+ klass.send(:include, subject)
88
+ end
89
+
90
+ it { should be_instance_of(object) }
91
+
92
+ it { should be_frozen }
93
+
94
+ it 'defines #hash and #inspect methods dynamically' do
95
+ subject.public_instance_methods(false).map(&:to_s).should =~ %w[ hash inspect ]
96
+ end
97
+
98
+ describe '#eql?' do
99
+ context 'when the objects are similar' do
100
+ let(:other) { instance.dup }
101
+
102
+ it { instance.eql?(other).should be(true) }
103
+ end
104
+
105
+ context 'when the objects are different' do
106
+ let(:other) { stub('other') }
107
+
108
+ it { instance.eql?(other).should be(false) }
109
+ end
110
+ end
111
+
112
+ describe '#==' do
113
+ context 'when the objects are similar' do
114
+ let(:other) { instance.dup }
115
+
116
+ it { (instance == other).should be(true) }
117
+ end
118
+
119
+ context 'when the objects are different' do
120
+ let(:other) { stub('other') }
121
+
122
+ it { (instance == other).should be(false) }
123
+ end
124
+ end
125
+
126
+ describe '#hash' do
127
+ it { instance.hash.should eql(klass.hash ^ first_name.hash) }
128
+ end
129
+
130
+ describe '#inspect' do
131
+ it { instance.inspect.should eql('#<User first_name="John">') }
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,49 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Equalizer::Methods, '#eql?' do
6
+ subject { object.eql?(other) }
7
+
8
+ let(:object) { described_class.new }
9
+
10
+ let(:described_class) do
11
+ Class.new do
12
+ include Equalizer::Methods
13
+
14
+ def cmp?(comparator, other)
15
+ !!(comparator and other)
16
+ end
17
+ end
18
+ end
19
+
20
+ context 'with the same object' do
21
+ let(:other) { object }
22
+
23
+ it { should be(true) }
24
+
25
+ it 'is symmetric' do
26
+ should eql(other.eql?(object))
27
+ end
28
+ end
29
+
30
+ context 'with an equivalent object' do
31
+ let(:other) { object.dup }
32
+
33
+ it { should be(true) }
34
+
35
+ it 'is symmetric' do
36
+ should eql(other.eql?(object))
37
+ end
38
+ end
39
+
40
+ context 'with an equivalent object of a subclass' do
41
+ let(:other) { Class.new(described_class).new }
42
+
43
+ it { should be(false) }
44
+
45
+ it 'is symmetric' do
46
+ should eql(other.eql?(object))
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,85 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Equalizer::Methods, '#==' do
6
+ subject { object == other }
7
+
8
+ let(:object) { described_class.new(true) }
9
+
10
+ let(:described_class) do
11
+ Class.new do
12
+ include Equalizer::Methods
13
+
14
+ attr_reader :boolean
15
+
16
+ def initialize(boolean)
17
+ @boolean = boolean
18
+ end
19
+
20
+ def cmp?(comparator, other)
21
+ boolean.send(comparator, other.boolean)
22
+ end
23
+ end
24
+ end
25
+
26
+ context 'with the same object' do
27
+ let(:other) { object }
28
+
29
+ it { should be(true) }
30
+
31
+ it 'is symmetric' do
32
+ should eql(other == object)
33
+ end
34
+ end
35
+
36
+ context 'with an equivalent object' do
37
+ let(:other) { object.dup }
38
+
39
+ it { should be(true) }
40
+
41
+ it 'is symmetric' do
42
+ should eql(other == object)
43
+ end
44
+ end
45
+
46
+ context 'with an equivalent object of a subclass' do
47
+ let(:other) { Class.new(described_class).new(true) }
48
+
49
+ it { should be(true) }
50
+
51
+ it 'is symmetric' do
52
+ should eql(other == object)
53
+ end
54
+ end
55
+
56
+ context 'with an object of another class' do
57
+ let(:other) { Class.new.new }
58
+
59
+ it { should be(false) }
60
+
61
+ it 'is symmetric' do
62
+ should eql(other == object)
63
+ end
64
+ end
65
+
66
+ context 'with an equivalent object after coercion' do
67
+ let(:other) { Object.new }
68
+
69
+ before do
70
+ # declare a private #coerce method
71
+ described_class.class_eval do
72
+ def coerce(other)
73
+ self.class.new(!!other)
74
+ end
75
+ private :coerce
76
+ end
77
+ end
78
+
79
+ it { should be(true) }
80
+
81
+ it 'is not symmetric' do
82
+ should_not eql(other == object)
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,9 @@
1
+ # encoding: utf-8
2
+
3
+ desc 'Run metrics with Heckle'
4
+ task :ci => %w[ ci:metrics metrics:heckle ]
5
+
6
+ namespace :ci do
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 ]
9
+ end
@@ -0,0 +1,45 @@
1
+ # encoding: utf-8
2
+
3
+ begin
4
+ require 'flay'
5
+ require 'yaml'
6
+
7
+ config = YAML.load_file(File.expand_path('../../../config/flay.yml', __FILE__)).freeze
8
+ threshold = config.fetch('threshold').to_i
9
+ total_score = config.fetch('total_score').to_f
10
+ files = Flay.expand_dirs_to_files(config.fetch('path', 'lib')).sort
11
+
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)
20
+
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
25
+
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
30
+
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)
34
+
35
+ if flay.masses.any?
36
+ flay.report
37
+ raise "#{flay.masses.size} chunks of code have a duplicate mass > #{threshold}"
38
+ end
39
+ end
40
+ end
41
+ rescue LoadError
42
+ task :flay do
43
+ $stderr.puts 'Flay is not available. In order to run flay, you must: gem install flay'
44
+ end
45
+ end
@@ -0,0 +1,49 @@
1
+ # encoding: utf-8
2
+
3
+ begin
4
+ require 'flog'
5
+ require 'yaml'
6
+
7
+ class Float
8
+ def round_to(n)
9
+ (self * 10**n).round.to_f * 10**-n
10
+ end
11
+ end
12
+
13
+ config = YAML.load_file(File.expand_path('../../../config/flog.yml', __FILE__)).freeze
14
+ threshold = config.fetch('threshold').to_f.round_to(1)
15
+
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
34
+
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
40
+
41
+ raise "#{bad_methods.size} methods have a flog complexity > #{threshold}"
42
+ end
43
+ end
44
+ end
45
+ rescue LoadError
46
+ task :flog do
47
+ $stderr.puts 'Flog is not available. In order to run flog, you must: gem install flog'
48
+ end
49
+ end