Простое клонирование объектов active_record, включая ассоциации и несколько операций над ассоциациями и атрибутами.
Смотрите здесь.
Цель заключалась в том, чтобы иметь возможность легко и быстро воспроизводить объекты ActiveRecord, включая их дочерние элементы, например, копируя сообщение в блоге, сохраняя связанные с ним теги или категории.
Этот драгоценный камень назван «Амеба», потому что амебы (маленькие формы жизни) хорошо размножаются. Их дети и внуки также быстро и легко размножаются.
Гем расширения ActiveRecord, позволяющий дублировать связанные объекты дочерних записей при дублировании модели активной записи.
Совместимость с Rails 5.2, 6.0, 6.1. Для Rails 4.2–5.1 используйте версию 3.x.
Поддерживает следующие типы ассоциаций
has_many
has_one :through
has_many :through
has_and_belongs_to_many
Простой DSL для настройки копируемых полей. DSL можно применить к вашим моделям рельсов или использовать «на лету».
Поддерживает дочерние элементы STI (наследование одной таблицы), наследующие настройки родительской амебы.
Несколько стилей конфигурации, таких как инклюзивный, эксклюзивный и неизбирательный (то есть копирование всего).
Поддерживает клонирование дочерних записей «Многие ко многим» или просто сохранение исходных ассоциаций.
Поддерживает автоматическую детализацию, т. е. рекурсивное копирование дочерних и внучатых записей.
Поддерживает предварительную обработку полей, чтобы указать уникальность и обеспечить целостность ваших данных в зависимости от потребностей вашей бизнес-логики, например добавление «Копии» или аналогичного текста.
Поддерживает предварительную обработку полей с помощью пользовательских лямбда-блоков, поэтому вы можете делать практически все, что захотите, если, например, вам нужна какая-то специальная логика при создании копий.
Amoeba может выполнять следующие операции предварительной обработки над полями скопированных записей.
набор
добавить в начало
добавить
аннулировать
настроить
регулярное выражение
надеюсь, что это так, как вы ожидаете:
установка драгоценного камня амеба
или просто добавьте его в свой Gemfile:
драгоценный камень «амеба»
Настройте свои модели с помощью одного из приведенных ниже стилей, а затем просто запустите метод amoeba_dup
на своей модели там, где вы обычно запускаете метод dup
:
p = Post.create(:title => "Hello World!", :content => "Lorum ipsum dolor")p.comments.create(:content => "Мне это нравится!")p.comments.create(: content => «Это отстой!»)puts Comment.all.count # должно быть 2my_copy = p.amoeba_dupmy_copy.saveputs Comment.all.count # должно быть 4
По умолчанию, если эта функция включена, amoeba автоматически копирует все связанные дочерние записи и связывает их с новой родительской записью.
Вы можете настроить поведение так, чтобы оно включало только те поля, которые вы перечислили, или только те поля, которые вы не исключаете. Из этих трех наиболее эффективным будет неизбирательный стиль, за которым следует включающий стиль, а эксклюзивный стиль будет самым медленным из-за необходимости дополнительной явной проверки каждого поля. Эта разница в производительности, вероятно, достаточно незначительна, поэтому вы можете выбрать стиль, который будет легче всего читать и писать, однако, если ваше дерево данных достаточно велико и вам нужен контроль над тем, какие поля копируются, инклюзивный стиль, вероятно, будет лучше. выбор, чем эксклюзивный стиль.
Обратите внимание, что эти примеры представляют собой лишь приблизительное приближение к сценариям реального мира и могут быть не особенно реалистичными. Они предназначены только для демонстрации использования функций.
Это самый простой вариант использования, который просто позволяет копировать любые известные ассоциации.
Если у вас есть модели для такого блога:
класс Post < ActiveRecord::Base has_many :commentsendclass Комментарий < ActiveRecord::Base принадлежит_to :postend
просто добавьте блок конфигурации amoeba в свою модель и вызовите метод Enable, чтобы включить копирование дочерних записей, например:
класс Post < ActiveRecord::Base has_many :комментарии амеба выполнима Комментарий endendclass < ActiveRecord::Base принадлежит_to :postend
Дочерние записи будут автоматически скопированы при запуске метода amoeba_dup
.
Если вы хотите скопировать только некоторые ассоциации, но не другие, вы можете использовать инклюзивный стиль:
класс Post < ActiveRecord::Base has_many :комментарии has_many :теги has_many: авторы амеба doenableinclude_association :tagsinclude_association :authors Комментарий endendclass < ActiveRecord::Base принадлежит_to :postend
Использование инклюзивного стиля в блоке амебы на самом деле подразумевает, что вы хотите включить амебу, поэтому нет необходимости запускать метод включения, хотя это тоже не повредит:
класс Post < ActiveRecord::Base has_many :комментарии has_many :теги has_many: авторы амеба doinclude_association :tagsinclude_association :authors Комментарий endendclass < ActiveRecord::Base принадлежит_to :postend
Вы также можете указать поля для копирования, передав массив. Если вы вызываете include_association
с одним значением, оно будет добавлено к списку уже включенных полей. Если вы передадите массив, ваш массив перезапишет исходные значения.
класс Post < ActiveRecord::Base has_many :комментарии has_many :теги has_many: авторы амеба doinclude_association [:теги, :авторы] Комментарий endendclass < ActiveRecord::Base принадлежит_to :postend
В этих примерах будут копироваться теги и авторы публикации, но не ее комментарии.
Включающий стиль при использовании автоматически отключит любой другой стиль, который был выбран ранее.
Если у вас больше полей, которые нужно включить, чем исключить, возможно, вы захотите сократить объем ввода и чтения, который вам нужно выполнить, используя эксклюзивный стиль. Будут скопированы все поля, которые не исключены явно:
класс Post < ActiveRecord::Base has_many :комментарии has_many :теги has_many: авторы амеба doexclude_association :комментарии Комментарий endendclass < ActiveRecord::Base принадлежит_to :postend
Этот пример делает то же самое, что и пример инклюзивного стиля: он копирует теги и авторов сообщения, но не его комментарии. Как и в случае с инклюзивным стилем, нет необходимости явно включать амебу при указании исключаемых полей.
Эксклюзивный стиль при использовании автоматически отключит любой другой стиль, который был выбран ранее, поэтому, если вы выбрали включаемые поля, а затем выбрали некоторые исключаемые поля, метод exclude_association
отключит ранее выбранный включающий стиль и уничтожит все соответствующие включаемые поля. .
Также, если вам нужно указать дополнительное условие для связи включения или исключения, вы можете указать имя метода в опции :if
.
класс Post < ActiveRecord::Base has_many :комментарии has_many :теги амеба doinclude_association :комментарии, если: :популярно? конец определенно популярный?лайки > 15 положить конец
После вызова Post.first.amoeba_dup
если likes
больше 15, чем все комментарии, тоже будут дублироваться, но в другой ситуации - никакие отношения клонироваться не будут. Такое же поведение будет и для exclude_association
.
Будьте в курсе ! Если вы написали:
класс Post < ActiveRecord::Base has_many :комментарии has_many :теги амеба doexclude_association :tagsinclude_association :комментарии, если: :popular? конец определенно популярный?лайки > 15 положить конец
стратегия включения будет выбрана независимо от результата popular?
вызов метода (то же самое и для обратной ситуации).
Если вы используете связь «многие-ко-многим», вы можете указать амебе, чтобы она фактически создавала дубликаты исходных связанных записей, а не просто поддерживала связь с исходными записями. Клонировать легко: просто сообщите амебе, какие поля клонировать, точно так же, как вы сообщаете ей, какие поля включить или исключить.
класс Post < ActiveRecord::Base has_and_belongs_to_many :предупреждения has_many :post_widgets has_many :widgets, :through => :post_widgets амеба doenableclone [:виджеты, :предупреждения] Предупреждение endendclass < ActiveRecord::Base has_and_belongs_to_many :postsendclass PostWidget < ActiveRecord::Base принадлежит_to :виджет принадлежит_to :postendclass Виджет < ActiveRecord::Base has_many :post_widgets has_many :posts, :through => :post_widgetsend
Этот пример фактически дублирует предупреждения и виджеты в базе данных. Если изначально в базе было 3 предупреждения, то при дублировании поста у вас в базе окажется 6 предупреждений. Это отличается от поведения по умолчанию, когда ваше новое сообщение будет просто повторно связано с любыми ранее существовавшими предупреждениями, а сами эти предупреждения не будут дублироваться.
По умолчанию амеба распознает и пытается скопировать любых дочерних элементов следующих типов ассоциаций:
есть один
имеет много
имеет и принадлежит многим
Вы можете контролировать, к каким типам ассоциаций применяется amoeba, используя метод recognize
в блоке конфигурации amoeba.
класс Post < ActiveRecord::Base has_one :конфигурация has_many :комментарии has_and_belongs_to_many :tags амеба распознает [:has_one, :has_and_belongs_to_many] Комментарий endendclass < ActiveRecord::Base принадлежит_to :postendclass Тег < ActiveRecord::Base has_and_belongs_to_many :postsend
В этом примере копируются данные конфигурации сообщения и сохраняются теги, связанные с новым сообщением, но не копируются комментарии сообщения, поскольку amoeba распознает и копирует только дочерние элементы ассоциаций has_one
и has_and_belongs_to_many
, а в этом примере комментарии не являются ассоциацией has_and_belongs_to_many
.
Если вы хотите, чтобы обычное поле (не основанное на ассоциации has_*
) не сохраняло свое значение при копировании, вы можете «обнулить» или «обнулить» поле, например:
класс Topic < ActiveRecord::Base has_many :postsendclass Post < ActiveRecord::Base принадлежит_к :тема has_many :комментарии амеба doenablenullify :date_publishednullify :topic_id Комментарий endendclass < ActiveRecord::Base принадлежит_to :postend
В этом примере будут скопированы все комментарии к сообщению. Это также аннулирует дату публикации и отделит сообщение от его исходной темы.
В отличие от инклюзивного и эксклюзивного стилей, указание пустых полей не позволит amoeba автоматически копировать все дочерние записи. Как и в случае с любым активным объектом записи, значение поля по умолчанию будет использоваться вместо nil
, если при миграции существует значение по умолчанию.
Если вы хотите просто установить для поля произвольное значение во всех дублированных объектах, вы можете использовать директиву set
. Например, если вы хотите скопировать объект, с которым связан какой-то процесс утверждения, вы, вероятно, захотите снова установить состояние нового объекта как открытое или «в работе».
класс Post < ActiveRecord::Base дозировка амебы :state_tracker => "open_for_editing" положить конец
В этом примере, когда сообщение дублируется, его полю state_tracker
всегда будет присвоено значение open_for_editing
для запуска.
Вы можете добавить строку в начало поля скопированного объекта на этапе копирования:
класс Post < ActiveRecord::Base amoeba doenableprepend :title => "Копия " положить конец
Вы можете добавить строку в конец поля скопированного объекта на этапе копирования:
класс Post < ActiveRecord::Base amoeba doenableappend :title => "Копия " положить конец
Вы можете запустить запрос на поиск и замену в поле скопированного объекта на этапе копирования:
класс Post < ActiveRecord::Base амеба doenableregex :contents => {:replace => /dog/, :with => 'cat'} положить конец
Вы можете запустить собственный метод или методы, чтобы делать практически все, что захотите, просто передать лямбда-блок или массив лямбда-блоков в директиву customize
. Каждый блок должен иметь одинаковую форму, то есть каждый блок должен принимать два параметра: исходный объект и вновь скопированный объект. Затем вы можете делать все, что пожелаете, например:
класс Post < ActiveRecord::Base amoeba doprepend :title => "Привет, мир!" customize(lambda { |original_post,new_post| if original_post.foo == "bar"new_post.baz = "qux" end})append :comments => "... знаю что Я говорю?" положить конец
или это, используя массив:
класс Post < ActiveRecord::Base has_and_belongs_to_many :tags amoeba doinclude_association :tagscustomize([лямбда do |orig_obj,copy_of_obj|# хорошие вещи идут здесь конец, лямбда do |orig_obj,copy_of_obj|# еще хорошие вещи идут здесь конец]) положить конец
Блоки Lambda, передаваемые для настройки, выполняются по умолчанию после копирования и предварительной обработки полей. Если вы хотите запустить метод перед какой-либо настройкой или предварительной обработкой поля, вы можете использовать override
двоюродного брата customize
. Использование такое же, как указано выше.
класс Post < ActiveRecord::Base amoeba doprepend :title => "Привет, мир!" override(lambda { |original_post,new_post| if original_post.foo == "bar"new_post.baz = "qux" end})append :comments => "... знаю что Я говорю?" положить конец
Вы можете применить один препроцессор к нескольким полям одновременно.
класс Post < ActiveRecord::Base amoeba doenableprepend :title => "Копия ", :contents => "Скопированное содержимое: " положить конец
Вы можете одновременно применить несколько директив предварительной обработки к одной модели.
класс Post < ActiveRecord::Base amoeba doprepend :title => "Копия ", :contents => "Исходное содержимое: "append :contents => " (скопированная версия)"regex :contents => {:replace => /dog/, :with => ' кот'} положить конец
Этот пример должен привести к чему-то вроде этого:
сообщение = Сообщение.создать( :title => "Привет, мир", :contents => "Я люблю собак, собаки потрясающие.")new_post = post.amoeba_dupnew_post.title # "Копия Hello world"new_post.contents # "Исходное содержание: Я люблю кошек, кошки потрясающие. (копированная версия)"
Как и nullify
, директивы предварительной обработки не включают автоматическое копирование связанных дочерних записей. Если используются только директивы предварительной обработки и вы хотите скопировать дочерние записи, а список include_association
или exclude_association
не указан, вы все равно должны явно включить копирование дочерних записей, вызвав метод Enable из блока amoeba вашей модели.
Вы можете использовать комбинацию методов конфигурации в блоке амебы каждой модели. Распознанные типы ассоциаций имеют приоритет над списками включения или исключения. Инклюзивный стиль имеет приоритет над эксклюзивным стилем, и эти два явных стиля имеют приоритет над неизбирательным стилем. Другими словами, если вы перечислите поля для копирования, amoeba скопирует только те поля, которые вы перечислите, или только те поля, которые вы не исключаете, в зависимости от обстоятельств. Кроме того, если тип поля не распознан, он не будет скопирован, независимо от того, отображается ли он в списке включения. Если вы хотите, чтобы amoeba автоматически копировала все ваши дочерние записи, не указывайте какие-либо поля, используя include_association
или exclude_association
.
Следующий пример синтаксиса совершенно допустим и приведет к использованию инклюзивного стиля. Порядок вызова методов конфигурации внутри блока amoeba не имеет значения:
класс Topic < ActiveRecord::Base has_many :postsendclass Post < ActiveRecord::Base принадлежит_к :тема has_many :комментарии has_many :теги has_many: авторы amoeba doexclude_association :authorsinclude_association :tagsnullify :date_publishedprepend :title => "Копия регулярного выражения "append :contents => " (скопированная версия)" :contents => {:replace => /dog/, :with => 'cat'}include_association :authorsenablenullify :topic_id Комментарий endendclass < ActiveRecord::Base принадлежит_to :postend
В этом примере будут скопированы все теги и авторы публикации, но не ее комментарии. Это также аннулирует дату публикации и отделит сообщение от его исходной темы. Он также будет предварительно обрабатывать поля сообщения, как и в предыдущем примере предварительной обработки.
Обратите внимание, что из-за приоритета используется включающий стиль, и список исключенных полей никогда не проверяется. Кроме того, метод enable
является избыточным, поскольку amoeba автоматически включается при использовании include_association
.
Директивы предварительной обработки запускаются после копирования дочерних записей и выполняются в указанном порядке.
Нулевые поля
Добавляет
Добавляет
Поиск и замена
Директивы предварительной обработки не влияют на списки включения и исключения.
Вы можете заставить амебу копировать цепочку так далеко, как вам хочется, просто добавьте блоки амебы к каждой модели, которую вы хотите копировать ее дочерние элементы. Amoeba автоматически вернется ко всем включенным внукам и также скопирует их.
класс Post < ActiveRecord::Base has_many :комментарии амеба выполнима Комментарий endendclass < ActiveRecord::Base принадлежит_кому: сообщение has_many :рейтинги амеба выполнима Рейтинг endendclass < ActiveRecord::Base принадлежит_to :commentend
В этом примере, когда сообщение копируется, amoeba скопирует все комментарии к сообщению, а также скопирует оценки каждого комментария.
Использовать ассоциацию has_one :through
просто, просто не забудьте включить amoeba для каждой модели с ассоциацией has_one
, и amoeba будет автоматически и рекурсивно детализироваться, например:
поставщик класса < ActiveRecord::Base has_one :аккаунт has_one :история, :через => :аккаунт амеба выполнима Учетная запись endendclass < ActiveRecord::Base принадлежит_то :поставщик has_one :история амеба выполнима История endendclass < ActiveRecord::Base принадлежит_to :accountend
Копирование ассоциаций has_many :through
работает автоматически. Они выполняют копирование так же, как ассоциация has_and_belongs_to_many
, то есть фактические дочерние записи не копируются, а ассоциации просто сохраняются. Если хотите, вы можете добавить несколько препроцессоров полей в среднюю модель, но это не является строго необходимым:
класс Ассамблеи < ActiveRecord::Base has_many: манифесты has_many :parts, :through => :manifests амеба выполнима Манифест endendclass < ActiveRecord::Base принадлежит_to: сборка принадлежит_то: часть amoeba doprepend :notes => "Копия " endendclass Part < ActiveRecord::Base has_many: манифесты has_many :assemblies, :through => :manifests амеба выполнима положить конец
Вы можете контролировать, как amoeba копирует ваш объект на лету, передав блок конфигурации методу amoeba модели. Метод настройки является статическим, но конфигурация применяется для каждого экземпляра отдельно.
класс Post < ActiveRecord::Base has_many :комментарии amoeba doenableprepend :title => "Копия " Комментарий endendclass < ActiveRecord::Base принадлежит_to :postendclass PostsController < ActionController def дубликат_a_postold_post = Post.create( :title => "Hello world", :contents => "Lorum ipsum") old_post.class.amoeba do prepend :contents => "Вот копия: "endnew_post = old_post.amoeba_dupnew_post.title # должно быть "Копия Hello world"new_post.contents # должно быть "Вот копия: Лорум ipsum"new_post.save положить конец
Если вы используете наследование одной таблицы, предоставляемое ActiveRecord, вы можете заставить amoeba автоматически обрабатывать дочерние классы так же, как и их родители. Все, что вам нужно сделать, это вызвать метод propagate
в блоке амебы родительского класса, и все дочерние классы должны скопировать аналогичным образом.
create_table :products, :force => true do |t| t.string :type # это столбец STI # они относятся ко всем продуктам t.строка :заголовок t.decimal: цена # это только для рубашек t.decimal: длина_рукава t.decimal :collar_size # это только для компьютеров t.integer:ram_size t.integer :hard_drive_sizeendclass Product < ActiveRecord::Base has_many: изображения has_and_belongs_to_many :categories амеба endendclass Рубашка < Productendclass Computer < Productendclass ProductsController def some_methodmy_shirt = Shirt.find(1)my_shirt.amoeba_dupmy_shirt.save# теперь эта рубашка должна:# - иметь собственную копию всех родительских изображений# - находиться в тех же категориях, что и родительская положить конец
В этом примере должны дублироваться все изображения и разделы, связанные с этой Рубашкой, которая является дочерней по отношению к Продукту.
По умолчанию при распространении используется подчиненное родительское воспитание, что означает, что будут применены настройки конфигурации родительского элемента, но любые дочерние параметры, если они присутствуют, будут либо добавляться, либо перезаписывать родительские настройки в зависимости от того, как вы вызываете методы DSL.
Вы можете изменить это поведение, так называемый «родительский стиль», чтобы отдать предпочтение родительским настройкам или игнорировать любые дочерние настройки.
Стиль воспитания :relaxed
отдает предпочтение родительским настройкам.
класс Product < ActiveRecord::Base has_many: изображения has_and_belongs_to_many :sections амеба doexclude_association :imagespropagate :релаксированный endendclass Рубашка < Товар include_association: изображения include_association: разделы prepend :title => "Копия "end"
В этом примере конфликтующие настройки include_association
для дочернего элемента будут игнорироваться и будет использоваться родительский параметр exclude_association
.