nfse-carioca 0.0.1 → 0.1.0

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 (36) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/Berksfile +6 -0
  4. data/Berksfile.lock +28 -0
  5. data/README.md +1 -0
  6. data/Rakefile +3 -7
  7. data/Vagrantfile +58 -0
  8. data/lib/nfse_carioca.rb +5 -12
  9. data/lib/nfse_carioca/cancelar_nfse.rb +28 -22
  10. data/lib/nfse_carioca/client.rb +31 -0
  11. data/lib/nfse_carioca/configuration.rb +49 -7
  12. data/lib/nfse_carioca/gerar_nfse.rb +14 -23
  13. data/lib/nfse_carioca/gerar_nfse_xml.rb +49 -103
  14. data/lib/nfse_carioca/response.rb +27 -0
  15. data/lib/nfse_carioca/version.rb +2 -2
  16. data/nfse_carioca.gemspec +4 -4
  17. data/spec/fixtures/vcr_cassettes/NFSeCarioca_CancelarNFSe/_execute_/when_NFSeCarioca_rps_number_does_not_exist/returns_error_message.yml +253 -0
  18. data/spec/fixtures/vcr_cassettes/NFSeCarioca_CancelarNFSe/_execute_/when_NFSeCarioca_rps_number_does_not_exist/returns_false.yml +253 -0
  19. data/spec/fixtures/vcr_cassettes/NFSeCarioca_CancelarNFSe/_execute_/when_NFSeCarioca_rps_number_exist/returns_false.yml +238 -0
  20. data/spec/fixtures/vcr_cassettes/NFSeCarioca_GerarNFSe/_call/when_NFSeCarioca_rps_number_already_exist/responds_with_error.yml +296 -0
  21. data/spec/fixtures/vcr_cassettes/NFSeCarioca_GerarNFSe/_call/when_NFSeCarioca_rps_number_already_exist/responds_with_message_error_E10_.yml +296 -0
  22. data/spec/fixtures/vcr_cassettes/NFSeCarioca_GerarNFSe/_call/when_NFSeCarioca_rps_number_does_not_exist_yet/generates_NFSeCarioca.yml +340 -0
  23. data/spec/lib/nfse_carioca/cancelar_nfse_spec.rb +13 -21
  24. data/spec/lib/nfse_carioca/configuration_spec.rb +26 -26
  25. data/spec/lib/nfse_carioca/gerar_nfse_spec.rb +31 -34
  26. data/spec/spec_helper.rb +7 -0
  27. metadata +40 -38
  28. data/CHANGELOG.md +0 -7
  29. data/CONTRIBUTING.md +0 -35
  30. data/lib/nfse_carioca/cancelar_nfse_xml.rb +0 -31
  31. data/lib/nfse_carioca/models/address.rb +0 -13
  32. data/lib/nfse_carioca/models/customer.rb +0 -15
  33. data/lib/nfse_carioca/models/invoice.rb +0 -14
  34. data/lib/nfse_carioca/models/provider.rb +0 -9
  35. data/lib/nfse_carioca/nota_carioca_client.rb +0 -31
  36. data/lib/nfse_carioca/nota_carioca_response.rb +0 -20
@@ -1,17 +1,16 @@
1
- RSpec.describe NFSeCarioca::CancelarNFSe do
1
+ RSpec.describe NfseCarioca::CancelarNfse do
2
+ before do
3
+ NfseCarioca.configure do |config|
4
+ config.cnpj = "12390095000186"
5
+ config.inscricao_municipal = "04807995"
6
+ config.test_mode = true
7
+ end
8
+ end
2
9
 
3
10
  describe "#execute!" do
4
-
5
- context 'when NFSeCarioca rps number does not exist' do
11
+ context 'when NFSeCarioca rps number does not exist', :vcr do
6
12
  let(:invoice_canceller) do
7
- described_class.new(rps_number: "0018070",
8
- cnpj: "12390095000186",
9
- inscricao_municipal: "04807995")
10
- end
11
-
12
- before do
13
- mock_response = SavonResponseMock.new(:cancelar_nfse_response, soap_response("CancelarNfseRespostaE78.xml"))
14
- allow_any_instance_of(described_class).to receive(:call).and_return(mock_response)
13
+ described_class.new("1000")
15
14
  end
16
15
 
17
16
  let(:response) { invoice_canceller.execute! }
@@ -21,20 +20,13 @@ RSpec.describe NFSeCarioca::CancelarNFSe do
21
20
  end
22
21
 
23
22
  it 'returns error message' do
