ar-octopus 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. data/.gitignore +11 -0
  2. data/.travis.yml +22 -0
  3. data/Appraisals +18 -0
  4. data/Gemfile +3 -12
  5. data/README.mkdn +63 -24
  6. data/Rakefile +70 -92
  7. data/ar-octopus.gemspec +25 -198
  8. data/lib/ar-octopus.rb +1 -0
  9. data/lib/octopus.rb +73 -25
  10. data/lib/octopus/association.rb +6 -5
  11. data/lib/octopus/association_collection.rb +58 -4
  12. data/lib/octopus/has_and_belongs_to_many_association.rb +4 -4
  13. data/lib/octopus/logger.rb +9 -4
  14. data/lib/octopus/migration.rb +155 -50
  15. data/lib/octopus/model.rb +98 -34
  16. data/lib/octopus/proxy.rb +124 -53
  17. data/lib/octopus/rails2/association.rb +46 -93
  18. data/lib/octopus/rails2/persistence.rb +1 -1
  19. data/lib/octopus/rails2/scope.rb +17 -0
  20. data/lib/octopus/rails3.1/singular_association.rb +34 -0
  21. data/lib/octopus/rails3.2/persistence.rb +12 -0
  22. data/lib/octopus/rails3/abstract_adapter.rb +39 -0
  23. data/lib/octopus/rails3/arel.rb +5 -5
  24. data/lib/octopus/rails3/log_subscriber.rb +22 -0
  25. data/lib/octopus/rails3/persistence.rb +10 -5
  26. data/lib/octopus/railtie.rb +13 -0
  27. data/lib/octopus/scope_proxy.rb +22 -16
  28. data/lib/octopus/version.rb +3 -0
  29. data/lib/tasks/octopus.rake +20 -0
  30. data/sample_app/Gemfile +2 -2
  31. data/sample_app/config/initializers/inflections.rb +1 -1
  32. data/sample_app/config/initializers/secret_token.rb +1 -1
  33. data/sample_app/db/migrate/20100720172730_create_items.rb +1 -1
  34. data/sample_app/db/migrate/20100720210335_create_sample_users.rb +1 -1
  35. data/sample_app/db/seeds.rb +1 -1
  36. data/sample_app/features/migrate.feature +12 -12
  37. data/sample_app/features/seed.feature +3 -3
  38. data/sample_app/features/step_definitions/web_steps.rb +5 -5
  39. data/sample_app/features/support/env.rb +8 -8
  40. data/sample_app/lib/tasks/cucumber.rake +2 -2
  41. data/sample_app/public/javascripts/effects.js +1 -1
  42. data/spec/config/shards.yml +38 -28
  43. data/spec/migrations/11_add_field_in_all_slaves.rb +1 -1
  44. data/spec/migrations/12_create_users_using_block.rb +2 -2
  45. data/spec/migrations/13_create_users_using_block_and_using.rb +2 -2
  46. data/spec/migrations/14_create_users_on_shards_of_a_group_with_versions.rb +11 -0
  47. data/spec/migrations/1_create_users_on_master.rb +1 -1
  48. data/spec/migrations/2_create_users_on_canada.rb +1 -1
  49. data/spec/migrations/3_create_users_on_both_shards.rb +1 -1
  50. data/spec/migrations/4_create_users_on_shards_of_a_group.rb +1 -1
  51. data/spec/migrations/5_create_users_on_multiples_groups.rb +1 -1
  52. data/spec/migrations/6_raise_exception_with_invalid_shard_name.rb +1 -1
  53. data/spec/migrations/7_raise_exception_with_invalid_multiple_shard_names.rb +1 -1
  54. data/spec/migrations/8_raise_exception_with_invalid_group_name.rb +1 -1
  55. data/spec/migrations/9_raise_exception_with_multiple_invalid_group_names.rb +1 -1
  56. data/spec/octopus/association_spec.rb +88 -70
  57. data/spec/octopus/log_subscriber_spec.rb +22 -0
  58. data/spec/octopus/logger_spec.rb +28 -15
  59. data/spec/octopus/migration_spec.rb +47 -43
  60. data/spec/octopus/model_spec.rb +179 -13
  61. data/spec/octopus/octopus_spec.rb +26 -4
  62. data/spec/octopus/proxy_spec.rb +61 -23
  63. data/spec/octopus/{replication_specs.rb → replication_spec.rb} +33 -26
  64. data/spec/octopus/scope_proxy_spec.rb +3 -3
  65. data/spec/octopus/sharded_spec.rb +9 -9
  66. data/spec/spec_helper.rb +10 -12
  67. data/spec/support/active_record/connection_adapters/modify_config_adapter.rb +17 -0
  68. data/spec/support/database_connection.rb +2 -0
  69. data/spec/{database_models.rb → support/database_models.rb} +27 -2
  70. data/spec/support/octopus_helper.rb +50 -0
  71. data/spec/tasks/octopus.rake_spec.rb +36 -0
  72. metadata +188 -169
  73. data/Gemfile.lock +0 -68
  74. data/lib/octopus/rails3/association.rb +0 -112
  75. data/spec/database_connection.rb +0 -4
  76. data/spec/octopus/controller_spec.rb +0 -34
  77. data/spec/octopus_helper.rb +0 -37
