项目作者: michaelnera

项目描述 :
An easy to setup and flexible importer for Ruby on Rails
高级语言: Ruby
项目地址: git://github.com/michaelnera/active_record_importer.git
创建时间: 2017-02-16T11:30:07Z
项目社区:https://github.com/michaelnera/active_record_importer

开源协议:MIT License

下载


ActiveRecordImporter

Supports only Rails 4 and 5

This gem helps you insert/update records easily. For now, it only accepts CSV file.
This also helps you monitor how many rows are imported, and how many rows failed.
This gem also allows you to easily import to any model with few configurations.

I’ll release an update to enable this on background job.

Installation

Add this line to your application’s Gemfile:

  1. gem 'active_record_importer'

And then execute:

  1. $ bundle

Or install it yourself as:

  1. $ gem install active_record_importer

Usage

For version 0.4.0

For the newest version (0.4.0), you don’t have to create Import table/model and controller.
I already fixed the errors when there’s no Import table/model on version 0.3.0.
You just need to add the acts_as_importable in your model you want to be importable, and you may now run:

  1. User.import!(file: File.open(PATH_TO_FILE))

insert will be the default insert method for this
If you want to use upsert or error_duplicate, define it in your importer options:

  1. class User < ActiveRecord::Base
  2. acts_as_importable insert_method: 'upsert',
  3. find_options: [:email]
  4. end

Or you may use in your console:

  1. User.acts_as_importable insert_method: 'error_duplicate', find_options: ['email']

If you don’t want to record the status of your import, you don’t have to do the remaining steps

Create Import table/model

I’ll add a generator on my next release

DB Migration:

  1. class ActiveRecordImporterMigration < ActiveRecord::Migration
  2. def change
  3. create_table :imports do |t|
  4. t.attachment :file
  5. t.attachment :failed_file
  6. t.text :properties
  7. t.string :resource, null: false
  8. t.integer :imported_rows, default: 0
  9. t.integer :failed_rows, default: 0
  10. t.timestamps
  11. end
  12. end
  13. end

Add Import Model:

  1. class Import < ActiveRecord::Base
  2. extend Enumerize
  3. store :properties, accessors: %i(insert_method find_options batch_size)
  4. enumerize :insert_method,
  5. in: %w(insert upsert error_duplicate),
  6. default: :upsert
  7. has_attached_file :file
  8. has_attached_file :failed_file
  9. attr_accessor :execute_on_create
  10. validates :resource, presence: true
  11. validate :check_presence_of_find_options
  12. validates_attachment :file,
  13. content_type: {
  14. content_type: %w(text/plain text/csv)
  15. }
  16. validates_attachment :failed_file,
  17. content_type: {
  18. content_type: %w(text/plain text/csv)
  19. }
  20. # I'll add import options in the next major release
  21. # accepts_nested_attributes_for :import_options, allow_destroy: true
  22. ### THIS IS VERSION 0.2.1 and below
  23. def execute
  24. resource_class.import!(self, execute_on_create)
  25. end
  26. ### THIS IS VERSION 0.2.1 and below
  27. def execute
  28. resource_class.import!(object: self, execute: execute_on_create)
  29. end
  30. ### THIS IS VERSION 0.2.1 and below
  31. def execute!
  32. resource_class.import!(self, true)
  33. end
  34. ### THIS IS VERSION 0.3.0
  35. def execute!
  36. resource_class.import!(object: self, execute: true)
  37. end
  38. def resource_class
  39. resource.safe_constantize
  40. end
  41. def batch_size
  42. super.to_i
  43. end
  44. ##
  45. # Override this if you prefer have
  46. # private permissions or you have
  47. # private methods for reading files
  48. ##
  49. def import_file
  50. local_path?(file) ? file.path : file.url
  51. end
  52. ##
  53. # Override this method if you have
  54. # private permissions or you have private methods
  55. # for reading/writing uploaded files
  56. ##
  57. def failed_file_path
  58. local_path?(failed_file) ? failed_file.path : failed_file.url
  59. end
  60. private
  61. def check_presence_of_find_options
  62. return if insert_method.insert?
  63. errors.add(:find_options, "can't be blank") if find_options.blank?
  64. end
  65. def local_path?(f)
  66. File.exist? f.path
  67. end
  68. end

Add acts_as_importable to any ActiveRecord model to make it importable

  1. class User < ActiveRecord::Base
  2. acts_as_importable
  3. end

You may also add import options:

  1. class User < ActiveRecord::Base
  2. acts_as_importable default_attributes: { first_name: 'Juan',
  3. last_name: 'dela Cruz' },
  4. find_options: %i(email),
  5. before_save: Proc.new { |user| user.password = 'temporarypassword123' }
  6. after_save: Proc.new { |user| puts "THIS IS CALLED AFTER OBJECT IS SAVED" }
  7. end

If you’re using ActiveRecord::Store, you may import values to your accessors by including them in the configuration:

  1. class User < ActiveRecord::Base
  2. store :properties, accessors: [:first_key, :second_key]
  3. acts_as_importable store_accessors: [:first_key, :second_key]
  4. end

Add import form

This is a sample import HAML form:

  1. # resource is your Model name
  2. = f.input :resource
  3. # batch_size is useful for large csv file
  4. = f.input :batch_size
  5. # insert_methods: [:upsert, :insert, :error_on_duplicate]
  6. = f.input :insert_method, collection: insert_methods, class: 'form-control insert-method'
  7. # `find_options` are the list of columns you want to use to update a certain instance or
  8. # error when a duplicate is found. This is not required when your insert_method is `:insert`
  9. = f.input :find_options
  10. = f.input :file, as: :file,
  11. input_html: { accept: '.csv' }

You may also add some options from the SmarterCSV gem:

  1. | Option | Default
  2. --------------------------------------------------------------
  3. | :convert_values_to_metric | nil
  4. | :value_converters | nil
  5. | :remove_empty_values | false
  6. | :comment_regexp | Regexp.new(/^#=>/)
  7. | :force_utf8 | true
  8. | :chunk_size | 500
  9. | :col_sep | ","

https://github.com/tilo/smarter_csv

  1. class User < ActiveRecord::Base
  2. acts_as_importable csv_opts: {
  3. chunk_size: 2000,
  4. col_sep: '|',
  5. convert_values_to_numeric: { only: [:age, :salary] }
  6. }
  7. end

I'll add more options SOON!

Create Imports Controller:

  1. class ImportsController < ApplicationController
  2. def create
  3. @import = Import.create!(import_params)
  4. @import.execute!
  5. end
  6. private
  7. def import_params
  8. params.require(:import).permit(:file, :resource, :insert_method, :batch_size)
  9. end
  10. end

Run it via Rails Console:

  1. File.open(PATH_TO_CSV_FILE) do |file|
  2. @import = Import.create!(
  3. resource: 'User',
  4. file: file,
  5. insert_method: 'upsert',
  6. find_options: 'first_name,last_name'
  7. )
  8. end
  9. @import.execute!

REMINDER:

Headers of your csv file should be formatted/transformed to column names of your IMPORTABLE model

Development

After checking out the repo, run bin/setup to install dependencies. Then, run rake spec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and tags, and push the .gem file to rubygems.org.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/michaelnera/active_record_importer. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.

License

The gem is available as open source under the terms of the MIT License.