Smuggle
Is a gem to manage exports and imports with ease, separating the logic from the models, resulting in a much cleaner codebase. Easy to use, with familiar structure.
Smuggle is not dependent on Rails, you can use it on ActiveModel/ActiveRecord models, as well as plain ruby objects and hashes.
Links:
Requirements
Installation
To install, run:
gem install smuggle
Or add the following to your Gemfile:
gem "smuggle"
Usage
Exporters
Given the following plain old ruby object:
class User
attr_accessor :name
def initialize(name)
@name = name
end
end
An exporter can be defined by inheriting from Smuggle::Exporter::Base and defining the attributes to export:
class UserExporter < Smuggle::Exporter::Base
attributes :name
end
Extra logic can be establish inside the exporter file, using the same name as the attribute:
class UserExporter < Smuggle::Exporter::Base
attributes :name
def name
super + " - exported"
end
end
If there are no attributes defined in the exporter and you are using ActiveModel or ActiveRecord, all the attributes of the record will be included. If it is a hash, then all values will be included.
To generate the csv data simply call:
users = [User.new("Rick Sanchez"), User.new("Morty Smith")]
Smuggle::Services::Export.call(scope: users, exporter: UserExporter)
# => "Full name,Full name\nRick Sanchez,Rick Sanchez\nMorty Smith,Morty Smith\n"
Or if you are using ActiveRecord, the exporter class will be automatically resolved from the scope:
Smuggle::Services::Export.call(scope: User.all)
To add labels for your attributes (to show in the header instead of the raw attribute keys) you can add attribute_labels to your exporter:
class UserExporter < Smuggle::Exporter::Base
attributes :name
attribute_labels name: "Full name"
end
users = [User.new("Rick Sanchez"), User.new("Morty Smith")]
Smuggle::Services::Export.call(scope: users, exporter: UserExporter)
# => "Full name\nRick Sanchez\nMorty Smith\n"
Importers
Given the following plain old ruby object:
class User
attr_accessor :name
def initialize(name)
@name = name
end
end
An importer can be defined by inheriting from Smuggle::Importer::Base and defining the attributes to export:
class UserImporter < Smuggle::Importer::Base
# If no attributes are defined, the importer will infer them from the model's .attribute_names
# If any attributes are explicitly defined, all other entries in the CSV are ignored
attributes :name
# Computed attributes from the row data
def name
[row[:first_name], row[:last_name]].join(" ")
end
def persist
# Create your instance here
model.new(to_h)
# The result is collected by the Import service
# If you want to persist your data, you can do so here. This is an example using ActiveRecord
# model.create(to_h)
end
end
For example:
Given the following users.csv
file:
"first_name","last_name"
"Rick","Sanchez"
"Morty","Smith"
Just run:
Smuggle::Services::Import.call(model: User, filepath: "users.csv")
# => [#<User name: "Rick Sanchez">, #<User name: "Morty Smith">]
The importer class will be resolved from the model name, otherwise you could explicitely set the importer like this:
Smuggle::Services::Import.call(model: User, filepath: "users.csv", importer: UserImporter)
Generators
If you are using rails you can use the following generators:
$ rails g smuggle:install
create app/exporters/application_exporter.rb
create app/importers/application_importer.rb
To generate an exporter, you can run the following command:
$ rails g smuggle:exporter user
create app/exporters/user_exporter.rb
You can also include the attributes you wish to export by running:
$ rails g smuggle:exporter user email username created_at
create app/exporters/user_exporter.rb
And to generate an importer, just run:
$ rails g smuggle:importer user email username full_name
create app/importers/user_importer.rb
Tests
To test, run:
bundle exec rspec spec/
Versioning
Read Semantic Versioning for details. Briefly, it means:
- Major (X.y.z) - Incremented for any backwards incompatible public API changes.
- Minor (x.Y.z) - Incremented for new, backwards compatible, public API enhancements/fixes.
- Patch (x.y.Z) - Incremented for small, backwards compatible, bug fixes.
License
Original work copyright 2017-2019 Inspire Innovation BV. Continued work copyright 2019 Pablo Crivella.
Read LICENSE for details.
The development of this gem has been sponsored by Inspire Innovation BV (Utrecht, The Netherlands).