makara 0.5.0 → 0.5.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.
Files changed (89) hide show
  1. checksums.yaml +4 -4
  2. data/.github/dependabot.yml +11 -0
  3. data/.github/workflows/CI.yml +88 -0
  4. data/.rspec +1 -1
  5. data/.rubocop.yml +15 -0
  6. data/.rubocop_todo.yml +670 -0
  7. data/CHANGELOG.md +14 -6
  8. data/Gemfile +1 -16
  9. data/README.md +2 -1
  10. data/Rakefile +1 -1
  11. data/gemfiles/activerecord_5.2.gemfile +8 -0
  12. data/gemfiles/activerecord_6.0.gemfile +8 -0
  13. data/gemfiles/activerecord_6.1.gemfile +8 -0
  14. data/gemfiles/activerecord_head.gemfile +6 -0
  15. data/lib/active_record/connection_adapters/jdbcmysql_makara_adapter.rb +4 -18
  16. data/lib/active_record/connection_adapters/jdbcpostgresql_makara_adapter.rb +4 -18
  17. data/lib/active_record/connection_adapters/makara_abstract_adapter.rb +3 -31
  18. data/lib/active_record/connection_adapters/makara_jdbcmysql_adapter.rb +4 -18
  19. data/lib/active_record/connection_adapters/makara_jdbcpostgresql_adapter.rb +4 -18
  20. data/lib/active_record/connection_adapters/makara_mysql2_adapter.rb +4 -20
  21. data/lib/active_record/connection_adapters/makara_postgis_adapter.rb +4 -19
  22. data/lib/active_record/connection_adapters/makara_postgresql_adapter.rb +4 -20
  23. data/lib/active_record/connection_adapters/mysql2_makara_adapter.rb +4 -20
  24. data/lib/active_record/connection_adapters/postgresql_makara_adapter.rb +4 -20
  25. data/lib/makara.rb +0 -2
  26. data/lib/makara/cache.rb +0 -2
  27. data/lib/makara/config_parser.rb +5 -14
  28. data/lib/makara/connection_wrapper.rb +24 -27
  29. data/lib/makara/context.rb +1 -0
  30. data/lib/makara/cookie.rb +1 -0
  31. data/lib/makara/error_handler.rb +0 -9
  32. data/lib/makara/errors/all_connections_blacklisted.rb +0 -2
  33. data/lib/makara/errors/blacklist_connection.rb +0 -2
  34. data/lib/makara/errors/blacklisted_while_in_transaction.rb +0 -2
  35. data/lib/makara/errors/invalid_shard.rb +1 -3
  36. data/lib/makara/errors/makara_error.rb +0 -1
  37. data/lib/makara/errors/no_connections_available.rb +0 -2
  38. data/lib/makara/logging/logger.rb +0 -4
  39. data/lib/makara/logging/subscriber.rb +0 -2
  40. data/lib/makara/middleware.rb +1 -2
  41. data/lib/makara/pool.rb +2 -7
  42. data/lib/makara/proxy.rb +25 -27
  43. data/lib/makara/railtie.rb +0 -2
  44. data/lib/makara/strategies/abstract.rb +1 -0
  45. data/lib/makara/strategies/priority_failover.rb +2 -0
  46. data/lib/makara/strategies/round_robin.rb +1 -3
  47. data/lib/makara/strategies/shard_aware.rb +0 -2
  48. data/lib/makara/version.rb +1 -3
  49. data/makara.gemspec +24 -5
  50. data/spec/active_record/connection_adapters/makara_abstract_adapter_error_handling_spec.rb +1 -6
  51. data/spec/active_record/connection_adapters/makara_abstract_adapter_spec.rb +0 -9
  52. data/spec/active_record/connection_adapters/makara_mysql2_adapter_spec.rb +9 -22
  53. data/spec/active_record/connection_adapters/makara_postgis_adapter_spec.rb +2 -10
  54. data/spec/active_record/connection_adapters/makara_postgresql_adapter_spec.rb +7 -20
  55. data/spec/cache_spec.rb +0 -1
  56. data/spec/config_parser_spec.rb +54 -56
  57. data/spec/connection_wrapper_spec.rb +1 -2
  58. data/spec/cookie_spec.rb +1 -1
  59. data/spec/middleware_spec.rb +1 -1
  60. data/spec/pool_spec.rb +3 -16
  61. data/spec/proxy_spec.rb +0 -4
  62. data/spec/spec_helper.rb +5 -1
  63. data/spec/strategies/priority_failover_spec.rb +3 -4
  64. data/spec/strategies/round_robin_spec.rb +4 -8
  65. data/spec/strategies/shard_aware_spec.rb +4 -5
  66. data/spec/support/deep_dup.rb +1 -1
  67. data/spec/support/helpers.rb +5 -5
  68. data/spec/support/mock_objects.rb +1 -4
  69. data/spec/support/mysql2_database.yml +2 -2
  70. data/spec/support/mysql2_database_with_custom_errors.yml +2 -2
  71. data/spec/support/pool_extensions.rb +0 -3
  72. data/spec/support/postgis_schema.rb +1 -1
  73. data/spec/support/postgresql_database.yml +0 -2
  74. data/spec/support/proxy_extensions.rb +1 -3
  75. data/spec/support/schema.rb +1 -1
  76. data/spec/support/user.rb +1 -2
  77. metadata +156 -20
  78. data/.travis.yml +0 -131
  79. data/gemfiles/ar-head.gemfile +0 -24
  80. data/gemfiles/ar30.gemfile +0 -36
  81. data/gemfiles/ar31.gemfile +0 -36
  82. data/gemfiles/ar32.gemfile +0 -36
  83. data/gemfiles/ar40.gemfile +0 -24
  84. data/gemfiles/ar41.gemfile +0 -24
  85. data/gemfiles/ar42.gemfile +0 -24
  86. data/gemfiles/ar50.gemfile +0 -24
  87. data/gemfiles/ar51.gemfile +0 -24
  88. data/gemfiles/ar52.gemfile +0 -24
  89. data/gemfiles/ar60.gemfile +0 -24
