Каков наилучший способ создания копии элемента в Ruby? [Дубликат]

Методы getter и setter - это методы доступа, что означает, что они обычно представляют собой открытый интерфейс для изменения членов частного класса. Вы используете методы getter и setter для определения свойства. Вы используете методы getter и setter как свойства вне класса, даже если вы определяете их в классе как методы. Эти свойства вне класса могут иметь другое имя от имени свойства в классе.

Есть некоторые преимущества использования методов getter и setter, таких как возможность позволить вам создавать элементы со сложными функциями, которые вы может получить доступ к таким свойствам. Они также позволяют создавать свойства только для чтения и только для записи.

Несмотря на то, что методы getter и setter полезны, вы должны быть осторожны, чтобы не злоупотреблять ими, потому что, помимо прочих проблем, они могут улучшить обслуживание кода трудно в определенных ситуациях. Кроме того, они предоставляют доступ к вашей реализации класса, например, к публичным пользователям. Практика ООП препятствует прямому доступу к свойствам внутри класса.

Когда вы пишете классы, вам всегда рекомендуется делать как можно больше ваших переменных экземпляра частным и соответственно добавлять методы getter и setter. Это происходит потому, что есть несколько раз, когда вы не можете позволить пользователям изменять определенные переменные в ваших классах. Например, если у вас есть частный статический метод, который отслеживает количество экземпляров, созданных для определенного класса, вы не хотите, чтобы пользователь модифицировал этот счетчик с помощью кода. Только инструкция конструктора должна увеличивать эту переменную всякий раз, когда она вызывается. В этой ситуации вы можете создать переменную частного экземпляра и разрешить метод getter только для переменной счетчика, что означает, что пользователи могут извлекать текущее значение только с помощью метода getter, и они не смогут устанавливать новые значения используя метод setter. Создание геттера без сеттера - это простой способ сделать определенные переменные в вашем классе доступными только для чтения.

364
задан tereško 21 January 2013 в 10:27
поделиться

10 ответов

Чтобы получить копию, используйте метод clone (или dup for rails 3.1):

# rails < 3.1
new_record = old_record.clone

#rails >= 3.1
new_record = old_record.dup

Затем вы можете изменить любые поля, которые вы хотите.

ActiveRecord переопределяет встроенный объект # clone , чтобы дать вам новую запись (не сохраненную в БД) с неназначенным идентификатором. Обратите внимание, что он не копирует ассоциации, поэтому вам нужно будет сделать это вручную, если вам нужно.

Rails 3.1 клон - это мелкая копия, вместо этого используйте dup ...

554
ответ дан waldo 26 August 2018 в 22:20
поделиться

Используйте ActiveRecord :: Base # dup , если вы не хотите копировать id

28
ответ дан Bryan Ash 26 August 2018 в 22:20
поделиться

Вы также можете проверить act_as_inheritable gem.

«Acts As Inheritable - это Ruby Gem, специально написанный для моделей Rails / ActiveRecord. Он предназначен для использования с Self-Referential Association или с моделью, имеющей родителя, который разделяет наследуемые атрибуты. Это позволит вам наследовать любой атрибут или отношение из родительской модели. "

Добавив acts_as_inheritable к вашим моделям вы получите доступ к этим методам:

inherit_attributes

class Person < ActiveRecord::Base

  acts_as_inheritable attributes: %w(favorite_color last_name soccer_team)

  # Associations
  belongs_to  :parent, class_name: 'Person'
  has_many    :children, class_name: 'Person', foreign_key: :parent_id
end

parent = Person.create(last_name: 'Arango', soccer_team: 'Verdolaga', favorite_color:'Green')

son = Person.create(parent: parent)
son.inherit_attributes
son.last_name # => Arango
son.soccer_team # => Verdolaga
son.favorite_color # => Green

inherit_relations

class Person < ActiveRecord::Base

  acts_as_inheritable associations: %w(pet)

  # Associations
  has_one     :pet
end

parent = Person.create(last_name: 'Arango')
parent_pet = Pet.create(person: parent, name: 'Mango', breed:'Golden Retriver')
parent_pet.inspect #=> #<Pet id: 1, person_id: 1, name: "Mango", breed: "Golden Retriver">

son = Person.create(parent: parent)
son.inherit_relations
son.pet.inspect # => #<Pet id: 2, person_id: 2, name: "Mango", breed: "Golden Retriver">

Надеюсь, это может вам помочь.

0
ответ дан esbanarango 26 August 2018 в 22:20
поделиться

Я обычно просто копирую атрибуты, меняя все, что мне нужно, меняя:

new_user = User.new(old_user.attributes.merge(:login => "newlogin"))
20
ответ дан Luís Ramalho 26 August 2018 в 22:20
поделиться

Поскольку при дублировании модели может быть больше логики, я бы предложил создать новый класс, где вы будете обрабатывать всю необходимую логику. Чтобы облегчить это, есть камень, который может помочь: clowne

В соответствии с их примерами документации для модели пользователя:

class User < ActiveRecord::Base
  # create_table :users do |t|
  #  t.string :login
  #  t.string :email
  #  t.timestamps null: false
  # end

  has_one :profile
  has_many :posts
end

Вы создаете свой cloner class:

class UserCloner < Clowne::Cloner
  adapter :active_record

  include_association :profile, clone_with: SpecialProfileCloner
  include_association :posts

  nullify :login

  # params here is an arbitrary Hash passed into cloner
  finalize do |_source, record, params|
    record.email = params[:email]
  end
end

