flipper-activerecord 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9a886563a18c40c7b980312b52cb6d5cb19fb69e
4
+ data.tar.gz: 6bfd3bb2d5614ead072521d28cecd4c72e2a9805
5
+ SHA512:
6
+ metadata.gz: 5a4469b453296c8085cb9fc271e81362132afa0508e98304c978288bf69695adf8314c0ed46139ce4572403216fb3814d6efbf8016f4d43f31eb5518a76d55aa
7
+ data.tar.gz: df879aa060f5d9c2d34047709b24ac2f53604484f4209e4dbbf9eecbea8560c1b20949b5730df1e1f192eb85131feb83169b619e8e92a309503a233525b4b8e0
@@ -0,0 +1,34 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /test/tmp/
9
+ /test/version_tmp/
10
+ /tmp/
11
+
12
+ ## Specific to RubyMotion:
13
+ .dat*
14
+ .repl_history
15
+ build/
16
+
17
+ ## Documentation cache and generated files:
18
+ /.yardoc/
19
+ /_yardoc/
20
+ /doc/
21
+ /rdoc/
22
+
23
+ ## Environment normalisation:
24
+ /.bundle/
25
+ /lib/bundler/man/
26
+
27
+ # for a library or gem, you might want to ignore these files since the code is
28
+ # intended to run in multiple environments; otherwise, check them in:
29
+ # Gemfile.lock
30
+ # .ruby-version
31
+ # .ruby-gemset
32
+
33
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
34
+ .rvmrc
data/Gemfile ADDED
@@ -0,0 +1,16 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
3
+
4
+ gem 'flipper', github: "bgentry/flipper"
5
+ gem 'database_cleaner'
6
+ gem 'pg'
7
+ gem 'rake'
8
+ gem 'rspec'
9
+ gem 'timecop'
10
+
11
+ group(:guard) do
12
+ gem 'guard'
13
+ gem 'guard-rspec'
14
+ gem 'guard-bundler'
15
+ gem 'rb-fsevent'
16
+ end
@@ -0,0 +1,105 @@
1
+ GIT
2
+ remote: git://github.com/bgentry/flipper.git
3
+ revision: 8f09e1c75db3a926fe12d1f91a8bb869a19d90f1
4
+ specs:
5
+ flipper (0.6.2)
6
+
7
+ PATH
8
+ remote: .
9
+ specs:
10
+ flipper-activerecord (0.1.0)
11
+ activerecord (>= 4.2.0.beta2)
12
+ flipper (~> 0.6.0)
13
+
14
+ GEM
15
+ remote: https://rubygems.org/
16
+ specs:
17
+ activemodel (4.2.0.beta2)
18
+ activesupport (= 4.2.0.beta2)
19
+ builder (~> 3.1)
20
+ activerecord (4.2.0.beta2)
21
+ activemodel (= 4.2.0.beta2)
22
+ activesupport (= 4.2.0.beta2)
23
+ arel (>= 6.0.0.beta1, < 6.1)
24
+ activesupport (4.2.0.beta2)
25
+ i18n (>= 0.7.0.beta1, < 0.8)
26
+ json (~> 1.7, >= 1.7.7)
27
+ minitest (~> 5.1)
28
+ thread_safe (~> 0.1)
29
+ tzinfo (~> 1.1)
30
+ arel (6.0.0.beta1)
31
+ builder (3.2.2)
32
+ celluloid (0.16.0)
33
+ timers (~> 4.0.0)
34
+ coderay (1.1.0)
35
+ database_cleaner (1.3.0)
36
+ diff-lcs (1.2.5)
37
+ ffi (1.9.5)
38
+ formatador (0.2.5)
39
+ guard (2.6.1)
40
+ formatador (>= 0.2.4)
41
+ listen (~> 2.7)
42
+ lumberjack (~> 1.0)
43
+ pry (>= 0.9.12)
44
+ thor (>= 0.18.1)
45
+ guard-bundler (2.0.0)
46
+ bundler (~> 1.0)
47
+ guard (~> 2.2)
48
+ guard-rspec (4.3.1)
49
+ guard (~> 2.1)
50
+ rspec (>= 2.14, < 4.0)
51
+ hitimes (1.2.2)
52
+ i18n (0.7.0.beta1)
53
+ json (1.8.1)
54
+ listen (2.7.11)
55
+ celluloid (>= 0.15.2)
56
+ rb-fsevent (>= 0.9.3)
57
+ rb-inotify (>= 0.9)
58
+ lumberjack (1.0.9)
59
+ method_source (0.8.2)
60
+ minitest (5.4.2)
61
+ pg (0.17.1)
62
+ pry (0.10.1)
63
+ coderay (~> 1.1.0)
64
+ method_source (~> 0.8.1)
65
+ slop (~> 3.4)
66
+ rake (10.3.2)
67
+ rb-fsevent (0.9.4)
68
+ rb-inotify (0.9.5)
69
+ ffi (>= 0.5.0)
70
+ rspec (3.1.0)
71
+ rspec-core (~> 3.1.0)
72
+ rspec-expectations (~> 3.1.0)
73
+ rspec-mocks (~> 3.1.0)
74
+ rspec-core (3.1.5)
75
+ rspec-support (~> 3.1.0)
76
+ rspec-expectations (3.1.2)
77
+ diff-lcs (>= 1.2.0, < 2.0)
78
+ rspec-support (~> 3.1.0)
79
+ rspec-mocks (3.1.2)
80
+ rspec-support (~> 3.1.0)
81
+ rspec-support (3.1.1)
82
+ slop (3.6.0)
83
+ thor (0.19.1)
84
+ thread_safe (0.3.4)
85
+ timecop (0.7.1)
86
+ timers (4.0.1)
87
+ hitimes
88
+ tzinfo (1.2.2)
89
+ thread_safe (~> 0.1)
90
+
91
+ PLATFORMS
92
+ ruby
93
+
94
+ DEPENDENCIES
95
+ database_cleaner
96
+ flipper!
97
+ flipper-activerecord!
98
+ guard
99
+ guard-bundler
100
+ guard-rspec
101
+ pg
102
+ rake
103
+ rb-fsevent
104
+ rspec
105
+ timecop
@@ -0,0 +1,13 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard 'bundler' do
5
+ watch('Gemfile')
6
+ watch(/^.+\.gemspec/)
7
+ end
8
+
9
+ guard 'rspec', cmd: 'rspec' do
10
+ watch(%r{^spec/.+_spec\.rb$}) { "spec" }
11
+ watch(%r{^lib/(.+)\.rb$}) { "spec" }
12
+ watch('spec/helper.rb') { "spec" }
13
+ end
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Blake Gentry
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, 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,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
@@ -0,0 +1,20 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/flipper/adapters/activerecord/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.name = "flipper-activerecord"
6
+ gem.version = Flipper::Adapters::ActiveRecord::VERSION
7
+ gem.authors = ["Blake Gentry"]
8
+ gem.email = ["blakesgentry@gmail.com"]
9
+ gem.description = %q{ActiveRecord adapter for Flipper}
10
+ gem.summary = %q{ActiveRecord adapter for Flipper}
11
+ gem.homepage = "https://github.com/bgentry/flipper-activerecord"
12
+ gem.require_paths = ["lib"]
13
+
14
+ gem.files = `git ls-files`.split($/)
15
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
16
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
17
+
18
+ gem.add_dependency 'flipper', '~> 0.6'
19
+ gem.add_dependency 'activerecord', '>= 4.2.0.beta2'
20
+ end
@@ -0,0 +1 @@
1
+ require 'flipper/adapters/activerecord'
@@ -0,0 +1,10 @@
1
+ module Flipper
2
+ module ActiveRecord
3
+ class Feature < ::ActiveRecord::Base
4
+ self.table_name = "flipper_features"
5
+
6
+ has_many :gates, foreign_key: "flipper_feature_id", :dependent => :destroy
7
+ default_scope { includes(:gates) }
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,9 @@
1
+ module Flipper
2
+ module ActiveRecord
3
+ class Gate < ::ActiveRecord::Base
4
+ self.table_name = "flipper_gates"
5
+
6
+ belongs_to :feature, foreign_key: "flipper_feature_id"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,152 @@
1
+ require 'set'
2
+ require 'flipper'
3
+ require 'active_record'
4
+
5
+ require 'flipper/activerecord/feature'
6
+ require 'flipper/activerecord/gate'
7
+
8
+ module Flipper
9
+ module Adapters
10
+ class ActiveRecord
11
+ include Flipper::Adapter
12
+
13
+ # Public: The name of the adapter.
14
+ attr_reader :name
15
+
16
+ def initialize(collection)
17
+ @collection = collection
18
+ @name = :activerecord
19
+ end
20
+
21
+ # Public: The set of known features.
22
+ def features
23
+ Flipper::ActiveRecord::Feature.select(:name).map(&:name).to_set
24
+ end
25
+
26
+ # Public: Adds a feature to the set of known features.
27
+ def add(feature)
28
+ Flipper::ActiveRecord::Feature.create!(name: feature.name.to_s)
29
+ true
30
+ end
31
+
32
+ # Public: Removes a feature from the set of known features.
33
+ def remove(feature)
34
+ clear(feature)
35
+ end
36
+
37
+ # Public: Clears all the gate values for a feature.
38
+ def clear(feature)
39
+ Flipper::ActiveRecord::Feature.where(name: feature.key).destroy_all
40
+ true
41
+ end
42
+
43
+ # Public: Gets the values for all gates for a given feature.
44
+ #
45
+ # Returns a Hash of Flipper::Gate#key => value.
46
+ def get(feature)
47
+ result = {}
48
+ f = Flipper::ActiveRecord::Feature.find_by(name: feature.key)
49
+
50
+ feature.gates.each do |gate|
51
+ result[gate.key] = case gate.data_type
52
+ when :boolean, :integer
53
+ if f
54
+ g = f.gates.detect {|g| g.name == gate.key.to_s}
55
+ g.value if g
56
+ end
57
+ when :set
58
+ if f
59
+ f.gates.select {|g| g.name == gate.key.to_s}.map{|g| g.value }.to_set
60
+ else
61
+ Set.new
62
+ end
63
+ else
64
+ unsupported_data_type gate.data_type
65
+ end
66
+ end
67
+
68
+ result
69
+ end
70
+
71
+ # Public: Enables a gate for a given thing.
72
+ #
73
+ # feature - The Flipper::Feature for the gate.
74
+ # gate - The Flipper::Gate to disable.
75
+ # thing - The Flipper::Type being disabled for the gate.
76
+ #
77
+ # Returns true.
78
+ def enable(feature, gate, thing)
79
+ f = Flipper::ActiveRecord::Feature.find_or_create_by(name: feature.key.to_s)
80
+ f.gates.find_or_create_by!(name: gate.key.to_s, value: thing.value.to_s)
81
+ true
82
+ # case gate.data_type
83
+ # when :boolean, :integer
84
+ # update feature.key, '$set' => {
85
+ # gate.key.to_s => thing.value.to_s,
86
+ # }
87
+ # when :set
88
+ # update feature.key, '$addToSet' => {
89
+ # gate.key.to_s => thing.value.to_s,
90
+ # }
91
+ # else
92
+ # unsupported_data_type gate.data_type
93
+ # end
94
+ #
95
+ # true
96
+ end
97
+
98
+ # Public: Disables a gate for a given thing.
99
+ #
100
+ # feature - The Flipper::Feature for the gate.
101
+ # gate - The Flipper::Gate to disable.
102
+ # thing - The Flipper::Type being disabled for the gate.
103
+ #
104
+ # Returns true.
105
+ def disable(feature, gate, thing)
106
+ conditions = { flipper_features: {name: feature.key} }
107
+
108
+ g = case gate.data_type
109
+ when :boolean
110
+ Flipper::ActiveRecord::Gate.joins(:feature).where(conditions).destroy_all
111
+ when :integer
112
+ conditions.merge!(name: gate.key.to_s)
113
+ Flipper::ActiveRecord::Gate.joins(:feature).where(conditions).
114
+ limit(1).update_all(value: thing.value.to_s)
115
+ when :set
116
+ conditions.merge!(name: gate.key.to_s, value: thing.value.to_s)
117
+ Flipper::ActiveRecord::Gate.joins(:feature).where(conditions).destroy_all
118
+ else
119
+ unsupported_data_type gate.data_type
120
+ end
121
+
122
+ true
123
+ end
124
+
125
+ # Private
126
+ def unsupported_data_type(data_type)
127
+ raise "#{data_type} is not supported by this adapter"
128
+ end
129
+
130
+ # Private
131
+ def find(key)
132
+ @collection.find_one(criteria(key)) || {}
133
+ end
134
+
135
+ # Private
136
+ def update(key, updates)
137
+ options = {:upsert => true}
138
+ @collection.update criteria(key), updates, options
139
+ end
140
+
141
+ # Private
142
+ def delete(key)
143
+ @collection.remove criteria(key)
144
+ end
145
+
146
+ # Private
147
+ def criteria(key)
148
+ {:_id => key.to_s}
149
+ end
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,7 @@
1
+ module Flipper
2
+ module Adapters
3
+ class ActiveRecord
4
+ VERSION = "0.1.0"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,21 @@
1
+ require 'rails/generators/base'
2
+ require "rails/generators/migration"
3
+ require "rails/generators/active_record"
4
+
5
+ # Extend Rails::Generators::Base so that it creates an AR migration
6
+ module Flipper
7
+ class ActiveRecordGenerator < Rails::Generators::Base
8
+ include Rails::Generators::Migration
9
+ desc "This generator creates a migration to create Flipper ActiveRecord tables."
10
+
11
+ source_paths << File.join(File.dirname(__FILE__), "templates")
12
+
13
+ def create_migration_file
14
+ migration_template "migration.rb", "db/migrate/create_flipper_tables.rb"
15
+ end
16
+
17
+ def self.next_migration_number(dirname)
18
+ ::ActiveRecord::Generators::Base.next_migration_number dirname
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,23 @@
1
+ class CreateFlipperTables < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :flipper_features do |t|
4
+ t.string :name, null: false, unique: true
5
+ t.timestamps null: false
6
+ end
7
+
8
+ create_table :flipper_gates do |t|
9
+ t.integer :flipper_feature_id, null: false
10
+ t.string :name, null: false
11
+ t.string :value
12
+ t.timestamps null: false
13
+ end
14
+ add_foreign_key :flipper_gates, :flipper_features, on_delete: :cascade
15
+ add_index :flipper_gates, [:flipper_feature_id, :name, :value], unique: true
16
+ end
17
+
18
+ def self.down
19
+ remove_foreign_key :flipper_gates, :flipper_features
20
+ drop_table :flipper_gates
21
+ drop_table :flipper_features
22
+ end
23
+ end
@@ -0,0 +1,42 @@
1
+ #!/bin/sh
2
+ #/ Usage: release
3
+ #/
4
+ #/ Tag the version in the repo and push the gem.
5
+ #/
6
+
7
+ set -e
8
+ cd $(dirname "$0")/..
9
+
10
+ [ "$1" = "--help" -o "$1" = "-h" -o "$1" = "help" ] && {
11
+ grep '^#/' <"$0"| cut -c4-
12
+ exit 0
13
+ }
14
+
15
+ gem_name=flipper-activerecord
16
+
17
+ # Build a new gem archive.
18
+ rm -rf $gem_name-*.gem
19
+ gem build -q $gem_name.gemspec
20
+
21
+ # Make sure we're on the master branch.
22
+ (git branch | grep -q '* master') || {
23
+ echo "Only release from the master branch."
24
+ exit 1
25
+ }
26
+
27
+ # Figure out what version we're releasing.
28
+ tag=v`ls $gem_name-*.gem | sed "s/^$gem_name-\(.*\)\.gem$/\1/"`
29
+
30
+ echo "Releasing $tag"
31
+
32
+ # Make sure we haven't released this version before.
33
+ git fetch -t origin
34
+
35
+ (git tag -l | grep -q "$tag") && {
36
+ echo "Whoops, there's already a '${tag}' tag."
37
+ exit 1
38
+ }
39
+
40
+ # Tag it and bag it.
41
+ gem push $gem_name-*.gem && git tag "$tag" &&
42
+ git push origin master && git push origin "$tag"
@@ -0,0 +1,12 @@
1
+ require 'helper'
2
+ require 'flipper/adapters/activerecord'
3
+ require 'flipper/spec/shared_adapter_specs'
4
+
5
+ describe Flipper::Adapters::ActiveRecord do
6
+ let(:pool) { ActiveRecord::Base.connection_pool }
7
+ # Que.connection = ::ActiveRecord if defined? ::ActiveRecord
8
+
9
+ subject { described_class.new(pool) }
10
+
11
+ it_should_behave_like 'a flipper adapter'
12
+ end
@@ -0,0 +1,3 @@
1
+ postgresql:
2
+ adapter: postgresql
3
+ database: flipper_activerecord_test
@@ -0,0 +1,56 @@
1
+ $:.unshift(File.expand_path('../../lib', __FILE__))
2
+
3
+ require 'bundler'
4
+ Bundler.setup :default
5
+ require 'yaml'
6
+ require 'flipper-activerecord'
7
+ require 'database_cleaner'
8
+
9
+ ENV["RAILS_ENV"] = "test"
10
+ config = YAML.load(File.read("spec/database.yml"))
11
+
12
+ ActiveRecord::Base.establish_connection config["postgresql"]
13
+ ActiveRecord::Migration.verbose = true
14
+
15
+ require 'generators/flipper-activerecord/templates/migration'
16
+ ActiveRecord::Schema.define do
17
+ CreateFlipperTables.up
18
+
19
+ # create_table :stories, primary_key: :story_id, force: true do |table|
20
+ # table.string :text
21
+ # table.boolean :scoped, default: true
22
+ # end
23
+ end
24
+
25
+ # Add this directory so the ActiveSupport autoloading works
26
+ ActiveSupport::Dependencies.autoload_paths << File.dirname(__FILE__)
27
+
28
+ RSpec.configure do |config|
29
+ config.filter_run :focused => true
30
+ config.alias_example_to :fit, :focused => true
31
+ config.alias_example_to :xit, :pending => true
32
+ config.run_all_when_everything_filtered = true
33
+ config.fail_fast = true
34
+
35
+ config.backtrace_exclusion_patterns = [
36
+ /rspec-(core|expectations)/,
37
+ ]
38
+
39
+ config.before(:suite) do
40
+ DatabaseCleaner.strategy = :transaction
41
+ end
42
+
43
+ config.before(:each) do
44
+ DatabaseCleaner.start
45
+ end
46
+
47
+ config.after(:each) do
48
+ DatabaseCleaner.clean
49
+ end
50
+
51
+ config.after(:suite) do
52
+ ActiveRecord::Schema.define do
53
+ CreateFlipperTables.down
54
+ end
55
+ end
56
+ end
metadata ADDED
@@ -0,0 +1,91 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: flipper-activerecord
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Blake Gentry
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-10-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: flipper
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.6'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activerecord
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 4.2.0.beta2
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 4.2.0.beta2
41
+ description: ActiveRecord adapter for Flipper
42
+ email:
43
+ - blakesgentry@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - ".gitignore"
49
+ - Gemfile
50
+ - Gemfile.lock
51
+ - Guardfile
52
+ - LICENSE
53
+ - flipper-activerecord.gemspec
54
+ - lib/flipper-activerecord.rb
55
+ - lib/flipper/activerecord/feature.rb
56
+ - lib/flipper/activerecord/gate.rb
57
+ - lib/flipper/adapters/activerecord.rb
58
+ - lib/flipper/adapters/activerecord/version.rb
59
+ - lib/generators/flipper/active_record_generator.rb
60
+ - lib/generators/flipper/templates/migration.rb
61
+ - script/release
62
+ - spec/activerecord_spec.rb
63
+ - spec/database.yml
64
+ - spec/helper.rb
65
+ homepage: https://github.com/bgentry/flipper-activerecord
66
+ licenses: []
67
+ metadata: {}
68
+ post_install_message:
69
+ rdoc_options: []
70
+ require_paths:
71
+ - lib
72
+ required_ruby_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ required_rubygems_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ requirements: []
83
+ rubyforge_project:
84
+ rubygems_version: 2.2.2
85
+ signing_key:
86
+ specification_version: 4
87
+ summary: ActiveRecord adapter for Flipper
88
+ test_files:
89
+ - spec/activerecord_spec.rb
90
+ - spec/database.yml
91
+ - spec/helper.rb