@@ -1,8 +1,7 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Makara::ConnectionWrapper do
4
-
5
- let(:proxy){ FakeProxy.new({:makara => {:blacklist_duration => 5, :connections => [{:role => 'master'}, {:role => 'slave'}, {:role => 'slave'}]}}) }
4
+ let(:proxy){ FakeProxy.new({makara: {blacklist_duration: 5, connections: [{role: 'master'}, {role: 'slave'}, {role: 'slave'}]}}) }
6
5
  let(:connection){ subject._makara_connection }
7
6
 
8
7
  subject{ proxy.master_pool.connections.first }
data/spec/cookie_spec.rb CHANGED
@@ -63,7 +63,7 @@ describe Makara::Cookie do
63
63
  end
64
64
 
65
65
  it 'allows custom cookie options to be provided' do
66
- Makara::Cookie.store(context_data, headers, { :secure => true })
66
+ Makara::Cookie.store(context_data, headers, { secure: true })
67
67
 
68
68
  expect(headers['Set-Cookie']).to include("#{cookie_key}=mysql%3A#{(now + 5).to_f}%7Credis%3A#{(now + 5).to_f};")
69
69
  expect(headers['Set-Cookie']).to include("path=/; max-age=10; expires=#{(Time.now + 10).httpdate}; secure; HttpOnly")
@@ -12,7 +12,7 @@ describe Makara::Middleware do
12
12
 
13
13
  let(:env){ {} }
14
14
  let(:proxy){ FakeProxy.new(config(1,2)) }
15
- let(:middleware){ described_class.new(app, :secure => true) }
15
+ let(:middleware){ described_class.new(app, secure: true) }
16
16
 
17
17
  let(:key){ Makara::Cookie::IDENTIFIER }
18
18
 
data/spec/pool_spec.rb CHANGED
@@ -1,10 +1,9 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Makara::Pool do
4
-
5
- let(:proxy){ FakeProxy.new({:makara => pool_config.merge(:connections => [])}) }
4
+ let(:proxy){ FakeProxy.new({makara: pool_config.merge(connections: [])}) }
6
5
  let(:pool){ Makara::Pool.new('test', proxy) }
