deep-cover 0.6.2 → 0.6.3.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (139) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -1
  3. data/.rspec +2 -1
  4. data/.rspec_all +2 -1
  5. data/.rubocop.yml +8 -9
  6. data/Gemfile +2 -0
  7. data/Rakefile +32 -6
  8. data/bin/cov +3 -3
  9. data/deep_cover.gemspec +3 -16
  10. data/exe/deep-cover +5 -0
  11. data/lib/deep_cover/cli/debugger.rb +1 -1
  12. data/lib/deep_cover/cli/exec.rb +1 -1
  13. data/lib/deep_cover/cli/instrumented_clone_reporter.rb +5 -3
  14. data/lib/deep_cover/cli/runner.rb +2 -2
  15. data/lib/deep_cover/{tools/dump_covered_code.rb → dump_covered_code.rb} +2 -0
  16. metadata +10 -203
  17. data/lib/deep-cover.rb +0 -3
  18. data/lib/deep_cover.rb +0 -22
  19. data/lib/deep_cover/analyser.rb +0 -23
  20. data/lib/deep_cover/analyser/base.rb +0 -104
  21. data/lib/deep_cover/analyser/branch.rb +0 -41
  22. data/lib/deep_cover/analyser/covered_code_source.rb +0 -21
  23. data/lib/deep_cover/analyser/function.rb +0 -14
  24. data/lib/deep_cover/analyser/node.rb +0 -54
  25. data/lib/deep_cover/analyser/per_char.rb +0 -38
  26. data/lib/deep_cover/analyser/per_line.rb +0 -41
  27. data/lib/deep_cover/analyser/ruby25_like_branch.rb +0 -211
  28. data/lib/deep_cover/analyser/statement.rb +0 -33
  29. data/lib/deep_cover/analyser/stats.rb +0 -54
  30. data/lib/deep_cover/analyser/subset.rb +0 -27
  31. data/lib/deep_cover/auto_run.rb +0 -71
  32. data/lib/deep_cover/autoload_tracker.rb +0 -215
  33. data/lib/deep_cover/backports.rb +0 -22
  34. data/lib/deep_cover/base.rb +0 -117
  35. data/lib/deep_cover/basics.rb +0 -22
  36. data/lib/deep_cover/builtin_takeover.rb +0 -10
  37. data/lib/deep_cover/config.rb +0 -104
  38. data/lib/deep_cover/config_setter.rb +0 -33
  39. data/lib/deep_cover/core_ext/autoload_overrides.rb +0 -112
  40. data/lib/deep_cover/core_ext/coverage_replacement.rb +0 -61
  41. data/lib/deep_cover/core_ext/exec_callbacks.rb +0 -27
  42. data/lib/deep_cover/core_ext/instruction_sequence_load_iseq.rb +0 -32
  43. data/lib/deep_cover/core_ext/load_overrides.rb +0 -19
  44. data/lib/deep_cover/core_ext/require_overrides.rb +0 -28
  45. data/lib/deep_cover/coverage.rb +0 -125
  46. data/lib/deep_cover/coverage/analysis.rb +0 -42
  47. data/lib/deep_cover/coverage/persistence.rb +0 -84
  48. data/lib/deep_cover/covered_code.rb +0 -145
  49. data/lib/deep_cover/custom_requirer.rb +0 -187
  50. data/lib/deep_cover/flag_comment_associator.rb +0 -68
  51. data/lib/deep_cover/load.rb +0 -66
  52. data/lib/deep_cover/memoize.rb +0 -48
  53. data/lib/deep_cover/module_override.rb +0 -39
  54. data/lib/deep_cover/node.rb +0 -23
  55. data/lib/deep_cover/node/arguments.rb +0 -51
  56. data/lib/deep_cover/node/assignments.rb +0 -273
  57. data/lib/deep_cover/node/base.rb +0 -155
  58. data/lib/deep_cover/node/begin.rb +0 -27
  59. data/lib/deep_cover/node/block.rb +0 -61
  60. data/lib/deep_cover/node/branch.rb +0 -32
  61. data/lib/deep_cover/node/case.rb +0 -113
  62. data/lib/deep_cover/node/collections.rb +0 -31
  63. data/lib/deep_cover/node/const.rb +0 -12
  64. data/lib/deep_cover/node/def.rb +0 -40
  65. data/lib/deep_cover/node/empty_body.rb +0 -32
  66. data/lib/deep_cover/node/exceptions.rb +0 -79
  67. data/lib/deep_cover/node/if.rb +0 -73
  68. data/lib/deep_cover/node/keywords.rb +0 -86
  69. data/lib/deep_cover/node/literals.rb +0 -100
  70. data/lib/deep_cover/node/loops.rb +0 -74
  71. data/lib/deep_cover/node/mixin/can_augment_children.rb +0 -65
  72. data/lib/deep_cover/node/mixin/check_completion.rb +0 -18
  73. data/lib/deep_cover/node/mixin/child_can_be_empty.rb +0 -27
  74. data/lib/deep_cover/node/mixin/executed_after_children.rb +0 -15
  75. data/lib/deep_cover/node/mixin/execution_location.rb +0 -66
  76. data/lib/deep_cover/node/mixin/filters.rb +0 -47
  77. data/lib/deep_cover/node/mixin/flow_accounting.rb +0 -71
  78. data/lib/deep_cover/node/mixin/has_child.rb +0 -145
  79. data/lib/deep_cover/node/mixin/has_child_handler.rb +0 -75
  80. data/lib/deep_cover/node/mixin/has_tracker.rb +0 -46
  81. data/lib/deep_cover/node/mixin/is_statement.rb +0 -20
  82. data/lib/deep_cover/node/mixin/rewriting.rb +0 -35
  83. data/lib/deep_cover/node/mixin/wrapper.rb +0 -15
  84. data/lib/deep_cover/node/module.rb +0 -66
  85. data/lib/deep_cover/node/root.rb +0 -20
  86. data/lib/deep_cover/node/send.rb +0 -161
  87. data/lib/deep_cover/node/short_circuit.rb +0 -42
  88. data/lib/deep_cover/node/splat.rb +0 -15
  89. data/lib/deep_cover/node/variables.rb +0 -16
  90. data/lib/deep_cover/parser_ext/range.rb +0 -21
  91. data/lib/deep_cover/problem_with_diagnostic.rb +0 -63
  92. data/lib/deep_cover/reporter.rb +0 -10
  93. data/lib/deep_cover/reporter/base.rb +0 -68
  94. data/lib/deep_cover/reporter/html.rb +0 -15
  95. data/lib/deep_cover/reporter/html/base.rb +0 -14
  96. data/lib/deep_cover/reporter/html/index.rb +0 -59
  97. data/lib/deep_cover/reporter/html/site.rb +0 -70
  98. data/lib/deep_cover/reporter/html/source.rb +0 -136
  99. data/lib/deep_cover/reporter/html/template/assets/32px.png +0 -0
  100. data/lib/deep_cover/reporter/html/template/assets/40px.png +0 -0
  101. data/lib/deep_cover/reporter/html/template/assets/deep_cover.css.sass +0 -336
  102. data/lib/deep_cover/reporter/html/template/assets/jquery-3.2.1.min.js +0 -4
  103. data/lib/deep_cover/reporter/html/template/assets/jquery-3.2.1.min.map +0 -1
  104. data/lib/deep_cover/reporter/html/template/assets/jstree.css +0 -1108
  105. data/lib/deep_cover/reporter/html/template/assets/jstree.js +0 -8424
  106. data/lib/deep_cover/reporter/html/template/assets/jstreetable.js +0 -1069
  107. data/lib/deep_cover/reporter/html/template/assets/throbber.gif +0 -0
  108. data/lib/deep_cover/reporter/html/template/index.html.erb +0 -75
  109. data/lib/deep_cover/reporter/html/template/source.html.erb +0 -35
  110. data/lib/deep_cover/reporter/istanbul.rb +0 -184
  111. data/lib/deep_cover/reporter/text.rb +0 -58
  112. data/lib/deep_cover/reporter/tree/util.rb +0 -86
  113. data/lib/deep_cover/tools.rb +0 -22
  114. data/lib/deep_cover/tools/blank.rb +0 -25
  115. data/lib/deep_cover/tools/builtin_coverage.rb +0 -55
  116. data/lib/deep_cover/tools/camelize.rb +0 -13
  117. data/lib/deep_cover/tools/content_tag.rb +0 -11
  118. data/lib/deep_cover/tools/covered.rb +0 -9
  119. data/lib/deep_cover/tools/execute_sample.rb +0 -34
  120. data/lib/deep_cover/tools/format.rb +0 -18
  121. data/lib/deep_cover/tools/format_char_cover.rb +0 -19
  122. data/lib/deep_cover/tools/format_generated_code.rb +0 -27
  123. data/lib/deep_cover/tools/indent_string.rb +0 -26
  124. data/lib/deep_cover/tools/merge.rb +0 -16
  125. data/lib/deep_cover/tools/number_lines.rb +0 -22
  126. data/lib/deep_cover/tools/our_coverage.rb +0 -11
  127. data/lib/deep_cover/tools/profiling.rb +0 -68
  128. data/lib/deep_cover/tools/render_template.rb +0 -13
  129. data/lib/deep_cover/tools/require_relative_dir.rb +0 -12
  130. data/lib/deep_cover/tools/scan_match_datas.rb +0 -10
  131. data/lib/deep_cover/tools/silence_warnings.rb +0 -18
  132. data/lib/deep_cover/tools/slice.rb +0 -9
  133. data/lib/deep_cover/tools/strip_heredoc.rb +0 -18
  134. data/lib/deep_cover/tools/truncate_backtrace.rb +0 -32
  135. data/lib/deep_cover/tracker_bucket.rb +0 -50
  136. data/lib/deep_cover/tracker_hits_per_path.rb +0 -35
  137. data/lib/deep_cover/tracker_storage.rb +0 -76
  138. data/lib/deep_cover/tracker_storage_per_path.rb +0 -34
  139. data/lib/deep_cover/version.rb +0 -5