24
- expect(response.error_message).to eq "Informe o número correto da NFSeCarioca."
23
+ expect(response.error_message).to eq "Informe o número correto da NFSe."
25
24
  end
26
25
  end
27
26
 
28
- context 'when NFSeCarioca rps number exist' do
27
+ context 'when NFSeCarioca rps number exist', :vcr do
29
28
  let(:invoice_canceller) do
30
- described_class.new(nf_number: "00000004",
31
- cnpj: "12390095000186",
32
- inscricao_municipal: "04807995")
33
- end
34
-
35
- before do
36
- mock_response = SavonResponseMock.new(:cancelar_nfse_response, soap_response("CancelarNfseResposta.xml"))
37
- allow_any_instance_of(described_class).to receive(:call).and_return(mock_response)
29
+ described_class.new("13")
38
30
  end
39
31
 
40
32
  let(:response) { invoice_canceller.execute! }
@@ -2,48 +2,48 @@ require 'spec_helper'
2
2
 
3
3
  RSpec.describe 'Configuration' do
4
4
  context 'when not configured' do
5
- before(:each) do
6
- NFSeCarioca.configure do |config|
5
+ before do
6
+ NfseCarioca.configure do |config|
7
7
  config.test_mode = false
8
8
 
9
- config.cert_pem_path = nil
10
- config.ca_cert_pem_path = nil
11
- config.cert_key_pem_path = nil
12
- config.ssl_cert_key_password = nil
9
+ config.cert_path = nil
10
+ config.ca_cert_path = nil
11
+ config.cert_key_path = nil
12
+ config.cert_key_password = nil
13
13
  end
14
14
  end
15
15
 
16
- after(:each) do
17
- NFSeCarioca.reset
16
+ after do
17
+ NfseCarioca.reset!
18
18
  end
19
19
 
20
- it { expect(NFSeCarioca.cert_pem_path).to be_nil }
21
- it { expect(NFSeCarioca.ca_cert_pem_path).to be_nil }
22
- it { expect(NFSeCarioca.cert_key_pem_path).to be_nil }
23
- it { expect(NFSeCarioca.ssl_cert_key_password).to be_nil }
24
- it { expect(NFSeCarioca.nota_carioca_wsdl_url).to eq "https://notacarioca.rio.gov.br/WSNacional/nfse.asmx?wsdl" }
20
+ it { expect(NfseCarioca.configuration.cert_path).to be_nil }
21
+ it { expect(NfseCarioca.configuration.ca_cert_path).to be_nil }
22
+ it { expect(NfseCarioca.configuration.cert_key_path).to be_nil }
23
+ it { expect(NfseCarioca.configuration.cert_key_password).to be_nil }
24
+ it { expect(NfseCarioca.configuration.wsdl).to eq "https://notacarioca.rio.gov.br/WSNacional/nfse.asmx?wsdl" }
25
25
  end
26
26
 
27
27
  context 'when configured' do
28
- before(:each) do
29
- NFSeCarioca.configure do |config|
28
+ before do
29
+ NfseCarioca.configure do |config|
30
30
  config.test_mode = true
31
31
 
32
- config.cert_pem_path = "/my/string/path/to/certicate.pem"
33
- config.ca_cert_pem_path = "/my/string/path/to/ca/certicate.pem"
34
- config.cert_key_pem_path = "/my/string/path/to/certicate/key.pem"
35
- config.ssl_cert_key_password = "very-secure-encrypted-password"
32
+ config.cert_path = "/my/string/path/to/certicate.pem"
33
+ config.ca_cert_path = "/my/string/path/to/ca/certicate.pem"
34
+ config.cert_key_path = "/my/string/path/to/certicate/key.pem"
35
+ config.cert_key_password = "very-secure-encrypted-password"
36
36
  end
37
37
  end
38
38
 
39
- after(:each) do
40
- NFSeCarioca.reset
39
+ after do
40
+ NfseCarioca.reset!
41
41
  end
42
42
 
43
- it { expect(NFSeCarioca.cert_pem_path).to eq "/my/string/path/to/certicate.pem" }
44
- it { expect(NFSeCarioca.ca_cert_pem_path).to eq "/my/string/path/to/ca/certicate.pem" }
45
- it { expect(NFSeCarioca.cert_key_pem_path).to eq "/my/string/path/to/certicate/key.pem" }
46
- it { expect(NFSeCarioca.ssl_cert_key_password).to eq "very-secure-encrypted-password" }
47
- it { expect(NFSeCarioca.nota_carioca_wsdl_url).to eq "https://homologacao.notacarioca.rio.gov.br/WSNacional/nfse.asmx?wsdl" }
43
+ it { expect(NfseCarioca.configuration.cert_path).to eq "/my/string/path/to/certicate.pem" }
44
+ it { expect(NfseCarioca.configuration.ca_cert_path).to eq "/my/string/path/to/ca/certicate.pem" }
45
+ it { expect(NfseCarioca.configuration.cert_key_path).to eq "/my/string/path/to/certicate/key.pem" }
46
+ it { expect(NfseCarioca.configuration.cert_key_password).to eq "very-secure-encrypted-password" }
47
+ it { expect(NfseCarioca.configuration.wsdl).to eq "https://homologacao.notacarioca.rio.gov.br/WSNacional/nfse.asmx?wsdl" }
48
48
  end
49
49
  end
@@ -1,16 +1,13 @@
1
- RSpec.describe NFSeCarioca::GerarNFSe do
1
+ RSpec.describe NfseCarioca::GerarNfse do
2
2
  let(:nota_fiscal_info) do