@@ -1,6 +1,6 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
1
+ require "spec_helper"
2
2
 
3
- describe Octopus do
3
+ describe Octopus, :shards => [] do
4
4
  describe "#config" do
5
5
  it "should load shards.yml file to start working" do
6
6
  Octopus.config().should be_kind_of(HashWithIndifferentAccess)
@@ -34,14 +34,14 @@ describe Octopus do
34
34
  describe "#shards=" do
35
35
  after(:each) do
36
36
  Octopus.instance_variable_set(:@config, nil)
37
- Thread.current[:connection_proxy] = Octopus::Proxy.new(Octopus.config())
37
+ Octopus::Model.send(:class_variable_set, :@@connection_proxy, nil)
38
38
  end
39
39
 
40
40
  it "should permit users to configure shards on initializer files, instead of on a yml file." do
41
41
  lambda { User.using(:crazy_shard).create!(:name => "Joaquim") }.should raise_error
42
42
 
43
43
  Octopus.setup do |config|
44
- config.shards = {:crazy_shard => {:adapter => "mysql2", :database => "octopus_shard5", :username => "root", :password => ""}}
44
+ config.shards = {:crazy_shard => {:adapter => "mysql", :database => "octopus_shard_5", :username => "root", :password => ""}}
45
45
  end
46
46
 
47
47
  lambda { User.using(:crazy_shard).create!(:name => "Joaquim") }.should_not raise_error
@@ -65,4 +65,26 @@ describe Octopus do
65
65
  end
66
66
  end
67
67
  end
68
+
69
+ describe "#enabled?" do
70
+ before do
71
+ Rails = mock()
72
+ end
73
+
74
+ after do
75
+ Object.send(:remove_const, :Rails)
76
+ end
77
+
78
+ it "should be if octopus is configured and should hook into current environment" do
79
+ Rails.stub!(:env).and_return('production')
80
+
81
+ Octopus.should be_enabled
82
+ end
83
+
84
+ it "should not be if octopus should not hook into current environment" do
85
+ Rails.stub!(:env).and_return('staging')
86
+
87
+ Octopus.should_not be_enabled
88
+ end
89
+ end
68
90
  end
@@ -1,12 +1,21 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
1
+ require "spec_helper"
2
2
 
3
3
  describe Octopus::Proxy do
4
- let(:proxy) { Octopus::Proxy.new(Octopus.config()) }
4
+ let(:proxy) { subject }
5
5
 
6
- describe "creating a new instance" do
6
+ describe "creating a new instance", :shards => [] do
7
7
  it "should initialize all shards and groups" do
