Skip to content

pcriv/smuggle

Repository files navigation

Smuggle

Gem Depfu Inline docs Maintainability Test Coverage

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

  1. Ruby 3.0.0

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).

About

Manage exports and imports with ease, separating the logic from the models

Topics

Resources

License

Code of conduct

Stars

Watchers

Forks

Packages

No packages published