3
3
  {
4
- identification_number: "01",
4
+ identification_number: invoice_number,
5
5
  description: "Venda do Serviço XYZ.",
6
- provider: {
7
- cnpj: "11199990000111",
8
- inscricao_municipal: "04807995"
9
- },
6
+ total: 12.0,
10
7
  customer: {
11
- kind: "Cnpj",
12
- code: "22220099000111",
13
- full_name: "XPTO Tecnologia Ltda.",
8
+ document_type: "Cnpj",
9
+ document_number: "98924674000187",
10
+ name: "XPTO Tecnologia Ltda.",
14
11
  phone_number: "2222-2222", #opcional
15
12
  email: "customer@example.com", #opcional
16
13
  address: {
@@ -26,25 +23,36 @@ RSpec.describe NFSeCarioca::GerarNFSe do
26
23
  }
27
24
  end
28
25
 
29
- let(:invoice) { NFSeCarioca::Invoice.new(nota_fiscal_info) }
26
+ let!(:invoice_generator) { described_class.new(nota_fiscal_info) }
30
27
 
31
- let!(:invoice_generator) { described_class.new(invoice) }
28
+ before do
29
+ NfseCarioca.configure do |config|
30
+ config.cnpj = "25331671000166"
31
+ config.inscricao_municipal = "04807995"
32
+ config.test_mode = true
33
+ end
34
+ end
32
35
 
33
- describe '#nfse_xml' do
34
- let(:xml) { invoice_generator.nfse_xml }
36
+ describe '#to_xml' do
37
+ let(:invoice_number) { "1024" }
38
+ let(:xml) { invoice_generator.to_xml }
35
39
 
36
40
 
37
41
  it 'contains correct identification number' do
38
- expect(xml).to include %{<Numero>01</Numero>}
42
+ expect(xml).to include %{<Numero>1024</Numero>}
39
43
  end
40
44
 
41
45
  it 'contains correct description' do
42
46
  expect(xml).to include %{<Discriminacao>Venda do Serviço XYZ.}
43
47
  end
44
48
 
49
+ it 'contains correct description' do
50
+ expect(xml).to include %{<ValorServicos>12.0</ValorServicos>}
51
+ end
52
+
45
53
  it 'contains correct provider information' do
46
54
  expect(xml).to include %{<Prestador>
47
- <Cnpj>11199990000111</Cnpj>
55
+ <Cnpj>25331671000166</Cnpj>
48
56
  <InscricaoMunicipal>04807995</InscricaoMunicipal>
49
57
  </Prestador>}
50
58
  end
@@ -53,7 +61,7 @@ RSpec.describe NFSeCarioca::GerarNFSe do
53
61
  expect(xml).to include %{
54
62
  <IdentificacaoTomador>
55
63
  <CpfCnpj>
56
- <Cnpj>22220099000111</Cnpj>
64
+ <Cnpj>98924674000187</Cnpj>
57
65
  </CpfCnpj>
58
66
  </IdentificacaoTomador>
59
67
  <RazaoSocial>XPTO Tecnologia Ltda.</RazaoSocial>
@@ -70,30 +78,19 @@ RSpec.describe NFSeCarioca::GerarNFSe do
70
78
  end
71
79
 
72
80
  describe "#call" do
73
- context 'when NFSeCarioca rps number does not exist yet' do
74
- before do
75
- mock_response = SavonResponseMock.new(:gerar_nfse_response, soap_response("GerarNfseResposta.xml"))
76
- allow_any_instance_of(described_class).to receive(:call).and_return(mock_response)
77
- end
78
-
81
+ context 'when NfseCarioca rps number does not exist yet', :vcr do
79
82
  let(:response) { invoice_generator.execute! }
83
+ let(:invoice_number) { "1034" }
80
84
 
81
- it 'generates NFSeCarioca number' do
82
- expect(response.number).to eq "4"
83
- end
84
-
85
- it 'generates NFSeCarioca verification code' do
86
- expect(response.verification_code).to eq "GSB9-BQU3"
85
+ it 'generates NfseCarioca' do
86
+ expect(response.number).to eq "21"
87
+ expect(response.verification_code).to eq "VBU7-WTNQ"
87
88
  end
88
89
  end
89
90
 
90
- context 'when NFSeCarioca rps number already exist' do
91
- before do
92
- mock_response = SavonResponseMock.new(:gerar_nfse_response, soap_response("GerarNfseRespostaE10.xml"))
93
- allow_any_instance_of(described_class).to receive(:call).and_return(mock_response)
94
- end
95
-
91
+ context 'when NfseCarioca rps number already exist', :vcr do
96
92
  let(:response) { invoice_generator.execute! }
93
+ let(:invoice_number) { "1030" }
97
94
 
98
95
  it "responds with error" do
99
96
  expect(response.error?).to be_truthy
data/spec/spec_helper.rb CHANGED
@@ -3,6 +3,13 @@ Bundler.setup
3
3
 
4
4
  require "nfse_carioca"
5
5
  require "pry"
6
+ require "vcr"
7
+
8
+ VCR.configure do |config|
9
+ config.cassette_library_dir = "spec/fixtures/vcr_cassettes"
10
+ config.hook_into :webmock
11
+ config.configure_rspec_metadata!
12
+ end
6
13
 
7
14
  Dir[File.join(File.dirname(__FILE__), '/support/**/*.rb')].each { |f| require f }
8
15
 
metadata CHANGED
@@ -1,101 +1,101 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nfse-carioca
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - LUZ Planilhas
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-01-29 00:00:00.000000000 Z
11
+ date: 2015-02-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: savon
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: 2.9.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ">="
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '0'
26
+ version: 2.9.0
27
27
  - !ruby/object:Gem::Dependency
28
- name: virtus
28
+ name: builder
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 1.0.4
33
+ version: 3.2.2
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 1.0.4
40
+ version: 3.2.2
41
41
  - !ruby/object:Gem::Dependency
42
- name: builder
42
+ name: bundler
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 3.2.2
48
- type: :runtime
47
+ version: '1.7'
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: 3.2.2
54
+ version: '1.7'
55
55
  - !ruby/object:Gem::Dependency
56
- name: activesupport
56
+ name: rake
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '4.1'
62
- type: :runtime
61
+ version: '10.0'
62
+ type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '4.1'
68
+ version: '10.0'
69
69
  - !ruby/object:Gem::Dependency
70
- name: bundler
70
+ name: rspec
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - "~>"
73
+ - - ">="
74
74
  - !ruby/object:Gem::Version
75
- version: '1.7'
75
+ version: '0'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - "~>"
80
+ - - ">="
81
81
  - !ruby/object:Gem::Version
82
- version: '1.7'
82
+ version: '0'
83
83
  - !ruby/object:Gem::Dependency
84
- name: rake
84
+ name: pry
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - "~>"
87
+ - - ">="
88
88
  - !ruby/object:Gem::Version
89
- version: '10.0'
89
+ version: '0'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - "~>"
94
+ - - ">="
95
95
  - !ruby/object:Gem::Version
96
- version: '10.0'
96
+ version: '0'
97
97
  - !ruby/object:Gem::Dependency
98
- name: rspec
98
+ name: vcr
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
101
  - - ">="
@@ -109,7 +109,7 @@ dependencies:
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0'
111
111
  - !ruby/object:Gem::Dependency
112
- name: pry
112
+ name: webmock
113
113
  requirement: !ruby/object:Gem::Requirement
114
114
  requirements:
115
115
  - - ">="
@@ -131,29 +131,31 @@ extra_rdoc_files: []
131
131
  files:
132
132
  - ".gitignore"
133
133
  - ".rspec"
134
- - CHANGELOG.md
135
- - CONTRIBUTING.md
134
+ - Berksfile
135
+ - Berksfile.lock
136
136
  - Gemfile
137
137
  - README.md
138
138
  - Rakefile
139
+ - Vagrantfile
139
140
  - lib/nfse_carioca.rb
140
141
  - lib/nfse_carioca/cancelar_nfse.rb
141
- - lib/nfse_carioca/cancelar_nfse_xml.rb
142
+ - lib/nfse_carioca/client.rb
142
143
  - lib/nfse_carioca/configuration.rb
143
144
  - lib/nfse_carioca/gerar_nfse.rb
144
145
  - lib/nfse_carioca/gerar_nfse_xml.rb
145
- - lib/nfse_carioca/models/address.rb
146
- - lib/nfse_carioca/models/customer.rb
147
- - lib/nfse_carioca/models/invoice.rb
148
- - lib/nfse_carioca/models/provider.rb
149
- - lib/nfse_carioca/nota_carioca_client.rb
150
- - lib/nfse_carioca/nota_carioca_response.rb
146
+ - lib/nfse_carioca/response.rb
151
147
  - lib/nfse_carioca/version.rb
152
148
  - nfse_carioca.gemspec
153
149
  - spec/fixtures/nota_carioca/CancelarNfseResposta.xml
154
150
  - spec/fixtures/nota_carioca/CancelarNfseRespostaE78.xml
155
151
  - spec/fixtures/nota_carioca/GerarNfseResposta.xml
156
152
  - spec/fixtures/nota_carioca/GerarNfseRespostaE10.xml
153
+ - spec/fixtures/vcr_cassettes/NFSeCarioca_CancelarNFSe/_execute_/when_NFSeCarioca_rps_number_does_not_exist/returns_error_message.yml
154
+ - spec/fixtures/vcr_cassettes/NFSeCarioca_CancelarNFSe/_execute_/when_NFSeCarioca_rps_number_does_not_exist/returns_false.yml
155
+ - spec/fixtures/vcr_cassettes/NFSeCarioca_CancelarNFSe/_execute_/when_NFSeCarioca_rps_number_exist/returns_false.yml
156
+ - spec/fixtures/vcr_cassettes/NFSeCarioca_GerarNFSe/_call/when_NFSeCarioca_rps_number_already_exist/responds_with_error.yml
157
+ - spec/fixtures/vcr_cassettes/NFSeCarioca_GerarNFSe/_call/when_NFSeCarioca_rps_number_already_exist/responds_with_message_error_E10_.yml
158
+ - spec/fixtures/vcr_cassettes/NFSeCarioca_GerarNFSe/_call/when_NFSeCarioca_rps_number_does_not_exist_yet/generates_NFSeCarioca.yml
157
159
  - spec/lib/nfse_carioca/cancelar_nfse_spec.rb
158
160
  - spec/lib/nfse_carioca/configuration_spec.rb
159
161
  - spec/lib/nfse_carioca/gerar_nfse_spec.rb
data/CHANGELOG.md DELETED
@@ -1,7 +0,0 @@
1
- ## 0.0.1 (Unreleased)
2
-
3
- ### features
4
-
5
- ### improvements
6
-
7
- ### bug fixes
data/CONTRIBUTING.md DELETED
@@ -1,35 +0,0 @@
1
- # Contributing
2
-
3
- We love pull requests. Here's a quick guide:
4
-
5
- 1. Fork the repo.
6
-
7
- 2. Create your feature branch (`git checkout -b my-new-feature`)
8
-
9
- 3. Run the tests. We only take pull requests with passing tests, and it's great
10
- to know that you have a clean slate: `bundle && rake`
11
-
12
- 4. Add a test for your change. Only refactoring and documentation changes
13
- require no new tests. If you are adding functionality or fixing a bug, we need
14
- a test!
15
-
16
- 5. Make the test pass.
17
-
18
- 6. Update [CHANGELOG.md][changelog] with a brief description of your changes under the `unreleased` heading.
19
-
20
- 7. Commit your changes (`git commit -am 'Added some feature'`)
21
-
22
- 8. Push to the branch (`git push origin my-new-feature`)
23
-
24
- 9. Create new Pull Request
25
-
26
- At this point you're waiting on us. We like to at least give you feedback, if not just
27
- accept it, within a few days, depending on our internal priorities.
28
-
29
- Some things that will increase the chance that your pull request is accepted is to follow the practices described on [Ruby style guide][rubysg], [Rails style guide][railssg] and [Better Specs][bs].
30
-
31
-
32
- [changelog]: https://github.com/luzvc/nfse-carioca/blob/master/CHANGELOG.md
33
- [rubysg]: https://github.com/bbatsov/ruby-style-guide
34
- [railssg]: https://github.com/bbatsov/rails-style-guide
35
- [bs]: http://betterspecs.org/