8
- proxy.instance_variable_get(:@shards).keys.to_set.should == ["canada", "brazil", "master", "sqlite_shard", "russia", "alone_shard", "aug2009", "postgresql_shard", "aug2010", "aug2011"].to_set
9
- proxy.instance_variable_get(:@groups).should == {"country_shards" => [:canada, :brazil, :russia], "history_shards" => [:aug2009, :aug2010, :aug2011]}
8
+ # FIXME: Don't test implementation details
9
+ proxy.instance_variable_get(:@shards).should include("canada", "brazil", "master", "sqlite_shard", "russia", "alone_shard",
10
+ "aug2009", "postgresql_shard", "aug2010", "aug2011")
11
+
12
+ proxy.instance_variable_get(:@shards).should include("protocol_shard") if Octopus.rails32?
13
+
14
+ proxy.has_group?("country_shards").should be_true
15
+ proxy.shards_for_group("country_shards").should include(:canada, :brazil, :russia)
16
+
17
+ proxy.has_group?("history_shards").should be_true
18
+ proxy.shards_for_group("history_shards").should include(:aug2009, :aug2010, :aug2011)
10
19
  end
11
20
 
12
21
  it "should initialize the block attribute as false" do
@@ -22,13 +31,16 @@ describe Octopus::Proxy do
22
31
  end
23
32
 
24
33
  it "should work with thiking sphinx" do
25
- proxy.instance_variable_get(:@config).should == {:adapter=>"mysql2", :password=>"", :database=>"octopus_shard1", :username=>"root", :flags=>2}
34
+ config = proxy.instance_variable_get(:@config)
35
+ config[:adapter].should == "mysql"
36
+ config[:database].should == "octopus_shard_1"
37
+ config[:username].should == "root"
26
38
  end
27
39
 
28
40
  it 'should create a set with all adapters, to ensure that is needed to clean the table name.' do
29
41
  adapters = proxy.instance_variable_get(:@adapters)
30
42
  adapters.should be_kind_of(Set)
31
- adapters.to_a.should == ["sqlite3", "mysql2", "postgresql"]
43
+ adapters.to_a.should =~ ["sqlite3", "mysql", "postgresql"]
32
44
  end
33
45
 
34
46
  it 'should respond correctly to respond_to?(:pk_and_sequence_for)' do
@@ -39,6 +51,19 @@ describe Octopus::Proxy do
39
51
  proxy.respond_to?(:primary_key).should be_true
40
52
  end
41
53
 
54
+ context 'when an adapter that modifies the config' do
55
+ before(:all) { OctopusHelper.set_octopus_env("modify_config") }
56
+ after(:all) { OctopusHelper.set_octopus_env("octopus") }
57
+
58
+ it 'should not fail with missing adapter second time round' do
59
+ Thread.current["octopus.current_shard"] = :modify_config_read
60
+
61
+ lambda { Octopus::Proxy.new(Octopus.config()) }.should_not raise_error("Please install the adapter: `gem install activerecord--adapter` (cannot load such file -- active_record/connection_adapters/_adapter)")
62
+
63
+ Thread.current["octopus.current_shard"] = nil
64
+ end
65
+ end
66
+
42
67
  describe "#should_clean_table_name?" do
43
68
  it 'should return true when you have a environment with multiple database types' do
44
69
  proxy.should_clean_table_name?.should be_true
@@ -46,7 +71,7 @@ describe Octopus::Proxy do
46
71
 
47
72
  context "when using a environment with a single table name" do
48
73
  before(:each) do
49
- set_octopus_env("production_replicated")
74
+ OctopusHelper.set_octopus_env("production_replicated")
50
75
  end
51
76
 
52
77
  it 'should return false' do
@@ -57,7 +82,7 @@ describe Octopus::Proxy do
57
82
 
58
83
  describe "should raise error if you have duplicated shard names" do
59
84
  before(:each) do
60
- set_octopus_env("production_raise_error")
85
+ OctopusHelper.set_octopus_env("production_raise_error")
61
86
  end
62
87
 
63
88
  it "should raise the error" do
@@ -67,17 +92,13 @@ describe Octopus::Proxy do
67
92
 
68
93
  describe "should initialize just the master when you don't have a shards.yml file" do
69
94
  before(:each) do
70
- set_octopus_env("crazy_environment")
95
+ OctopusHelper.set_octopus_env("crazy_environment")
71
96
  end
72
97
 
73
98
  it "should initialize just the master shard" do
74
99
  proxy.instance_variable_get(:@shards).keys.should == ["master"]
75
100
  end
76
101
 
77
- it "should not initialize the groups variable" do
78
- proxy.instance_variable_get(:@groups).should == {}
79
- end
80
-
81
102
  it "should not initialize replication" do
