edools_mymoip 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +7 -0
  2. data/.document +5 -0
  3. data/.gitignore +15 -0
  4. data/.travis.yml +4 -0
  5. data/CHANGELOG.md +128 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE.txt +20 -0
  8. data/README.md +3 -0
  9. data/Rakefile +10 -0
  10. data/lib/mymoip.rb +57 -0
  11. data/lib/mymoip/bank_debit.rb +22 -0
  12. data/lib/mymoip/commission.rb +54 -0
  13. data/lib/mymoip/credit_card.rb +73 -0
  14. data/lib/mymoip/exceptions.rb +15 -0
  15. data/lib/mymoip/formatter.rb +23 -0
  16. data/lib/mymoip/instruction.rb +134 -0
  17. data/lib/mymoip/json_parser.rb +11 -0
  18. data/lib/mymoip/payer.rb +75 -0
  19. data/lib/mymoip/payment.rb +10 -0
  20. data/lib/mymoip/payment_methods.rb +46 -0
  21. data/lib/mymoip/payment_slip.rb +74 -0
  22. data/lib/mymoip/payments/bank_debit_payment.rb +27 -0
  23. data/lib/mymoip/payments/credit_card_payment.rb +53 -0
  24. data/lib/mymoip/payments/payment_slip_payment.rb +12 -0
  25. data/lib/mymoip/purchase.rb +40 -0
  26. data/lib/mymoip/request.rb +34 -0
  27. data/lib/mymoip/requests/payment_request.rb +53 -0
  28. data/lib/mymoip/requests/transparent_request.rb +36 -0
  29. data/lib/mymoip/validators.rb +12 -0
  30. data/lib/mymoip/version.rb +3 -0
  31. data/mymoip.gemspec +30 -0
  32. data/test/fixtures/fixture.rb +73 -0
  33. data/test/fixtures/vcr_cassettes/payment_request.yml +37 -0
  34. data/test/fixtures/vcr_cassettes/payment_request_with_payment_slip.yml +32 -0
  35. data/test/fixtures/vcr_cassettes/transparent_request.yml +34 -0
  36. data/test/fixtures/vcr_cassettes/transparent_request_with_commissions.yml +35 -0
  37. data/test/lib/test_bank_debit.rb +36 -0
  38. data/test/lib/test_bank_debit_payment.rb +32 -0
  39. data/test/lib/test_commission.rb +121 -0
  40. data/test/lib/test_credit_card_payment.rb +107 -0
  41. data/test/lib/test_creditcard.rb +206 -0
  42. data/test/lib/test_formatter.rb +49 -0
  43. data/test/lib/test_instruction.rb +243 -0
  44. data/test/lib/test_mymoip.rb +79 -0
  45. data/test/lib/test_payer.rb +249 -0
  46. data/test/lib/test_payment.rb +15 -0
  47. data/test/lib/test_payment_methods.rb +54 -0
  48. data/test/lib/test_payment_request.rb +120 -0
  49. data/test/lib/test_payment_slip.rb +78 -0
  50. data/test/lib/test_payment_slip_payment.rb +8 -0
  51. data/test/lib/test_purchase.rb +109 -0
  52. data/test/lib/test_request.rb +88 -0
  53. data/test/lib/test_transparent_request.rb +71 -0
  54. data/test/lib/test_validators.rb +13 -0
  55. data/test/live_test.rb +4 -0
  56. data/test/test_helper.rb +17 -0
  57. metadata +251 -0
