acts_as_tenant 0.3.5 → 0.3.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- NWRkOGI4YTQ3YWJiNDZmMDg5YmQ5ZjZiNGVlNzRjZGUzOGYyYTYxMw==
4
+ MmNiZWQ1ZWI4YWU2Y2M4YTMxNjRiN2RkYTA0MzNkN2VkZTRhNTczOA==
5
5
  data.tar.gz: !binary |-
6
- M2I2OTE5NTlmZmM2NDk1MDY5NzIyMGFhMjMwOGU1ODRkOTRlNTg1Nw==
6
+ YzUzZmVmOTdhY2NlZDQ3NGMxZWRjMDYxY2RjODBmOWE1MjJlOTRiMA==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- Y2EwYmZlNTU5Y2RmM2YyMjQzNjE2MjljY2I1YjIxYThkZjllNTExYzY3OTRh
10
- Y2U3YzAyYzc2NGRlNGUyYmY1NjY1MTdkZmNjMGNkMzFkNzE1MjRjNWU2OTQz
11
- YjljN2I3OTk2MjQxNmM4YjAzMzQwMzFmMmY0MGVhZjNiMDBmOTc=
9
+ NzhmYWY4NmY3ZTkxZjE0MTIzZjdlYjc2ZTI5ZmFhMWI0OGEzZmRhYzFmYjQ3
10
+ ODhiMmVmMmE0MTFiYjBiZTQyODg1ZTU3MzRiMzZmNmJmMzYwYzZkMTI1OGJi
11
+ ZDRjODllOWMzNmVhZmUxNmFkMmM1MjRjNDhmZjU4MTc3Y2YyMjQ=
12
12
  data.tar.gz: !binary |-
13
- OTQ2ZjZmMzMwYTc0NmZlMjYyMzg0OGE4MTQ2NWMxYjc5ODYwMTNmNWMzMjJm
14
- Yzg4NzJiOTU0MjcyMGE1ZGQxMjc5Zjk4NTJkMWU5YWNjNDBjZWE2OTlmNTE2
15
- NWIwYjAxYjkwNGRiMjIzNGY2YmVlYmFmYWQyMTNhMTgyYzIzODU=
13
+ NmU4NjEzZGQyOGQ5ZTk2MzgyMzMyYjRmMDczNGIwOTQ4NDA4YTNmNzc0MTU4
14
+ ZDc5M2UwNDMzMTI5YmExN2I1MjRhYTgxMDgyYjI0MjY3OWZkNjgyOTQxOGMw
15
+ MzYyNThlNjhiMTM2YTdhZTQ0NzQ4ODQxZDUzYTZlNTNhM2YyNzQ=
@@ -1,3 +1,7 @@
1
+ 0.3.6
2
+ -----
3
+ * Added method `set_current_tenant_by_subdomain_or_domain` (thx preth00nker)
4
+
1
5
  0.3.5
2
6
  -----
3
7
  * Fix to degredation introduced after 3.1 that prevented tenant_id from being set during initialization (thx jorgevaldivia)
data/README.md CHANGED
@@ -41,8 +41,11 @@ There are three ways to set the current tenant: (1) by using the subdomain to lo
41
41
  class ApplicationController < ActionController::Base
42
42
  set_current_tenant_by_subdomain(:account, :subdomain)
43
43
  end
44
+
44
45
  This tells acts_as_tenant to use the current subdomain to identify the current tenant. In addition, it tells acts_as_tenant that tenants are represented by the Account model and this model has a column named 'subdomain' which can be used to lookup the Account using the actual subdomain. If ommitted, the parameters will default to the values used above.
45
46
 
47
+ Alternatively, you could locate the tenant using the method set_current_tenant_by_subdomain_or_domain( :account, :subdomain, :domain ) which will try to match a record first by subdomain. in case it fails, by domain.
48
+
46
49
  **Setting the current tenant in a controller, manually**
47
50
 
48
51
  class ApplicationController < ActionController::Base
@@ -17,17 +17,16 @@ Gem::Specification.new do |s|
17
17
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
18
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
19
  s.require_paths = ["lib"]