82
103
  proxy.instance_variable_get(:@replicated).should be_nil
83
104
  end
@@ -86,7 +107,7 @@ describe Octopus::Proxy do
86
107
 
87
108
  describe "when you have a replicated environment" do
88
109
  before(:each) do
89
- set_octopus_env("production_replicated")
110
+ OctopusHelper.set_octopus_env("production_replicated")
90
111
  end
91
112
 
92
113
  it "should have the replicated attribute as true" do
@@ -101,12 +122,20 @@ describe Octopus::Proxy do
101
122
  describe "when you have a rails application" do
102
123
  before(:each) do
103
124
  Rails = mock()
104
- set_octopus_env("octopus_rails")
125
+ OctopusHelper.set_octopus_env("octopus_rails")
126
+ end
127
+
128
+ after(:each) do
129
+ Object.send(:remove_const, :Rails)
130
+ Octopus.instance_variable_set(:@config, nil)
131
+ Octopus.instance_variable_set(:@rails_env, nil)
132
+ OctopusHelper.clean_connection_proxy()
105
133
  end
106
134
 
107
135
  it "should initialize correctly octopus common variables for the environments" do
108
136
  Rails.stub!(:env).and_return('staging')
109
137
  Octopus.instance_variable_set(:@rails_env, nil)
138
+ Octopus.instance_variable_set(:@environments, nil)
110
139
  Octopus.config()
111
140
 
112
141
  proxy.instance_variable_get(:@replicated).should be_true
@@ -116,17 +145,32 @@ describe Octopus::Proxy do
116
145
 
117
146
  it "should initialize correctly the shards for the staging environment" do
118
147
  Rails.stub!(:env).and_return('staging')
148
+ Octopus.instance_variable_set(:@rails_env, nil)
149
+ Octopus.instance_variable_set(:@environments, nil)
150
+ Octopus.config()
119
151
 
120
152
  proxy.instance_variable_get(:@shards).keys.to_set.should == Set.new(["slave1", "slave2", "master"])
121
153
  end
122
154
 
155
+ it "should initialize correctly the shard octopus_shard value for logging" do
156
+ Rails.stub!(:env).and_return('staging')
157
+ Octopus.instance_variable_set(:@rails_env, nil)
158
+ Octopus.instance_variable_set(:@environments, nil)
159
+ Octopus.config()
160
+
161
+ proxy.instance_variable_get(:@shards)['slave1'].spec.config.should have_key :octopus_shard
162
+ end
163
+
123
164
  it "should initialize correctly the shards for the production environment" do
124
165
  Rails.stub!(:env).and_return('production')
166
+ Octopus.instance_variable_set(:@rails_env, nil)
167
+ Octopus.instance_variable_set(:@environments, nil)
168
+ Octopus.config()
125
169
 
126
170
  proxy.instance_variable_get(:@shards).keys.to_set.should == Set.new(["slave3", "slave4", "master"])
127
171
  end
128
172
 
129
- describe "using the master connection" do
173
+ describe "using the master connection", :shards => [:russia, :master] do
130
174
  before(:each) do
131
175
  Rails.stub!(:env).and_return('development')
132
176
  end
@@ -163,12 +207,6 @@ describe Octopus::Proxy do
163
207
  u.save()
164
208
  end
165
209
  end
166
-
167
- after(:each) do
168
- Object.send(:remove_const, :Rails)
169
- Octopus.instance_variable_set(:@config, nil)
170
- Octopus.instance_variable_set(:@rails_env, nil)
171
- end
172
210
  end
173
211
 
174
212
  describe "returning the correct connection" do
@@ -1,8 +1,8 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
1
+ require "spec_helper"
2
2
 
3
3
  describe "when the database is replicated" do
4
4
  it "should send all writes/reads queries to master when you have a non replicated model" do
5
- using_environment :production_replicated do
5
+ OctopusHelper.using_environment :production_replicated do
6
6
  u = User.create!(:name => "Replicated")
7
7
  User.count.should == 1
8
8
  User.find(u.id).should == u
@@ -10,8 +10,8 @@ describe "when the database is replicated" do
10
10
  end
11
11
 
12
12
  it "should send all writes queries to master" do