@@ -0,0 +1,40 @@
1
+ module MyMoip
2
+ class Purchase
3
+ attr_accessor :id, :price, :credit_card, :payer, :reason
4
+ attr_reader :code
5
+
6
+ def initialize(attrs)
7
+ @id = attrs.fetch(:id) { rand }
8
+ @price = attrs.fetch(:price)
9
+ @credit_card = MyMoip::CreditCard.new(attrs.fetch(:credit_card))
10
+ @payer = MyMoip::Payer.new(attrs.fetch(:payer))
11
+ @reason = attrs.fetch(:reason)
12
+ end
13
+
14
+ def checkout!
15
+ authorization = get_authorization!
16
+ payment = MyMoip::CreditCardPayment.new(@credit_card,
17
+ installments: 1)
18
+ request = MyMoip::PaymentRequest.new(@id)
19
+ request.api_call(payment, token: authorization.token)
20
+
21
+ @code = request.code
22
+ request.success?
23
+ end
24
+
25
+ private
26
+
27
+ def get_authorization!
28
+ instruction = MyMoip::Instruction.new(
29
+ id: @id,
30
+ payment_reason: @reason,
31
+ values: [@price],
32
+ payer: @payer
33
+ )
34
+
35
+ request = MyMoip::TransparentRequest.new(@id)
36
+ request.api_call(instruction)
37
+ request
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,34 @@
1
+ module MyMoip
2
+ class Request
3
+ include HTTParty
4
+
5
+ attr_reader :id, :response
6
+
7
+ def initialize(id)
8
+ @id = id
9
+ end
10
+
11
+ def api_call(params, opts = {})
12
+ opts[:logger] ||= MyMoip.logger
13
+ opts[:username] ||= MyMoip.token
14
+ opts[:password] ||= MyMoip.key
15
+
16
+ unless opts[:username].present? && opts[:password].present?
17
+ MyMoip.ensure_key_and_token_set!
18
+ end
19
+
20
+ opts[:logger].info "New #{self.class} being sent to MoIP."
21
+ opts[:logger].debug "#{self.class} of ##{@id} with #{params.inspect}"
22
+
23
+ url = MyMoip.api_url + params.delete(:path)
24
+ params[:basic_auth] = { username: opts[:username], password: opts[:password] }
25
+
26
+ @response = HTTParty.send params.delete(:http_method), url, params
27
+
28
+ opts[:logger].debug "#{self.class} of ##{@id} to #{url} had response #{@response.inspect}"
29
+ end
30
+ end
31
+ end
32
+
33
+ requests = Dir[File.dirname(__FILE__) + "/requests/*.rb"]
34
+ requests.each { |f| require f }
@@ -0,0 +1,53 @@
1
+ module MyMoip
2
+ class PaymentRequest < Request
3
+
4
+ HTTP_METHOD = :get
5
+ PATH = "/rest/pagamento?callback=?"
6
+ REQUIRES_AUTH = false
7
+ FORMAT = :json
8
+ PAYMENT_SLIP_PATH = "/Instrucao.do?token="
9
+
10
+ attr_reader :token
11
+
12
+ def api_call(data, opts)
13
+ @token = opts[:token]
14
+
15
+ opts[:referer_url] ||= MyMoip.default_referer_url
16
+ opts[:parser] ||= MyMoip::JsonParser
17
+
18
+ json = JSON.generate({
19
+ pagamentoWidget: {
20
+ referer: opts[:referer_url],
21
+ token: token,
22
+ dadosPagamento: data.to_json
23
+ }
24
+ })
25
+
26
+ params = {
27
+ query: { pagamentoWidget: json },
28
+ http_method: HTTP_METHOD,
29
+ requires_auth: REQUIRES_AUTH,
30
+ path: PATH,
31
+ format: FORMAT
32
+ }
33
+ params[:parser] = opts.delete(:parser) unless opts[:parser].nil?
34
+
35
+ super(params, opts)
36
+ end
37
+
38
+ def success?
39
+ @response && @response["StatusPagamento"] == "Sucesso"
40
+ end
41
+
42
+ def url
43
+ MyMoip.api_url + PAYMENT_SLIP_PATH + token if success?
44
+ end
45
+
46
+ def code
47
+ @response["CodigoMoIP"]
48
+ rescue NoMethodError => e
49
+ nil
50
+ end
51
+
52
+ end
53
+ end
@@ -0,0 +1,36 @@
1
+ module MyMoip
2
+ class TransparentRequest < Request
3
+
4
+ HTTP_METHOD = :post
5
+ PATH = "/ws/alpha/EnviarInstrucao/Unica"
6
+ REQUIRES_AUTH = true
7
+
8
+ def api_call(data, opts = {})
9
+ params = {
10
+ body: data.to_xml,
11
+ http_method: HTTP_METHOD,
12
+ requires_auth: REQUIRES_AUTH,
13
+ path: PATH
14
+ }
15
+
16
+ super(params, opts)
17
+ end
18
+
19
+ def success?
20
+ @response["EnviarInstrucaoUnicaResponse"]["Resposta"]["Status"] == "Sucesso"
21
+ rescue NoMethodError
22
+ false
23
+ end
24
+
25
+ def token
26
+ @response["EnviarInstrucaoUnicaResponse"]["Resposta"]["Token"]
27
+ rescue NoMethodError
28
+ end
29
+
30
+ def id
31
+ @response["EnviarInstrucaoUnicaResponse"]["Resposta"]["ID"]
32
+ rescue NoMethodError
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,12 @@
1
+ module MyMoip
2
+ module Validators
3
+ private
4
+
5
+ def valid_url?(url)
6
+ uri = URI.parse(url)
7
+ uri.kind_of?(URI::HTTP) or uri.kind_of?(URI::HTTPS)
8
+ rescue URI::InvalidURIError
9
+ false
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,3 @@
1
+ module MyMoip
2
+ VERSION = '0.8.1'
3
+ end
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'mymoip/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "edools_mymoip"
8
+ spec.version = MyMoip::VERSION
9
+ spec.authors = ["Vinicius Magalhaes"]
10
+ spec.email = ["viniciusmkm@gmail.com"]
11
+ spec.description = %q{The easier way to use Moip's transparent checkout.}
12
+ spec.summary = %q{MoIP transactions in a gem to call your own.}
13
+ spec.homepage = "https://github.com/Edools/mymoip"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "activemodel"
22
+ spec.add_dependency "builder"
23
+ spec.add_dependency "httparty"
24
+ spec.add_development_dependency "bundler", "~> 1.3"
25
+ spec.add_development_dependency "mocha"
26
+ spec.add_development_dependency "rake"
27
+ spec.add_development_dependency "rdoc", "~> 3.12"
28
+ spec.add_development_dependency "vcr"
29
+ spec.add_development_dependency "webmock"
30
+ end
@@ -0,0 +1,73 @@
1
+ class Fixture
2
+ def self.payer(params = {})
3
+ params = {
4
+ id: "your_own_payer_id",
5
+ name: "Juquinha da Rocha",
6
+ email: "juquinha@rocha.com",
7
+ address_street: "Felipe Neri",
8
+ address_street_number: "406",
9
+ address_street_extra: "Sala 501",
10
+ address_neighbourhood: "Auxiliadora",
11
+ address_city: "Porto Alegre",
12
+ address_state: "RS",
13
+ address_country: "BRA",
14
+ address_cep: "90440-150",
15
+ address_phone: "(51)3040-5060"
16
+ }.merge(params)
17
+ MyMoip::Payer.new(params)
18
+ end
19
+
20
+ def self.instruction(params={})
21
+ params = {
22
+ id: "your_own_instruction_id",
23
+ payment_reason: "some payment_reason",
24
+ values: [100.0, 200.0],
25
+ payer: payer,
26
+ installments: [
27
+ {min: 2, max: 12, forward_taxes: true, fee: 1.99}
28
+ ]
29
+ }.merge(params)
30
+ MyMoip::Instruction.new(params)
31
+ end
32
+
33
+ def self.credit_card(params = {})
34
+ params = {
35
+ logo: :visa,
36
+ card_number: "4916654211627608",
37
+ expiration_date: "06/15",
38
+ security_code: "000",
39
+ owner_name: "Juquinha da Rocha",
40
+ owner_birthday: Date.new(1984, 11, 3),
41
+ owner_phone: "(51)3040-5060",
42
+ owner_cpf: "522.116.706-95"
43
+ }.merge(params)
44
+ MyMoip::CreditCard.new(params)
45
+ end
46
+
47
+ def self.commission(params = {})
48
+ params = {
49
+ reason: 'Because we can',
50
+ receiver_login: 'commissioned_indentifier',
51
+ fixed_value: 23.4
52
+ }.merge(params)
53
+ MyMoip::Commission.new(params)
54
+ end
55
+
56
+ def self.payment_slip(params = {})
57
+ params = {
58
+ expiration_date: DateTime.new(2020, 1, 1),
59
+ expiration_days: 7,
60
+ expiration_days_type: :business_day,
61
+ instruction_line_1: 'Line 1',
62
+ instruction_line_2: 'Line 2',
63
+ instruction_line_3: 'Line 3',
64
+ logo_url: 'http://www.myurl.com/logo.png'
65
+ }.merge(params)
66
+ MyMoip::PaymentSlip.new(params)
67
+ end
68
+
69
+ def self.bank_debit(params = {})
70
+ params = { bank: :itau }.merge(params)
71
+ MyMoip::BankDebit.new(params)
72
+ end
73
+ end
@@ -0,0 +1,37 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: https://YOUR_MOIP_TOKEN:YOUR_MOIP_KEY@desenvolvedor.moip.com.br/sandbox/rest/pagamento?callback=?&pagamentoWidget=%7B%22pagamentoWidget%22:%7B%22referer%22:%22http://localhost/default%22,%22token%22:%22D2O0U1W2K1P1D1R2C2E3H0N3L5X3A2E4E3X0X030T090T0A1D1V2T8X500E3%22,%22dadosPagamento%22:%7B%22Forma%22:%22CartaoCredito%22,%22Parcelas%22:1,%22CartaoCredito%22:%7B%22Numero%22:%224916654211627608%22,%22Expiracao%22:%2206/15%22,%22CodigoSeguranca%22:%22000%22,%22Portador%22:%7B%22Nome%22:%22Juquinha%20da%20Rocha%22,%22DataNascimento%22:%2203/11/1984%22,%22Telefone%22:%22(51)3040-5060%22,%22Identidade%22:%22522.116.706-95%22%7D%7D,%22Instituicao%22:%22Visa%22,%22Recebimento%22:%22AVista%22%7D%7D%7D
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers: {}
10
+ response:
11
+ status:
12
+ code: 200
13
+ message: OK
14
+ headers:
15
+ Date:
16
+ - Tue, 13 Nov 2012 01:03:56 GMT
17
+ Server:
18
+ - Apache/2.2.23 (CentOS)
19
+ Content-Length:
20
+ - '272'
21
+ Vary:
22
+ - Accept-Encoding
23
+ Content-Type:
24
+ - application/json
25
+ body:
26
+ encoding: ASCII-8BIT
27
+ string: !binary |-
28
+ Pyh7IlN0YXR1cyI6IkVtQW5hbGlzZSIsIkNvZGlnbyI6MCwiQ29kaWdvUmV0
29
+ b3JubyI6IiIsIlRheGFNb0lQIjoiMTUuMTkiLCJTdGF0dXNQYWdhbWVudG8i
30
+ OiJTdWNlc3NvIiwiQ2xhc3NpZmljYWNhbyI6eyJDb2RpZ28iOjk5OSwiRGVz
31
+ Y3JpY2FvIjoiTsOjbyBzdXBvcnRhZG8gbm8gYW1iaWVudGUgU2FuZGJveCJ9
32
+ LCJDb2RpZ29Nb0lQIjoxMDI1OTYsIk1lbnNhZ2VtIjoiUmVxdWlzacOnw6Nv
33
+ IHByb2Nlc3NhZGEgY29tIHN1Y2Vzc28iLCJUb3RhbFBhZ28iOiIyMDAuMDAi
34
+ fSk=
35
+ http_version:
36
+ recorded_at: Tue, 13 Nov 2012 01:01:06 GMT
37
+ recorded_with: VCR 2.2.5
@@ -0,0 +1,32 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: https://YOUR_MOIP_TOKEN:YOUR_MOIP_KEY@desenvolvedor.moip.com.br/sandbox/rest/pagamento?callback=?&pagamentoWidget=%7B%22pagamentoWidget%22:%7B%22referer%22:%22http://localhost/default%22,%22token%22:%22D2O0U1W2K1P1D1R2C2E3H0N3L5X3A2E4E3X0X030T090T0A1D1V2T8X500E3%22,%22dadosPagamento%22:%7B%22Forma%22:%22BoletoBancario%22%7D%7D%7D
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers: {}
10
+ response:
11
+ status:
12
+ code: 200
13
+ message: OK
14
+ headers:
15
+ Date:
16
+ - Tue, 24 Sep 2013 13:46:21 GMT
17
+ Server:
18
+ - Apache
19
+ Content-Length:
20
+ - '92'
21
+ Vary:
22
+ - Accept-Encoding
23
+ Content-Type:
24
+ - application/json
25
+ body:
26
+ encoding: ASCII-8BIT
27
+ string: !binary |-
28
+ Pyh7IkNvZGlnbyI6MCwiU3RhdHVzUGFnYW1lbnRvIjoiU3VjZXNzbyIsIk1l
29
+ bnNhZ2VtIjoiUmVxdWlzaW8gcHJvY2Vzc2FkYSBjb20gc3VjZXNzbyJ9KQ==
30
+ http_version:
31
+ recorded_at: Tue, 24 Sep 2013 13:46:22 GMT
32
+ recorded_with: VCR 2.5.0
@@ -0,0 +1,34 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: https://YOUR_MOIP_TOKEN:YOUR_MOIP_KEY@desenvolvedor.moip.com.br/sandbox/ws/alpha/EnviarInstrucao/Unica
6
+ body:
7
+ encoding: US-ASCII
8
+ string: <EnviarInstrucao><InstrucaoUnica TipoValidacao="Transparente"><Razao>some
9
+ payment_reason</Razao><Valores><Valor moeda="BRL">100.00</Valor><Valor moeda="BRL">200.00</Valor></Valores><IdProprio>my_new_instruction_id</IdProprio><Pagador><Nome>Juquinha
10
+ da Rocha</Nome><Email>juquinha@rocha.com</Email><IdPagador>your_own_payer_id</IdPagador><EnderecoCobranca><Logradouro>Felipe
11
+ Neri</Logradouro><Numero>406</Numero><Complemento>Sala 501</Complemento><Bairro>Auxiliadora</Bairro><Cidade>Porto
12
+ Alegre</Cidade><Estado>RS</Estado><Pais>BRA</Pais><CEP>90440-150</CEP><TelefoneFixo>(51)3040-5060</TelefoneFixo></EnderecoCobranca></Pagador></InstrucaoUnica></EnviarInstrucao>
13
+ headers: {}
14
+ response:
15
+ status:
16
+ code: 200
17
+ message: OK
18
+ headers:
19
+ Date:
20
+ - Tue, 13 Nov 2012 01:03:52 GMT
21
+ Server:
22
+ - Apache/2.2.23 (CentOS)
23
+ Content-Length:
24
+ - '273'
25
+ Vary:
26
+ - Accept-Encoding
27
+ Content-Type:
28
+ - text/xml;charset=UTF-8
29
+ body:
30
+ encoding: US-ASCII
31
+ string: <ns1:EnviarInstrucaoUnicaResponse xmlns:ns1="http://www.moip.com.br/ws/alpha/"><Resposta><ID>201210171118501100000001102691</ID><Status>Sucesso</Status><Token>D2O0U1W2K1P1D1R2C2E3H0N3L5X3A2E4E3X0X030T090T0A1D1V2T8X500E3</Token></Resposta></ns1:EnviarInstrucaoUnicaResponse>
32
+ http_version:
33
+ recorded_at: Tue, 13 Nov 2012 01:01:02 GMT
34
+ recorded_with: VCR 2.2.5
@@ -0,0 +1,35 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: https://YOUR_MOIP_TOKEN:YOUR_MOIP_KEY@desenvolvedor.moip.com.br/sandbox/ws/alpha/EnviarInstrucao/Unica
6
+ body:
7
+ encoding: US-ASCII
8
+ string: <EnviarInstrucao><InstrucaoUnica TipoValidacao="Transparente"><Razao>some
9
+ payment_reason</Razao><Valores><Valor moeda="BRL">100.00</Valor><Valor moeda="BRL">200.00</Valor></Valores><IdProprio>your_own_instruction_id</IdProprio><Comissoes><Comissionamento><Razao>Because
10
+ we can</Razao><Comissionado><LoginMoIP>commissioned_id</LoginMoIP></Comissionado><ValorFixo>5</ValorFixo></Comissionamento></Comissoes><Pagador><Nome>Juquinha
11
+ da Rocha</Nome><Email>juquinha@rocha.com</Email><IdPagador>your_own_payer_id</IdPagador><EnderecoCobranca><Logradouro>Felipe
12
+ Neri</Logradouro><Numero>406</Numero><Complemento>Sala 501</Complemento><Bairro>Auxiliadora</Bairro><Cidade>Porto
13
+ Alegre</Cidade><Estado>RS</Estado><Pais>BRA</Pais><CEP>90440-150</CEP><TelefoneFixo>(51)3040-5060</TelefoneFixo></EnderecoCobranca></Pagador></InstrucaoUnica></EnviarInstrucao>
14
+ headers: {}
15
+ response:
16
+ status:
17
+ code: 200
18
+ message: OK
19
+ headers:
20
+ Date:
21
+ - Wed, 28 Nov 2012 14:20:49 GMT
22
+ Server:
23
+ - Apache/2.2.23 (CentOS)
24
+ Content-Length:
25
+ - '273'
26
+ Vary:
27
+ - Accept-Encoding
28
+ Content-Type:
29
+ - text/xml;charset=UTF-8
30
+ body:
31
+ encoding: US-ASCII
32
+ string: <ns1:EnviarInstrucaoUnicaResponse xmlns:ns1="http://www.moip.com.br/ws/alpha/"><Resposta><ID>YOUR_REQUEST_ID</ID><Status>Sucesso</Status><Token>YOUR_MOIP_TOKEN</Token></Resposta></ns1:EnviarInstrucaoUnicaResponse>
33
+ http_version:
34
+ recorded_at: Wed, 28 Nov 2012 14:17:23 GMT
35
+ recorded_with: VCR 2.2.5
@@ -0,0 +1,36 @@
1
+ require_relative '../test_helper'
2
+
3
+ class TestBankDebit < Test::Unit::TestCase
4
+ def test_initialization_and_setters
5
+ subject = MyMoip::BankDebit.new(bank: :itau)
6
+ assert_equal :itau, subject.bank
7
+ end
8
+
9
+ def test_initialization_and_setters_with_string_keys
10
+ subject = MyMoip::BankDebit.new('bank' => :itau)
11
+ assert_equal :itau, subject.bank
12
+ end
13
+
14
+ def test_validate_presence_of_bank_attribute
15
+ subject = Fixture.bank_debit(bank: nil)
16
+ assert subject.invalid? && subject.errors[:bank].present?,
17
+ 'should be invalid without a bank name'
18
+ end
19
+
20
+ def test_converts_bank_string_to_symbol
21
+ subject = Fixture.bank_debit(bank: "itau")
22
+ assert_equal :itau, subject.bank
23
+ end
24
+
25
+ def test_accepts_any_bank_from_available_banks_constant
26
+ MyMoip::BankDebit::AVAILABLE_BANKS.each do |bank|
27
+ subject = Fixture.bank_debit(bank: bank)
28
+ assert subject.valid?, 'should be valid'
29
+ end
30
+ end
31
+
32
+ def test_dont_accept_bank_out_of_available_banks_constant
33
+ subject = Fixture.bank_debit(bank: :unavailable_bank)
34
+ assert subject.invalid? && subject.errors[:bank].present?, 'should not be valid'
35
+ end
36
+ end