class SpecialProfileCloner < Clowne::Cloner
  adapter :active_record

  nullify :name
end

, а затем использовать его:

user = User.last
#=> <#User(login: 'clown', email: 'clown@circus.example.com')>

cloned = UserCloner.call(user, email: 'fake@example.com')
cloned.persisted?
# => false

cloned.save!
cloned.login
# => nil
cloned.email
# => "fake@example.com"

# associations:
cloned.posts.count == user.posts.count
# => true
cloned.profile.name
# => nil

Пример, скопированный из проекта, но он даст четкое представление о том, чего вы можете достичь.

Для быстрой и простой записи я бы пошел с:

Model.new(Model.last.attributes.reject {|k,_v| k.to_s == 'id'}

0
ответ дан Paulo Fidalgo 26 August 2018 в 22:20
поделиться

В зависимости от ваших потребностей и стиля программирования вы также можете использовать комбинацию нового метода класса и слияния. Из-за отсутствия лучшего примера предположим, что у вас есть задание, запланированное на определенную дату, и вы хотите дублировать его на другую дату. Фактические атрибуты задачи не важны, поэтому:

old_task = Task.find(task_id)
new_task = Task.new(old_task.attributes.merge({:scheduled_on => some_new_date}))

создаст новую задачу с :id => nil, :scheduled_on => some_new_date и всеми другими атрибутами, аналогичными исходной задаче. Используя Task.new, вам нужно будет явно вызвать save, поэтому, если вы хотите, чтобы он был сохранен автоматически, измените Task.new на Task.create.

Мир.

65
ответ дан Phillip Koebbe 26 August 2018 в 22:20
поделиться

Если вам нужна глубокая копия с ассоциациями, я рекомендую deep_cloneable gem.

8
ответ дан raidfive 26 August 2018 в 22:20
поделиться

Легкий способ:

#your rails >= 3.1 (i was done it with Rails 5.0.0.1)
  o = Model.find(id)
 # (Range).each do |item|
 (1..109).each do |item|
   new_record = o.dup
   new_record.save
 end

Или

# if your rails < 3.1
 o = Model.find(id)
 (1..109).each do |item|
   new_record = o.clone
   new_record.save
 end     
2
ответ дан ThienSuBS 26 August 2018 в 22:20
поделиться

Вам также может понравиться Amoeba gem для ActiveRecord 3.2.

В вашем случае вы, вероятно, захотите использовать nullify, regex или prefix, доступные в конфигурации DSL.

Он поддерживает простое и автоматическое рекурсивное дублирование ассоциаций has_one, has_many и has_and_belongs_to_many, предварительную обработку полей и гибкую и мощную конфигурацию DSL, которая может быть применена

обязательно проверьте Amoeba Documentation , но использование довольно просто ...

просто

]
gem install amoeba

или добавить

gem 'amoeba'

к вашему Gemfile

, затем добавить блок амебы к вашей модели и запустить метод dup, как обычно

class Post < ActiveRecord::Base
  has_many :comments
  has_and_belongs_to_many :tags

  amoeba do
    enable
  end
end

class Comment < ActiveRecord::Base
  belongs_to :post
end

class Tag < ActiveRecord::Base
  has_and_belongs_to_many :posts
end

class PostsController < ActionController
  def some_method
    my_post = Post.find(params[:id])
    new_post = my_post.dup
    new_post.save
  end
end

Вы также можете контролировать, какие поля копируются разными способами, но, например, если вы хотите, чтобы комментарии не дублировались, но вы хотели поддерживать те же теги, вы могли бы сделать что-то вроде этого:

class Post < ActiveRecord::Base
  has_many :comments
  has_and_belongs_to_many :tags

  amoeba do
    exclude_field :comments
  end
end

Вы также можете создавать поля предварительной обработки, чтобы указать уникальность как с префиксами, так и с суффиксами, поскольку мы в качестве регулярных выражений. Кроме того, есть множество вариантов, поэтому вы можете писать в наиболее читаемом стиле для своей цели:

class Post < ActiveRecord::Base
  has_many :comments
  has_and_belongs_to_many :tags

  amoeba do
    include_field :tags
    prepend :title => "Copy of "
    append :contents => " (copied version)"
    regex :contents => {:replace => /dog/, :with => "cat"}
  end
end

Рекурсивное копирование ассоциаций легко, просто включите амебу на дочерние модели, а также

class Post < ActiveRecord::Base
  has_many :comments

  amoeba do
    enable
  end
end

class Comment < ActiveRecord::Base
  belongs_to :post
  has_many :ratings

  amoeba do
    enable
  end
end

class Rating < ActiveRecord::Base
  belongs_to :comment
end

У конфигурации DSL есть еще больше возможностей, поэтому обязательно проверьте документацию.

Наслаждайтесь! :)

31
ответ дан Vaughn Draughon 26 August 2018 в 22:20
поделиться

Вот пример переопределения метода ActiveRecord #dup для настройки дублирования экземпляра и включения дублирования ссылок:

class Offer < ApplicationRecord
  has_many :offer_items

  def dup
    super.tap do |new_offer|

      # change title of the new instance
      new_offer.title = "Copy of #{@offer.title}"

      # duplicate offer_items as well
      self.offer_items.each { |offer_item| new_offer.offer_items << offer_item.dup }
    end
  end
end

Примечание: этот метод не требует внешнего камня, но требует более новых Версия ActiveRecord с реализованным методом #dup

0
ответ дан Zoran Majstorovic 26 August 2018 в 22:20
поделиться
Другие вопросы по тегам:

Похожие вопросы: