Baike.dev
All toolsOnline toolsAI codingTrendingOpen sourceNewsSubmit
Log in
< Back to tools
A

amoeba

> 编程语言
Open source

A ruby gem to allow the copying of ActiveRecord objects and their associated children, configurable with a DSL on the model

822 stars0 likes4 views
WebsiteGitHub

About

A ruby gem to allow the copying of ActiveRecord objects and their associated children, configurable with a DSL on the model

Amoeba

Easy cloning of active_record objects including associations and several operations under associations and attributes.

Interested in contributing?

See here.

What?

The goal was to be able to easily and quickly reproduce ActiveRecord objects including their children, for example copying a blog post maintaining its associated tags or categories.

This gem is named "Amoeba" because amoebas are (small life forms that are) good at reproducing. Their children and grandchildren also reproduce themselves quickly and easily.

Technical Details

An ActiveRecord extension gem to allow the duplication of associated child record objects when duplicating an active record model.

Rails 5.2, 6.0, 6.1 compatible. For Rails 4.2 to 5.1 use version 3.x.

Features

  • Supports the following association types
    • has_many
    • has_one :through
    • has_many :through
    • has_and_belongs_to_many
  • A simple DSL for configuration of which fields to copy. The DSL can be applied to your rails models or used on the fly.
  • Supports STI (Single Table Inheritance) children inheriting their parent amoeba settings.
  • Multiple configuration styles such as inclusive, exclusive and indiscriminate (aka copy everything).
  • Supports cloning of the children of Many-to-Many records or merely maintaining original associations
  • Supports automatic drill-down i.e. recursive copying of child and grandchild records.
  • Supports preprocessing of fields to help indicate uniqueness and ensure the integrity of your data depending on your business logic needs, e.g. prepending "Copy of " or similar text.
  • Supports preprocessing of fields with custom lambda blocks so you can do basically whatever you want if, for example, you need some custom logic while making copies.
  • Amoeba can perform the following preprocessing operations on fields of copied records
    • set
    • prepend
    • append
    • nullify
    • customize
    • regex

Usage

Installation

is hopefully as you would expect:

bash
gem install amoeba

or just add it to your Gemfile:

bash
gem 'amoeba'

Configure your models with one of the styles below and then just run the amoeba_dup method on your model where you would run the dup method normally:

ruby
p = Post.create(:title => "Hello World!", :content => "Lorum ipsum dolor")
p.comments.create(:content => "I love it!")
p.comments.create(:content => "This sucks!")

puts Comment.all.count # should be 2

my_copy = p.amoeba_dup
my_copy.save

puts Comment.all.count # should be 4

By default, when enabled, amoeba will copy any and all associated child records automatically and associate them with the new parent record.

You can configure the behavior to only include fields that you list or to only include fields that you don't exclude. Of the three, the most performant will be the indiscriminate style, followed by the inclusive style, and the exclusive style will be the slowest because of the need for an extra explicit check on each field. This performance difference is likely negligible enough that you can choose the style to use based on which is easiest to read and write, however, if your data tree is large enough and you need control over what fields get copied, inclusive style is probably a better choice than exclusive style.

Configuration

Please note that these examples are only loose approximations of real world scenarios and may not be particularly realistic, they are only for the purpose of demonstrating feature usage.

Indiscriminate Style

This is the most basic usage case and will simply enable the copying of any known associations.

If you have some models for a blog about like this:

ruby
class Post  15
  end
end

After call Post.first.amoeba_dup if likes is larger 15 than all comments will be duplicated too, but in another situation - no relations will be cloned. Same behavior will be for exclude_association.

Be aware! If you wrote:

ruby
class Post  15
  end
end

inclusion strategy will be chosen regardless of the result of popular? method call (the same for reverse situation).

Cloning

If you are using a Many-to-Many relationship, you may tell amoeba to actually make duplicates of the original related records rather than merely maintaining association with the original records. Cloning is easy, merely tell amoeba which fields to clone in the same way you tell it which fields to include or exclude.

ruby
class Post  :post_widgets

  amoeba do
    enable
    clone [:widgets, :warnings]
  end
end

class Warning  :post_widgets
end

This example will actually duplicate the warnings and widgets in the database. If there were originally 3 warnings in the database then, upon duplicating a post, you will end up with 6 warnings in the database. This is in contrast to the default behavior where your new post would merely be re-associated with any previously existing warnings and those warnings themselves would not be duplicated.

Limiting Association Types

By default, amoeba recognizes and attempts to copy any children of the following association types:

  • has one
  • has many
  • has and belongs to many

You may control which association types amoeba applies itself to by using the recognize method within the amoeba configuration block.

ruby
class Post  15
  end
end

Here date_published is always nullified, while topic_id is only nullified when popular? returns a truthy value on the post being copied. The :if option accepts a method name only.

Set

If you wish to just set a field to an arbitrary value on all duplicated objects you may use the set directive. For example, if you wanted to copy an object that has some kind of approval process associated with it, you likely may wish to set the new object's state to be open or "in progress" again.

ruby
class Post  "open_for_editing"
  end
end

In this example, when a post is duplicated, it's state_tracker field will always be given a value of open_for_editing to start.

Prepend

You may add a string to the beginning of a copied object's field during the copy phase:

ruby
class Post  "Copy of "
  end
end

Append

You may add a string to the end of a copied object's field during the copy phase:

ruby
class Post  "Copy of "
  end
end

Regex

You may run a search and replace query on a copied object's field during the copy phase:

ruby
class Post  {:replace => /dog/, :with => 'cat'}
  end
end

Custom Methods

Customize

You may run a custom method or methods to do basically anything you like, simply pass a lambda block, or an array of lambda blocks to the customize directive. Each block must have the same form, meaning that each block must accept two parameters, the original object and the newly copied object. You may then do whatever you wish, like this:

ruby
class Post  "Hello world! "

    customize(lambda { |original_post,new_post|
      if original_post.foo == "bar"
        new_post.baz = "qux"
      end
    })

    append :comments => "... know what I'm sayin?"
  end
end

or this, using an array:

ruby
class Post  "Hello world! "

    override(lambda { |original_post,new_post|
      if original_post.foo == "bar"
        new_post.baz = "qux"
      end
    })

    append :comments => "... know what I'm sayin?"
  end
end

Chaining

You may apply a single preprocessor to multiple fields at once.

ruby
class Post  "Copy of ", :contents => "Copied contents: "
  end
end

Stacking

You may apply multiple preprocessing directives to a single model at once.

ruby
class Post  "Copy of ", :contents => "Original contents: "
    append :contents => " (copied version)"
    regex :contents => {:replace => /dog/, :with => 'cat'}
  end
end

This example should result in something like this:

ruby
post = Post.create(
  :title => "Hello world",
  :contents =>  "I like dogs, dogs are awesome."
)

new_post = post.amoeba_dup

new_post.title # "Copy of Hello world"
new_post.contents # "Original contents: I like cats, cats are awesome. (copied version)"

Like nullify, the preprocessing directives do not automatically enable the copying of associated child records. If only preprocessing directives are used and you do want to copy child records and no include_association or exclude_association list is provided, you must still explicitly enable the copying of child records by calling the enable method from within the amoeba block on your model.

Precedence

You may use a combination of configuration methods within each model's amoeba block. Recognized association types take precedence over inclusion or exclusion lists. Inclusive style takes precedence over exclusive style, and these two explicit styles take precedence over the indiscriminate style. In other words, if you list fields to copy, amoeba will only copy the fields you list, or only copy the fields you don't exclude as the case may be. Additionally, if a field type is not recognized it will not be copied, regardless of whether it appears in an inclusion list. If you want amoeba to automatically copy all of your child records, do not list any fields using either include_association or exclude_association.

The following example syntax is perfectly valid, and will result in the usage of inclusive style. The order in which you call the configuration methods within the amoeba block does not matter:

ruby
class Topic  "Copy of "
    append :contents => " (copied version)"
    regex :contents => {:replace => /dog/, :with => 'cat'}
    include_association :authors
    enable
    nullify :topic_id
  end
end

class Comment  :account

  amoeba do
    enable
  end
end

class Account  :manifests

  amoeba do
    enable
  end
end

class Manifest  "Copy of "
  end
end

class Part  :manifests

  amoeba do
    enable
  end
end

On The Fly Configuration

You may control how amoeba copies your object, on the fly, by passing a configuration block to the model's amoeba method. The configuration method is static but the configuration is applied on a per instance basis.

ruby
class Post  "Copy of "
  end
end

class Comment  "Hello world",
      :contents => "Lorum ipsum"
    )

    old_post.class.amoeba do
      prepend :contents => "Here's a copy: "
    end

    new_post = old_post.amoeba_dup

    new_post.title # should be "Copy of Hello world"
    new_post.contents # should be "Here's a copy: Lorum ipsum"
    new_post.save
  end
end

Inheritance

If you are using the Single Table Inheritance provided by ActiveRecord, you may cause amoeba to automatically process child classes in the same way as their parents. All you need to do is call the propagate method within the amoeba block of the parent class and all child classes should copy in a similar manner.

ruby
create_table :products, :force => true do |t|
  t.string :type # this is the STI column

  # these belong to all products
  t.string :title
  t.decimal :price

  # these are for shirts only
  t.decimal :sleeve_length
  t.decimal :collar_size

  # these are for computers only
  t.integer :ram_size
  t.integer :hard_drive_size
end

class Product  "Copy of "
end

In this example, the conflicting include_association settings on the child will be ignored and the parent exclude_association setting will be used, while the prepend setting on the child will be honored because it doesn't conflict with the parent.

Strict Parenting

The :strict style will ignore child settings altogether and inherit any parent settings.

ruby
class Product  "Copy of "
end

In this example, the only processing that will happen when a Shirt is duplicated is whatever processing is allowed by the parent. So in this case the parent's exclude_association directive takes precedence over the child's include_association settings, and not only that, but none of the other settings for the

Issues· 41 open

View all issuesOpen on GitHub

No open issues yet, or sync has not completed.

> Tags

Ruby

No comments yet. Be the first to share.

> Details

PublishedAug 1, 2026
UpdatedSep 17, 2026
Category编程语言
PricingOpen source

> Related tools

T
TypeScript
JavaScript 的超集,为前端与全栈提供静态类型
P
Python
通用编程语言,广泛用于 Web、数据与 AI
G
Go
Google 推出的简洁高效系统语言