rambling-trie 1.0.2 → 2.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.
Files changed (70) hide show
  1. checksums.yaml +5 -5
  2. data/Gemfile +6 -3
  3. data/Guardfile +3 -1
  4. data/README.md +30 -12
  5. data/Rakefile +8 -0
  6. data/lib/rambling-trie.rb +2 -0
  7. data/lib/rambling/trie.rb +48 -26
  8. data/lib/rambling/trie/comparable.rb +6 -3
  9. data/lib/rambling/trie/compressible.rb +16 -0
  10. data/lib/rambling/trie/compressor.rb +39 -24
  11. data/lib/rambling/trie/configuration.rb +3 -1
  12. data/lib/rambling/trie/configuration/properties.rb +18 -9
  13. data/lib/rambling/trie/configuration/provider_collection.rb +38 -17
  14. data/lib/rambling/trie/container.rb +123 -36
  15. data/lib/rambling/trie/enumerable.rb +6 -4
  16. data/lib/rambling/trie/inspectable.rb +2 -0
  17. data/lib/rambling/trie/invalid_operation.rb +3 -1
  18. data/lib/rambling/trie/nodes.rb +13 -0
  19. data/lib/rambling/trie/nodes/compressed.rb +98 -0
  20. data/lib/rambling/trie/nodes/missing.rb +12 -0
  21. data/lib/rambling/trie/nodes/node.rb +183 -0
  22. data/lib/rambling/trie/nodes/raw.rb +82 -0
  23. data/lib/rambling/trie/readers.rb +3 -1
  24. data/lib/rambling/trie/readers/plain_text.rb +3 -11
  25. data/lib/rambling/trie/serializers.rb +3 -1
  26. data/lib/rambling/trie/serializers/file.rb +2 -0
  27. data/lib/rambling/trie/serializers/marshal.rb +15 -5
  28. data/lib/rambling/trie/serializers/yaml.rb +21 -5
  29. data/lib/rambling/trie/serializers/zip.rb +15 -8
  30. data/lib/rambling/trie/stringifyable.rb +8 -2
  31. data/lib/rambling/trie/version.rb +3 -1
  32. data/rambling-trie.gemspec +21 -10
  33. data/spec/assets/test_words.es_DO.txt +1 -0
  34. data/spec/integration/rambling/trie_spec.rb +44 -35
  35. data/spec/lib/rambling/trie/comparable_spec.rb +8 -15
  36. data/spec/lib/rambling/trie/compressor_spec.rb +90 -13
  37. data/spec/lib/rambling/trie/configuration/properties_spec.rb +21 -13
  38. data/spec/lib/rambling/trie/configuration/provider_collection_spec.rb +18 -34
  39. data/spec/lib/rambling/trie/container_spec.rb +183 -217
  40. data/spec/lib/rambling/trie/enumerable_spec.rb +14 -9
  41. data/spec/lib/rambling/trie/inspectable_spec.rb +36 -11
  42. data/spec/lib/rambling/trie/nodes/compressed_spec.rb +37 -0
  43. data/spec/lib/rambling/trie/nodes/node_spec.rb +9 -0
  44. data/spec/lib/rambling/trie/nodes/raw_spec.rb +179 -0
  45. data/spec/lib/rambling/trie/readers/plain_text_spec.rb +3 -1
  46. data/spec/lib/rambling/trie/serializers/file_spec.rb +6 -4
  47. data/spec/lib/rambling/trie/serializers/marshal_spec.rb +5 -7
  48. data/spec/lib/rambling/trie/serializers/yaml_spec.rb +5 -7
  49. data/spec/lib/rambling/trie/serializers/zip_spec.rb +18 -20
  50. data/spec/lib/rambling/trie/stringifyable_spec.rb +14 -11
  51. data/spec/lib/rambling/trie_spec.rb +18 -11
  52. data/spec/spec_helper.rb +10 -5
  53. data/spec/support/config.rb +10 -0
  54. data/spec/support/helpers/add_word.rb +20 -0
  55. data/spec/support/helpers/one_line_heredoc.rb +11 -0
  56. data/spec/support/shared_examples/a_compressible_trie.rb +40 -0
  57. data/spec/support/shared_examples/a_serializable_trie.rb +10 -6
  58. data/spec/support/shared_examples/a_serializer.rb +9 -1
  59. data/spec/support/shared_examples/a_trie_data_structure.rb +2 -0
  60. data/spec/support/shared_examples/a_trie_node.rb +127 -0
  61. data/spec/{lib/rambling/trie/compressed_node_spec.rb → support/shared_examples/a_trie_node_implementation.rb} +25 -72
  62. metadata +42 -31
  63. data/lib/rambling/trie/compressable.rb +0 -14
  64. data/lib/rambling/trie/compressed_node.rb +0 -120
  65. data/lib/rambling/trie/missing_node.rb +0 -8
  66. data/lib/rambling/trie/node.rb +0 -97
  67. data/lib/rambling/trie/raw_node.rb +0 -96
  68. data/spec/lib/rambling/trie/node_spec.rb +0 -86
  69. data/spec/lib/rambling/trie/raw_node_spec.rb +0 -389
  70. data/spec/support/shared_examples/a_compressable_trie.rb +0 -26
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rambling
2
4
  module Trie
