metahash-rb 1.0.1

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4017650f154dc99effbfb8b354353dc193088eed
4
+ data.tar.gz: 49824be43db47b292c7326284b2aff91257e021f
5
+ SHA512:
6
+ metadata.gz: ccdc5d25310105884c35fbbe09c07c7814ccc83953ba33e954abc3403faa862376543f9b17ecd0f3a517b9548dc5f406afcee6d9305a277fed8ceba737e5a36a
7
+ data.tar.gz: 9fa68441c4516a83050f90efc62b4369d3c5a97c959eb2b5b0b9e37f04cede5dfa014bdecc82c74e629888aead1827bdd4fd3a8238efe68961bb986d8b828c36
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ README.html
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in MetaHash.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2013 TinderBox
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,53 @@
1
+ MetaHash
2
+ ========
3
+
4
+ [![](https://ci.solanolabs.com:443/TinderBox/MetaHash/badges/110842.png?badge_token=a5097ea0ff487d291e94285ed9e4c8fabeca5fd1)](https://ci.solanolabs.com:443/TinderBox/MetaHash/suites/110842)
5
+ [![Code Climate](https://codeclimate.com/github/NullVoxPopuli/MetaHash/badges/gpa.svg)](https://codeclimate.com/github/NullVoxPopuli/MetaHash)
6
+ [![Dependency Status](https://gemnasium.com/NullVoxPopuli/MetaHash.svg)](https://gemnasium.com/NullVoxPopuli/MetaHash)
7
+
8
+
9
+ Provides a subclass of Hash and a wrapper around Rails' serialize attribute for object-like access to hashes without validating existence of nested hashes
10
+
11
+ ## Examples
12
+ #### Access nested hashes using method / object syntax
13
+
14
+ h = Metadata.new
15
+ h # => {}
16
+ h.outer.inner # => {}
17
+
18
+ #### Access to values stored in nested hashes via method call syntax
19
+
20
+ h = Metadata.new( { outer: { inner: { hash_key: "value" } } } )
21
+ h.outer.inner.hash_key # => "value"
22
+
23
+ #### Set values for nested hash structures without the nested hashes having to be initially defined
24
+
25
+ h = Metadata.new( {} )
26
+
27
+ ## Using with ActiveRecord
28
+
29
+ #### In your Gemfile
30
+
31
+ gem "metahash-rb"
32
+
33
+ #### in your ActiveRecord model
34
+
35
+ has_metadata
36
+
37
+ or
38
+
39
+ has_metadata :field_not_called_metadata
40
+
41
+
42
+ ## Support
43
+
44
+ This gem has been tested with Ruby 2.0, and rails 3.2, 4.1
45
+
46
+
47
+ ## Contributing
48
+
49
+ 1. Fork the project
50
+ 2. Create a new, descriptively named branch
51
+ 3. Add Test(s)!
52
+ 4. Commit your proposed changes
53
+ 5. Submit a pull request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,50 @@
1
+ require "metahash/metadata"
2
+ require "metahash/version"
3
+ require 'active_record'
4
+
5
+ module MetaHash
6
+
7
+ # When an active record object is loaded, convert to Metadata.
8
+ # If for whatever reason we lose this Metadata class, we can read
9
+ # the hash by creating class Metadata < Hash; end
10
+ #
11
+ # @param [Symbol] serialized_field name of the field to convert to Metadata
12
+ def has_metadata(serialized_field = "metadata")
13
+ # tell Active Record that the field is going to be JSON
14
+ # serialized, because JSON > YAML
15
+ serialize serialized_field, JSON
16
+
17
+ after_initialize do |record|
18
+ # first check the type of the field
19
+ # proceed if hash, abort if Metadata
20
+ if record.has_attribute?(serialized_field)
21
+ if [Hash, NilClass].include?(record.send(serialized_field).class)
22
+ # alias the old method / field
23
+ backup_name = "#{serialized_field}_original".to_sym
24
+ # alias_method backup_name, serialized_field
25
+ record.define_singleton_method backup_name, record.method(serialized_field)
26
+ # name the metadata accessor the same as the original field
27
+ # rails should automatically serialize this on save
28
+ initial_value = record.send(backup_name) || {}
29
+ record.send("#{serialized_field}=", Metadata.new(initial_value))
30
+ end
31
+ end
32
+ end
33
+
34
+
35
+ # create a before_save hook to store a pure Hash in the DB
36
+ before_save do |record|
37
+ @temp_metadata = record.send(serialized_field)
38
+ record.send("#{serialized_field}=", @temp_metadata.to_hash) if @temp_metadata
39
+ end
40
+
41
+ # restore the metadata to the field
42
+ after_save do |record|
43
+ record.send("#{serialized_field}=", @temp_metadata) if @temp_metadata
44
+ end
45
+
46
+ end
47
+
48
+ end
49
+
50
+ ActiveRecord::Base.send :extend, MetaHash
@@ -0,0 +1,135 @@
1
+ # MetadataHash - A specific use of ruby's Hash
2
+ #
3
+ # overrides Hash's method missing, providing the following functionality:
4
+ # 1. Access Nested hashes using the method / attribute syntax
5
+ # i.e.: h = {}
6
+ # h.middle.inner == {}
7
+ #
8
+ # 2. Access to values stored in nested hashes via method call syntax
9
+ # i.e.: h = { middle: { inner: { key: "value" } } }
10
+ # h.middle.inner.key == "value"
11
+ #
12
+ # 3. Set values for nested hash structures without middle nested hashes
13
+ # having to be defined
14
+ # i.e.: h = {}
15
+ # h.middle.inner = 3
16
+ # h == { middle: { inner: 3 } }
17
+ #
18
+ # 4. Old hash square bracket access still works
19
+ # i.e.: h = { inner: { key: "value" } }
20
+ # h[:inner][:key] == "value"
21
+ #
22
+ class Metadata < Hash
23
+
24
+ # the hash being passed in will have all its subhashes converted to
25
+ # metadata hashes.
26
+ # this is needed to we can have the
27
+ #
28
+ # @raise [ArgumentError] if one of the keys is method of Hash
29
+ # @raise [ArgumentError] if hash is not a type of Hash or Metadata
30
+ # @param [Hash] hash the structure to convert to Metadata
31
+ def initialize(hash = {})
32
+ # for maybe instantiating nested hashes that we
33
+ # aren't yet sure if they are going to have values or not
34
+ @empty_nested_hashes = []
35
+
36
+ if hash.is_a?(Metadata)
37
+ # we have nothing to do
38
+ return hash
39
+ elsif hash.is_a?(Hash)
40
+ # recursively create nested metadata objects
41
+ hash.each do |key, value|
42
+ if not valid_key?(key)
43
+ raise ArgumentError.new("Not Allowed. '#{key}' is a reserved method.")
44
+ end
45
+
46
+ self[key] = (
47
+ if value.is_a?(Hash)
48
+ Metadata.new(value)
49
+ elsif value.is_a?(Array)
50
+ # ensure hashes kept in an array are also converted to metadata
51
+ array = value.map{ |element|
52
+ element.is_a?(Hash) ? Metadata.new(element) : element
53
+ }
54
+ else
55
+ value
56
+ end
57
+ )
58
+ end
59
+ else
60
+ raise ArgumentError.new("Field must be a Hash or Metadata")
61
+ end
62
+ end
63
+
64
+
65
+ # this is what allows functionality mentioned in the class comment to happen
66
+ # @raise [ArgumentError] if one of the keys is method of Hash
67
+ def method_missing(method_name, *args)
68
+ # check for assignment
69
+ if (key = method_name.to_s).include?("=")
70
+ key = key.chop.to_sym
71
+ if not self.valid_key?(key)
72
+ raise ArgumentError.new("Not Allowed. '#{key}' is a reserved method.")
73
+ end
74
+
75
+ if not @empty_nested_hashes.empty?
76
+ deepest_metadata = self
77
+ @empty_nested_hashes.each do |key|
78
+ deepest_metadata = deepest_metadata[key] = Metadata.new
79
+ end
80
+ @empty_nested_hashes = []
81
+ deepest_metadata[key] = args[0]
82
+ end
83
+ else
84
+ value = self[method_name]
85
+ if not value
86
+ @empty_nested_hashes << method_name.to_sym
87
+ value = self
88
+ end
89
+ value
90
+ end
91
+
92
+ end
93
+
94
+ # Metdata has indifferent access
95
+ def [](key)
96
+ super(key.to_sym)
97
+ end
98
+
99
+ # # Metadata has indifferent access,
100
+ # # so just say that all the keys are symbols.
101
+ def []=(key, value)
102
+ super(key.to_sym, value)
103
+ end
104
+
105
+ # tests the ability to use this key as a key in a hash
106
+ # @param [Symbol] key
107
+ # @return [Boolean] whether or not this can be used as a hash key
108
+ def valid_key?(key)
109
+ # second parameter says that we are
110
+ # looking at private methods as well
111
+ not self.respond_to?(key, true)
112
+ end
113
+
114
+ # convert to regular hash, recursively
115
+ def to_hash
116
+ hash = {}
117
+ self.each do |k,v|
118
+ hash[k] = (
119
+ if v.is_a?(Metadata)
120
+ v.to_hash
121
+ elsif v.is_a?(Array)
122
+ v.map{ |e| e.is_a?(Metadata) ? e.to_hash : e }
123
+ else
124
+ v
125
+ end
126
+ )
127
+ end
128
+
129
+ hash
130
+ end
131
+
132
+ def to_ary
133
+ self.to_hash.to_a
134
+ end
135
+ end
@@ -0,0 +1,3 @@
1
+ module MetaHash
2
+ VERSION = "1.0.1"
3
+ end
@@ -0,0 +1,32 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ lib = File.expand_path('../lib', __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require "metahash/version"
6
+
7
+ Gem::Specification.new do |s|
8
+ s.name = "metahash-rb"
9
+ s.version = MetaHash::VERSION
10
+ s.platform = Gem::Platform::RUBY
11
+ s.license = "MIT"
12
+ s.authors = ["L. Preston Sego III"]
13
+ s.email = "LPSego3+dev@gmail.com"
14
+ s.homepage = "https://github.com/NullVoxPopuli/MetaHash"
15
+ s.summary = "MetaHash-#{MetaHash::VERSION}"
16
+ s.description = "Provides a subclass of Hash and a wrapper around Rails' serialize attribute for object-like access to hashes without validating existence of nested hashes."
17
+
18
+
19
+ s.files = `git ls-files`.split($/)
20
+ s.executables = s.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
21
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
22
+ s.require_paths = ["lib"]
23
+
24
+
25
+ s.add_runtime_dependency "activerecord", ">= 3.0.0"
26
+ s.add_development_dependency "bundler"
27
+ s.add_development_dependency "rspec"
28
+ s.add_development_dependency "sqlite3"
29
+ s.add_development_dependency "pry-byebug"
30
+ s.add_development_dependency "codeclimate-test-reporter"
31
+
32
+ end
@@ -0,0 +1,58 @@
1
+ require "spec_helper"
2
+
3
+ describe Metadata do
4
+
5
+ it "is a hash" do
6
+ expect(Metadata.new).to be_kind_of(Hash)
7
+ end
8
+
9
+ describe "pruning empty hashes" do
10
+ let(:m){Metadata.new}
11
+
12
+ it "removes empty hashes" do
13
+ m.a.b.c
14
+ expect(m.send :prune).to eq Metadata.new
15
+ end
16
+
17
+ it "does not remove valid hashes" do
18
+ m.a.b = 2
19
+ expect(m.send :prune).to_not eq Metadata.new
20
+ end
21
+
22
+ it "does not pollute itself with empty hashes" do
23
+ m.a.b.c
24
+ expect(m).to eq Metadata.new
25
+ end
26
+ end
27
+
28
+
29
+ it "sets a non-exsiting deep value" do
30
+ m = Metadata.new
31
+ m.a.b = 2
32
+ expect(m.a.b).to eq 2
33
+ end
34
+
35
+ it "has indifferent access" do
36
+ m = Metadata.new(a: 2)
37
+ expect(m.a).to eq 2
38
+ expect(m[:a]).to eq 2
39
+ expect(m["a"]).to eq 2
40
+ end
41
+
42
+ it "allows method-style access to nested hashes" do
43
+ m = Metadata.new(h)
44
+ expect(m.outer.inner.k).to eq h[:outer][:inner][:k]
45
+ end
46
+
47
+ it "allows hash-style access to nested hashes" do
48
+ m = Metadata.new(h)
49
+ expect(m[:outer][:inner][:k]).to eq h[:outer][:inner][:k]
50
+ end
51
+
52
+ it "does not allow hash keys to conflict with the name of a method of the Hash object" do
53
+ expect{
54
+ Metadata.new( outer: { key: "value" } )
55
+ }.to raise_error
56
+ end
57
+
58
+ end
@@ -0,0 +1,54 @@
1
+ require "spec_helper"
2
+
3
+ describe MetaHash do
4
+
5
+
6
+ before(:each) do
7
+ m = TestObject.new(metadata: h, name: "MetaHash")
8
+ m.save
9
+ end
10
+
11
+ after(:each) do
12
+ TestObject.destroy_all
13
+ end
14
+
15
+ it "converts the chose field to Metadata" do
16
+ p = TestObject.last
17
+ expect(p.metadata).to be_kind_of(Metadata)
18
+ end
19
+
20
+ it "instantiates with an empty hash if the field is nil" do
21
+ p = TestObject.new
22
+ p.metadata = nil
23
+ p = TestObject.last
24
+ expect(p.metadata).to_not be_nil
25
+ expect(p.metadata).to be_kind_of(Metadata)
26
+ end
27
+
28
+ it "does not modify the original contents of the chosen field on initialization" do
29
+ p = TestObject.last
30
+ expect(p.metadata.to_hash).to eq Metadata.new(h)
31
+ end
32
+
33
+ it "creates an alias for the original field's data" do
34
+ p = TestObject.last
35
+ expect(p.metadata_original).to eq Metadata.new(h)
36
+ end
37
+
38
+ it "doesn't error when a the attribute is not included in the retrieval of a record" do
39
+ expect{
40
+ p = TestObject.select("name")
41
+ }.to_not raise_error
42
+ end
43
+
44
+ it "converts the field back to a Hash before saving to the database" do
45
+ db = ActiveRecord::Base.connection.select_all("
46
+ SELECT metadata
47
+ FROM test_objects
48
+ ").last
49
+ p = db["metadata"]
50
+ expect(JSON.load(p)).to eq JSON.load(h.to_json)
51
+ end
52
+
53
+
54
+ end
@@ -0,0 +1,32 @@
1
+
2
+ require "rubygems"
3
+ require "bundler/setup"
4
+ require "metahash"
5
+ require "pry-byebug" # binding.pry to debug!
6
+ require "codeclimate-test-reporter"
7
+ CodeClimate::TestReporter.start
8
+
9
+ ActiveRecord::Base.establish_connection adapter: "sqlite3", database: ":memory:"
10
+ load File.dirname(__FILE__) + '/support/schema.rb'
11
+ Dir[File.dirname(__FILE__) + '/support/**/*.rb'].each {|file| require file }
12
+
13
+
14
+
15
+
16
+ # This file was generated by the `rspec --init` command. Conventionally, all
17
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
18
+ # Require this file using `require "spec_helper"` to ensure that it is only
19
+ # loaded once.
20
+ #
21
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
22
+ RSpec.configure do |config|
23
+ config.treat_symbols_as_metadata_keys_with_true_values = true
24
+ config.run_all_when_everything_filtered = true
25
+ config.filter_run :focus
26
+
27
+ # Run specs in random order to surface order dependencies. If you find an
28
+ # order dependency and want to debug it, you can fix the order by providing
29
+ # the seed, which is printed after each run.
30
+ # --seed 1234
31
+ config.order = 'random'
32
+ end
@@ -0,0 +1,3 @@
1
+ class TestObject < ActiveRecord::Base
2
+ has_metadata
3
+ end
@@ -0,0 +1,9 @@
1
+ def h
2
+ {
3
+ outer: {
4
+ inner: {
5
+ k: "value"
6
+ }
7
+ }
8
+ }
9
+ end
@@ -0,0 +1,8 @@
1
+ ActiveRecord::Schema.define do
2
+ self.verbose = false
3
+
4
+ create_table :test_objects, :force => true do |t|
5
+ t.string :name
6
+ t.text :metadata
7
+ end
8
+ end
@@ -0,0 +1,3 @@
1
+ :tddium:
2
+ :ruby_version: ruby-2.1.2
3
+ :coverage: true
metadata ADDED
@@ -0,0 +1,151 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: metahash-rb
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ platform: ruby
6
+ authors:
7
+ - L. Preston Sego III
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-09-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activerecord
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 3.0.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 3.0.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: sqlite3
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: pry-byebug
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: codeclimate-test-reporter
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: Provides a subclass of Hash and a wrapper around Rails' serialize attribute
98
+ for object-like access to hashes without validating existence of nested hashes.
99
+ email: LPSego3+dev@gmail.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - ".gitignore"
105
+ - ".rspec"
106
+ - Gemfile
107
+ - LICENSE
108
+ - README.md
109
+ - Rakefile
110
+ - lib/metahash.rb
111
+ - lib/metahash/metadata.rb
112
+ - lib/metahash/version.rb
113
+ - metahash.gemspec
114
+ - spec/metadata_spec.rb
115
+ - spec/metahash_spec.rb
116
+ - spec/spec_helper.rb
117
+ - spec/support/models/test_object.rb
118
+ - spec/support/sample_hashes.rb
119
+ - spec/support/schema.rb
120
+ - tddium.yml
121
+ homepage: https://github.com/NullVoxPopuli/MetaHash
122
+ licenses:
123
+ - MIT
124
+ metadata: {}
125
+ post_install_message:
126
+ rdoc_options: []
127
+ require_paths:
128
+ - lib
129
+ required_ruby_version: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - ">="
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ required_rubygems_version: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ requirements: []
140
+ rubyforge_project:
141
+ rubygems_version: 2.4.1
142
+ signing_key:
143
+ specification_version: 4
144
+ summary: MetaHash-1.0.1
145
+ test_files:
146
+ - spec/metadata_spec.rb
147
+ - spec/metahash_spec.rb
148
+ - spec/spec_helper.rb
149
+ - spec/support/models/test_object.rb
150
+ - spec/support/sample_hashes.rb
151
+ - spec/support/schema.rb