7
- let(:pool_config){ {:blacklist_duration => 5} }
6
+ let(:pool_config){ {blacklist_duration: 5} }
8
7
  let(:master_pool){ Makara::Pool.new('master', proxy) }
9
8
 
10
9
  it 'should wrap connections with a ConnectionWrapper as theyre added to the pool' do
@@ -13,7 +12,7 @@ describe Makara::Pool do
13
12
  connection_a = FakeConnection.new(something: 'a')
14
13
 
15
14
  wrapper_a = pool.add(pool_config){ connection_a }
16
- wrapper_b = pool.add(pool_config.merge(:weight => 2)){ FakeConnection.new }
15
+ wrapper_b = pool.add(pool_config.merge(weight: 2)){ FakeConnection.new }
17
16
 
18
17
  connections = pool.connections
19
18
  weighted_connections = pool.strategy.instance_variable_get("@weighted_connections")
@@ -29,7 +28,6 @@ describe Makara::Pool do
29
28
  end
30
29
 
31
30
  it 'should determine if its completely blacklisted' do
32
-
33
31
  pool.add(pool_config){ FakeConnection.new }
34
32
  pool.add(pool_config){ FakeConnection.new }
35
33
 
@@ -41,7 +39,6 @@ describe Makara::Pool do
41
39
  end
42
40
 
43
41
  it 'sends methods to all underlying objects if asked to' do
44
-
45
42
  a = FakeConnection.new
46
43
  b = FakeConnection.new
47
44
 
@@ -52,11 +49,9 @@ describe Makara::Pool do
52
49
  expect(b).to receive(:query).with('test').once
53
50
 
54
51
  pool.send_to_all :query, 'test'
55
-
56
52
  end
57
53
 
58
54
  it 'only sends methods to underlying objects which are not blacklisted' do
59
-
60
55
  a = FakeConnection.new
61
56
  b = FakeConnection.new
62
57
  c = FakeConnection.new
@@ -72,11 +67,9 @@ describe Makara::Pool do
72
67
  wrapper_c._makara_blacklist!
73
68
 
74
69
  pool.send_to_all :query, 'test'
75
-
76
70
  end
77
71
 
78
72
  it 'provides the next connection and blacklists' do
79
-
80
73
  connection_a = FakeConnection.new(something: 'a')
81
74
  connection_b = FakeConnection.new(something: 'b')
82
75
 
@@ -96,7 +89,6 @@ describe Makara::Pool do
96
89
  expect(wrapper_a._makara_blacklisted?).to eq(false)
97
90
  expect(wrapper_b._makara_blacklisted?).to eq(false)
98
91
  end
99
-
100
92
  end
101
93
 
102
94
  it 'provides the same connection if the context has not changed and the proxy is sticky' do
@@ -126,7 +118,6 @@ describe Makara::Pool do
126
118
  end
127
119
 
128
120
  it 'raises an error when all connections are blacklisted' do
129
-
130
121
  wrapper_a = pool.add(pool_config.dup){ FakeConnection.new }
131
122
  wrapper_b = pool.add(pool_config.dup){ FakeConnection.new }
132
123
 
@@ -135,7 +126,6 @@ describe Makara::Pool do
135
126
 
136
127
  allow(pool).to receive(:next).and_return(wrapper_a, wrapper_b, nil)
137
128
 
138
-
139
129
  begin
140
130
  pool.provide do |connection|
141
131
  raise Makara::Errors::BlacklistConnection.new(connection, StandardError.new('failure'))
@@ -147,7 +137,6 @@ describe Makara::Pool do
147
137
  end
148
138
 
149
139
  it 'skips blacklisted connections when choosing the next one' do
150
-
151
140
  pool.add(pool_config){ FakeConnection.new }
152
141
  pool.add(pool_config){ FakeConnection.new }
153
142
 
@@ -155,7 +144,6 @@ describe Makara::Pool do
155
144
  wrapper_b._makara_blacklist!
156
145
 
157
146
  10.times{ pool.provide{|connection| expect(connection).not_to eq(wrapper_b) } }
158
-
159
147
  end
160
148
 
161
149
  it 'should error out while blacklisted in transaction' do
@@ -180,5 +168,4 @@ describe Makara::Pool do
180
168
  end