3
5
  # Provides the String representation behavior for the trie data structure.
@@ -6,14 +8,18 @@ module Rambling
6
8
  # @return [String] the string representation of the current node.
7
9
  # @raise [InvalidOperation] if node is not terminal or is root.
8
10
  def as_word
9
- raise Rambling::Trie::InvalidOperation, 'Cannot represent branch as a word' if letter && !terminal?
11
+ if letter && !terminal?
12
+ raise Rambling::Trie::InvalidOperation,
13
+ 'Cannot represent branch as a word'
14
+ end
15
+
10
16
  to_s
11
17
  end
12
18
 
13
19
  # String representation of the current node.
14
20
  # @return [String] the string representation of the current node.
15
21
  def to_s
16
- parent.to_s << letter.to_s
22
+ parent.to_s + letter.to_s
17
23
  end
18
24
  end
19
25
  end
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rambling
2
4
  module Trie
3
5
  # Current version of the rambling-trie.
4
- VERSION = '1.0.2'.freeze
6
+ VERSION = '2.2.0'
5
7
  end
6
8
  end
@@ -1,26 +1,37 @@
1
- # -*- encoding: utf-8 -*-
2
- $:.push File.expand_path('../lib', __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH.push File.expand_path('../lib', __FILE__)
3
4
  require 'rambling/trie/version'
4
5
 
5
6
  Gem::Specification.new do |gem|
6
7
  gem.authors = ['Edgar Gonzalez', 'Lilibeth De La Cruz']
7
8
  gem.email = ['edggonzalezg@gmail.com', 'lilibethdlc@gmail.com']
8
- gem.description = 'The Rambling Trie is a Ruby implementation of the trie data structure, which includes compression abilities and is designed to be very fast to traverse.'
9
+
10
+ gem.description = <<~DESCRIPTION.gsub(%r{\s+}, ' ')
11
+ The Rambling Trie is a Ruby implementation of the trie data structure, which
12
+ includes compression abilities and is designed to be very fast to traverse.
13
+ DESCRIPTION
14
+
9
15
  gem.summary = 'A Ruby implementation of the trie data structure.'
10
16
  gem.homepage = 'http://github.com/gonzedge/rambling-trie'
11
17
  gem.date = Time.now.strftime '%Y-%m-%d'
12
18
 
13
- gem.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename f }
14
- gem.files = `git ls-files -- {lib,*file,*.gemspec,LICENSE*,README*}`.split "\n"
15
- gem.test_files = `git ls-files -- {test,spec,features}/*`.split "\n"
16
- gem.require_paths = ['lib']
19
+ executables = `git ls-files -- bin/*`.split "\n"
20
+ files = `git ls-files -- {lib,*file,*.gemspec,LICENSE*,README*}`.split "\n"
21
+ test_files = `git ls-files -- {test,spec,features}/*`.split "\n"
22
+
23
+ gem.executables = executables.map { |f| File.basename f }
24
+ gem.files = files
25
+ gem.test_files = test_files
26
+ gem.require_paths = %w(lib)
17
27
 
18
28
  gem.name = 'rambling-trie'
19
29
  gem.license = 'MIT'
20
30
  gem.version = Rambling::Trie::VERSION
21
31
  gem.platform = Gem::Platform::RUBY
32
+ gem.required_ruby_version = '>= 2.5', '<= 3.0.0'
22
33
 
23
- gem.add_development_dependency 'rspec', '~> 3.5'
24
- gem.add_development_dependency 'rake', '~> 12.0'
25
- gem.add_development_dependency 'yard', '~> 0.9.5'
34
+ gem.add_development_dependency 'rake', '~> 13.0'
35
+ gem.add_development_dependency 'rspec', '~> 3.9'
36
+ gem.add_development_dependency 'yard', '~> 0.9.25'
26
37
  end
@@ -21,3 +21,4 @@ tus
21
21
  tuyos
22
22
  verdad
23
23
  verdadero
24
+ 🙃
@@ -1,61 +1,72 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
4
+ require 'zip'
2
5
 
3
6
  describe Rambling::Trie do
4
- describe 'with words provided directly' do
5
- it_behaves_like 'a compressable trie' do
6
- let(:words) { %w[a couple of words for our full trie integration test] }
7
- let(:trie) { Rambling::Trie.create { |t| words.each { |w| t << w } } }
7
+ let(:assets_path) { File.join ::SPEC_ROOT, 'assets' }
8
+
9
+ context 'when providing words directly' do
10
+ it_behaves_like 'a compressible trie' do
11
+ let(:trie) { Rambling::Trie.create }
12
+ let(:words) { %w(a couple of words for our full trie integration test) }
13
+
14
+ before do
15
+ trie.concat words
16
+ end
8
17
  end
9
18
  end
10
19
 
11
- describe 'with words from a file' do
12
- it_behaves_like 'a compressable trie' do
13
- let(:filepath) { File.join ::SPEC_ROOT, 'assets', 'test_words.en_US.txt' }
14
- let(:words) { File.readlines(filepath).map &:chomp! }
15
- let(:trie) { Rambling::Trie.create filepath }
20
+ context 'when provided with words with unicode characters' do
21
+ it_behaves_like 'a compressible trie' do
22
+ let(:trie) { Rambling::Trie.create }
23
+ let(:words) do
24
+ %w(poquísimas palabras para nuestra prueba de integración completa 🙃)
25
+ end
26
+
27
+ before do
28
+ trie.concat words
29
+ end
16
30
  end
17
31
  end
18
32
 
19
- describe 'with words with unicode characters' do
20
- it_behaves_like 'a compressable trie' do
21
- let(:words) { %w[poquísimas palabras para nuestra prueba de integración completa] }
22
- let(:trie) { Rambling::Trie.create { |t| words.each { |w| t << w } } }
33
+ context 'when provided with a filepath' do
34
+ let(:trie) { Rambling::Trie.create filepath }
35
+ let(:words) { File.readlines(filepath).map(&:chomp) }
36
+
37
+ context 'with english words' do
38
+ it_behaves_like 'a compressible trie' do
39
+ let(:filepath) { File.join assets_path, 'test_words.en_US.txt' }
40
+ end
23
41
  end
24
- end
25
42
 
26
- describe 'with words with unicode characters from a file' do
27
- it_behaves_like 'a compressable trie' do
28
- let(:filepath) { File.join ::SPEC_ROOT, 'assets', 'test_words.en_US.txt' }
29
- let(:words) { File.readlines(filepath).map &:chomp! }
30
- let(:trie) { Rambling::Trie.create filepath }
43
+ context 'with unicode characters' do
44
+ it_behaves_like 'a compressible trie' do
45
+ let(:filepath) { File.join assets_path, 'test_words.es_DO.txt' }
46
+ end
31
47
  end
32
48
  end
33
49
 
34
- describe 'saving and loading full trie from a file' do
35
- let(:words_filepath) { File.join ::SPEC_ROOT, 'assets', 'test_words.en_US.txt' }
36
- let(:words) { File.readlines(words_filepath).map &:chomp! }
37
- let(:trie_to_serialize) { Rambling::Trie.create words_filepath }
38
- let(:trie_filename) { File.join ::SPEC_ROOT, '..', 'tmp', 'trie-root' }
50
+ describe 'dump and load' do
51
+ let(:words_filepath) { File.join assets_path, 'test_words.en_US.txt' }
52
+ let(:words) { File.readlines(words_filepath).map(&:chomp) }
39
53
 
40
54
  context 'when serialized with Ruby marshal format (default)' do
41
55
  it_behaves_like 'a serializable trie' do
42
- let(:trie_filepath) { "#{trie_filename}.marshal" }
43
- let(:loaded_trie) { Rambling::Trie.load trie_filepath }
44
- let(:serializer) { nil }
56
+ let(:trie_to_serialize) { Rambling::Trie.create words_filepath }
57
+ let(:format) { :marshal }
45
58
  end
46
59
  end
47
60
 
48
61
  context 'when serialized with YAML' do
49
62
  it_behaves_like 'a serializable trie' do
50
- let(:trie_filepath) { "#{trie_filename}.yml" }
51
- let(:loaded_trie) { Rambling::Trie.load trie_filepath }
52
- let(:serializer) { nil }
63
+ let(:trie_to_serialize) { Rambling::Trie.create words_filepath }
64
+ let(:format) { :yml }
53
65
  end
54
66
  end
55
67
 
56
68
  context 'when serialized with zipped Ruby marshal format' do
57
69
  before do
58
- require 'zip'
59
70
  @original_on_exists_proc = ::Zip.on_exists_proc
60
71
  @original_continue_on_exists_proc = ::Zip.continue_on_exists_proc
61
72
  ::Zip.on_exists_proc = true
@@ -63,15 +74,13 @@ describe Rambling::Trie do
63
74
  end
64
75
 
65
76
  after do
66
- require 'zip'
67
77
  ::Zip.on_exists_proc = @original_on_exists_proc
68
78
  ::Zip.continue_on_exists_proc = @original_continue_on_exists_proc
69
79
  end
70
80
 
71
81
  it_behaves_like 'a serializable trie' do
72
- let(:trie_filepath) { "#{trie_filename}.marshal.zip" }
73
- let(:loaded_trie) { Rambling::Trie.load trie_filepath }
74
- let(:serializer) { nil }
82
+ let(:trie_to_serialize) { Rambling::Trie.create words_filepath }
83
+ let(:format) { 'marshal.zip' }
75
84
  end
76
85
  end
77
86
  end
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  describe Rambling::Trie::Comparable do
4
6
  describe '#==' do
5
- let(:node_1) { Rambling::Trie::RawNode.new }
6
- let(:node_2) { Rambling::Trie::RawNode.new }
7
+ let(:node_1) { Rambling::Trie::Nodes::Raw.new }
8
+ let(:node_2) { Rambling::Trie::Nodes::Raw.new }
7
9
 
8
10
  context 'when the nodes do not have the same letter' do
9
11
  before do
@@ -67,37 +69,28 @@ describe Rambling::Trie::Comparable do
67
69
  context 'when the nodes have the same letter and the same children' do
68
70
  before do
69
71
  node_1.letter = :t
70
- node_1.add 'hese'
71
- node_1.add 'hree'
72
- node_1.add 'hings'
72
+ add_words node_1, %w(hese hree hings)
73
73
 
74
74
  node_2.letter = :t
75
- node_2.add 'hese'
76
- node_2.add 'hree'
77
- node_2.add 'hings'
75
+ add_words node_2, %w(hese hree hings)
78
76
  end
79
77
 
80
78
  it 'returns true' do
81
79
  expect(node_1).to eq node_2
82
- expect(node_1[:h][:e][:s][:e]).to eq node_2[:h][:e][:s][:e]
83
80
  end
84
81
  end
85
82
 
86
83
  context 'when the nodes have the same letter but different children' do
87
84
  before do
88
85
  node_1.letter = :t
89
- node_1.add 'hese'
90
- node_1.add 'wo'
86
+ add_words node_1, %w(hese wo)
91
87
 
92
88
  node_2.letter = :t
93
- node_2.add 'hese'
94
- node_2.add 'hree'
95
- node_2.add 'hings'
89
+ add_words node_2, %w(hese hree hings)
96
90
  end
97
91
 
98
92
  it 'returns false' do
99
93
  expect(node_1).not_to eq node_2
100
- expect(node_1[:h][:e][:s][:e]).to eq node_2[:h][:e][:s][:e]
101
94
  end
102
95
  end
103
96
  end
@@ -1,31 +1,108 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  describe Rambling::Trie::Compressor do
4
6
  let(:compressor) { Rambling::Trie::Compressor.new }
5
7
 
6
8
  describe '#compress' do
7
- let(:words) { %w(a few words hello hell) }
8
- let(:root) do
9
- Rambling::Trie::RawNode.new
9
+ let(:node) { Rambling::Trie::Nodes::Raw.new }
10
+
11
+ it 'compresses the node' do
12
+ add_words node, %w(a few words hello hell)
13
+ compressed = compressor.compress node
14
+
15
+ expect(compressed.children_tree.keys).to eq %i(a f w h)
16
+ end
17
+
18
+ context 'with at least one word' do
19
+ before do
20
+ add_words node, %w(all the words)
21
+ end
22
+
23
+ it 'keeps the node letter nil' do
24
+ compressed = compressor.compress node
25
+
26
+ expect(compressed.letter).to be_nil
27
+ end
10
28
  end
11
29
 
12
- before do
13
- words.each { |w| root.add w.clone }
30
+ context 'with a single word' do
31
+ before do
32
+ add_word node, 'all'
33
+ end
34
+
35
+ it 'compresses into a single node without children' do
36
+ compressed = compressor.compress node
37
+
38
+ expect(compressed[:a].letter).to eq :all
39
+ expect(compressed[:a].children.size).to eq 0
40
+ expect(compressed[:a]).to be_terminal
41
+ expect(compressed[:a]).to be_compressed
42
+ end
14
43
  end
15
44
 
16
- it 'generates a new root with the words from the passed root' do
17
- new_root = compressor.compress root
45
+ context 'with two words' do
46
+ before do
47
+ add_words node, %w(all ask)
48
+ end
49
+
50
+ it 'compresses into corresponding three nodes' do
51
+ compressed = compressor.compress node
52
+
53
+ expect(compressed[:a].letter).to eq :a
54
+ expect(compressed[:a].children.size).to eq 2
55
+
56
+ expect(compressed[:a][:l].letter).to eq :ll
57
+ expect(compressed[:a][:s].letter).to eq :sk
58
+
59
+ expect(compressed[:a][:l].children.size).to eq 0
60
+ expect(compressed[:a][:s].children.size).to eq 0
18
61
 
19
- expect(words).not_to be_empty
20
- words.each do |word|
21
- expect(new_root).to include word
62
+ expect(compressed[:a][:l]).to be_terminal
63
+ expect(compressed[:a][:s]).to be_terminal
64
+
65
+ expect(compressed[:a][:l]).to be_compressed
66
+ expect(compressed[:a][:s]).to be_compressed
22
67
  end
23
68
  end
24
69
 
25
- it 'compresses the new root' do
26
- new_root = compressor.compress root
70
+ it 'reassigns the parent nodes correctly' do
71
+ add_words node, %w(repay rest repaint)
72
+ compressed = compressor.compress node
73
+
74
+ expect(compressed[:r].letter).to eq :re
75
+ expect(compressed[:r].parent).to eq compressed
76
+ expect(compressed[:r].children.size).to eq 2
77
+
78
+ expect(compressed[:r][:p].letter).to eq :pa
79
+ expect(compressed[:r][:p].parent).to eq compressed[:r]
80
+ expect(compressed[:r][:p].children.size).to eq 2
81
+
82
+ expect(compressed[:r][:s].letter).to eq :st
83
+ expect(compressed[:r][:s].parent).to eq compressed[:r]
84
+ expect(compressed[:r][:s].children.size).to eq 0
85
+
86
+ expect(compressed[:r][:p][:y].letter).to eq :y
87
+ expect(compressed[:r][:p][:y].parent).to eq compressed[:r][:p]
88
+ expect(compressed[:r][:p][:y].children.size).to eq 0
89
+
90
+ expect(compressed[:r][:p][:i].letter).to eq :int
91
+ expect(compressed[:r][:p][:i].parent).to eq compressed[:r][:p]
92
+ expect(compressed[:r][:p][:i].children.size).to eq 0
93
+ end
94
+
95
+ it 'does not compress terminal nodes' do
96
+ add_words node, %w(you your yours)
97
+ compressed = compressor.compress node
98
+
99
+ expect(compressed[:y].letter).to eq :you
100
+
101
+ expect(compressed[:y][:r].letter).to eq :r
102
+ expect(compressed[:y][:r]).to be_compressed
27
103
 
28
- expect(new_root.children_tree.keys).to eq %i(a few words hell)
104
+ expect(compressed[:y][:r][:s].letter).to eq :s
105
+ expect(compressed[:y][:r][:s]).to be_compressed
29
106
  end
30
107
  end
31
108
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  describe Rambling::Trie::Configuration::Properties do
@@ -6,27 +8,33 @@ describe Rambling::Trie::Configuration::Properties do
6
8
  describe '.new' do
7
9
  it 'configures the serializers' do
8
10
  serializers = properties.serializers
9
- expect(serializers.keys).to match_array %i(marshal yaml yml zip)
10
11
 
11
- expect(serializers[:marshal]).to be_instance_of Rambling::Trie::Serializers::Marshal
12
- expect(serializers[:yaml]).to be_instance_of Rambling::Trie::Serializers::Yaml
13
- expect(serializers[:yml]).to be_instance_of Rambling::Trie::Serializers::Yaml
14
- expect(serializers[:zip]).to be_instance_of Rambling::Trie::Serializers::Zip
12
+ expect(serializers.formats).to match_array %i(marshal yaml yml zip)
13
+ expect(serializers.providers.to_a).to match_array [
14
+ [:marshal, Rambling::Trie::Serializers::Marshal],
15
+ [:yaml, Rambling::Trie::Serializers::Yaml],
16
+ [:yml, Rambling::Trie::Serializers::Yaml],
17
+ [:zip, Rambling::Trie::Serializers::Zip],
18
+ ]
15
19
  end
16
20
 
17
21
  it 'configures the readers' do
18
22
  readers = properties.readers
19
- expect(readers.keys).to match_array %i(txt)
20
23
 
21
- expect(readers[:txt]).to be_instance_of Rambling::Trie::Readers::PlainText
24
+ expect(readers.formats).to match_array %i(txt)
25
+ expect(readers.providers.to_a).to match_array [
26
+ [:txt, Rambling::Trie::Readers::PlainText],
27
+ ]
22
28
  end
23
29
 
24
30
  it 'configures the compressor' do
25
- expect(properties.compressor).to be_instance_of Rambling::Trie::Compressor
31
+ compressor = properties.compressor
32
+ expect(compressor).to be_instance_of Rambling::Trie::Compressor
26
33
  end
27
34
 
28
35
  it 'configures the root_builder' do
29
- expect(properties.root_builder.call).to be_instance_of Rambling::Trie::RawNode
36
+ root = properties.root_builder.call
37
+ expect(root).to be_instance_of Rambling::Trie::Nodes::Raw
30
38
  end
31
39
  end
32
40
 
@@ -37,13 +45,13 @@ describe Rambling::Trie::Configuration::Properties do
37
45
  end
38
46
 
39
47
  it 'resets the serializers and readers to initial values' do
40
- expect(properties.serializers.keys).to include :test
41
- expect(properties.readers.keys).to include :test
48
+ expect(properties.serializers.formats).to include :test
49
+ expect(properties.readers.formats).to include :test
42
50
 
43
51
  properties.reset
44
52
 
45
- expect(properties.serializers.keys).not_to include :test
46
- expect(properties.readers.keys).not_to include :test
53
+ expect(properties.serializers.formats).not_to include :test
54
+ expect(properties.readers.formats).not_to include :test
47
55
  end
48
56
  end
49
57
  end