rspec-core 3.1.7 → 3.2.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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/.yardopts +1 -0
- data/Changelog.md +84 -0
- data/README.md +10 -1
- data/lib/rspec/core.rb +28 -8
- data/lib/rspec/core/backport_random.rb +12 -9
- data/lib/rspec/core/configuration.rb +350 -112
- data/lib/rspec/core/configuration_options.rb +14 -7
- data/lib/rspec/core/dsl.rb +7 -4
- data/lib/rspec/core/example.rb +86 -50
- data/lib/rspec/core/example_group.rb +247 -86
- data/lib/rspec/core/filter_manager.rb +38 -93
- data/lib/rspec/core/flat_map.rb +4 -4
- data/lib/rspec/core/formatters.rb +10 -6
- data/lib/rspec/core/formatters/base_formatter.rb +7 -4
- data/lib/rspec/core/formatters/base_text_formatter.rb +12 -12
- data/lib/rspec/core/formatters/console_codes.rb +8 -7
- data/lib/rspec/core/formatters/deprecation_formatter.rb +5 -3
- data/lib/rspec/core/formatters/documentation_formatter.rb +10 -4
- data/lib/rspec/core/formatters/helpers.rb +6 -4
- data/lib/rspec/core/formatters/html_formatter.rb +13 -8
- data/lib/rspec/core/formatters/html_printer.rb +26 -10
- data/lib/rspec/core/formatters/profile_formatter.rb +10 -7
- data/lib/rspec/core/formatters/protocol.rb +27 -18
- data/lib/rspec/core/formatters/snippet_extractor.rb +14 -7
- data/lib/rspec/core/hooks.rb +252 -211
- data/lib/rspec/core/memoized_helpers.rb +16 -16
- data/lib/rspec/core/metadata.rb +67 -28
- data/lib/rspec/core/metadata_filter.rb +151 -24
- data/lib/rspec/core/minitest_assertions_adapter.rb +5 -2
- data/lib/rspec/core/mocking_adapters/flexmock.rb +1 -1
- data/lib/rspec/core/mocking_adapters/mocha.rb +8 -8
- data/lib/rspec/core/notifications.rb +155 -94
- data/lib/rspec/core/option_parser.rb +16 -10
- data/lib/rspec/core/pending.rb +11 -9
- data/lib/rspec/core/project_initializer.rb +1 -1
- data/lib/rspec/core/project_initializer/spec/spec_helper.rb +10 -8
- data/lib/rspec/core/rake_task.rb +37 -52
- data/lib/rspec/core/reporter.rb +30 -7
- data/lib/rspec/core/ruby_project.rb +12 -4
- data/lib/rspec/core/runner.rb +5 -8
- data/lib/rspec/core/sandbox.rb +37 -0
- data/lib/rspec/core/shared_example_group.rb +41 -15
- data/lib/rspec/core/test_unit_assertions_adapter.rb +3 -3
- data/lib/rspec/core/version.rb +1 -1
- data/lib/rspec/core/warnings.rb +2 -2
- data/lib/rspec/core/world.rb +12 -28
- metadata +44 -31
- metadata.gz.sig +0 -0
@@ -20,7 +20,7 @@ module RSpec
|
|
20
20
|
#
|
21
21
|
# @example
|
22
22
|
#
|
23
|
-
# #
|
23
|
+
# # Explicit declaration of subject.
|
24
24
|
# describe Person do
|
25
25
|
# subject { Person.new(:birthdate => 19.years.ago) }
|
26
26
|
# it "should be eligible to vote" do
|
@@ -29,7 +29,7 @@ module RSpec
|
|
29
29
|
# end
|
30
30
|
# end
|
31
31
|
#
|
32
|
-
# #
|
32
|
+
# # Implicit subject => { Person.new }.
|
33
33
|
# describe Person do
|
34
34
|
# it "should be eligible to vote" do
|
35
35
|
# subject.should be_eligible_to_vote
|
@@ -37,17 +37,17 @@ module RSpec
|
|
37
37
|
# end
|
38
38
|
# end
|
39
39
|
#
|
40
|
-
# #
|
40
|
+
# # One-liner syntax - expectation is set on the subject.
|
41
41
|
# describe Person do
|
42
42
|
# it { is_expected.to be_eligible_to_vote }
|
43
43
|
# # or
|
44
44
|
# it { should be_eligible_to_vote }
|
45
45
|
# end
|
46
46
|
#
|
47
|
-
# @note Because `subject` is designed to create state that is reset
|
48
|
-
# each example, and `before(:context)` is designed to setup
|
49
|
-
# shared across _all_ examples in an example group,
|
50
|
-
# intended to be used in a `before(:context)` hook.
|
47
|
+
# @note Because `subject` is designed to create state that is reset
|
48
|
+
# between each example, and `before(:context)` is designed to setup
|
49
|
+
# state that is shared across _all_ examples in an example group,
|
50
|
+
# `subject` is _not_ intended to be used in a `before(:context)` hook.
|
51
51
|
#
|
52
52
|
# @see #should
|
53
53
|
# @see #should_not
|
@@ -211,8 +211,8 @@ EOS
|
|
211
211
|
# though we have yet to see this in practice. You've been warned.
|
212
212
|
#
|
213
213
|
# @note Because `let` is designed to create state that is reset between
|
214
|
-
# each example, and `before(:context)` is designed to setup state that
|
215
|
-
# shared across _all_ examples in an example group, `let` is _not_
|
214
|
+
# each example, and `before(:context)` is designed to setup state that
|
215
|
+
# is shared across _all_ examples in an example group, `let` is _not_
|
216
216
|
# intended to be used in a `before(:context)` hook.
|
217
217
|
#
|
218
218
|
# @example
|
@@ -221,10 +221,10 @@ EOS
|
|
221
221
|
# let(:thing) { Thing.new }
|
222
222
|
#
|
223
223
|
# it "does something" do
|
224
|
-
# #
|
224
|
+
# # First invocation, executes block, memoizes and returns result.
|
225
225
|
# thing.do_something
|
226
226
|
#
|
227
|
-
# #
|
227
|
+
# # Second invocation, returns the memoized value.
|
228
228
|
# thing.should be_something
|
229
229
|
# end
|
230
230
|
# end
|
@@ -302,8 +302,8 @@ EOS
|
|
302
302
|
end
|
303
303
|
|
304
304
|
# Declares a `subject` for an example group which can then be wrapped
|
305
|
-
# with `expect` using `is_expected` to make it the target of an
|
306
|
-
# in a concise, one-line example.
|
305
|
+
# with `expect` using `is_expected` to make it the target of an
|
306
|
+
# expectation in a concise, one-line example.
|
307
307
|
#
|
308
308
|
# Given a `name`, defines a method with that name which returns the
|
309
309
|
# `subject`. This lets you declare the subject once and access it
|
@@ -348,9 +348,9 @@ EOS
|
|
348
348
|
end
|
349
349
|
end
|
350
350
|
|
351
|
-
# Just like `subject`, except the block is invoked by an implicit
|
352
|
-
# hook. This serves a dual purpose of setting up state and
|
353
|
-
# memoized reference to that state.
|
351
|
+
# Just like `subject`, except the block is invoked by an implicit
|
352
|
+
# `before` hook. This serves a dual purpose of setting up state and
|
353
|
+
# providing a memoized reference to that state.
|
354
354
|
#
|
355
355
|
# @example
|
356
356
|
#
|
data/lib/rspec/core/metadata.rb
CHANGED
@@ -25,30 +25,51 @@ module RSpec
|
|
25
25
|
# @see Configuration#filter_run_including
|
26
26
|
# @see Configuration#filter_run_excluding
|
27
27
|
module Metadata
|
28
|
+
# Matches strings either at the beginning of the input or prefixed with a
|
29
|
+
# whitespace, containing the current path, either postfixed with the
|
30
|
+
# separator, or at the end of the string. Match groups are the character
|
31
|
+
# before and the character after the string if any.
|
32
|
+
#
|
33
|
+
# http://rubular.com/r/fT0gmX6VJX
|
34
|
+
# http://rubular.com/r/duOrD4i3wb
|
35
|
+
# http://rubular.com/r/sbAMHFrOx1
|
36
|
+
def self.relative_path_regex
|
37
|
+
@relative_path_regex ||= /(\A|\s)#{File.expand_path('.')}(#{File::SEPARATOR}|\s|\Z)/
|
38
|
+
end
|
39
|
+
|
28
40
|
# @api private
|
29
41
|
#
|
30
42
|
# @param line [String] current code line
|
31
43
|
# @return [String] relative path to line
|
32
44
|
def self.relative_path(line)
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
#
|
37
|
-
# http://rubular.com/r/fT0gmX6VJX
|
38
|
-
# http://rubular.com/r/duOrD4i3wb
|
39
|
-
# http://rubular.com/r/sbAMHFrOx1
|
40
|
-
#
|
41
|
-
|
42
|
-
regex = /(\A|\s)#{File.expand_path('.')}(#{File::SEPARATOR}|\s|\Z)/
|
43
|
-
|
44
|
-
line = line.sub(regex, "\\1.\\2")
|
45
|
-
line = line.sub(/\A([^:]+:\d+)$/, '\\1')
|
46
|
-
return nil if line == '-e:1'
|
45
|
+
line = line.sub(relative_path_regex, "\\1.\\2".freeze)
|
46
|
+
line = line.sub(/\A([^:]+:\d+)$/, '\\1'.freeze)
|
47
|
+
return nil if line == '-e:1'.freeze
|
47
48
|
line
|
48
49
|
rescue SecurityError
|
49
50
|
nil
|
50
51
|
end
|
51
52
|
|
53
|
+
# @private
|
54
|
+
# Iteratively walks up from the given metadata through all
|
55
|
+
# example group ancestors, yielding each metadata hash along the way.
|
56
|
+
def self.ascending(metadata)
|
57
|
+
yield metadata
|
58
|
+
return unless (group_metadata = metadata.fetch(:example_group) { metadata[:parent_example_group] })
|
59
|
+
|
60
|
+
loop do
|
61
|
+
yield group_metadata
|
62
|
+
break unless (group_metadata = group_metadata[:parent_example_group])
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# @private
|
67
|
+
# Returns an enumerator that iteratively walks up the given metadata through all
|
68
|
+
# example group ancestors, yielding each metadata hash along the way.
|
69
|
+
def self.ascend(metadata)
|
70
|
+
enum_for(:ascending, metadata)
|
71
|
+
end
|
72
|
+
|
52
73
|
# @private
|
53
74
|
# Used internally to build a hash from an args array.
|
54
75
|
# Symbols are converted into hash keys with a value of `true`.
|
@@ -67,6 +88,17 @@ module RSpec
|
|
67
88
|
hash
|
68
89
|
end
|
69
90
|
|
91
|
+
# @private
|
92
|
+
def self.deep_hash_dup(object)
|
93
|
+
return object.dup if Array === object
|
94
|
+
return object unless Hash === object
|
95
|
+
|
96
|
+
object.inject(object.dup) do |duplicate, (key, value)|
|
97
|
+
duplicate[key] = deep_hash_dup(value)
|
98
|
+
duplicate
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
70
102
|
# @private
|
71
103
|
def self.backtrace_from(block)
|
72
104
|
return caller unless block.respond_to?(:source_location)
|
@@ -114,10 +146,11 @@ module RSpec
|
|
114
146
|
file_path_and_line_number_from(caller)
|
115
147
|
end
|
116
148
|
|
117
|
-
|
118
|
-
metadata[:file_path]
|
119
|
-
metadata[:line_number]
|
120
|
-
metadata[:location]
|
149
|
+
relative_file_path = Metadata.relative_path(file_path)
|
150
|
+
metadata[:file_path] = relative_file_path
|
151
|
+
metadata[:line_number] = line_number.to_i
|
152
|
+
metadata[:location] = "#{relative_file_path}:#{line_number}"
|
153
|
+
metadata[:absolute_file_path] = File.expand_path(relative_file_path)
|
121
154
|
end
|
122
155
|
|
123
156
|
def file_path_and_line_number_from(backtrace)
|
@@ -128,16 +161,16 @@ module RSpec
|
|
128
161
|
|
129
162
|
def description_separator(parent_part, child_part)
|
130
163
|
if parent_part.is_a?(Module) && child_part =~ /^(#|::|\.)/
|
131
|
-
''
|
164
|
+
''.freeze
|
132
165
|
else
|
133
|
-
' '
|
166
|
+
' '.freeze
|
134
167
|
end
|
135
168
|
end
|
136
169
|
|
137
170
|
def build_description_from(parent_description=nil, my_description=nil)
|
138
171
|
return parent_description.to_s unless my_description
|
139
172
|
separator = description_separator(parent_description, my_description)
|
140
|
-
parent_description.to_s + separator
|
173
|
+
(parent_description.to_s + separator) << my_description.to_s
|
141
174
|
end
|
142
175
|
|
143
176
|
def ensure_valid_user_keys
|
@@ -171,9 +204,11 @@ module RSpec
|
|
171
204
|
group_metadata.update(example_metadata)
|
172
205
|
|
173
206
|
example_metadata[:example_group] = group_metadata
|
207
|
+
example_metadata[:shared_group_inclusion_backtrace] = SharedExampleGroupInclusionStackFrame.current_backtrace
|
174
208
|
example_metadata.delete(:parent_example_group)
|
175
209
|
|
176
|
-
|
210
|
+
description_args = description.nil? ? [] : [description]
|
211
|
+
hash = new(example_metadata, user_metadata, description_args, block)
|
177
212
|
hash.populate
|
178
213
|
hash.metadata
|
179
214
|
end
|
@@ -215,16 +250,18 @@ module RSpec
|
|
215
250
|
Proc.new do |hash, key|
|
216
251
|
case key
|
217
252
|
when :example_group
|
218
|
-
# We commonly get here when rspec-core is applying a previously
|
219
|
-
# filter rule, such as when a gem configures:
|
253
|
+
# We commonly get here when rspec-core is applying a previously
|
254
|
+
# configured filter rule, such as when a gem configures:
|
220
255
|
#
|
221
256
|
# RSpec.configure do |c|
|
222
257
|
# c.include MyGemHelpers, :example_group => { :file_path => /spec\/my_gem_specs/ }
|
223
258
|
# end
|
224
259
|
#
|
225
|
-
# It's confusing for a user to get a deprecation at this point in
|
226
|
-
# we issue a deprecation from the config APIs
|
227
|
-
#
|
260
|
+
# It's confusing for a user to get a deprecation at this point in
|
261
|
+
# the code, so instead we issue a deprecation from the config APIs
|
262
|
+
# that take a metadata hash, and MetadataFilter sets this thread
|
263
|
+
# local to silence the warning here since it would be so
|
264
|
+
# confusing.
|
228
265
|
unless RSpec.thread_local_metadata[:silence_metadata_example_group_deprecations]
|
229
266
|
RSpec.deprecate("The `:example_group` key in an example group's metadata hash",
|
230
267
|
:replacement => "the example group's hash directly for the " \
|
@@ -275,6 +312,7 @@ module RSpec
|
|
275
312
|
:parent_example_group,
|
276
313
|
:execution_result,
|
277
314
|
:file_path,
|
315
|
+
:absolute_file_path,
|
278
316
|
:full_description,
|
279
317
|
:line_number,
|
280
318
|
:location,
|
@@ -405,7 +443,8 @@ module RSpec
|
|
405
443
|
# `metadata[:example_group][:described_class]` when you use
|
406
444
|
# anonymous controller specs) such that changes are written
|
407
445
|
# back to the top-level metadata hash.
|
408
|
-
# * Exposes the parent group metadata as
|
446
|
+
# * Exposes the parent group metadata as
|
447
|
+
# `[:example_group][:example_group]`.
|
409
448
|
class LegacyExampleGroupHash
|
410
449
|
include HashImitatable
|
411
450
|
|
@@ -8,13 +8,8 @@ module RSpec
|
|
8
8
|
module MetadataFilter
|
9
9
|
class << self
|
10
10
|
# @private
|
11
|
-
def
|
12
|
-
filters.
|
13
|
-
end
|
14
|
-
|
15
|
-
# @private
|
16
|
-
def all_apply?(filters, metadata)
|
17
|
-
filters.all? { |k, v| filter_applies?(k, v, metadata) }
|
11
|
+
def apply?(predicate, filters, metadata)
|
12
|
+
filters.__send__(predicate) { |k, v| filter_applies?(k, v, metadata) }
|
18
13
|
end
|
19
14
|
|
20
15
|
# @private
|
@@ -48,9 +43,8 @@ module RSpec
|
|
48
43
|
end
|
49
44
|
|
50
45
|
def location_filter_applies?(locations, metadata)
|
51
|
-
|
52
|
-
|
53
|
-
line_number ? line_number_filter_applies?(line_number, metadata) : true
|
46
|
+
line_numbers = example_group_declaration_lines(locations, metadata)
|
47
|
+
line_numbers.empty? || line_number_filter_applies?(line_numbers, metadata)
|
54
48
|
end
|
55
49
|
|
56
50
|
def line_number_filter_applies?(line_numbers, metadata)
|
@@ -59,14 +53,13 @@ module RSpec
|
|
59
53
|
end
|
60
54
|
|
61
55
|
def relevant_line_numbers(metadata)
|
62
|
-
|
63
|
-
[metadata[:line_number]].compact + (relevant_line_numbers(parent_of metadata))
|
56
|
+
Metadata.ascend(metadata).map { |meta| meta[:line_number] }
|
64
57
|
end
|
65
58
|
|
66
|
-
def
|
67
|
-
|
68
|
-
|
69
|
-
|
59
|
+
def example_group_declaration_lines(locations, metadata)
|
60
|
+
FlatMap.flat_map(Metadata.ascend(metadata)) do |meta|
|
61
|
+
locations[meta[:absolute_file_path]]
|
62
|
+
end.uniq
|
70
63
|
end
|
71
64
|
|
72
65
|
def filters_apply?(key, value, metadata)
|
@@ -75,14 +68,6 @@ module RSpec
|
|
75
68
|
value.all? { |k, v| filter_applies?(k, v, subhash) }
|
76
69
|
end
|
77
70
|
|
78
|
-
def parent_of(metadata)
|
79
|
-
if metadata.key?(:example_group)
|
80
|
-
metadata[:example_group]
|
81
|
-
else
|
82
|
-
metadata[:parent_example_group]
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
71
|
def silence_metadata_example_group_deprecations
|
87
72
|
RSpec.thread_local_metadata[:silence_metadata_example_group_deprecations] = true
|
88
73
|
yield
|
@@ -91,5 +76,147 @@ module RSpec
|
|
91
76
|
end
|
92
77
|
end
|
93
78
|
end
|
79
|
+
|
80
|
+
# Tracks a collection of filterable items (e.g. modules, hooks, etc)
|
81
|
+
# and provides an optimized API to get the applicable items for the
|
82
|
+
# metadata of an example or example group.
|
83
|
+
#
|
84
|
+
# There are two implementations, optimized for different uses.
|
85
|
+
# @private
|
86
|
+
module FilterableItemRepository
|
87
|
+
# This implementation is simple, and is optimized for frequent
|
88
|
+
# updates but rare queries. `append` and `prepend` do no extra
|
89
|
+
# processing, and no internal memoization is done, since this
|
90
|
+
# is not optimized for queries.
|
91
|
+
#
|
92
|
+
# This is ideal for use by a example or example group, which may
|
93
|
+
# be updated multiple times with globally configured hooks, etc,
|
94
|
+
# but will not be queried frequently by other examples or examle
|
95
|
+
# groups.
|
96
|
+
# @private
|
97
|
+
class UpdateOptimized
|
98
|
+
attr_reader :items_and_filters
|
99
|
+
|
100
|
+
def initialize(applies_predicate)
|
101
|
+
@applies_predicate = applies_predicate
|
102
|
+
@items_and_filters = []
|
103
|
+
end
|
104
|
+
|
105
|
+
def append(item, metadata)
|
106
|
+
@items_and_filters << [item, metadata]
|
107
|
+
end
|
108
|
+
|
109
|
+
def prepend(item, metadata)
|
110
|
+
@items_and_filters.unshift [item, metadata]
|
111
|
+
end
|
112
|
+
|
113
|
+
def items_for(request_meta)
|
114
|
+
@items_and_filters.each_with_object([]) do |(item, item_meta), to_return|
|
115
|
+
to_return << item if item_meta.empty? ||
|
116
|
+
MetadataFilter.apply?(@applies_predicate, item_meta, request_meta)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
unless [].respond_to?(:each_with_object) # For 1.8.7
|
121
|
+
undef items_for
|
122
|
+
def items_for(request_meta)
|
123
|
+
@items_and_filters.inject([]) do |to_return, (item, item_meta)|
|
124
|
+
to_return << item if item_meta.empty? ||
|
125
|
+
MetadataFilter.apply?(@applies_predicate, item_meta, request_meta)
|
126
|
+
to_return
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# This implementation is much more complex, and is optimized for
|
133
|
+
# rare (or hopefully no) updates once the queries start. Updates
|
134
|
+
# incur a cost as it has to clear the memoization and keep track
|
135
|
+
# of applicable keys. Queries will be O(N) the first time an item
|
136
|
+
# is provided with a given set of applicable metadata; subsequent
|
137
|
+
# queries with items with the same set of applicable metadata will
|
138
|
+
# be O(1) due to internal memoization.
|
139
|
+
#
|
140
|
+
# This is ideal for use by config, where filterable items (e.g. hooks)
|
141
|
+
# are typically added at the start of the process (e.g. in `spec_helper`)
|
142
|
+
# and then repeatedly queried as example groups and examples are defined.
|
143
|
+
# @private
|
144
|
+
class QueryOptimized < UpdateOptimized
|
145
|
+
alias find_items_for items_for
|
146
|
+
private :find_items_for
|
147
|
+
|
148
|
+
def initialize(applies_predicate)
|
149
|
+
super
|
150
|
+
@applicable_keys = Set.new
|
151
|
+
@proc_keys = Set.new
|
152
|
+
@memoized_lookups = Hash.new do |hash, applicable_metadata|
|
153
|
+
hash[applicable_metadata] = find_items_for(applicable_metadata)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def append(item, metadata)
|
158
|
+
super
|
159
|
+
handle_mutation(metadata)
|
160
|
+
end
|
161
|
+
|
162
|
+
def prepend(item, metadata)
|
163
|
+
super
|
164
|
+
handle_mutation(metadata)
|
165
|
+
end
|
166
|
+
|
167
|
+
def items_for(metadata)
|
168
|
+
# The filtering of `metadata` to `applicable_metadata` is the key thing
|
169
|
+
# that makes the memoization actually useful in practice, since each
|
170
|
+
# example and example group have different metadata (e.g. location and
|
171
|
+
# description). By filtering to the metadata keys our items care about,
|
172
|
+
# we can ignore extra metadata keys that differ for each example/group.
|
173
|
+
# For example, given `config.include DBHelpers, :db`, example groups
|
174
|
+
# can be split into these two sets: those that are tagged with `:db` and those
|
175
|
+
# that are not. For each set, this method for the first group in the set is
|
176
|
+
# still an `O(N)` calculation, but all subsequent groups in the set will be
|
177
|
+
# constant time lookups when they call this method.
|
178
|
+
applicable_metadata = applicable_metadata_from(metadata)
|
179
|
+
|
180
|
+
if applicable_metadata.any? { |k, _| @proc_keys.include?(k) }
|
181
|
+
# It's unsafe to memoize lookups involving procs (since they can
|
182
|
+
# be non-deterministic), so we skip the memoization in this case.
|
183
|
+
find_items_for(applicable_metadata)
|
184
|
+
else
|
185
|
+
@memoized_lookups[applicable_metadata]
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
private
|
190
|
+
|
191
|
+
def handle_mutation(metadata)
|
192
|
+
@applicable_keys.merge(metadata.keys)
|
193
|
+
@proc_keys.merge(proc_keys_from metadata)
|
194
|
+
@memoized_lookups.clear
|
195
|
+
end
|
196
|
+
|
197
|
+
def applicable_metadata_from(metadata)
|
198
|
+
@applicable_keys.inject({}) do |hash, key|
|
199
|
+
hash[key] = metadata[key] if metadata.key?(key)
|
200
|
+
hash
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def proc_keys_from(metadata)
|
205
|
+
metadata.each_with_object([]) do |(key, value), to_return|
|
206
|
+
to_return << key if Proc === value
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
unless [].respond_to?(:each_with_object) # For 1.8.7
|
211
|
+
undef proc_keys_from
|
212
|
+
def proc_keys_from(metadata)
|
213
|
+
metadata.inject([]) do |to_return, (key, value)|
|
214
|
+
to_return << key if Proc === value
|
215
|
+
to_return
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
94
221
|
end
|
95
222
|
end
|