rails-importer 0.0.6

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0ee6bdad79ed8a4d980a915735461df003bca4d5
4
+ data.tar.gz: 2105757e2d2515fdcb8f4e1483df2f85bc683f07
5
+ SHA512:
6
+ metadata.gz: 046fc96c808dfcce475bd1ada1e0c5467ec78b4d6ccee48a46d9a5029ca25d1d573d252f4f94a02249eaf96e896db33c28d64c63dc7eefa46d831cd1ee5bf996
7
+ data.tar.gz: 1ff316fc036a83d9a93c82ceb5404441e3cb6a8413e42fff0b9a3969b1b9bf61147589e0321634debb76c14ff6df6f1fcd4cf2f987372e38f7d7378709607f05
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2016 Bruno Porto
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,62 @@
1
+ # Rails Importer
2
+
3
+ Rails Importer (XML, XLS, CSV)
4
+
5
+ ## How to install
6
+
7
+ Add it to your **Gemfile**:
8
+ ```ruby
9
+ gem 'rails-importer'
10
+ ```
11
+
12
+ Run the following command to install it:
13
+ ```sh
14
+ $ bundle install
15
+ $ rails generate rails_importer:install
16
+ ```
17
+
18
+ ## Generators
19
+
20
+ You can generate importers `app/importers/example_importer.rb`
21
+
22
+ ```sh
23
+ $ rails generate rails_importer:importer example
24
+ ```
25
+
26
+ Generator will make a file with content like:
27
+
28
+ ```ruby
29
+ class ExampleImporter < RailsImporter::Base
30
+
31
+ importer do
32
+ fields :name, :email, :age
33
+ each_record do |record, params|
34
+ MyModel.find_or_create_by(name: record[:name], email: record[:email], age: record[:age])
35
+ end
36
+ end
37
+
38
+ # importer :simple do
39
+ # xml_structure :root, :row
40
+ # fields :name, :email, :age
41
+ # each_record do |record, params|
42
+ # ...
43
+ # end
44
+ # end
45
+
46
+ end
47
+ ```
48
+
49
+ ### How to use
50
+
51
+ You can call `import` from **Importers** objects:
52
+ ```erb
53
+ file = params[:import][:file]
54
+ records = ExampleImporter.import(file, extra_param: 'Extra')
55
+ ```
56
+
57
+ Or with context:
58
+
59
+ ```erb
60
+ file = params[:import][:file]
61
+ records = ExampleImporter.import(file, context: :simple, extra_param: 'Extra')
62
+ ```
data/Rakefile ADDED
@@ -0,0 +1,34 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'RailsImporter'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.rdoc')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+
18
+
19
+
20
+
21
+
22
+ Bundler::GemHelper.install_tasks
23
+
24
+ require 'rake/testtask'
25
+
26
+ Rake::TestTask.new(:test) do |t|
27
+ t.libs << 'lib'
28
+ t.libs << 'test'
29
+ t.pattern = 'test/**/*_test.rb'
30
+ t.verbose = false
31
+ end
32
+
33
+
34
+ task default: :test
@@ -0,0 +1,5 @@
1
+ class Railtie < ::Rails::Railtie
2
+ initializer 'activeservice.autoload', :before => :set_autoload_paths do |app|
3
+ app.config.autoload_paths += %( #{Rails.application.config.root}/app/importers )
4
+ end
5
+ end
@@ -0,0 +1,4 @@
1
+ en:
2
+ importer:
3
+ error:
4
+ invalid_file_type: "Invalid file type allowed: %{file_types}"
@@ -0,0 +1,12 @@
1
+ require 'rails/generators/base'
2
+
3
+ module RailsImporter
4
+ class ImporterGenerator < Rails::Generators::NamedBase
5
+ source_root File.expand_path("../../templates", __FILE__)
6
+
7
+ def generate_importer
8
+ @importer_name = file_name.classify
9
+ template "generic_importer.erb", File.join('app/importers', "#{file_name.underscore}_importer.rb")
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,16 @@
1
+ require 'rails/generators/base'
2
+
3
+ module RailsImporter
4
+ module Generators
5
+
6
+ class InstallGenerator < Rails::Generators::Base
7
+ source_root File.expand_path("../../../../config", __FILE__)
8
+
9
+ def copy_locales
10
+ directory 'locales', 'config/locales'
11
+ end
12
+
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,18 @@
1
+ class <%= @importer_name %>Importer < RailsImporter::Base
2
+
3
+ importer do
4
+ fields :name, :email, :age
5
+ each_record do |record, params|
6
+ MyModel.find_or_create_by(name: record[:name], email: record[:email], age: record[:age])
7
+ end
8
+ end
9
+
10
+ # importer :simple do
11
+ # xml_structure :root, :row
12
+ # fields :name, :email, :age
13
+ # each_record do |record, params|
14
+ # ...
15
+ # end
16
+ # end
17
+
18
+ end
@@ -0,0 +1 @@
1
+ require 'rails_importer'
@@ -0,0 +1,138 @@
1
+ require 'ostruct'
2
+
3
+ module RailsImporter
4
+
5
+ def self.included(base)
6
+ base.extend ClassMethods
7
+
8
+ base.define_singleton_method 'colunas' do |*args|
9
+ @importacao_colunas = nil unless @importacao_colunas.present?
10
+ if args.present?
11
+ @importacao_colunas = (args.first.is_a?(Hash) ? args.inject(:merge) : args)
12
+ else
13
+ @importacao_colunas
14
+ end
15
+ end
16
+
17
+ base.define_singleton_method 'estrutura_xml' do |*args|
18
+ @importacao_estrutura_xml = nil unless @importacao_estrutura_xml.present?
19
+ if args.present?
20
+ @importacao_estrutura_xml = args
21
+ else
22
+ @importacao_estrutura_xml
23
+ end
24
+ end
25
+
26
+ base.define_singleton_method 'para_cada_linha' do |&block|
27
+ define_singleton_method('importar_registro') do |registro, *args|
28
+ block.call(registro, *args)
29
+ end
30
+ end
31
+
32
+ end
33
+
34
+ def self.tipos_de_arquivos
35
+ [:csv, :xls, :xml]
36
+ end
37
+
38
+ module ClassMethods
39
+
40
+ def importacao(&block)
41
+ block.call if block_given?
42
+ self.class.class_eval do
43
+ define_method :importar do |arquivo, *args|
44
+ retorno = []
45
+ begin
46
+ ext = (File.extname(arquivo.path)[1..-1] rescue nil)
47
+ if arquivo.respond_to?(:path) and RailsImporter.tipos_de_arquivos.include?(ext.to_sym)
48
+ registros = self.send("#{ext}_para_array", arquivo)
49
+ if registros.present? and registros.is_a?(Array)
50
+ retorno = registros.map do |registro|
51
+ importar_registro(registro, *args)
52
+ end
53
+ end
54
+ else
55
+ raise "Tipo de arquivo inválido. Arquivos permitidos: #{RailsImporter.tipos_de_arquivos.join(', ')}"
56
+ end
57
+ rescue Exception => e
58
+ retorno = e.message
59
+ end
60
+ retorno
61
+ end
62
+ end
63
+ end
64
+
65
+ private
66
+ def csv_para_array(arquivo)
67
+ registros = []
68
+ linha=0
69
+ CSV.foreach(arquivo.path, {:headers => false, :col_sep => ';', :force_quotes => true}) do |row|
70
+ if linha>0
71
+ registros << object_values(row) unless array_blank?(row)
72
+ end
73
+ linha+=1
74
+ end
75
+ registros
76
+ end
77
+
78
+ def xml_para_array(arquivo)
79
+ registros = []
80
+ estrutura_xml = self.send(:estrutura_xml)
81
+ if estrutura_xml.present? and estrutura_xml.is_a?(Array) and estrutura_xml.count > 0
82
+ xml = Hash.from_xml(arquivo.read)
83
+ estrutura_xml.each do |node|
84
+ xml = xml[node.to_s]
85
+ end
86
+ xml.each do |elem|
87
+ registros << object_values(elem.values) unless array_blank?(elem.values)
88
+ end
89
+ else
90
+ raise 'Você precisa informar a estrutura_xml no seu Model'
91
+ end
92
+ registros
93
+ end
94
+
95
+ def xls_para_array(arquivo)
96
+ registros = []
97
+ Spreadsheet.client_encoding = 'UTF-8'
98
+ documento = Spreadsheet.open(arquivo.path)
99
+ planilha = documento.worksheet 0
100
+ planilha.each_with_index do |row, i|
101
+ next unless i>0
102
+ registros << object_values(row) unless array_blank?(row)
103
+ end
104
+ registros
105
+ end
106
+
107
+ def object_values(array)
108
+ atributos = self.send(:colunas)
109
+ atributos = atributos.keys if atributos.is_a?(Hash)
110
+ args = array[0...atributos.size]
111
+ hValores = Hash[atributos.zip(args)]
112
+ #Ajustando valores inválidos
113
+ atributos.each do |atributo|
114
+ if hValores[atributo].present?
115
+ if hValores[atributo].is_a?(Numeric)
116
+ hValores[atributo] = hValores[atributo].to_i if hValores[atributo].modulo(1).zero?
117
+ hValores[atributo] = (hValores[atributo].to_s.strip rescue '#ERRO AO CONVERTER NUMERO PARA TEXTO')
118
+ elsif hValores[atributo].is_a?(Date)
119
+ hValores[atributo] = (I18n.l(hValores[atributo], format: '%d/%m/%Y') rescue hValores[atributo]).to_s
120
+ elsif hValores[atributo].is_a?(DateTime)
121
+ hValores[atributo] = (I18n.l(hValores[atributo], format: '%d/%m/%Y %H:%i:%s') rescue hValores[atributo]).to_s
122
+ else
123
+ #PARA QUALQUER OUTRO VALOR, FORÇA CONVERTER PARA STRING
124
+ hValores[atributo] = (hValores[atributo].to_s.strip rescue '')
125
+ end
126
+ else
127
+ hValores[atributo] = ''
128
+ end
129
+ end
130
+ OpenStruct.new(hValores)
131
+ end
132
+
133
+ def array_blank?(array)
134
+ array.all? {|i|i.nil? or i==''}
135
+ end
136
+
137
+ end
138
+ end
@@ -0,0 +1,136 @@
1
+ require 'builder'
2
+ require 'spreadsheet'
3
+
4
+ module RailsImporter
5
+ class Base
6
+ class_attribute :importers
7
+
8
+ class << self
9
+ def import(file, *args)
10
+ context = args.try(:context) || :default
11
+ result = []
12
+ begin
13
+ ext = (File.extname(file.path)[1..-1] rescue nil)
14
+ if file.respond_to?(:path) and self.file_types.include?(ext.to_sym)
15
+ rows = self.send("import_from_#{ext}", file)
16
+ if rows.present? and rows.is_a?(Array)
17
+ result = rows.map do |record|
18
+ self.importers[context][:each_record].call(record, *args) if self.importers[context][:each_record].is_a?(Proc)
19
+ end.compact
20
+ end
21
+ else
22
+ I18n.t(:invalid_file_type, file_types: self.file_types.join(', '), scope: [:importer, :error])
23
+ end
24
+ rescue Exception => e
25
+ result = e.message
26
+ end
27
+ result
28
+ end
29
+
30
+ def file_types
31
+ [:csv, :xls, :xml]
32
+ end
33
+
34
+ # def method_missing(m, *args, &block)
35
+ # if m =~ /_url|_path/
36
+ # Rails.application.routes.url_helpers.send(m, args)
37
+ # end
38
+ # end
39
+
40
+ def importer(name=:default, &block)
41
+ (self.importers ||= {})[name] ||= {fields: [], xml_structure: [:records, :record], each_record: nil}
42
+ @importer_name = name
43
+ block.call if block_given?
44
+ self.importers[name]
45
+ end
46
+
47
+ def fields(*attributes)
48
+ importer_value(:fields, attributes)
49
+ end
50
+
51
+ def xml_structure(*attributes)
52
+ importer_value(:xml_structure, attributes)
53
+ end
54
+
55
+ def each_record(&block)
56
+ importer_value(:each_record, block)
57
+ end
58
+
59
+ private
60
+ def import_from_csv(file, context=:default)
61
+ records = []
62
+ line = 0
63
+ CSV.foreach(file.path, {:headers => false, :col_sep => ';', :force_quotes => true}) do |row|
64
+ if line>0
65
+ records << object_values(row, context) unless array_blank?(row)
66
+ end
67
+ line+=1
68
+ end
69
+ records
70
+ end
71
+
72
+ def import_from_xml(file, context=:default)
73
+ records = []
74
+ xml_structure = self.importers[context][:xml_structure]
75
+ xml = Hash.from_xml(file.read)
76
+ xml_structure.each do |node|
77
+ xml = xml[node.to_s]
78
+ end
79
+ xml.each do |elem|
80
+ records << object_values(elem.values, context) unless array_blank?(elem.values)
81
+ end
82
+ records
83
+ end
84
+
85
+ def import_from_xls(file, context=:default)
86
+ records = []
87
+ Spreadsheet.client_encoding = 'UTF-8'
88
+ document = Spreadsheet.open(file.path)
89
+ spreadsheet = document.worksheet 0
90
+ spreadsheet.each_with_index do |row, i|
91
+ next unless i>0
92
+ records << object_values(row, context) unless array_blank?(row)
93
+ end
94
+ records
95
+ end
96
+
97
+ def object_values(array, context=:default)
98
+ attributes = self.importers[context][:fields]
99
+ attributes = attributes.keys if attributes.is_a?(Hash)
100
+ args = array[0...attributes.size]
101
+ hValues = Hash[attributes.zip(args)]
102
+ attributes.each do |attr|
103
+ if hValues[attr].present?
104
+ if hValues[attr].is_a?(Numeric)
105
+ hValues[attr] = hValues[attr].to_i if hValues[attr].modulo(1).zero?
106
+ hValues[attr] = (hValues[attr].to_s.strip rescue '#ERRO AO CONVERTER NUMERO PARA TEXTO')
107
+ elsif hValues[attr].is_a?(Date)
108
+ hValues[attr] = (I18n.l(hValues[attr], format: '%d/%m/%Y') rescue hValues[attr]).to_s
109
+ elsif hValues[attr].is_a?(DateTime)
110
+ hValues[attr] = (I18n.l(hValues[attr], format: '%d/%m/%Y %H:%i:%s') rescue hValues[attr]).to_s
111
+ else
112
+ #PARA QUALQUER OUTRO VALOR, FORÇA CONVERTER PARA STRING
113
+ hValues[attr] = (hValues[attr].to_s.strip rescue '')
114
+ end
115
+ else
116
+ hValues[attr] = ''
117
+ end
118
+ end
119
+ OpenStruct.new(hValues)
120
+ end
121
+
122
+ def array_blank?(array)
123
+ array.all? {|i|i.nil? or i==''}
124
+ end
125
+
126
+ def importer_value(key, attributes)
127
+ if attributes.present?
128
+ self.importers[@importer_name][key] = attributes
129
+ else
130
+ self.importers[@importer_name][key]
131
+ end
132
+ end
133
+ end
134
+
135
+ end
136
+ end
@@ -0,0 +1,4 @@
1
+ module RailsImporter
2
+ class Engine < ::Rails::Engine
3
+ end
4
+ end
@@ -0,0 +1,3 @@
1
+ module RailsImporter
2
+ VERSION = "0.0.6"
3
+ end
@@ -0,0 +1,5 @@
1
+ require 'rails_importer/engine'
2
+ require 'rails_importer/base'
3
+
4
+ module RailsImporter
5
+ end
metadata ADDED
@@ -0,0 +1,98 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rails-importer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.6
5
+ platform: ruby
6
+ authors:
7
+ - Bruno Porto
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-02-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '3'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '6'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '3'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '6'
33
+ - !ruby/object:Gem::Dependency
34
+ name: spreadsheet
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '1.1'
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 1.1.4
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '1.1'
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 1.1.4
53
+ description: Rails Importer (CSV, XLS, XML)
54
+ email:
55
+ - brunotporto@gmail.com
56
+ executables: []
57
+ extensions: []
58
+ extra_rdoc_files: []
59
+ files:
60
+ - MIT-LICENSE
61
+ - README.md
62
+ - Rakefile
63
+ - config/initializers/rails_importer.rb
64
+ - config/locales/importers.en.yml
65
+ - lib/generators/rails_importer/importer_generator.rb
66
+ - lib/generators/rails_importer/install_generator.rb
67
+ - lib/generators/templates/generic_importer.erb
68
+ - lib/rails-importer.rb
69
+ - lib/rails_importer.rb
70
+ - lib/rails_importer/_importer.rb
71
+ - lib/rails_importer/base.rb
72
+ - lib/rails_importer/engine.rb
73
+ - lib/rails_importer/version.rb
74
+ homepage: https://github.com/brunoporto/rails-importer
75
+ licenses:
76
+ - MIT
77
+ metadata: {}
78
+ post_install_message:
79
+ rdoc_options: []
80
+ require_paths:
81
+ - lib
82
+ required_ruby_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ required_rubygems_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ requirements: []
93
+ rubyforge_project:
94
+ rubygems_version: 2.5.1
95
+ signing_key:
96
+ specification_version: 4
97
+ summary: Rails Importer
98
+ test_files: []