181
169
  10.times{ master_pool.provide{|connection| expect(connection).not_to eq(wrapper_a) } }
182
170
  end
183
-
184
171
  end
data/spec/proxy_spec.rb CHANGED
@@ -1,10 +1,8 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Makara::Proxy do
4
-
5
4
  let(:klass){ FakeProxy }
6
5
 
7
-
8
6
  it 'sets up a master and slave pool no matter the number of connections' do
9
7
  proxy = klass.new(config(0, 0))
10
8
  expect(proxy.master_pool).to be_a(Makara::Pool)
@@ -23,7 +21,6 @@ describe Makara::Proxy do
23
21
  expect(proxy.slave_pool).to be_a(Makara::Pool)
24
22
  end
25
23
 
26
-
27
24
  it 'instantiates N connections within each pool' do
28
25
  proxy = klass.new(config(1, 2))
29
26
 
@@ -89,7 +86,6 @@ describe Makara::Proxy do
89
86
  end
90
87
  end
91
88
 
92
-
93
89
  describe '#appropriate_pool' do
94
90
  let(:proxy) { klass.new(config(1,1)) }
95
91
 
data/spec/spec_helper.rb CHANGED
@@ -6,7 +6,7 @@ require 'yaml'
6
6
  require 'rack'
7
7
 
8
8
  begin
9
- require 'byebug'
9
+ require 'pry'
10
10
  rescue LoadError
11
11
  end
12
12
 
@@ -15,6 +15,10 @@ begin
15
15
  rescue LoadError
16
16
  end
17
17
 
18
+ if RUBY_VERSION >= "2.7.0"
19
+ Warning[:deprecated] = true
20
+ end
21
+
18
22
  RSpec.configure do |config|
19
23
  config.run_all_when_everything_filtered = true
20
24
  config.filter_run :focus
@@ -1,10 +1,10 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Makara::Strategies::PriorityFailover do
4
- let(:proxy){ FakeProxy.new({:makara => pool_config.merge(makara_config).merge(:connections => [])}) }
4
+ let(:proxy){ FakeProxy.new({makara: pool_config.merge(makara_config).merge(connections: [])}) }
5
5
  let(:pool){ Makara::Pool.new('master', proxy) }
6
- let(:pool_config){ {:blacklist_duration => 5} }
7
- let(:makara_config) { { :master_strategy => 'failover' } }
6
+ let(:pool_config){ {blacklist_duration: 5} }
7
+ let(:makara_config) { { master_strategy: 'failover' } }
8
8
  let(:strategy) { pool.strategy }
9
9
 
10
10
  it 'should use the strategy' do
@@ -45,5 +45,4 @@ describe Makara::Strategies::PriorityFailover do
45
45
  expect(strategy.current.something).to eql('b')
46
46
  expect(strategy.next.something).to eql('b')
47
47
  end
48
-
49
48
  end
@@ -1,9 +1,9 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Makara::Strategies::RoundRobin do
4
- let(:proxy){ FakeProxy.new({:makara => pool_config.merge(makara_config).merge(:connections => [])}) }
4
+ let(:proxy){ FakeProxy.new({makara: pool_config.merge(makara_config).merge(connections: [])}) }
5
5
  let(:pool){ Makara::Pool.new('test', proxy) }
6
- let(:pool_config){ {:blacklist_duration => 5} }
6
+ let(:pool_config){ {blacklist_duration: 5} }
7
7
  let(:makara_config) { {} }
8
8
  let(:strategy) { pool.strategy }
9
9
 
@@ -14,7 +14,7 @@ describe Makara::Strategies::RoundRobin do
14
14
  end
15
15
 
16
16
  context 'bad config' do
17
- let(:makara_config) { { :test_strategy => 'SomethingElse::Here' } }
17
+ let(:makara_config) { { test_strategy: 'SomethingElse::Here' } }
18
18
  it 'should raise name error' do