13
- using_environment :production_replicated do
14
- Cat.create!(:name => "Slave Cat")
13
+ OctopusHelper.using_environment :production_replicated do
14
+ Cat.create!(:name => "Slave Cat")
15
15
  Cat.find_by_name("Slave Cat").should be_nil
16
16
  Client.create!(:name => "Slave Client")
17
17
  Client.find_by_name("Slave Client").should_not be_nil
@@ -19,27 +19,26 @@ describe "when the database is replicated" do
19
19
  end
20
20
 
21
21
  it "should allow to create multiple models on the master" do
22
- using_environment :production_replicated do
23
- Cat.create!([{:name => "Slave Cat 1"}, {:name => "Slave Cat 2"}])
22
+ OctopusHelper.using_environment :production_replicated do
23
+ Cat.create!([{:name => "Slave Cat 1"}, {:name => "Slave Cat 2"}])
24
24
  Cat.find_by_name("Slave Cat 1").should be_nil
25
25
  Cat.find_by_name("Slave Cat 2").should be_nil
26
26
  end
27
27
  end
28
-
28
+
29
29
  it "should allow #using syntax to send queries to master" do
30
- Cat.create!(:name => "Master Cat")
31
-
32
- using_environment :production_fully_replicated do
30
+ Cat.create!(:name => "Master Cat")
31
+
32
+ OctopusHelper.using_environment :production_fully_replicated do
33
33
  Cat.using(:master).find_by_name("Master Cat").should_not be_nil
34
34
  end
35
35
  end
36
36
 
37
37
  it "should send the count query to a slave" do
38
- pending()
39
- # using_environment :production_replicated do
40
- # Cat.create!(:name => "Slave Cat")
41
- # Cat.count.should == 0
42
- # end
38
+ OctopusHelper.using_environment :production_replicated do
39
+ Cat.create!(:name => "Slave Cat")
40
+ Cat.count.should == 0
41
+ end
43
42
  end
44
43
  end
45
44
 
@@ -47,31 +46,39 @@ end
47
46
  describe "when the database is replicated and the entire application is replicated" do
48
47
  before(:each) do
49
48
  Octopus.stub!(:env).and_return("production_fully_replicated")
50
- clean_connection_proxy()
49
+ OctopusHelper.clean_connection_proxy()
50
+ end
51
+
52
+ it "should send all writes queries to master" do
53
+ OctopusHelper.using_environment :production_fully_replicated do
54
+ Cat.create!(:name => "Slave Cat")
55
+ Cat.find_by_name("Slave Cat").should be_nil
56
+ Client.create!(:name => "Slave Client")
57
+ Client.find_by_name("Slave Client").should be_nil
58
+ end
51
59
  end
52
60
 
53
61
  it "should send all writes queries to master" do
54
- using_environment :production_fully_replicated do
55
- Cat.create!(:name => "Slave Cat")
62
+ OctopusHelper.using_environment :production_fully_replicated do
63
+ Cat.create!(:name => "Slave Cat")
56
64
  Cat.find_by_name("Slave Cat").should be_nil
57
65
  Client.create!(:name => "Slave Client")
58
66
  Client.find_by_name("Slave Client").should be_nil
59
67
  end
60
- end
61
-
68
+ end
69
+
62
70
  it "should work with validate_uniquess_of" do
63
71
  Keyboard.create!(:name => "thiago")
64
-
65
- using_environment :production_fully_replicated do
72
+
73
+ OctopusHelper.using_environment :production_fully_replicated do
66
74
  k = Keyboard.new(:name => "thiago")
67
75
  k.save.should be_false
68
- k.errors.should == {:name=>["has already been taken"]}
76
+ k.errors.full_messages.should == ["Name has already been taken"]
69
77
  end
70
78
  end
71
-
79
+
72
80
  it "should reset current shard if slave throws an exception" do
73
-
74
- using_environment :production_fully_replicated do
81
+ OctopusHelper.using_environment :production_fully_replicated do
75
82
  Cat.create!(:name => "Slave Cat")
76
83
  Cat.connection.current_shard.should eql(:master)
77
84
  begin
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
1
+ require "spec_helper"
2
2
 
3
3
  describe Octopus::ScopeProxy do
4
4
  it "should allow nested queries" do
