virtus 0.0.9 → 0.0.10
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/.gitignore +11 -0
- data/.travis.yml +0 -1
- data/{History.md → Changelog.md} +10 -3
- data/Gemfile +4 -6
- data/README.md +140 -81
- data/Rakefile +1 -15
- data/config/flay.yml +1 -1
- data/config/roodi.yml +1 -1
- data/config/site.reek +8 -2
- data/examples/custom_coercion_spec.rb +2 -2
- data/examples/override_attribute_methods_spec.rb +40 -0
- data/lib/virtus.rb +1 -0
- data/lib/virtus/attribute/default_value.rb +17 -3
- data/lib/virtus/class_methods.rb +6 -3
- data/lib/virtus/support/type_lookup.rb +3 -4
- data/lib/virtus/version.rb +3 -0
- data/spec/unit/virtus/attribute/default_value/class_methods/new_spec.rb +3 -6
- data/spec/unit/virtus/attribute/default_value/instance_methods/evaluate_spec.rb +10 -2
- data/tasks/metrics/ci.rake +2 -3
- data/tasks/spec.rake +19 -7
- data/virtus.gemspec +17 -186
- metadata +80 -48
- data/tasks/metrics/heckle.rake +0 -261
data/tasks/metrics/heckle.rake
DELETED
@@ -1,261 +0,0 @@
|
|
1
|
-
$LOAD_PATH.unshift(File.expand_path('../../../lib', __FILE__))
|
2
|
-
|
3
|
-
# original code by Ashley Moran:
|
4
|
-
# http://aviewfromafar.net/2007/11/1/rake-task-for-heckling-your-specs
|
5
|
-
|
6
|
-
begin
|
7
|
-
require 'pathname'
|
8
|
-
require 'backports'
|
9
|
-
require 'active_support/inflector'
|
10
|
-
require 'heckle'
|
11
|
-
require 'mspec'
|
12
|
-
require 'mspec/utils/name_map'
|
13
|
-
|
14
|
-
SKIP_METHODS = %w[ blank_slate_method_added ].freeze
|
15
|
-
|
16
|
-
class NameMap
|
17
|
-
def file_name(method, constant)
|
18
|
-
map = MAP[method]
|
19
|
-
name = if map
|
20
|
-
map[constant] || map[:default]
|
21
|
-
else
|
22
|
-
method.gsub(/[?!=]\z/, '')
|
23
|
-
end
|
24
|
-
"#{name}_spec.rb"
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
desc 'Heckle each module and class'
|
29
|
-
task :heckle => :verify_rcov do
|
30
|
-
unless Ruby2Ruby::VERSION == '1.2.2'
|
31
|
-
raise "ruby2ruby version #{Ruby2Ruby::VERSION} may not work properly, 1.2.2 *only* is recommended for use with heckle"
|
32
|
-
end
|
33
|
-
|
34
|
-
require 'veritas'
|
35
|
-
root_module = 'Veritas'
|
36
|
-
|
37
|
-
spec_dir = Pathname('spec/unit')
|
38
|
-
|
39
|
-
NameMap::MAP.each do |op, method|
|
40
|
-
next if method.kind_of?(Hash)
|
41
|
-
NameMap::MAP[op] = { :default => method }
|
42
|
-
end
|
43
|
-
|
44
|
-
%w[
|
45
|
-
Veritas::Relation::Header
|
46
|
-
Veritas::Algebra::Difference::Methods
|
47
|
-
Veritas::Algebra::Intersection::Methods
|
48
|
-
Veritas::Algebra::Join::Methods
|
49
|
-
Veritas::Algebra::Product::Methods
|
50
|
-
Veritas::Algebra::Projection::Methods
|
51
|
-
Veritas::Algebra::Rename::Methods
|
52
|
-
Veritas::Algebra::Rename::Aliases
|
53
|
-
Veritas::Algebra::Restriction::Methods
|
54
|
-
Veritas::Algebra::Union::Methods
|
55
|
-
].each do |mod|
|
56
|
-
NameMap::MAP['-'][mod] = 'difference'
|
57
|
-
NameMap::MAP['&'][mod] = 'intersect'
|
58
|
-
NameMap::MAP['+'][mod] = 'join'
|
59
|
-
NameMap::MAP['*'][mod] = 'product'
|
60
|
-
NameMap::MAP['|'][mod] = 'union'
|
61
|
-
end
|
62
|
-
|
63
|
-
NameMap::MAP['==']['Veritas::Relation::Operation::Order::Direction'] = 'eql'
|
64
|
-
|
65
|
-
NameMap::MAP['|']['Veritas::Relation::Operation::Order::DirectionSet'] = 'union'
|
66
|
-
|
67
|
-
%w[
|
68
|
-
Veritas::Function::Connective::Conjunction::Methods
|
69
|
-
Veritas::Function::Connective::Disjunction::Methods
|
70
|
-
Veritas::Function::Connective::Negation::Methods
|
71
|
-
].each do |mod|
|
72
|
-
NameMap::MAP['&'][mod] = 'and'
|
73
|
-
NameMap::MAP['|'][mod] = 'or'
|
74
|
-
NameMap::MAP['-'][mod] = 'not'
|
75
|
-
end
|
76
|
-
|
77
|
-
aliases = Hash.new { |h,mod| h[mod] = Hash.new { |h,method| h[method] = method } }
|
78
|
-
|
79
|
-
aliases['Veritas::Attribute::Numeric']['range'] = 'size'
|
80
|
-
aliases['Veritas::Attribute::String']['range'] = 'length'
|
81
|
-
|
82
|
-
aliases['Veritas::Aggregate::Minimum::Methods']['min'] = 'minimum'
|
83
|
-
aliases['Veritas::Aggregate::Maximum::Methods']['max'] = 'maximum'
|
84
|
-
aliases['Veritas::Aggregate::Mean::Methods']['avg'] = 'mean'
|
85
|
-
aliases['Veritas::Aggregate::Mean::Methods']['average'] = 'mean'
|
86
|
-
aliases['Veritas::Aggregate::Variance::Methods']['var'] = 'variance'
|
87
|
-
aliases['Veritas::Aggregate::StandardDeviation::Methods']['stddev'] = 'standard_deviation'
|
88
|
-
|
89
|
-
aliases['Veritas::Function::Numeric::Addition::Methods']['+'] = 'add'
|
90
|
-
aliases['Veritas::Function::Numeric::Subtraction::Methods']['-'] = 'subtract'
|
91
|
-
aliases['Veritas::Function::Numeric::Multiplication::Methods']['*'] = 'multiply'
|
92
|
-
aliases['Veritas::Function::Numeric::Division::Methods']['/'] = 'divide'
|
93
|
-
aliases['Veritas::Function::Numeric::Absolute::Methods']['abs'] = 'absolute'
|
94
|
-
aliases['Veritas::Function::Numeric::Exponentiation::Methods']['**'] = 'exponent'
|
95
|
-
aliases['Veritas::Function::Numeric::Exponentiation::Methods']['pow'] = 'exponent'
|
96
|
-
aliases['Veritas::Function::Numeric::Modulo::Methods']['%'] = 'modulo'
|
97
|
-
aliases['Veritas::Function::Numeric::Modulo::Methods']['mod'] = 'modulo'
|
98
|
-
aliases['Veritas::Function::Numeric::Random::Methods']['rand'] = 'random'
|
99
|
-
aliases['Veritas::Function::Numeric::SquareRoot::Methods']['sqrt'] = 'square_root'
|
100
|
-
aliases['Veritas::Function::Numeric::UnaryPlus::Methods']['+@'] = 'unary_plus'
|
101
|
-
aliases['Veritas::Function::Numeric::UnaryMinus::Methods']['-@'] = 'unary_minus'
|
102
|
-
|
103
|
-
map = NameMap.new
|
104
|
-
|
105
|
-
heckle_caught_modules = Hash.new { |hash, key| hash[key] = [] }
|
106
|
-
unhandled_mutations = 0
|
107
|
-
|
108
|
-
ObjectSpace.each_object(Module) do |mod|
|
109
|
-
next unless mod.name =~ /\A#{root_module}(?::|\z)/
|
110
|
-
|
111
|
-
spec_prefix = spec_dir.join(mod.name.underscore)
|
112
|
-
|
113
|
-
specs = []
|
114
|
-
|
115
|
-
# get the public class methods
|
116
|
-
metaclass = class << mod; self end
|
117
|
-
ancestors = metaclass.ancestors
|
118
|
-
|
119
|
-
spec_class_methods = mod.singleton_methods(false)
|
120
|
-
|
121
|
-
spec_class_methods.reject! do |method|
|
122
|
-
%w[ yaml_new yaml_tag_subclasses? included nesting constants ].include?(method.to_s)
|
123
|
-
end
|
124
|
-
|
125
|
-
if mod.ancestors.include?(Singleton)
|
126
|
-
spec_class_methods.reject! { |method| method.to_s == 'instance' }
|
127
|
-
end
|
128
|
-
|
129
|
-
# get the protected and private class methods
|
130
|
-
other_class_methods = metaclass.protected_instance_methods(false) |
|
131
|
-
metaclass.private_instance_methods(false)
|
132
|
-
|
133
|
-
ancestors.each do |ancestor|
|
134
|
-
other_class_methods -= ancestor.protected_instance_methods(false) |
|
135
|
-
ancestor.private_instance_methods(false)
|
136
|
-
end
|
137
|
-
|
138
|
-
other_class_methods.reject! do |method|
|
139
|
-
method.to_s == 'allocate' || SKIP_METHODS.include?(method.to_s)
|
140
|
-
end
|
141
|
-
|
142
|
-
other_class_methods.reject! do |method|
|
143
|
-
next unless spec_class_methods.any? { |specced| specced.to_s == $1 }
|
144
|
-
|
145
|
-
spec_class_methods << method
|
146
|
-
end
|
147
|
-
|
148
|
-
# get the instances methods
|
149
|
-
spec_methods = mod.public_instance_methods(false)
|
150
|
-
|
151
|
-
other_methods = mod.protected_instance_methods(false) |
|
152
|
-
mod.private_instance_methods(false)
|
153
|
-
|
154
|
-
other_methods.reject! do |method|
|
155
|
-
next unless spec_methods.any? { |specced| specced.to_s == $1 }
|
156
|
-
|
157
|
-
spec_methods << method
|
158
|
-
end
|
159
|
-
|
160
|
-
# map the class methods to spec files
|
161
|
-
spec_class_methods.each do |method|
|
162
|
-
method = aliases[mod.name][method]
|
163
|
-
next if SKIP_METHODS.include?(method.to_s)
|
164
|
-
|
165
|
-
spec_file = spec_prefix.join('class_methods').join(map.file_name(method, mod.name))
|
166
|
-
|
167
|
-
unless spec_file.file?
|
168
|
-
raise "No spec file #{spec_file} for #{mod}.#{method}"
|
169
|
-
end
|
170
|
-
|
171
|
-
specs << [ ".#{method}", [ spec_file ] ]
|
172
|
-
end
|
173
|
-
|
174
|
-
# map the instance methods to spec files
|
175
|
-
spec_methods.each do |method|
|
176
|
-
method = aliases[mod.name][method]
|
177
|
-
next if SKIP_METHODS.include?(method.to_s)
|
178
|
-
|
179
|
-
spec_file = spec_prefix.join(map.file_name(method, mod.name))
|
180
|
-
|
181
|
-
unless spec_file.file?
|
182
|
-
raise "No spec file #{spec_file} for #{mod}##{method}"
|
183
|
-
end
|
184
|
-
|
185
|
-
specs << [ "##{method}", [ spec_file ] ]
|
186
|
-
end
|
187
|
-
|
188
|
-
# non-public methods are considered covered if they can be mutated
|
189
|
-
# and any spec fails for the current or descendant modules
|
190
|
-
other_methods.each do |method|
|
191
|
-
descedant_specs = []
|
192
|
-
|
193
|
-
ObjectSpace.each_object(Module) do |descedant|
|
194
|
-
next unless descedant.name =~ /\A#{root_module}(?::|\z)/ && mod >= descedant
|
195
|
-
descedant_spec_prefix = spec_dir.join(descedant.name.underscore)
|
196
|
-
descedant_specs.concat(Pathname.glob(descedant_spec_prefix.join('*_spec.rb')))
|
197
|
-
|
198
|
-
if method.to_s == 'initialize'
|
199
|
-
descedant_specs.concat(Pathname.glob(descedant_spec_prefix.join('class_methods/new_spec.rb')))
|
200
|
-
end
|
201
|
-
end
|
202
|
-
|
203
|
-
specs << [ "##{method}", descedant_specs ]
|
204
|
-
end
|
205
|
-
|
206
|
-
other_class_methods.each do |method|
|
207
|
-
descedant_specs = []
|
208
|
-
|
209
|
-
ObjectSpace.each_object(Module) do |descedant|
|
210
|
-
next unless descedant.name =~ /\A#{root_module}(?::|\z)/ && mod >= descedant
|
211
|
-
descedant_spec_prefix = spec_dir.join(descedant.name.underscore)
|
212
|
-
descedant_specs.concat(Pathname.glob(descedant_spec_prefix.join('class_methods/*_spec.rb')))
|
213
|
-
end
|
214
|
-
|
215
|
-
specs << [ ".#{method}", descedant_specs ]
|
216
|
-
end
|
217
|
-
|
218
|
-
specs.sort.each do |(method, spec_files)|
|
219
|
-
puts "Heckling #{mod}#{method}"
|
220
|
-
IO.popen("spec #{spec_files.join(' ')} --heckle '#{mod}#{method}'") do |pipe|
|
221
|
-
while line = pipe.gets
|
222
|
-
case line = line.chomp
|
223
|
-
when "The following mutations didn't cause test failures:"
|
224
|
-
heckle_caught_modules[mod.name] << method
|
225
|
-
when '+++ mutation'
|
226
|
-
unhandled_mutations += 1
|
227
|
-
end
|
228
|
-
end
|
229
|
-
end
|
230
|
-
end
|
231
|
-
end
|
232
|
-
|
233
|
-
if unhandled_mutations > 0
|
234
|
-
error_message_lines = [ "*************\n" ]
|
235
|
-
|
236
|
-
error_message_lines << "Heckle found #{unhandled_mutations} " \
|
237
|
-
"mutation#{"s" unless unhandled_mutations == 1} " \
|
238
|
-
"that didn't cause spec violations\n"
|
239
|
-
|
240
|
-
heckle_caught_modules.each do |mod, methods|
|
241
|
-
error_message_lines << "#{mod} contains the following " \
|
242
|
-
'poorly-specified methods:'
|
243
|
-
methods.each do |method|
|
244
|
-
error_message_lines << " - #{method}"
|
245
|
-
end
|
246
|
-
error_message_lines << ''
|
247
|
-
end
|
248
|
-
|
249
|
-
error_message_lines << 'Get your act together and come back ' \
|
250
|
-
'when your specs are doing their job!'
|
251
|
-
|
252
|
-
raise error_message_lines.join("\n")
|
253
|
-
else
|
254
|
-
puts 'Well done! Your code withstood a heckling.'
|
255
|
-
end
|
256
|
-
end
|
257
|
-
rescue LoadError
|
258
|
-
task :heckle do
|
259
|
-
abort 'Heckle or mspec is not available. In order to run heckle, you must: gem install heckle mspec'
|
260
|
-
end
|
261
|
-
end
|