rnfse 0.0.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 (47) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +25 -0
  3. data/.rspec +1 -0
  4. data/.travis.yml +14 -0
  5. data/CHANGELOG.md +0 -0
  6. data/Gemfile +3 -0
  7. data/Guardfile +15 -0
  8. data/LICENSE.md +41 -0
  9. data/Procfile +1 -0
  10. data/README.md +59 -0
  11. data/Rakefile +12 -0
  12. data/lib/provedores.yml +11 -0
  13. data/lib/rnfse/api/abrasf_1_0/definitions.json +374 -0
  14. data/lib/rnfse/api/abrasf_1_0/recepcionar_lote_rps.json +46 -0
  15. data/lib/rnfse/api/abrasf_1_0.rb +55 -0
  16. data/lib/rnfse/api/iss_net_1_0.rb +6 -0
  17. data/lib/rnfse/api.rb +101 -0
  18. data/lib/rnfse/configuration.rb +18 -0
  19. data/lib/rnfse/core.rb +9 -0
  20. data/lib/rnfse/hash.rb +148 -0
  21. data/lib/rnfse/string.rb +36 -0
  22. data/lib/rnfse/version.rb +3 -0
  23. data/lib/rnfse/xml_builder/abrasf_1_0.rb +95 -0
  24. data/lib/rnfse/xml_builder/iss_net_1_0.rb +70 -0
  25. data/lib/rnfse/xml_builder.rb +10 -0
  26. data/lib/rnfse.rb +22 -0
  27. data/rnfse.gemspec +60 -0
  28. data/spec/api/abrasf_1_0/recepcionar_lote_rps_json_spec.rb +123 -0
  29. data/spec/api/abrasf_1_0_spec.rb +26 -0
  30. data/spec/api/iss_net_1_0_spec.rb +99 -0
  31. data/spec/api_spec.rb +65 -0
  32. data/spec/core_spec.rb +35 -0
  33. data/spec/fixtures/abrasf_1_0/enviar_lote_rps_envio.xml +47 -0
  34. data/spec/fixtures/abrasf_1_0/nfse.xsd +771 -0
  35. data/spec/fixtures/certificate.pem +18 -0
  36. data/spec/fixtures/iss_net_1_0/enviar_lote_rps_envio.xml +75 -0
  37. data/spec/fixtures/key.pem +15 -0
  38. data/spec/fixtures/vcr_cassettes/iss_net_1_0_recepcionar_lote_rps.yml +214 -0
  39. data/spec/hash_spec.rb +205 -0
  40. data/spec/spec_helper.rb +29 -0
  41. data/spec/string_spec.rb +38 -0
  42. data/spec/support/.keep +0 -0
  43. data/spec/version_spec.rb +5 -0
  44. data/spec/xml_builder/abrasf_1_0_spec.rb +63 -0
  45. data/spec/xml_builder/iss_net_1_0_spec.rb +86 -0
  46. data/spec/xml_builder_spec.rb +14 -0
  47. metadata +433 -0
@@ -0,0 +1,55 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'json-schema'
3
+
4
+ module Rnfse::API::Abrasf10
5
+
6
+ module ClassMethods
7
+
8
+ def recepcionar_lote_rps(hash = {})
9
+ file = get_filepath('recepcionar_lote_rps.json')
10
+ json = Rnfse::Hash.camelize_and_symbolize_keys(hash, false).to_json
11
+ errors = JSON::Validator.fully_validate(file, json)
12
+ if errors.empty?
13
+ xml = xml_builder.build_recepcionar_lote_rps_xml(hash)
14
+ xml.sign!(certificate: File.read(self.certificate), key: File.read(self.key))
15
+ response = self.soap_client.call(
16
+ :recepcionar_lote_rps,
17
+ soap_action: 'RecepcionarLoteRps',
18
+ message_tag: 'RecepcionarLoteRps',
19
+ message: { :'xml!' => "<![CDATA[#{xml}]]>" })
20
+ parse_response(response)
21
+ else
22
+ raise ArgumentError, errors, caller
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def parse_response(response)
29
+ hash = Rnfse::Hash.new(response.body)
30
+ lote_rps_response = hash[:recepcionar_lote_rps_response]
31
+ if !lote_rps_response.empty? and lote_rps_response[:recepcionar_lote_rps_result]
32
+ xml = hash[:recepcionar_lote_rps_response][:recepcionar_lote_rps_result]
33
+ hash[:recepcionar_lote_rps_response][:recepcionar_lote_rps_result] =
34
+ Nori.new.parse(xml)
35
+ end
36
+ hash.underscore_and_symbolize_keys
37
+ end
38
+
39
+ def get_filepath(filename)
40
+ File.join(File.expand_path(File.dirname(__FILE__)), 'abrasf_1_0', filename)
41
+ end
42
+
43
+ end
44
+
45
+ extend ClassMethods
46
+
47
+ def self.extended(base)
48
+ base.extend(ClassMethods)
49
+ end
50
+
51
+ def self.included(base)
52
+ base.send(:include, ClassMethods)
53
+ end
54
+
55
+ end
@@ -0,0 +1,6 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'json-schema'
3
+
4
+ module Rnfse::API::IssNet10
5
+ include Rnfse::API::Abrasf10
6
+ end
data/lib/rnfse/api.rb ADDED
@@ -0,0 +1,101 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module Rnfse
4
+
5
+ class API
6
+ attr_accessor :namespace
7
+ attr_accessor :endpoint
8
+ attr_accessor :api
9
+ attr_accessor :certificate
10
+ attr_accessor :key
11
+ attr_accessor :xml_builder
12
+ attr_accessor :soap_client
13
+ attr_accessor :verbose
14
+
15
+ def initialize(options = {})
16
+ options = load_options(options)
17
+
18
+ file = Pathname.new(File.expand_path('../..', __FILE__))
19
+ provedores = YAML.load_file(file.join('provedores.yml'))
20
+
21
+ case
22
+ when has_options(options, 'provedor', 'homologacao')
23
+ provedor = provedores['homologacao'][options['provedor'].to_s]
24
+ raise ArgumentError, 'provedor de homologação inexistente', caller if provedor.nil?
25
+ self.namespace = provedor['namespace']
26
+ self.endpoint = provedor['endpoint']
27
+ self.api = provedor['api']
28
+
29
+ when has_options(options, 'provedor', 'municipio')
30
+ provedor = provedores['producao'][options['provedor'].to_s]
31
+ raise ArgumentError, 'provedor inexistente', caller if provedor.nil?
32
+ self.namespace = provedor['namespace']
33
+ self.endpoint = provedor['endpoint'] % { municipio: options['municipio'] }
34
+ self.api = provedor['api']
35
+
36
+ when has_options(options, 'padrao', 'namespace', 'endpoint')
37
+ self.namespace = options['namespace'].to_s
38
+ self.endpoint = options['endpoint'].to_s
39
+ self.api = options['padrao'].to_s
40
+
41
+ else
42
+ raise ArgumentError, 'opções inválidas', caller
43
+ end
44
+
45
+ if has_options(options, 'certificate', 'key')
46
+ self.certificate = options['certificate']
47
+ self.key = options['key']
48
+ else
49
+ raise ArgumentError, 'opções de assinatura digital faltando', caller
50
+ end
51
+
52
+ self.verbose = options['verbose'] || false
53
+ self.xml_builder = options['xml_builder'] || XMLBuilder.new(padrao: self.api)
54
+ self.soap_client = options['soap_client'] || savon_client
55
+
56
+ extend self.class.const_get(String.camelize(self.api))
57
+ end
58
+
59
+ private
60
+
61
+ def savon_client
62
+ savon_hash = {
63
+ soap_version: 2,
64
+ env_namespace: :soap,
65
+ namespace_identifier: nil,
66
+ ssl_verify_mode: :peer,
67
+ ssl_cert_file: self.certificate,
68
+ ssl_cert_key_file: self.key,
69
+ endpoint: self.endpoint,
70
+ namespace: self.namespace
71
+ }
72
+
73
+ savon_hash = savon_hash.merge(
74
+ log: true,
75
+ log_level: :debug,
76
+ pretty_print_xml: true
77
+ ) if self.verbose
78
+
79
+ Savon.client(savon_hash)
80
+ end
81
+
82
+ def has_options(hash, *options)
83
+ options.each { |option| return false if hash[option].nil? }
84
+ end
85
+
86
+ def load_options(hash)
87
+ hash = Rnfse::Hash.new(hash)
88
+ hash = hash.stringify_keys
89
+ config = Rnfse::Configuration.instance
90
+
91
+ attrs = ['provedor', 'municipio', 'namespace', 'endpoint',
92
+ 'verbose', 'api', 'certificate', 'key', 'xml_builder',
93
+ 'soap_client', 'verbose', 'homologacao']
94
+ attrs.each do |attr|
95
+ hash[attr] = config.send(attr) if hash[attr].nil?
96
+ end
97
+ hash
98
+ end
99
+
100
+ end
101
+ end
@@ -0,0 +1,18 @@
1
+ module Rnfse
2
+ class Configuration
3
+ include Singleton
4
+
5
+ attr_accessor :provedor
6
+ attr_accessor :municipio
7
+ attr_accessor :namespace
8
+ attr_accessor :endpoint
9
+ attr_accessor :homologacao
10
+ attr_accessor :api
11
+ attr_accessor :certificate
12
+ attr_accessor :key
13
+ attr_accessor :xml_builder
14
+ attr_accessor :soap_client
15
+ attr_accessor :verbose
16
+
17
+ end
18
+ end
data/lib/rnfse/core.rb ADDED
@@ -0,0 +1,9 @@
1
+ module Rnfse
2
+ def self.configuration
3
+ Rnfse::Configuration.instance
4
+ end
5
+
6
+ def self.configure
7
+ yield(configuration) if block_given?
8
+ end
9
+ end
data/lib/rnfse/hash.rb ADDED
@@ -0,0 +1,148 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module Rnfse
4
+ class Hash < ::Hash
5
+
6
+ def initialize(constructor = {})
7
+ if constructor.is_a?(::Hash)
8
+ super()
9
+ update(constructor)
10
+ else
11
+ super(constructor)
12
+ end
13
+ end
14
+
15
+ def update(other_hash)
16
+ if other_hash.is_a?(Hash)
17
+ super(other_hash)
18
+ else
19
+ other_hash.to_hash.each_pair do |key, value|
20
+ self[key] = value
21
+ end
22
+ end
23
+ end
24
+
25
+ def self.stringify_keys(obj)
26
+ self.transform_keys(obj) { |key| key.to_s rescue key }
27
+ end
28
+
29
+ def stringify_keys
30
+ self.class.stringify_keys(self)
31
+ end
32
+
33
+ def self.symbolize_keys(obj)
34
+ self.transform_keys(obj) { |key| key.to_sym rescue key }
35
+ end
36
+
37
+ def symbolize_keys
38
+ self.class.symbolize_keys(self)
39
+ end
40
+
41
+ def self.camelize_and_symbolize_keys(obj, uppercase_first_letter = true)
42
+ self.transform_keys(obj) do |key|
43
+ String.camelize(key.to_s, uppercase_first_letter).to_sym rescue key
44
+ end
45
+ end
46
+
47
+ def camelize_and_symbolize_keys(uppercase_first_letter = true)
48
+ self.class.camelize_and_symbolize_keys(self, uppercase_first_letter)
49
+ end
50
+
51
+ def self.underscore_and_symbolize_keys(obj)
52
+ self.transform_keys(obj) do |key|
53
+ String.underscore(key.to_s).to_sym rescue key
54
+ end
55
+ end
56
+
57
+ def underscore_and_symbolize_keys
58
+ self.class.underscore_and_symbolize_keys(self)
59
+ end
60
+
61
+ def self.transform_keys(obj, &block)
62
+ case
63
+ when obj.kind_of?(::Hash)
64
+ result = {}
65
+ obj.each_key do |key|
66
+ result[yield(key)] = self.transform_keys(obj[key], &block)
67
+ end
68
+ result
69
+ when obj.kind_of?(::Array)
70
+ result = []
71
+ obj.each do |elem|
72
+ result << self.transform_keys(elem, &block)
73
+ end
74
+ result
75
+ else
76
+ obj
77
+ end
78
+ end
79
+
80
+ def transform_keys(&block)
81
+ self.class.transform_keys(self, &block)
82
+ end
83
+
84
+ def self.transform_values(obj, key, &block)
85
+ key = /\A#{key}\Z/ unless key.kind_of?(::Regexp)
86
+
87
+ case
88
+ when obj.kind_of?(::Hash)
89
+ result = {}
90
+ obj.each_key do |k|
91
+ if key.match(k)
92
+ result[k] = yield(obj[k])
93
+ else
94
+ result[k] = self.transform_values(obj[k], key, &block)
95
+ end
96
+ end
97
+ result
98
+ when obj.kind_of?(::Array)
99
+ result = []
100
+ obj.each do |elem|
101
+ result << self.transform_values(elem, key, &block)
102
+ end
103
+ result
104
+ else
105
+ obj
106
+ end
107
+ end
108
+
109
+ def transform_values(key, &block)
110
+ self.class.transform_values(self, key, &block)
111
+ end
112
+
113
+ def self.replace_key_values(obj, key, &block)
114
+ parent = key.to_s.split('/').first
115
+ childs = key.to_s.split('/')[1..-1]
116
+ regex = key.kind_of?(::Regexp) ? key : /\A#{parent}\Z/
117
+
118
+ case
119
+ when obj.kind_of?(::Hash)
120
+ result = {}
121
+ obj.each_key do |k|
122
+ case
123
+ when (regex.match(k) and childs.empty?)
124
+ result.merge!(yield(k, obj[k]))
125
+ when (regex.match(k) and !childs.empty?)
126
+ result[k] = self.replace_key_values(obj[k], childs.join('/'), &block)
127
+ else
128
+ result[k] = self.replace_key_values(obj[k], key, &block)
129
+ end
130
+ end
131
+ result
132
+ when obj.kind_of?(::Array)
133
+ result = []
134
+ obj.each do |elem|
135
+ result << self.replace_key_values(elem, key, &block)
136
+ end
137
+ result
138
+ else
139
+ obj
140
+ end
141
+ end
142
+
143
+ def replace_key_values(key, &block)
144
+ self.class.replace_key_values(self, key, &block)
145
+ end
146
+
147
+ end
148
+ end
@@ -0,0 +1,36 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module Rnfse
4
+ class String < ::String
5
+ def self.camelize(term, uppercase_first_letter = true)
6
+ string = term.to_s
7
+ if uppercase_first_letter
8
+ string = string.sub(/^[a-z\d]*/) { $&.capitalize }
9
+ else
10
+ string = string.sub(/^(?:(?=\b|[A-Z_])|\w)/) { $&.downcase }
11
+ end
12
+ string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { $2.capitalize }
13
+ string.gsub!('/', '::')
14
+ string
15
+ end
16
+
17
+ def camelize
18
+ self.class.camelize(self)
19
+ end
20
+
21
+ def self.underscore(term)
22
+ word = term.to_s.gsub('::', '/')
23
+ word.gsub!(/(?:([A-Za-z\d])|^)(?=\b|[^a-z])/)
24
+ #{ "#{$1}#{$1 && '_'}#{$2.downcase}" }
25
+ word.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
26
+ word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
27
+ word.tr!("-", "_")
28
+ word.downcase!
29
+ word
30
+ end
31
+
32
+ def underscore
33
+ self.class.underscore(self)
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,3 @@
1
+ module Rnfse
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,95 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module Rnfse::XMLBuilder::Abrasf10
4
+
5
+ module ClassMethods
6
+
7
+ def build_recepcionar_lote_rps_xml(hash = {})
8
+ hash = prepare_hash(hash)
9
+ inner_xml = ::Gyoku.xml(hash, key_converter: :none)
10
+ xml_builder('EnviarLoteRpsEnvio', inner_xml).doc
11
+ end
12
+
13
+ private
14
+
15
+ # prepara um hash para ser convertido a xml com o Gyoku
16
+ def prepare_hash(hash)
17
+ hash = camelize_hash(hash)
18
+ hash = wrap_rps(hash)
19
+ hash = add_tc_namespace(hash)
20
+ hash = clean_numerics(hash)
21
+ hash = date_to_utc(hash)
22
+ hash = fix_booleans(hash)
23
+ hash
24
+ end
25
+
26
+ # converte booleanos para 1 ou 2
27
+ def fix_booleans(hash)
28
+ regex = /(IssRetido|OptanteSimplesNacional|IncentivadorCultural)\Z/
29
+ Rnfse::Hash.transform_values(hash, regex) do |val|
30
+ val ? 1 : 2
31
+ end
32
+ end
33
+
34
+ # converte datas para GMT-0
35
+ def date_to_utc(hash)
36
+ Rnfse::Hash.transform_values(hash, /DataEmissao\Z/) do |val|
37
+ DateTime.parse(val).new_offset(0).strftime('%FT%T')
38
+ end
39
+ end
40
+
41
+ # limpa tags que por precaução é melhor só conter números
42
+ def clean_numerics(hash)
43
+ regex = /(Cep|Cnae|Cpf|Cnpj)\Z/
44
+ Rnfse::Hash.transform_values(hash, regex) { |val| val.gsub(/\D/, '') }
45
+ end
46
+
47
+ # converte as chaves do hash para CamelCase
48
+ def camelize_hash(hash)
49
+ Rnfse::Hash.camelize_and_symbolize_keys(hash)
50
+ end
51
+
52
+ # encapsula as rps dentro de listaRps com Rps/InfRps
53
+ def wrap_rps(hash)
54
+ hash[:LoteRps][:ListaRps] = {
55
+ :Rps => { :InfRps => hash[:LoteRps][:ListaRps] }
56
+ }
57
+ hash
58
+ end
59
+
60
+ # adiciona o namespace tc nas tags dentro de loteRps
61
+ def add_tc_namespace(hash)
62
+ hash[:LoteRps] = Rnfse::Hash.transform_keys(hash[:LoteRps]) { |key| "tc:#{key}".to_sym }
63
+ hash
64
+ end
65
+
66
+ def xmlns
67
+ 'http://www.abrasf.org.br/servico_enviar_lote_rps_envio.xsd'
68
+ end
69
+
70
+ # namespace dos tipos complexos
71
+ def xmlns_tc
72
+ 'http://www.abrasf.org.br/tipos_complexos.xsd'
73
+ end
74
+
75
+ # instancia o builder a ser utilizado na geracao do xml
76
+ def xml_builder(root, inner_xml)
77
+ Nokogiri::XML::Builder.new(encoding: 'utf-8') do |xml|
78
+ xml.send(root.to_sym, 'xmlns' => xmlns, 'xmlns:tc' => xmlns_tc) do
79
+ xml << inner_xml
80
+ end
81
+ end
82
+ end
83
+ end
84
+
85
+ extend ClassMethods
86
+
87
+ def self.extended(base)
88
+ base.extend(ClassMethods)
89
+ end
90
+
91
+ def self.included(base)
92
+ base.send(:include, ClassMethods)
93
+ end
94
+
95
+ end
@@ -0,0 +1,70 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module Rnfse::XMLBuilder::IssNet10
4
+ include Rnfse::XMLBuilder::Abrasf10
5
+
6
+ private
7
+
8
+ # prepara um hash para ser convertido a xml com o Gyoku
9
+ def prepare_hash(hash)
10
+ hash = camelize_hash(hash)
11
+ hash = wrap_rps(hash)
12
+ hash = add_tc_namespace(hash)
13
+ hash = clean_numerics(hash)
14
+ hash = date_to_utc(hash)
15
+ hash = fix_booleans(hash)
16
+ hash = wrap_cpf_cnpj(hash)
17
+ hash = add_municipio_prestacao_servico(hash)
18
+ hash = add_estado(hash)
19
+ hash = add_cidade(hash)
20
+ hash = alter_aliquota(hash)
21
+
22
+ hash
23
+ end
24
+
25
+ # alterar o formato da aliquota de 0 a 1 para 0 a 100
26
+ def alter_aliquota(hash)
27
+ Rnfse::Hash.transform_values(hash, 'tc:Aliquota') do |val|
28
+ "%.2f" % (val * 100)
29
+ end
30
+ end
31
+
32
+ # troca a tag Endereco/CodigoMunicipio por Cidade
33
+ def add_cidade(hash)
34
+ Rnfse::Hash.replace_key_values(hash, 'tc:Endereco/tc:CodigoMunicipio') do |key, value|
35
+ { 'tc:Cidade' => value }
36
+ end
37
+ end
38
+
39
+ # troca a tag Uf pela tag Estado
40
+ def add_estado(hash)
41
+ Rnfse::Hash.replace_key_values(hash, 'tc:Uf') do |key, value|
42
+ { :'tc:Estado' => value }
43
+ end
44
+ end
45
+
46
+ # troca a tag CodigoMunicipio para MunicipioPrestacaoServico
47
+ # na tag Servico
48
+ def add_municipio_prestacao_servico(hash)
49
+ Rnfse::Hash.replace_key_values(hash, 'tc:Servico/tc:CodigoMunicipio') do |key, value|
50
+ { :'tc:MunicipioPrestacaoServico' => value }
51
+ end
52
+ end
53
+
54
+ # encapsula as tags cpf ou cnpj em uma tag cpfcnpj
55
+ def wrap_cpf_cnpj(hash)
56
+ regex = /(Cpf|Cnpj)\Z/
57
+ Rnfse::Hash.replace_key_values(hash, regex) do |key, value|
58
+ { :'tc:CpfCnpj' => { key => value } }
59
+ end
60
+ end
61
+
62
+ def xmlns
63
+ 'http://www.issnetonline.com.br/webserviceabrasf/vsd/servico_enviar_lote_rps_envio.xsd'
64
+ end
65
+
66
+ def xmlns_tc
67
+ 'http://www.issnetonline.com.br/webserviceabrasf/vsd/tipos_complexos.xsd'
68
+ end
69
+
70
+ end
@@ -0,0 +1,10 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module Rnfse
4
+ class XMLBuilder
5
+ def initialize(options)
6
+ options = Hash.stringify_keys(options)
7
+ extend self.class.const_get(String.camelize(options['padrao']))
8
+ end
9
+ end
10
+ end
data/lib/rnfse.rb ADDED
@@ -0,0 +1,22 @@
1
+ require 'date'
2
+ require 'pathname'
3
+ require 'yaml'
4
+ require 'json'
5
+ require 'json-schema'
6
+ require 'gyoku'
7
+ require 'nokogiri-xmlsec1'
8
+ require 'savon'
9
+
10
+ require 'rnfse/core'
11
+ require 'rnfse/version'
12
+ require 'rnfse/configuration'
13
+ require 'rnfse/hash'
14
+ require 'rnfse/string'
15
+
16
+ require 'rnfse/api'
17
+ require 'rnfse/api/abrasf_1_0'
18
+ require 'rnfse/api/iss_net_1_0'
19
+
20
+ require 'rnfse/xml_builder'
21
+ require 'rnfse/xml_builder/abrasf_1_0'
22
+ require 'rnfse/xml_builder/iss_net_1_0'
data/rnfse.gemspec ADDED
@@ -0,0 +1,60 @@
1
+ # encoding: utf-8
2
+
3
+ lib = File.expand_path('../lib', __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+
6
+ require 'rnfse/version'
7
+
8
+ Gem::Specification.new do |s|
9
+ s.name = 'rnfse'
10
+ s.version = Rnfse::VERSION
11
+ s.summary = %q{Biblioteca para a API de Nota Fiscal de Serviços
12
+ eletrônica (NFS-e) da ABRASF.}
13
+ s.description = %q{Biblioteca para integração com as várias implementações
14
+ municipais de Nota Fiscal de Serviços eletrônica (NFS-e) que utilizam uma
15
+ das versões ou variações dos padrões
16
+ [ABRASF](http://www.abrasf.org.br/paginas_multiplas_detalhes.php?cod_pagina=1).}
17
+
18
+ s.required_ruby_version = '>= 1.9.3'
19
+
20
+ s.licenses = ['MIT', 'GPL-3']
21
+
22
+ s.authors = ['Renan Mendes Carvalho']
23
+ s.email = ['aitherios@gmail.com']
24
+ s.homepage = 'https://github.com/aitherios/rnfse'
25
+
26
+ s.files = `git ls-files`.split($/)
27
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
28
+ s.require_paths = ['lib']
29
+ s.rubyforge_project = '[none]'
30
+
31
+ s.add_dependency 'json', '~> 1.0'
32
+ s.add_dependency 'json-schema', '~> 2.0'
33
+ s.add_dependency 'gyoku', '~> 1.0'
34
+ s.add_dependency 'nokogiri-xmlsec1', '~> 0.0.7'
35
+ s.add_dependency 'savon', '~> 2.0'
36
+
37
+ s.add_development_dependency 'bundler', '~> 1.3'
38
+ s.add_development_dependency 'rake', '~> 10.0'
39
+ s.add_development_dependency 'rdoc', '~> 4.0'
40
+ s.add_development_dependency 'gem-release', '~> 0.7.0'
41
+ s.add_development_dependency 'rspec', '~> 3.0.0'
42
+ s.add_development_dependency 'rspec-its', '~> 1.0.0'
43
+ s.add_development_dependency 'simplecov', '~> 0.7.0'
44
+ s.add_development_dependency 'codeclimate-test-reporter', '>= 0'
45
+ s.add_development_dependency 'equivalent-xml', '~> 0.4.0'
46
+ s.add_development_dependency 'wwtd', '~> 0.5.0'
47
+ s.add_development_dependency 'vcr', '~> 2.9'
48
+ s.add_development_dependency 'webmock', '~> 1.0'
49
+ s.add_development_dependency 'guard', '~> 2.6.0'
50
+ s.add_development_dependency 'guard-rspec', '~> 4.2.0'
51
+ s.add_development_dependency 'guard-bundler', '~> 2.0.0'
52
+ s.add_development_dependency 'foreman', '~> 0.74.0'
53
+ s.add_development_dependency 'pry', '~> 0.10'
54
+ if RUBY_VERSION.to_i < 2
55
+ s.add_development_dependency 'pry-debugger', '~> 0.2'
56
+ else
57
+ s.add_development_dependency 'pry-byebug', '~> 1.0'
58
+ end
59
+
60
+ end