@@ -1,25 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module DeepCover
4
- module Tools::Blank
5
- BLANK_RE = /\A[[:space:]]*\z/
6
-
7
- # Homemade poor-man's blank?
8
- # Based, but modified, on https://github.com/rails/rails/blob/5-0-stable/activesupport/lib/active_support/core_ext/object/blank.rb
9
- def blank?(obj)
10
- if obj.is_a?(String)
11
- obj.empty? || obj =~ BLANK_RE
12
- else
13
- obj.respond_to?(:empty?) ? !!obj.empty? : !obj
14
- end
15
- end
16
-
17
- def present?(obj)
18
- !blank?(obj)
19
- end
20
-
21
- def presence(obj)
22
- obj if present?(obj)
23
- end
24
- end
25
- end
@@ -1,55 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module DeepCover
4
- module Tools::BuiltinCoverage
5
- def builtin_coverage(source, filename, lineno)
6
- require 'coverage'
7
- filename = File.absolute_path(File.expand_path(filename))
8
- ::Coverage.start
9
- begin
10
- Tools.silence_warnings do
11
- execute_sample -> { run_with_line_coverage(source, filename, lineno) }
12
- end
13
- ensure
14
- result = ::Coverage.result
15
- end
16
- unshift_coverage(result.fetch(filename), lineno)
17
- end
18
-
19
- if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
20
- # Executes the source as if it was in the specified file while
21
- # builtin coverage information is still captured
22
- def run_with_line_coverage(source, filename = nil, lineno = 1)
23
- source = shift_source(source, lineno)
24
- Object.to_java.getRuntime.executeScript(source, filename)
25
- end
26
- else
27
- # In ruby 2.0 and 2.1, using 2, 3 or 4 as lineno with RubyVM::InstructionSequence.compile
28
- # will cause the coverage result to be truncated.
29
- # 1: [1,2,nil,1]
30
- # 2: [nil,1,2,nil]
31
- # 3: [nil,nil,1,2]
32
- # 4: [nil,nil,nil,1]
33
- # 5: [nil,nil,nil,nil,1,2,nil,1]
34
- # Using 1 and 5 or more do not seem to show this issue.
35
- # The workaround is to create the fake lines manually and always use the default lineno
36
-
37
- # Executes the source as if it was in the specified file while
38
- # builtin coverage information is still captured
39
- def run_with_line_coverage(source, filename = nil, lineno = 1)
40
- source = shift_source(source, lineno)
41
- RubyVM::InstructionSequence.compile(source, filename).eval
42
- end
43
- end
44
-
45
- private
46
-
47
- def shift_source(source, lineno)
48
- "\n" * (lineno - 1) + source
49
- end
50
-
51
- def unshift_coverage(coverage, lineno)
52
- coverage[(lineno - 1)..-1]
53
- end
54
- end
55
- end
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module DeepCover
4
- module Tools
5
- module Camelize
6
- extend self # Loaded before bootstrap
7
- # Poor man's camelize. 'an_example' => 'AnExample'
8
- def camelize(string)
9
- string.to_s.gsub(/([a-z\d]*)[_?!]?/) { Regexp.last_match(1).capitalize }
10
- end
11
- end
12
- end
13
- end
@@ -1,11 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module DeepCover
4
- module Tools::ContentTag
5
- # Poor man's content_tag. No HTML escaping included
6
- def content_tag(tag, content, **options)
7
- attrs = options.map { |kind, value| %{ #{kind}="#{value}"} }.join
8
- "<#{tag}#{attrs}>#{content}</#{tag}>"
9
- end
10
- end
11
- end
@@ -1,9 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module DeepCover
4
- module Tools::Covered
5
- def covered?(runs)
6
- runs && runs > 0
7
- end
8
- end
9
- end
@@ -1,34 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module DeepCover
4
- module Tools::ExecuteSample
5
- class ExceptionInSample < StandardError
6
- end
7
-
8
- # Returns true if the code would have continued, false if the rescue was triggered.
9
- def execute_sample(to_execute, source: nil)
10
- # Disable some annoying warning by ruby. We are testing edge cases, so warnings are to be expected.
11
- Tools.silence_warnings do
12
- if to_execute.is_a?(CoveredCode)
13
- to_execute.execute_code
14
- else
15
- to_execute.call
16
- end
17
- end
18
- true
19
- rescue StandardError => e
20
- # In our samples, a simple `raise` is expected and doesn't need to be rescued
21
- return false if e.is_a?(RuntimeError) && e.message.empty?
22
-
23
- source = to_execute.covered_source if to_execute.is_a?(CoveredCode)
24
- raise unless source
25
-
26
- inner_msg = Tools.indent_string("#{e.class.name}: #{e.message}", 4)
27
- source = Tools.indent_string(source, 4)
28
- msg = "Exception when executing the sample:\n#{inner_msg}\n*Code follows*\n#{source}"
29
- new_exc = ExceptionInSample.new(msg)
30
- new_exc.set_backtrace(e.backtrace)
31
- raise new_exc
32
- end
33
- end
34
- end
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module DeepCover
4
- module Tools::Format
5
- CONVERT = Hash.new(' ')
6
- CONVERT[0] = 'x '
7
- CONVERT[nil] = '- '
8
-
9
- def format(*results, filename: nil, source: nil)
10
- source ||= File.read(filename)
11
- results.map! { |counts| counts.map { |c| CONVERT[c] } }
12
- [*results, source.lines].transpose.map do |parts|
13
- *line_results, line = parts
14
- Term::ANSIColor.white(line_results.join) + line.to_s
15
- end
16
- end
17
- end
18
- end
@@ -1,19 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module DeepCover
4
- module Tools::FormatCharCover
5
- COLOR = {'x' => :red, ' ' => :green, '-' => :faint}.freeze
6
- WHITESPACE_MAP = Hash.new { |_, v| v }.merge!(' ' => '·', "\t" => '→ ')
7
- def format_char_cover(covered_code, show_whitespace: false, **options)
8
- bc = covered_code.char_cover(**options)
9
- covered_code.buffer.source_lines.map.with_index do |line, line_index|
10
- next line if line.strip =~ /^#[ >]/
11
- line.chars.map.with_index do |c, c_index|
12
- color = COLOR[bc[line_index][c_index]]
13
- c = WHITESPACE_MAP[c] if show_whitespace
14
- Term::ANSIColor.send(color, c)
15
- end.join
16
- end
17
- end
18
- end
19
- end
@@ -1,27 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module DeepCover
4
- module Tools::FormatGeneratedCode
5
- def format_generated_code(covered_code)
6
- inserts = []
7
- generated_code = covered_code.instrument_source do |inserted, _node, expr_limit|
8
- inserts << [expr_limit, inserted.size]
9
- Term::ANSIColor.yellow(inserted)
10
- end
11
-
12
- inserts = inserts.sort_by { |exp, _| [exp.line, exp.column] }.reverse
13
- generated_lines = generated_code.split("\n")
14
-
15
- inserts.each do |exp_limit, size|
16
- # Line index starts at 1, so array index returns the next line
17
- comment_line = generated_lines[exp_limit.line]
18
- next if Tools.blank?(comment_line)
19
- next unless comment_line.start_with?('#>')
20
- next if comment_line.start_with?('#>X')
21
- next unless comment_line.size >= exp_limit.column
22
- comment_line.insert(exp_limit.column, ' ' * size)
23
- end
24
- generated_lines.join("\n")
25
- end
26
- end
27
- end
@@ -1,26 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module DeepCover
4
- module Tools::IndentString
5
- # In-place implementation copied from active-support.
6
- IMPLEMENTATION = ->(amount, indent_string = nil, indent_empty_lines = false) do
7
- indent_string = indent_string || self[/^[ \t]/] || ' '
8
- re = indent_empty_lines ? /^/ : /^(?!$)/
9
- gsub!(re, indent_string * amount)
10
- end
11
-
12
- # Same as #indent! from active-support
13
- # https://github.com/rails/rails/blob/10e1f1f9a129f2f197a44009a99b73b8ff9dbc0d/activesupport/lib/active_support/core_ext/string/indent.rb#L7
14
- def indent_string!(string, *args)
15
- string.instance_exec(*args, &IMPLEMENTATION)
16
- end
17
-
18
- # Same as #indent from active-support
19
- # https://github.com/rails/rails/blob/10e1f1f9a129f2f197a44009a99b73b8ff9dbc0d/activesupport/lib/active_support/core_ext/string/indent.rb#L42
20
- def indent_string(string, *args)
21
- string = string.dup
22
- indent_string!(string, *args)
23
- string
24
- end
25
- end
26
- end
@@ -1,16 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module DeepCover
4
- module Tools::Merge
5
- def merge(*hashes)
6
- if hashes.last.is_a?(Symbol)
7
- oper = hashes.pop
8
- merge(*hashes) { |a, b| a.public_send(oper, b) }
9
- elsif !block_given?
10
- merge(*hashes, &:last)
11
- else
12
- hashes.inject { |result, h| result.merge(h) { |key, a, b| yield [a, b] } }
13
- end
14
- end
15
- end
16
- end
@@ -1,22 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module DeepCover
4
- module Tools::NumberLines
5
- def number_lines(lines, lineno: 1, bad_linenos: [])
6
- max_lineno = lineno + lines.size - 1
7
- nb_lineno_digits = max_lineno.to_s.size
8
- lines.map.with_index do |line, i|
9
- cur_lineno = lineno + i
10
- cur_lineno_s = cur_lineno.to_s.rjust(nb_lineno_digits)
11
- if bad_linenos.include?(cur_lineno)
12
- cur_lineno_s = "*#{cur_lineno_s}" unless bad_linenos.empty?
13
- prefix = Term::ANSIColor.red("#{cur_lineno_s} | ")
14
- else
15
- cur_lineno_s = " #{cur_lineno_s}" unless bad_linenos.empty?
16
- prefix = Term::ANSIColor.white("#{cur_lineno_s} | ")
17
- end
18
- "#{prefix}#{line}"
19
- end
20
- end
21
- end
22
- end
@@ -1,11 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module DeepCover
4
- module Tools::OurCoverage
5
- def our_coverage(source, filename, lineno, **options)
6
- covered_code = CoveredCode.new(source: source, path: filename, lineno: lineno)
7
- Tools.execute_sample(covered_code)
8
- covered_code.line_coverage(options)[(lineno - 1)..-1]
9
- end
10
- end
11
- end
@@ -1,68 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'delegate'
4
-
5
- module DeepCover
6
- module Tools::Profiling
7
- # Simple forwarding to implementation
8
- def start
9
- profiler.start
10
- end
11
-
12
- def stop
13
- @results = profiler.stop
14
- end
15
-
16
- def pause
17
- profiler.pause
18
- end
19
-
20
- def resume
21
- profiler.resume
22
- end
23
-
24
- def report
25
- profiler.report(@results)
26
- end
27
-
28
- # Basic utilities using forwarding methods
29
- def profile(do_start = true)
30
- return yield unless do_start
31
- start
32
- yield
33
- stop
34
- report
35
- end
36
-
37
- def dont_profile
38
- pause if profiler_loaded?
39
- yield
40
- ensure
41
- resume if profiler_loaded?
42
- end
43
-
44
- def profiler_loaded?
45
- !!@profiler
46
- end
47
-
48
- private
49
-
50
- # Dependency injection
51
- def profiler
52
- @profiler = RubyProfProfiler.new
53
- end
54
-
55
- class RubyProfProfiler < SimpleDelegator
56
- def initialize
57
- raise 'Profiling is not available when using JRuby' if RUBY_PLATFORM == 'java'
58
- require 'ruby-prof'
59
- super(RubyProf)
60
- end
61
-
62
- def report(results)
63
- printer = RubyProf::GraphPrinter.new(results)
64
- printer.print(STDOUT, {})
65
- end
66
- end
67
- end
68
- end
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module DeepCover
4
- module Tools::RenderTemplate
5
- def render_template(template, bound_object)
6
- require 'erb'
7
- caller_path = Pathname.new(caller(1..1).first.partition(/\.rb:\d/).first).dirname
8
- template = caller_path.join("template/#{template}.html.erb").read
9
- erb = ERB.new(template)
10
- erb.result(bound_object.instance_eval { binding })
11
- end
12
- end
13
- end
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module DeepCover
4
- module Tools::RequireRelativeDir
5
- def require_relative_dir(dir_name, except: [])
6
- dir = File.dirname(caller(1..1).first.partition(/\.rb:\d/).first)
7
- Dir["#{dir}/#{dir_name}/*.rb"].sort.each do |file|
8
- require file unless except.include? File.basename(file, '.rb')
9
- end
10
- end
11
- end
12
- end
@@ -1,10 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module DeepCover
4
- module Tools::RequireRelativeDir
5
- # Like String#scan, but return the MatchData object instead
6
- def scan_match_datas(source, matcher)
7
- source.to_enum(:scan, matcher).map { Regexp.last_match }
8
- end
9
- end
10
- end
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module DeepCover
4
- module Tools::SilenceWarnings
5
- # copied from: activesupport/lib/active_support/core_ext/kernel/reporting.rb
6
- def silence_warnings
7
- with_warnings(nil) { yield }
8
- end
9
-
10
- def with_warnings(flag)
11
- old_verbose = $VERBOSE
12
- $VERBOSE = flag
13
- yield
14
- ensure
15
- $VERBOSE = old_verbose
16
- end
17
- end
18
- end
@@ -1,9 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module DeepCover
4
- module Tools::Slice
5
- def slice(hash, *keys)
6
- keys.each_with_object({}) { |k, h| h[k] = hash[k] if hash.has_key?(k) }
7
- end
8
- end
9
- end