19
19
  expect {
20
20
  pool
@@ -23,13 +23,12 @@ describe Makara::Strategies::RoundRobin do
23
23
  end
24
24
 
25
25
  context 'given in config' do
26
- let(:makara_config) { { :test_strategy => 'round_robin' } }
26
+ let(:makara_config) { { test_strategy: 'round_robin' } }
27
27
  it 'should use the strategy' do
28
28
  expect(pool.strategy).to be_instance_of(Makara::Strategies::RoundRobin)
29
29
  end
30
30
  end
31
31
 
32
-
33
32
  it 'should loop through with weights' do
34
33
  wrapper_a = pool.add(pool_config){ FakeConnection.new(something: 'a') }
35
34
  wrapper_b = pool.add(pool_config){ FakeConnection.new(something: 'b') }
@@ -61,7 +60,4 @@ describe Makara::Strategies::RoundRobin do
61
60
  expect(strategy.next.something).to eql('c')
62
61
  expect(strategy.next.something).to eql('b')
63
62
  end
64
-
65
-
66
-
67
63
  end
@@ -1,7 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Makara::Strategies::ShardAware do
4
-
5
4
  def with_shard(shard_id)
6
5
  begin
7
6
  Thread.current['makara_shard_id'] = shard_id
@@ -12,7 +11,7 @@ describe Makara::Strategies::ShardAware do
12
11
  end
13
12
 
14
13
  describe "failover strategy with shard awareness," do
15
- let(:proxy){ FakeProxy.new({:makara => pool_config.merge(makara_config).merge(:connections => [])}) }
14
+ let(:proxy){ FakeProxy.new({makara: pool_config.merge(makara_config).merge(connections: [])}) }
16
15
  let(:pool){ Makara::Pool.new('master', proxy) }
17
16
  let(:pool_config){ { blacklist_duration: 5} }
18
17
  let(:makara_config) { {
@@ -109,7 +108,7 @@ describe Makara::Strategies::ShardAware do
109
108
  end
110
109
 
111
110
  describe "round_robin strategy with shard awareness," do
112
- let(:proxy){ FakeProxy.new({:makara => pool_config.merge(makara_config).merge(:connections => [])}) }
111
+ let(:proxy){ FakeProxy.new({makara: pool_config.merge(makara_config).merge(connections: [])}) }
113
112
  let(:pool){ Makara::Pool.new('master', proxy) }
114
113
  let(:pool_config){ { blacklist_duration: 5} }
115
114
  let(:makara_config) { {
@@ -186,7 +185,7 @@ describe Makara::Strategies::ShardAware do
186
185
  end
187
186
 
188
187
  describe "uses the configured failover strategy when shard_aware set to false," do
189
- let(:proxy){ FakeProxy.new({:makara => pool_config.merge(makara_config).merge(:connections => [])}) }
188
+ let(:proxy){ FakeProxy.new({makara: pool_config.merge(makara_config).merge(connections: [])}) }
190
189
  let(:pool){ Makara::Pool.new('master', proxy) }
191
190
  let(:pool_config){ { blacklist_duration: 5} }
192
191
  let(:makara_config) { {
@@ -202,7 +201,7 @@ describe Makara::Strategies::ShardAware do
202
201
  end
203
202
 
204
203
  describe "uses the configured roundrobin strategy when shard_aware set to false," do
205
- let(:proxy){ FakeProxy.new({:makara => pool_config.merge(makara_config).merge(:connections => [])}) }
204
+ let(:proxy){ FakeProxy.new({makara: pool_config.merge(makara_config).merge(connections: [])}) }
206
205
  let(:pool){ Makara::Pool.new('master', proxy) }
207
206
  let(:pool_config){ { blacklist_duration: 5} }
208
207
  let(:makara_config) { {
@@ -9,4 +9,4 @@ unless Hash.respond_to?(:deep_dup)
9
9
  duplicate
10
10
  end
11
11
  end
12
- end
12
+ end
@@ -11,16 +11,16 @@ module SpecHelpers
11
11
 
12
12
  def config(masters = 1, slaves = 2)
13
13
  connections = []
14
- masters.times{ connections << {:role => 'master'} }
15
- slaves.times{ connections << {:role => 'slave'} }
14
+ masters.times{ connections << {role: 'master'} }
15
+ slaves.times{ connections << {role: 'slave'} }
16
16
  {
17
- :makara => {
17
+ makara: {
18
18
  # Defaults:
19
19
  # :master_ttl => 5,
20
20
  # :blacklist_duration => 30,
21
21
  # :sticky => true
22
- :id => 'mock_mysql',
23
- :connections => connections
22
+ id: 'mock_mysql',
23
+ connections: connections
24
24
  }
25
25
  }
26
26
  end
@@ -1,7 +1,6 @@
1
1
  require 'active_record/connection_adapters/makara_abstract_adapter'
2
2
 
3
3
  class FakeConnection < Struct.new(:config)
4
-
5
4
  def ping
6
5
  'ping!'
7
6
  end
@@ -32,7 +31,6 @@ class FakeConnection < Struct.new(:config)
32
31
  end
33
32
 
34
33
  class FakeDatabaseAdapter < Struct.new(:config)
35
-
36
34
  def execute(sql, name = nil)
37
35
  []
38
36
  end
@@ -48,11 +46,9 @@ class FakeDatabaseAdapter < Struct.new(:config)
48
46
  def active?
49
47
  true
50
48
  end
51
-
52
49
  end
53
50
 
54
51
  class FakeProxy < Makara::Proxy
55
-
56
52
  send_to_all :ping
57
53
  hijack_method :execute
58
54
 
@@ -62,6 +58,7 @@ class FakeProxy < Makara::Proxy
62
58
 
63
59
  def needs_master?(method_name, args)
64
60
  return false if args.first =~ /^select/
61
+
65
62
  true
66
63
  end
67
64
  end
@@ -1,9 +1,9 @@
1
1
  test:
2
2
  adapter: 'mysql2_makara'
3
3
  database: 'makara_test'
4
- username: 'root'
4
+ host: <%= ENV["MYSQL_HOST"] %>
5
+ username: root
5
6
  password: ''
6
-
7
7
  timeout: 5000
8
8
  connect_timeout: 1
9
9
  read_timeout: 1
@@ -1,9 +1,9 @@
1
1
  test:
2
2
  adapter: 'mysql2_makara'
3
3
  database: 'makara_test'
4
- username: 'root'
4
+ host: <%= ENV["MYSQL_HOST"] %>
5
+ username: root
5
6
  password: ''
6
-
7
7
  timeout: 5000
8
8
  connect_timeout: 1
9
9
  read_timeout: 1
@@ -1,5 +1,4 @@
1
1
  module PoolExtensions
2
-
3
2
  def connections
4
3
  @connections
5
4
  end
@@ -7,8 +6,6 @@ module PoolExtensions
7
6
  def connection_count
8
7
  @connections.length
9
8
  end
10
-
11
9
  end
12
10
 
13
-
14
11
  Makara::Pool.send(:include, PoolExtensions)
@@ -5,7 +5,7 @@ conn.execute "create extension if not exists postgis"
5
5
  if conn.table_exists? "towns"
6
6
  conn.execute("TRUNCATE TABLE towns")
7
7
  else
8
- conn.create_table "towns", :force => true do |t|
8
+ conn.create_table "towns", force: true do |t|
9
9
  t.st_point "location"
10
10
  end
11
11
  end
@@ -1,8 +1,6 @@
1
1
  test:
2
2
  adapter: 'postgresql_makara'
3
3
  database: 'makara_test'
4
- username: 'root'
5
- password: ''
6
4
 
7
5
  timeout: 5000
8
6
 
@@ -1,7 +1,6 @@
1
1
  module ProxyExtensions
2
-
3
2
  attr_reader :master_pool, :slave_pool, :id
4
-
3
+
5
4
  def master_for?(sql)
6
5
  pool_for(sql) == master_pool
7
6
  end
@@ -27,7 +26,6 @@ module ProxyExtensions
27
26
  def sticky=(s)
28
27
  @sticky = s
29
28
  end
30
-
31
29
  end
32
30
 
33
31
  Makara::Proxy.send(:include, ProxyExtensions)
@@ -4,6 +4,6 @@ if conn.table_exists? "users"
4
4
  conn.execute("TRUNCATE TABLE users")
5
5
  else
6
6
  conn.create_table "users" do |t|
7
- t.string "name"
7
+ t.string "name"
8
8
  end
9
9
  end
data/spec/support/user.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  module Test
2
2
  class User < ::ActiveRecord::Base
3
-
4
3
  end
5
- end
4
+ end