20
-
20
+
21
21
  #add_runtime_dependency("rails")
22
22
  s.add_runtime_dependency('request_store', '>= 1.0.5')
23
23
  s.add_dependency('rails','>= 3.1')
24
24
  #s.add_dependency('request_store', '>= 1.0.5')
25
25
 
26
- s.add_development_dependency('rspec')
26
+ s.add_development_dependency('rspec', '>=3.0')
27
27
  s.add_development_dependency('rspec-rails')
28
28
  s.add_development_dependency('database_cleaner')
29
29
  s.add_development_dependency('sqlite3')
30
- s.add_development_dependency('debugger')
31
30
 
32
- s.add_development_dependency('sidekiq')
31
+ s.add_development_dependency('sidekiq', '>= 3.0')
33
32
  end
@@ -26,6 +26,34 @@ module ActsAsTenant
26
26
  end
27
27
  end
28
28
  end
29
+
30
+ # 01/27/2014 Christian Yerena / @preth00nker
31
+ # this method adds the possibility of use the domain as a possible second argument to find
32
+ # the current_tenant.
33
+ def set_current_tenant_by_subdomain_or_domain(tenant = :account, primary_column = :subdomain, second_column = :domain )
34
+ self.class_eval do
35
+ cattr_accessor :tenant_class, :tenant_primary_column, :tenant_second_column
36
+ end
37
+
38
+ self.tenant_class = tenant.to_s.camelcase.constantize
39
+ self.tenant_primary_column = primary_column.to_sym
40
+ self.tenant_second_column = second_column.to_sym
41
+
42
+ self.class_eval do
43
+ before_filter :find_tenant_by_subdomain_or_domain
44
+ helper_method :current_tenant
45
+
46
+ private
47
+ def find_tenant_by_subdomain_or_domain
48
+ ActsAsTenant.current_tenant = tenant_class.where(tenant_primary_column => request.subdomains.last).first || tenant_class.where(tenant_second_column => request.domain).first
49
+ end
50
+
51
+ def current_tenant
52
+ ActsAsTenant.current_tenant
53
+ end
54
+ end
55
+ end
56
+
29
57
 
30
58
  # This method sets up a method that allows manual setting of the current_tenant. This method should
31
59
  # be used in a before_filter. In addition, a helper is setup that returns the current_tenant
@@ -1,7 +1,7 @@
1
1
  module ActsAsTenant::Sidekiq
2
2
  # Get the current tenant and store in the message to be sent to Sidekiq.
3
3
  class Client
4
- def call(worker_class, msg, queue)
4
+ def call(worker_class, msg, queue, redis_pool)
5
5
  msg['acts_as_tenant'] ||=
