reek 1.1.3 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +44 -4
- data/License.txt +20 -0
- data/README.rdoc +83 -0
- data/Rakefile +0 -1
- data/bin/reek +3 -11
- data/config/defaults.reek +20 -1
- data/features/masking_smells.feature +111 -0
- data/features/options.feature +49 -0
- data/features/reports.feature +90 -0
- data/features/samples.feature +284 -0
- data/features/stdin.feature +43 -0
- data/features/step_definitions/reek_steps.rb +35 -0
- data/features/support/env.rb +38 -0
- data/lib/reek.rb +1 -1
- data/lib/reek/adapters/application.rb +47 -0
- data/lib/reek/adapters/config_file.rb +31 -0
- data/lib/reek/adapters/core_extras.rb +72 -0
- data/lib/reek/{object_source.rb → adapters/object_source.rb} +15 -19
- data/lib/reek/{rake_task.rb → adapters/rake_task.rb} +2 -2
- data/lib/reek/adapters/report.rb +91 -0
- data/lib/reek/adapters/source.rb +53 -0
- data/lib/reek/{spec.rb → adapters/spec.rb} +45 -60
- data/lib/reek/block_context.rb +1 -1
- data/lib/reek/class_context.rb +26 -6
- data/lib/reek/code_context.rb +8 -0
- data/lib/reek/code_parser.rb +82 -39
- data/lib/reek/command_line.rb +85 -0
- data/lib/reek/configuration.rb +51 -0
- data/lib/reek/detector_stack.rb +39 -0
- data/lib/reek/exceptions.reek +8 -1
- data/lib/reek/method_context.rb +53 -11
- data/lib/reek/module_context.rb +1 -2
- data/lib/reek/name.rb +8 -2
- data/lib/reek/sexp_formatter.rb +2 -0
- data/lib/reek/smell_warning.rb +26 -8
- data/lib/reek/smells/control_couple.rb +8 -4
- data/lib/reek/smells/data_clump.rb +88 -0
- data/lib/reek/smells/duplication.rb +11 -9
- data/lib/reek/smells/feature_envy.rb +3 -4
- data/lib/reek/smells/large_class.rb +17 -17
- data/lib/reek/smells/long_method.rb +10 -8
- data/lib/reek/smells/long_parameter_list.rb +16 -10
- data/lib/reek/smells/long_yield_list.rb +1 -1
- data/lib/reek/smells/nested_iterators.rb +3 -3
- data/lib/reek/smells/simulated_polymorphism.rb +58 -0
- data/lib/reek/smells/smell_detector.rb +94 -27
- data/lib/reek/smells/uncommunicative_name.rb +23 -23
- data/lib/reek/smells/utility_function.rb +27 -11
- data/lib/reek/sniffer.rb +183 -0
- data/reek.gemspec +5 -5
- data/spec/quality/reek_source_spec.rb +15 -0
- data/spec/reek/adapters/report_spec.rb +49 -0
- data/spec/reek/adapters/should_reek_of_spec.rb +108 -0
- data/spec/reek/adapters/should_reek_only_of_spec.rb +87 -0
- data/spec/reek/adapters/should_reek_spec.rb +92 -0
- data/spec/reek/block_context_spec.rb +7 -1
- data/spec/reek/class_context_spec.rb +39 -16
- data/spec/reek/code_context_spec.rb +7 -7
- data/spec/reek/code_parser_spec.rb +6 -1
- data/spec/reek/config_spec.rb +3 -3
- data/spec/reek/configuration_spec.rb +12 -0
- data/spec/reek/method_context_spec.rb +2 -2
- data/spec/reek/name_spec.rb +24 -0
- data/spec/reek/object_source_spec.rb +23 -0
- data/spec/reek/singleton_method_context_spec.rb +2 -2
- data/spec/reek/smell_warning_spec.rb +53 -0
- data/spec/reek/smells/data_clump_spec.rb +87 -0
- data/spec/reek/smells/duplication_spec.rb +13 -17
- data/spec/reek/smells/feature_envy_spec.rb +23 -28
- data/spec/reek/smells/large_class_spec.rb +109 -34
- data/spec/reek/smells/long_method_spec.rb +140 -3
- data/spec/reek/smells/long_parameter_list_spec.rb +1 -2
- data/spec/reek/smells/simulated_polymorphism_spec.rb +50 -0
- data/spec/reek/smells/smell_detector_spec.rb +53 -0
- data/spec/reek/smells/uncommunicative_name_spec.rb +20 -7
- data/spec/reek/smells/utility_function_spec.rb +76 -67
- data/spec/reek/sniffer_spec.rb +10 -0
- data/spec/samples/all_but_one_masked/clean_one.rb +6 -0
- data/spec/samples/all_but_one_masked/dirty.rb +7 -0
- data/spec/samples/all_but_one_masked/masked.reek +5 -0
- data/spec/samples/clean_due_to_masking/clean_one.rb +6 -0
- data/spec/samples/clean_due_to_masking/clean_three.rb +6 -0
- data/spec/samples/clean_due_to_masking/clean_two.rb +6 -0
- data/spec/samples/clean_due_to_masking/dirty_one.rb +7 -0
- data/spec/samples/clean_due_to_masking/dirty_two.rb +7 -0
- data/spec/samples/clean_due_to_masking/masked.reek +7 -0
- data/spec/samples/corrupt_config_file/corrupt.reek +1 -0
- data/spec/samples/corrupt_config_file/dirty.rb +7 -0
- data/spec/samples/empty_config_file/dirty.rb +7 -0
- data/spec/samples/empty_config_file/empty.reek +0 -0
- data/spec/samples/exceptions.reek +4 -0
- data/spec/{slow/samples → samples}/inline.rb +0 -0
- data/spec/samples/masked/dirty.rb +7 -0
- data/spec/samples/masked/masked.reek +3 -0
- data/spec/samples/mixed_results/clean_one.rb +6 -0
- data/spec/samples/mixed_results/clean_three.rb +6 -0
- data/spec/samples/mixed_results/clean_two.rb +6 -0
- data/spec/samples/mixed_results/dirty_one.rb +7 -0
- data/spec/samples/mixed_results/dirty_two.rb +7 -0
- data/spec/samples/not_quite_masked/dirty.rb +8 -0
- data/spec/samples/not_quite_masked/masked.reek +5 -0
- data/spec/{slow/samples → samples}/optparse.rb +0 -0
- data/spec/samples/overrides/masked/dirty.rb +7 -0
- data/spec/samples/overrides/masked/lower.reek +5 -0
- data/spec/samples/overrides/upper.reek +5 -0
- data/spec/{slow/samples → samples}/redcloth.rb +0 -0
- data/spec/samples/three_clean_files/clean_one.rb +6 -0
- data/spec/samples/three_clean_files/clean_three.rb +6 -0
- data/spec/samples/three_clean_files/clean_two.rb +6 -0
- data/spec/samples/two_smelly_files/dirty_one.rb +7 -0
- data/spec/samples/two_smelly_files/dirty_two.rb +7 -0
- data/spec/spec.opts +1 -1
- data/spec/spec_helper.rb +4 -4
- data/tasks/reek.rake +8 -5
- data/tasks/test.rake +51 -0
- metadata +75 -25
- data/README.txt +0 -6
- data/lib/reek/options.rb +0 -92
- data/lib/reek/report.rb +0 -81
- data/lib/reek/smells/smells.rb +0 -81
- data/lib/reek/source.rb +0 -127
- data/spec/reek/options_spec.rb +0 -13
- data/spec/reek/report_spec.rb +0 -48
- data/spec/reek/smells/smell_spec.rb +0 -24
- data/spec/slow/inline_spec.rb +0 -43
- data/spec/slow/optparse_spec.rb +0 -108
- data/spec/slow/redcloth_spec.rb +0 -101
- data/spec/slow/reek_source_spec.rb +0 -20
- data/spec/slow/script_spec.rb +0 -55
- data/spec/slow/source_list_spec.rb +0 -40
- data/tasks/rspec.rake +0 -21
data/lib/reek/block_context.rb
CHANGED
data/lib/reek/class_context.rb
CHANGED
@@ -2,8 +2,11 @@ require 'set'
|
|
2
2
|
require 'reek/code_context'
|
3
3
|
|
4
4
|
class Class
|
5
|
-
def is_overriding_method?(
|
6
|
-
|
5
|
+
def is_overriding_method?(name)
|
6
|
+
sym = name.to_sym
|
7
|
+
mine = instance_methods(false)
|
8
|
+
dads = superclass.instance_methods(true)
|
9
|
+
(mine.include?(sym) and dads.include?(sym)) or (mine.include?(name) and dads.include?(name))
|
7
10
|
end
|
8
11
|
end
|
9
12
|
|
@@ -15,12 +18,22 @@ module Reek
|
|
15
18
|
ClassContext.new(res[0], res[1], exp[2])
|
16
19
|
end
|
17
20
|
|
21
|
+
def ClassContext.from_s(src)
|
22
|
+
source = src.to_reek_source
|
23
|
+
sniffer = Sniffer.new(source)
|
24
|
+
CodeParser.new(sniffer).process_class(source.syntax_tree)
|
25
|
+
end
|
26
|
+
|
27
|
+
attr_reader :conditionals, :parsed_methods
|
28
|
+
|
29
|
+
# SMELL: inconsistent with other contexts (not linked to the sexp)
|
18
30
|
def initialize(outer, name, superclass = nil)
|
19
31
|
super(outer, nil)
|
20
32
|
@name = name
|
21
33
|
@superclass = superclass
|
22
34
|
@parsed_methods = []
|
23
35
|
@instance_variables = Set.new
|
36
|
+
@conditionals = []
|
24
37
|
end
|
25
38
|
|
26
39
|
def myself
|
@@ -28,9 +41,8 @@ module Reek
|
|
28
41
|
end
|
29
42
|
|
30
43
|
def find_module(modname)
|
31
|
-
sym = modname.to_s
|
32
44
|
return nil unless myself
|
33
|
-
@myself.
|
45
|
+
@myself.const_or_nil(modname.to_s)
|
34
46
|
end
|
35
47
|
|
36
48
|
def is_overriding_method?(name)
|
@@ -50,8 +62,8 @@ module Reek
|
|
50
62
|
@instance_variables << Name.new(sym)
|
51
63
|
end
|
52
64
|
|
53
|
-
def record_method(
|
54
|
-
@parsed_methods <<
|
65
|
+
def record_method(meth)
|
66
|
+
@parsed_methods << meth
|
55
67
|
end
|
56
68
|
|
57
69
|
def outer_name
|
@@ -65,5 +77,13 @@ module Reek
|
|
65
77
|
def variable_names
|
66
78
|
@instance_variables
|
67
79
|
end
|
80
|
+
|
81
|
+
def record_conditional(exp)
|
82
|
+
@conditionals << exp
|
83
|
+
end
|
84
|
+
|
85
|
+
def parameterized_methods(min_clump_size)
|
86
|
+
parsed_methods.select {|meth| meth.parameters.length >= min_clump_size }
|
87
|
+
end
|
68
88
|
end
|
69
89
|
end
|
data/lib/reek/code_context.rb
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
class Module
|
2
|
+
|
3
|
+
def const_or_nil(sym)
|
4
|
+
const_defined?(sym) ? const_get(sym) : nil
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
1
8
|
module Reek
|
2
9
|
|
3
10
|
#
|
@@ -16,6 +23,7 @@ module Reek
|
|
16
23
|
@myself = nil
|
17
24
|
end
|
18
25
|
|
26
|
+
# SMELL: Temporary Field -- @name isn't always initialized
|
19
27
|
def matches?(strings)
|
20
28
|
me = @name.to_s
|
21
29
|
strings.any? do |str|
|
data/lib/reek/code_parser.rb
CHANGED
@@ -9,15 +9,33 @@ require 'reek/method_context'
|
|
9
9
|
require 'reek/singleton_method_context'
|
10
10
|
require 'reek/yield_call_context'
|
11
11
|
|
12
|
+
#
|
13
|
+
# Extensions to +Sexp+ to allow +CodeParser+ to navigate the abstract
|
14
|
+
# syntax tree more easily.
|
15
|
+
#
|
16
|
+
class Sexp
|
17
|
+
def children
|
18
|
+
find_all { |item| Sexp === item }
|
19
|
+
end
|
20
|
+
|
21
|
+
def is_language_node?
|
22
|
+
first.class == Symbol
|
23
|
+
end
|
24
|
+
|
25
|
+
def has_type?(type)
|
26
|
+
is_language_node? and first == type
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
12
30
|
module Reek
|
13
31
|
|
14
32
|
class CodeParser
|
15
33
|
|
16
|
-
#
|
17
|
-
#
|
18
|
-
|
19
|
-
|
20
|
-
@
|
34
|
+
#
|
35
|
+
# Creates a new Ruby code checker.
|
36
|
+
#
|
37
|
+
def initialize(sniffer, ctx = StopContext.new)
|
38
|
+
@sniffer = sniffer
|
21
39
|
@element = ctx
|
22
40
|
end
|
23
41
|
|
@@ -25,6 +43,7 @@ module Reek
|
|
25
43
|
meth = "process_#{exp[0]}"
|
26
44
|
meth = :process_default unless self.respond_to?(meth)
|
27
45
|
self.send(meth, exp)
|
46
|
+
@element
|
28
47
|
end
|
29
48
|
|
30
49
|
def process_default(exp)
|
@@ -39,10 +58,12 @@ module Reek
|
|
39
58
|
end
|
40
59
|
|
41
60
|
def process_class(exp)
|
42
|
-
|
61
|
+
scope = ClassContext.create(@element, exp)
|
62
|
+
push(scope) do
|
43
63
|
process_default(exp) unless @element.is_struct?
|
44
64
|
check_smells(:class)
|
45
65
|
end
|
66
|
+
scope
|
46
67
|
end
|
47
68
|
|
48
69
|
def process_defn(exp)
|
@@ -53,14 +74,16 @@ module Reek
|
|
53
74
|
handle_context(SingletonMethodContext, :defs, exp)
|
54
75
|
end
|
55
76
|
|
56
|
-
def process_args(exp)
|
57
|
-
exp[1..-1].each {|sym| @element.record_parameter(sym) }
|
58
|
-
end
|
77
|
+
def process_args(exp) end
|
59
78
|
|
60
79
|
def process_attrset(exp)
|
61
80
|
@element.record_depends_on_self if /^@/ === exp[1].to_s
|
62
81
|
end
|
63
82
|
|
83
|
+
def process_zsuper(exp)
|
84
|
+
@element.record_use_of_self
|
85
|
+
end
|
86
|
+
|
64
87
|
def process_lit(exp)
|
65
88
|
val = exp[1]
|
66
89
|
@element.record_depends_on_self if val == :self
|
@@ -70,11 +93,6 @@ module Reek
|
|
70
93
|
process(exp[1])
|
71
94
|
handle_context(BlockContext, :iter, exp[2..-1])
|
72
95
|
end
|
73
|
-
|
74
|
-
def process_dasgn_curr(exp)
|
75
|
-
@element.record_parameter(exp[1])
|
76
|
-
process_default(exp)
|
77
|
-
end
|
78
96
|
|
79
97
|
def process_block(exp)
|
80
98
|
@element.count_statements(CodeParser.count_statements(exp))
|
@@ -90,19 +108,10 @@ module Reek
|
|
90
108
|
process_default(exp)
|
91
109
|
end
|
92
110
|
|
93
|
-
def process_fcall(exp)
|
94
|
-
@element.record_use_of_self
|
95
|
-
process_default(exp)
|
96
|
-
end
|
97
|
-
|
98
111
|
def process_cfunc(exp)
|
99
112
|
@element.record_depends_on_self
|
100
113
|
end
|
101
114
|
|
102
|
-
def process_vcall(exp)
|
103
|
-
@element.record_use_of_self
|
104
|
-
end
|
105
|
-
|
106
115
|
def process_attrasgn(exp)
|
107
116
|
process_call(exp)
|
108
117
|
end
|
@@ -112,7 +121,46 @@ module Reek
|
|
112
121
|
end
|
113
122
|
|
114
123
|
def process_if(exp)
|
124
|
+
@element.record_conditional(exp[1])
|
125
|
+
count_clause(exp[2])
|
126
|
+
count_clause(exp[3])
|
115
127
|
handle_context(IfContext, :if, exp)
|
128
|
+
@element.count_statements(-1)
|
129
|
+
end
|
130
|
+
|
131
|
+
def process_while(exp)
|
132
|
+
process_until(exp)
|
133
|
+
end
|
134
|
+
|
135
|
+
def process_until(exp)
|
136
|
+
count_clause(exp[2])
|
137
|
+
process_default(exp)
|
138
|
+
@element.count_statements(-1)
|
139
|
+
end
|
140
|
+
|
141
|
+
def process_for(exp)
|
142
|
+
count_clause(exp[3])
|
143
|
+
process_case(exp)
|
144
|
+
end
|
145
|
+
|
146
|
+
def process_rescue(exp)
|
147
|
+
count_clause(exp[1])
|
148
|
+
process_case(exp)
|
149
|
+
end
|
150
|
+
|
151
|
+
def process_resbody(exp)
|
152
|
+
process_when(exp)
|
153
|
+
end
|
154
|
+
|
155
|
+
def process_case(exp)
|
156
|
+
@element.record_conditional(exp[1])
|
157
|
+
process_default(exp)
|
158
|
+
@element.count_statements(-1)
|
159
|
+
end
|
160
|
+
|
161
|
+
def process_when(exp)
|
162
|
+
count_clause(exp[2])
|
163
|
+
process_default(exp)
|
116
164
|
end
|
117
165
|
|
118
166
|
def process_ivar(exp)
|
@@ -131,36 +179,35 @@ module Reek
|
|
131
179
|
end
|
132
180
|
|
133
181
|
def process_self(exp)
|
134
|
-
@element.
|
182
|
+
@element.record_use_of_self
|
183
|
+
end
|
184
|
+
|
185
|
+
def count_clause(sexp)
|
186
|
+
if sexp and !sexp.has_type?(:block)
|
187
|
+
@element.count_statements(1)
|
188
|
+
end
|
135
189
|
end
|
136
190
|
|
137
191
|
def self.count_statements(exp)
|
138
192
|
stmts = exp[1..-1]
|
139
193
|
ignore = 0
|
140
|
-
ignore = 1 if is_expr?(stmts[0], :args)
|
141
194
|
ignore += 1 if stmts[1] == s(:nil)
|
142
195
|
stmts.length - ignore
|
143
196
|
end
|
144
197
|
|
145
198
|
private
|
146
199
|
|
147
|
-
def self.is_expr?(exp, type)
|
148
|
-
Array === exp and exp[0] == type
|
149
|
-
end
|
150
|
-
|
151
|
-
def self.is_global_variable?(exp)
|
152
|
-
is_expr?(exp, :gvar)
|
153
|
-
end
|
154
|
-
|
155
200
|
def handle_context(klass, type, exp)
|
156
|
-
|
201
|
+
scope = klass.new(@element, exp)
|
202
|
+
push(scope) do
|
157
203
|
process_default(exp)
|
158
204
|
check_smells(type)
|
159
205
|
end
|
206
|
+
scope
|
160
207
|
end
|
161
208
|
|
162
209
|
def check_smells(type)
|
163
|
-
@
|
210
|
+
@sniffer.examine(@element, type)
|
164
211
|
end
|
165
212
|
|
166
213
|
def push(context)
|
@@ -169,9 +216,5 @@ module Reek
|
|
169
216
|
yield
|
170
217
|
@element = orig
|
171
218
|
end
|
172
|
-
|
173
|
-
def pop(exp)
|
174
|
-
@element = @element.outer
|
175
|
-
end
|
176
219
|
end
|
177
220
|
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'reek'
|
3
|
+
|
4
|
+
module Reek
|
5
|
+
|
6
|
+
# SMELL: Greedy Module
|
7
|
+
# This creates the command-line parser AND invokes it. And for the
|
8
|
+
# -v and -h options it also executes them. And it holds the config
|
9
|
+
# options for the rest of the application.
|
10
|
+
class Options
|
11
|
+
|
12
|
+
CTX_SORT = '%m%c %w (%s)'
|
13
|
+
SMELL_SORT = '%m[%s] %c %w'
|
14
|
+
|
15
|
+
def self.default_options
|
16
|
+
{
|
17
|
+
:format => CTX_SORT,
|
18
|
+
:show_all => false,
|
19
|
+
:quiet => false
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
# SMELL: Global Variable
|
24
|
+
@@opts = default_options
|
25
|
+
|
26
|
+
def self.[](key)
|
27
|
+
@@opts[key]
|
28
|
+
end
|
29
|
+
|
30
|
+
def initialize(argv)
|
31
|
+
@argv = argv
|
32
|
+
@parser = OptionParser.new
|
33
|
+
set_options
|
34
|
+
end
|
35
|
+
|
36
|
+
def parse
|
37
|
+
@parser.parse!(@argv)
|
38
|
+
@argv
|
39
|
+
end
|
40
|
+
|
41
|
+
def set_options
|
42
|
+
@parser.banner = <<EOB
|
43
|
+
Usage: #{@parser.program_name} [options] [files]
|
44
|
+
|
45
|
+
Examples:
|
46
|
+
|
47
|
+
#{@parser.program_name} lib/*.rb
|
48
|
+
#{@parser.program_name} -q -a lib
|
49
|
+
cat my_class.rb | #{@parser.program_name}
|
50
|
+
|
51
|
+
See http://wiki.github.com/kevinrutherford/reek for detailed help.
|
52
|
+
|
53
|
+
EOB
|
54
|
+
|
55
|
+
@parser.separator "Common options:"
|
56
|
+
|
57
|
+
@parser.on("-h", "--help", "Show this message") do
|
58
|
+
puts @parser
|
59
|
+
exit(0)
|
60
|
+
end
|
61
|
+
@parser.on("-v", "--version", "Show version") do
|
62
|
+
puts "#{@parser.program_name} #{Reek::VERSION}"
|
63
|
+
exit(0)
|
64
|
+
end
|
65
|
+
|
66
|
+
@parser.separator "\nReport formatting:"
|
67
|
+
|
68
|
+
@parser.on("-a", "--[no-]show-all", "Show all smells, including those masked by config settings") do |opt|
|
69
|
+
@@opts[:show_all] = opt
|
70
|
+
end
|
71
|
+
@parser.on("-q", "--quiet", "Suppress headings for smell-free source files") do
|
72
|
+
@@opts[:quiet] = true
|
73
|
+
end
|
74
|
+
@parser.on('-f', "--format FORMAT", 'Specify the format of smell warnings') do |arg|
|
75
|
+
@@opts[:format] = arg unless arg.nil?
|
76
|
+
end
|
77
|
+
@parser.on('-c', '--context-first', "Sort by context; sets the format string to \"#{CTX_SORT}\"") do
|
78
|
+
@@opts[:format] = CTX_SORT
|
79
|
+
end
|
80
|
+
@parser.on('-s', '--smell-first', "Sort by smell; sets the format string to \"#{SMELL_SORT}\"") do
|
81
|
+
@@opts[:format] = SMELL_SORT
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Reek
|
2
|
+
|
3
|
+
#
|
4
|
+
# Represents a single set of configuration options for a smell detector
|
5
|
+
#
|
6
|
+
class SmellConfiguration
|
7
|
+
|
8
|
+
# The name of the config field that specifies whether a smell is
|
9
|
+
# enabled. Set to +true+ or +false+.
|
10
|
+
ENABLED_KEY = 'enabled'
|
11
|
+
|
12
|
+
# The name of the config field that sets scope-specific overrides
|
13
|
+
# for other values in the current smell detector's configuration.
|
14
|
+
OVERRIDES_KEY = 'overrides'
|
15
|
+
|
16
|
+
attr_reader :hash
|
17
|
+
|
18
|
+
def initialize(hash)
|
19
|
+
@hash = hash
|
20
|
+
end
|
21
|
+
|
22
|
+
# SMELL: Getter
|
23
|
+
def enabled?
|
24
|
+
@hash[ENABLED_KEY]
|
25
|
+
end
|
26
|
+
|
27
|
+
def overrides_for(context)
|
28
|
+
Overrides.new(@hash.fetch(OVERRIDES_KEY, {})).for_context(context)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Retrieves the value, if any, for the given +key+.
|
32
|
+
#
|
33
|
+
# Returns +fall_back+ if this config has no value for the key.
|
34
|
+
#
|
35
|
+
def value(key, context, fall_back)
|
36
|
+
overrides_for(context).each { |conf| return conf[key] if conf.has_key?(key) }
|
37
|
+
return @hash.fetch(key, fall_back)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class Overrides
|
42
|
+
def initialize(hash)
|
43
|
+
@hash = hash
|
44
|
+
end
|
45
|
+
|
46
|
+
def for_context(context)
|
47
|
+
contexts = @hash.keys.select {|ckey| context.matches?([ckey])}
|
48
|
+
contexts.map { |exc| @hash[exc] }
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|