@@ -6,13 +6,13 @@ describe Octopus::ScopeProxy do
6
6
  @user1 = User.using(:brazil).create!(:name => "Thiago P", :number => 3)
7
7
  @user2 = User.using(:brazil).create!(:name => "Thiago", :number => 1)
8
8
  @user3 = User.using(:brazil).create!(:name => "Thiago", :number => 2)
9
-
9
+
10
10
  User.using(:brazil).where(:name => "Thiago").where(:number => 4).order(:number).all.should == []
11
11
  User.using(:brazil).where(:name => "Thiago").using(:canada).where(:number => 2).using(:brazil).order(:number).all.should == [@user3]
12
12
  User.using(:brazil).where(:name => "Thiago").using(:canada).where(:number => 4).using(:brazil).order(:number).all.should == []
13
13
  end
14
14
  end
15
-
15
+
16
16
  it "should raise a exception when trying to send a query to a shard that don't exists" do
17
17
  lambda { User.using(:dont_exists).all }.should raise_exception("Nonexistent Shard Name: dont_exists")
18
18
  end
@@ -1,33 +1,33 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
1
+ require "spec_helper"
2
2
 
3
3
  describe "when the database is not entire sharded" do
4
4
  before(:each) do
5
5
  Octopus.stub!(:env).and_return("not_entire_sharded")
6
- clean_connection_proxy()
6
+ OctopusHelper.clean_connection_proxy()
7
7
  end
8
-
8
+
9
9
  it "should not send all queries to the specified slave" do
10
10
  pending()
11
11
  # User.create!(:name => "Thiago")
12
- #
13
- # using_environment :not_entire_sharded do
12
+ #
13
+ # using_environment :not_entire_sharded do
14
14
  # Octopus.using(:russia) do
15
15
  # User.create!(:name => "Thiago")
16
16
  # end
17
17
  # end
18
- #
18
+ #
19
19
  # User.count.should == 2
20
20
  end
21
-
21
+
22
22
  it "should pick the shard based on current_shard when you have a sharded model" do
23
23
  Cat.create!(:name => "Thiago")
24
24
 
25
- using_environment :not_entire_sharded do
25
+ OctopusHelper.using_environment :not_entire_sharded do
26
26
  Octopus.using(:russia) do
27
27
  Cat.create!(:name => "Thiago")
28
28
  end
29
29
  end
30
-
30
+
31
31
  Cat.count.should == 1
32
32
  end
33
33
  end
@@ -1,20 +1,18 @@
1
1
  require "rubygems"
2
- require "bundler"
3
-
4
- Bundler.setup()
5
-
6
- require File.expand_path(File.dirname(__FILE__)) + "/database_connection"
2
+ require "pry"
3
+ require "bundler/setup"
7
4
  require "octopus"
8
- require "octopus_helper"
9
- require "action_controller"
10
- require 'rspec/core'
11
5
 
12
- MIGRATIONS_ROOT = File.expand_path(File.join(File.dirname(__FILE__), 'migrations'))
6
+ Octopus.instance_variable_set(:@directory, File.dirname(__FILE__))
7
+
8
+ # Requires supporting files with custom matchers and macros, etc,
9
+ # in ./support/ and its subdirectories.
10
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
13
11
 
14
12
  RSpec.configure do |config|
15
13
  config.before(:each) do
16
- Octopus.stub!(:directory).and_return(File.dirname(__FILE__))
17
- require "database_models"
18
- clean_all_shards()
14
+ OctopusHelper.clean_all_shards(example.metadata[:shards])
19
15
  end
20
16
  end
17
+
18
+ $: << File.expand_path(File.join(File.dirname(__FILE__), "support"))
@@ -0,0 +1,17 @@
1
+ module ActiveRecord
2
+ class Base
3
+ def self.modify_config_connection(config)
4
+ ConnectionAdapters::ModifyConfigAdapter.new(config)
5
+ end
6
+ end
7
+
8
+ module ConnectionAdapters
9
+ class ModifyConfigAdapter < AbstractAdapter
10
+
11
+ def initialize(config)
12
+ config.replace(config.symbolize_keys)
13
+ end
14
+
15
+ end
16
+ end
17
+ end