6
6
  {
7
7
  'class' => ActsAsTenant.current_tenant.class.name,
@@ -1,3 +1,3 @@
1
1
  module ActsAsTenant
2
- VERSION = "0.3.5"
2
+ VERSION = "0.3.6"
3
3
  end
@@ -7,7 +7,7 @@ describe ActsAsTenant::Configuration do
7
7
  end
8
8
 
9
9
  it 'provides defaults' do
10
- ActsAsTenant.configuration.require_tenant.should_not be_true
10
+ expect(ActsAsTenant.configuration.require_tenant).not_to be_truthy
11
11
  end
12
12
  end
13
13
 
@@ -21,7 +21,7 @@ describe ActsAsTenant::Configuration do
21
21
  config.require_tenant = true
22
22
  end
23
23
 
24
- ActsAsTenant.configuration.require_tenant.should be_true
24
+ expect(ActsAsTenant.configuration.require_tenant).to eq(true)
25
25
  end
26
26
 
27
27
  end
@@ -5,6 +5,7 @@ ActiveRecord::Schema.define(:version => 1) do
5
5
  create_table :accounts, :force => true do |t|
6
6
  t.column :name, :string
7
7
  t.column :subdomain, :string
8
+ t.column :domain, :string
8
9
  end
9
10
 
10
11
  create_table :projects, :force => true do |t|
@@ -110,11 +111,11 @@ describe ActsAsTenant do
110
111
  end
111
112
 
112
113
  describe 'is_scoped_as_tenant should return the correct value when true' do
113
- it {Project.respond_to?(:scoped_by_tenant?).should == true}
114
+ it {expect(Project.respond_to?(:scoped_by_tenant?)).to eq(true)}
114
115
  end
115
116
 
116
117
  describe 'is_scoped_as_tenant should return the correct value when false' do
117
- it {UnscopedModel.respond_to?(:scoped_by_tenant?).should == false}
118
+ it {expect(UnscopedModel.respond_to?(:scoped_by_tenant?)).to eq(false)}
118
119
  end
119
120
 
120
121
  describe 'tenant_id should be immutable, if already set' do
@@ -123,7 +124,7 @@ describe ActsAsTenant do
123
124
  @project = @account.projects.create!(:name => 'bar')
124
125
  end
125
126
 
126
- it { lambda {@project.account_id = @account.id + 1}.should raise_error }
127
+ it { expect {@project.account_id = @account.id + 1}.to raise_error }
127
128
  end
128
129
 
129
130
  describe 'tenant_id should be mutable, if not already set' do
@@ -132,8 +133,8 @@ describe ActsAsTenant do
132
133
  @project = Project.create!(:name => 'bar')
133
134
  end
134
135
 
135
- it { @project.account_id.should be_nil }
136
- it { lambda { @project.account = @account }.should_not raise_error }
136
+ it { expect(@project.account_id).to be_nil }
137
+ it { expect { @project.account = @account }.not_to raise_error }
137
138
  end
138
139
 
139
140
  describe 'tenant_id should auto populate after initialization' do
@@ -141,7 +142,7 @@ describe ActsAsTenant do
141
142
  @account = Account.create!(:name => 'foo')
142
143
  ActsAsTenant.current_tenant = @account
143
144
  end
144
- it {Project.new.account_id.should == @account.id}
145
+ it {expect(Project.new.account_id).to eq(@account.id)}
145
146
  end
146
147
 
147
148
  describe 'Handles custom foreign_key on tenant model' do
@@ -151,7 +152,7 @@ describe ActsAsTenant do
151
152
  @custom_foreign_key_task = CustomForeignKeyTask.create!(:name => 'foo')
152
153
  end
153
154
 
154
- it { @custom_foreign_key_task.account.should == @account }
155
+ it { expect(@custom_foreign_key_task.account).to eq(@account) }
155
156
  end
156
157
 
157
158
  # Scoping models
@@ -167,8 +168,8 @@ describe ActsAsTenant do
167
168
  @projects = Project.all
168
169
  end
169
170
 
170
- it { @projects.length.should == 1 }
171
- it { @projects.should == [@project1] }
171
+ it { expect(@projects.length).to eq(1) }
172
+ it { expect(@projects).to eq([@project1]) }
172
173
  end
173
174
 
174
175
  describe 'Project.unscoped.all should return the unscoped value' do
@@ -183,7 +184,7 @@ describe ActsAsTenant do
183
184
  @projects = Project.unscoped
184
185
  end
185
186
 
186
- it { @projects.count.should == 2 }
187
+ it { expect(@projects.count).to eq(2) }
187
188
  end
188
189
 
189
190
  describe 'Querying the tenant from a scoped model without a tenant set' do
@@ -219,11 +220,11 @@ describe ActsAsTenant do
219
220
  end
220
221
 
221
222
  it 'should correctly set the tenant on the task created with current_tenant set' do
222
- @task2.account.should == @account
223
+ expect(@task2.account).to eq(@account)
223
224
  end
224
225
 
225
226
  it 'should filter out the non-tenant task from the project' do
226
- @tasks.length.should == 1
227
+ expect(@tasks.length).to eq(1)
227
228
  end
228
229
  end
229
230
 
@@ -237,17 +238,17 @@ describe ActsAsTenant do
237
238
  @task = @project2.tasks.create!(:name => 'bar')
238
239
  end
239
240
 
240
- it { @task.update_attributes(:project_id => @project1.id).should == false }
241
+ it { expect(@task.update_attributes(:project_id => @project1.id)).to eq(false) }
241
242
  end
242
243
 
243
244
  describe "Create and save an AaT-enabled child without it having a parent" do
244
245
  @account = Account.create!(:name => 'baz')
245
246
  ActsAsTenant.current_tenant = @account
246
- Task.create(:name => 'bar').valid?.should == true
247
+ it { expect(Task.create(:name => 'bar').valid?).to eq(true) }
247
248
  end
248
249
 
249
250
  describe "It should be possible to use aliased associations" do
250
- it { AliasedTask.create(:name => 'foo', :project_alias => @project2).valid?.should == true }
251
+ it { expect(AliasedTask.create(:name => 'foo', :project_alias => @project2).valid?).to eq(true) }
251
252
  end
252
253
 
253
254
  # Additional default_scopes
@@ -268,8 +269,8 @@ describe ActsAsTenant do
268
269
  end
269
270
 
270
271
  it 'should apply both the tenant scope and the user defined default_scope, including :order' do
271
- @tasks.length.should == 3
272
- @tasks.should == [@task2, @task3, @task4]
272
+ expect(@tasks.length).to eq(3)
273
+ expect(@tasks).to eq([@task2, @task3, @task4])
273
274
  end
274
275
  end
275
276
 
@@ -282,13 +283,13 @@ describe ActsAsTenant do
282
283
  end
283
284
 
284
285
  it 'should not be possible to create a duplicate within the same tenant' do
285
- Project.create(:name => 'existing_name').valid?.should == false
286
+ expect(Project.create(:name => 'existing_name').valid?).to eq(false)
286
287
  end
287
288
 
288
289
  it 'should be possible to create a duplicate outside the tenant scope' do
289
290
  account = Account.create!(:name => 'baz')
290
291
  ActsAsTenant.current_tenant = account
291
- Project.create(:name => 'bar').valid?.should == true
292
+ expect(Project.create(:name => 'bar').valid?).to eq(true)
292
293
  end
293
294
  end
294
295
 
@@ -297,8 +298,8 @@ describe ActsAsTenant do
297
298
  UniqueTask.create!(:name => 'foo', :user_defined_scope => 'unique_scope')
298
299
  end
299
300
 
300
- it { UniqueTask.create(:name => 'foo', :user_defined_scope => 'another_scope').should be_valid }
301
- it { UniqueTask.create(:name => 'foo', :user_defined_scope => 'unique_scope').should_not be_valid }
301
+ it { expect(UniqueTask.create(:name => 'foo', :user_defined_scope => 'another_scope')).to be_valid }
302
+ it { expect(UniqueTask.create(:name => 'foo', :user_defined_scope => 'unique_scope')).not_to be_valid }
302
303
  end
303
304
 
304
305
  describe 'When using validates_uniqueness_of in a NON-aat model' do
@@ -306,7 +307,7 @@ describe ActsAsTenant do
306
307
  UnscopedModel.create!(:name => 'foo')
307
308
  end
308
309
  it 'should not be possible to create duplicates' do
309
- UnscopedModel.create(:name => 'foo').valid?.should == false
310
+ expect(UnscopedModel.create(:name => 'foo').valid?).to eq(false)
310
311
  end
311
312
  end
312
313
 
@@ -316,7 +317,7 @@ describe ActsAsTenant do
316
317
  @account = Account.create!(:name => 'baz')
317
318
 
318
319
  ActsAsTenant.with_tenant(@account) do
319
- ActsAsTenant.current_tenant.should eq(@account)
320
+ expect(ActsAsTenant.current_tenant).to eq(@account)
320
321
  end
321
322
  end
322
323
 
@@ -329,7 +330,7 @@ describe ActsAsTenant do
329
330
 
330
331
  end
331
332
 
332
- ActsAsTenant.current_tenant.should eq(@account1)
333
+ expect(ActsAsTenant.current_tenant).to eq(@account1)
333
334
  end
334
335
 
335
336
  it "should return the value of the block" do
@@ -341,7 +342,7 @@ describe ActsAsTenant do
341
342
  "something"
342
343
  end
343
344
 
344
- value.should eq "something"
345
+ expect(value).to eq "something"
345
346
  end
346
347
 
347
348
  it "should raise an error when no block is provided" do
@@ -355,7 +356,7 @@ describe ActsAsTenant do
355
356
  before do
356
357
  @account1 = Account.create!(:name => 'foo')
357
358
  @project1 = @account1.projects.create!(:name => 'foobar')
358
- ActsAsTenant.configuration.stub(require_tenant: true)
359
+ allow(ActsAsTenant.configuration).to receive_messages(require_tenant: true)
359
360
  end
360
361
 
361
362
  it "should raise an error when no tenant is provided" do
@@ -14,7 +14,7 @@ describe ActsAsTenant::Sidekiq do
14
14
  ActsAsTenant.current_tenant = account
15
15
 
16
16
  msg = {}
17
- subject.call(nil, msg, nil) { }
17
+ subject.call(nil, msg, nil, nil) { }
18
18
  expect(msg).to eq message
19
19
  end
20
20
 
@@ -22,7 +22,7 @@ describe ActsAsTenant::Sidekiq do
22
22
  expect(ActsAsTenant.current_tenant).to be_nil
23
23
 
24
24
  msg = {}
25
- subject.call(nil, msg, nil) { }
25
+ subject.call(nil, msg, nil, nil) { }
26
26
  expect(msg).not_to eq message
27
27
  end
28
28
  end
@@ -54,7 +54,7 @@ describe ActsAsTenant::Sidekiq do
54
54
  describe 'Sidekiq configuration' do
55
55
  describe 'client configuration' do
56
56
  it 'includes ActsAsTenant client' do
57
- expect(Sidekiq.client_middleware.exists?(ActsAsTenant::Sidekiq::Client)).to be_true
57
+ expect(Sidekiq.client_middleware.exists?(ActsAsTenant::Sidekiq::Client)).to eq(true)
58
58
  end
59
59
  end
60
60
 
@@ -28,6 +28,6 @@ describe ApplicationController2, :type => :controller do
28
28
 
29
29
  it 'Finds the correct tenant using the filter command' do
30
30
  get :index
31
- ActsAsTenant.current_tenant.name.should eq 'account1'
31
+ expect(ActsAsTenant.current_tenant.name).to eq 'account1'
32
32
  end
33
33
  end
@@ -0,0 +1,42 @@
1
+ require "spec_helper"
2
+
3
+ #Setup test specific ApplicationController
4
+ class Account; end # this is so the spec will work in isolation
5
+
6
+ class ApplicationController < ActionController::Base
7
+ include Rails.application.routes.url_helpers
8
+ set_current_tenant_by_subdomain_or_domain
9
+ end
10
+
11
+ # Start testing
12
+ describe ApplicationController, :type => :controller do
13
+ controller do
14
+ def index
15
+ render :text => "custom called"
16
+ end
17
+ end
18
+
19
+ it 'Finds the correct tenant with a example1.com' do
20
+ @request.host = "example1.com"
21
+ expect(Account).to receive(:where).with({subdomain: nil}) {[]}
22
+ expect(Account).to receive(:where).with({domain: 'example1.com'}) {['account1']}
23
+ get :index
24
+ expect(ActsAsTenant.current_tenant).to eq 'account1'
25
+ end
26
+
27
+ it 'Finds the correct tenant with a subdomain.example.com' do
28
+ @request.host = "subdomain.example.com"
29
+ expect(Account).to receive(:where).with({subdomain: 'subdomain'}) {['account1']}
30
+ get :index
31
+ expect(ActsAsTenant.current_tenant).to eq "account1"
32
+ end
33
+
34
+ it 'Finds the correct tenant with a www.subdomain.example.com' do
35
+ @request.host = "subdomain.example.com"
36
+ expect(Account).to receive(:where).with({subdomain: 'subdomain'}) {['account1']}
37
+ get :index
38
+ expect(ActsAsTenant.current_tenant).to eq "account1"
39
+ end
40
+
41
+
42
+ end
@@ -18,15 +18,15 @@ describe ApplicationController, :type => :controller do
18
18
 
19
19
  it 'Finds the correct tenant with a subdomain.example.com' do
20
20
  @request.host = "account1.example.com"
21
- Account.should_receive(:where).with({subdomain: 'account1'}) {['account1']}
21
+ expect(Account).to receive(:where).with({subdomain: 'account1'}) {['account1']}
22
22
  get :index
23
- ActsAsTenant.current_tenant.should eq 'account1'
23
+ expect(ActsAsTenant.current_tenant).to eq 'account1'
24
24
  end
25
25
 
26
26
  it 'Finds the correct tenant with a www.subdomain.example.com' do
27
27
  @request.host = "www.account1.example.com"
28
- Account.should_receive(:where).with({subdomain: 'account1'}) {['account1']}
28
+ expect(Account).to receive(:where).with({subdomain: 'account1'}) {['account1']}
29
29
  get :index
30
- ActsAsTenant.current_tenant.should eq 'account1'
30
+ expect(ActsAsTenant.current_tenant).to eq 'account1'
31
31
  end
32
32
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: acts_as_tenant
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.5
4
+ version: 0.3.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Erwin Matthijssen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-04-01 00:00:00.000000000 Z
11
+ date: 2014-08-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: request_store
@@ -44,14 +44,14 @@ dependencies:
44
44
  requirements:
45
45
  - - ! '>='
46
46
  - !ruby/object:Gem::Version
47
- version: '0'
47
+ version: '3.0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - ! '>='
53
53
  - !ruby/object:Gem::Version
54
- version: '0'
54
+ version: '3.0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rspec-rails
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -94,34 +94,20 @@ dependencies:
94
94
  - - ! '>='
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
- - !ruby/object:Gem::Dependency
98
- name: debugger
99
- requirement: !ruby/object:Gem::Requirement
100
- requirements:
101
- - - ! '>='
102
- - !ruby/object:Gem::Version
103
- version: '0'
104
- type: :development
105
- prerelease: false
106
- version_requirements: !ruby/object:Gem::Requirement
107
- requirements:
108
- - - ! '>='
109
- - !ruby/object:Gem::Version
110
- version: '0'
111
97
  - !ruby/object:Gem::Dependency
112
98
  name: sidekiq
113
99
  requirement: !ruby/object:Gem::Requirement
114
100
  requirements:
115
101
  - - ! '>='
116
102
  - !ruby/object:Gem::Version
117
- version: '0'
103
+ version: '3.0'
118
104
  type: :development
119
105
  prerelease: false
120
106
  version_requirements: !ruby/object:Gem::Requirement
121
107
  requirements:
122
108
  - - ! '>='
123
109
  - !ruby/object:Gem::Version
124
- version: '0'
110
+ version: '3.0'
125
111
  description: Integrates multi-tenancy into a Rails application in a convenient and
126
112
  out-of-your way manner
127
113
  email:
@@ -149,6 +135,7 @@ files:
149
135
  - spec/acts_as_tenant/model_extensions_spec.rb
150
136
  - spec/acts_as_tenant/sidekiq_spec.rb
151
137
  - spec/acts_as_tenant/tenant_by_filter_spec.rb
138
+ - spec/acts_as_tenant/tenant_by_subdomain_or_domain.rb
152
139
  - spec/acts_as_tenant/tenant_by_subdomain_spec.rb
153
140
  - spec/database.yml
154
141
  - spec/spec_helper.rb
@@ -180,6 +167,7 @@ test_files:
180
167
  - spec/acts_as_tenant/model_extensions_spec.rb
181
168
  - spec/acts_as_tenant/sidekiq_spec.rb
182
169
  - spec/acts_as_tenant/tenant_by_filter_spec.rb
170
+ - spec/acts_as_tenant/tenant_by_subdomain_or_domain.rb
183
171
  - spec/acts_as_tenant/tenant_by_subdomain_spec.rb
184
172
  - spec/database.yml
185
173
  - spec/spec_helper.rb